正确枚举 Winlogon 桌面窗口层次

正确枚举 Winlogon 桌面窗口层次桌面的切换检测 最简单的就是通过一个循环不断去获取当前活动桌面 ActiveDeskto 主要通过 OpenInputDes 来获取 然后分析桌面名称 当桌面切换到 Winlogon 桌面时

大家好,欢迎来到IT知识分享网。

目录

前言

原理解释

原理实现

 Winlogon 桌面窗口层次


本文出处链接:正确枚举 Winlogon 桌面窗口层次。

前言

众所周知,从 Windows 7 开始,Winlogon 桌面不再使用 SASWindow 作为背景窗口,而是采用了一套新的安全桌面模式。我发现 CSDN 上在这方面的研究很少。

在我前面的几篇文章里面已经详细分析并实现了,编程化拦截与 Winlogon 有关的登陆事件(如 Ctrl + Alt + Delete 快捷键)。在之前,我浅谈过一些有关 LogonUI Interface 登陆 UI 界面的内容,但并未做详细的解释。这篇文章将就如何动态枚举 Winlogon 桌面的窗口层次并进行记录进行详细的讲解。如有错误,敬请点拨。

专栏文章:

正确枚举 Winlogon 桌面窗口层次

原理解释

首先,我们需要知道 Winlogon 桌面(登陆桌面,运行在控制台会话)和 Default 桌面(用户默认桌面,运行在 UI 会话)均是由 Winlogon.exe 所创建的。

Winlogon.exe 是 Windows 操作系统中负责处理用户登录和注销等会话管理功能的进程。通常情况下,每个控制台会话(即本地会话)会有一个对应的 Winlogon.exe 实例,用于处理用户认证等任务。

UI 会话 是指用户交互的会话,通常指的是图形用户界面(GUI)环境下的桌面会话。每个用户登录到系统时,Windows 会为该用户分配一个会话 ID,并启动一个桌面环境来处理用户的输入和输出。

在多用户环境中,每个用户的 UI 会话可以与一个特定的控制台会话关联。控制台会话是指直接连接到物理屏幕、键盘和鼠标的会话(如通过直接本地登录),而其他 UI 会话则可能是通过远程桌面或其他方式登录的。

所以,用户一般接触最多的就是交互式用户会话和桌面,对于 Winlogon 桌面可能知之甚少。其实用户所熟知的 UAC 对话框,就是运行在 Winlogon 桌面下的系统程序所创建的窗口。

正确枚举 Winlogon 桌面窗口层次
UAC 对话框界面

LogonUI.exe 进程:这是登陆用户交互式界面进程,Winlogon.exe 进程会在需要在登陆桌面下显示交互式界面时,启动该进程。并通过进程间通信(IPC)技术,如管道、RPC 等完成多进程同步事务。用户看到的登陆界面、CAD 界面、UAC 界面均最终由它启动。

LogonUI 运行时模块:LogonUI.exe 实质上是一个加载容器,用于装载运行时需要的接口模块。第一个加载的模块为:LogonUIController.dll。其他运行时模块通过延迟加载技术完成加载。

窗口工作站:窗口工作站是与进程关联的安全对象,包含一个或多个绑定的桌面对象、剪贴板等等。Winlogon 桌面和 Default 桌面默认运行在 Winsta0 窗口工作站下。进程必须以合适的访问权限打开窗口工作站,才能够打开指定的桌面访问句柄。进程每次获取的窗口工作站的访问句柄不同,桌面的访问句柄就不同。如果需要获知对象句柄的名称,则需要通过 WINAPI GetUserObjectInformation。

工作线程:进程连接到窗口工作站后,系统会将桌面分配给建立连接的线程。 

为了在运行时研究清楚 Winlogon 桌面有哪些窗口,就必须切换程序的工作线程然后枚举活动桌面的窗口。

正确枚举 Winlogon 桌面窗口层次
登陆界面示例

频繁切换工作线程绑定的桌面是不被允许的,因为 SetThreadDesktop 之前不能有任何桌面窗口服务正在运作(微软说会造成安全问题)。在实际测试过程当中,我们发现连续第二次切换时就会引起失败。但是我们又需要进行动态监测,所以必须要每次都能够及时切换工作线程的桌面。解决方案很简单,就是每次切换时创建一个新的线程作为工作线程,然后在新的线程里面切换桌面。

然后,打开窗口工作站和桌面需要 SYSTEM 令牌的 Local System 权限,所以必须首先以管理员身份运行,然后再从 SYSTEM 进程复制模拟令牌来重新启动高权限进程。

然后,枚举窗口这边也很简单,通过 EnumWindows 和 EnumChildWindows 枚举并保存窗口层次。

至于桌面的切换检测,最简单的就是通过一个循环不断去获取当前活动桌面(ActiveDesktop),主要通过 OpenInputDesktop 来获取,然后分析桌面名称,当桌面切换到 Winlogon 桌面时切换线程并遍历桌面窗口,当桌面切换到 Default 桌面时,停止记录并保存日志;

原理实现

下面代码在主要登录用户的管理员账户下运行一切正常:

#include <windows.h> #include <iostream> #include <fstream> #include <string> #include <vector> #include <ctime> #include <sstream> #include <codecvt> #include <functional> #include <tlhelp32.h> #include <userenv.h> #include <sddl.h> // 记录窗口信息的结构体,使用宽字符 struct WindowInfo { std::wstring className; std::wstring windowTitle; HWND hwnd; std::vector<WindowInfo> children; }; // 工作线程传递信息的结构体 struct MYTHREADINFO { HDESK hDesktop; std::vector<WindowInfo> wndInfo; }; // 启用特定的权限(例如 SeDebugPrivilege) bool EnablePrivilege(LPCWSTR privilege) { HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token." << std::endl; return false; } if (!LookupPrivilegeValueW(NULL, privilege, &luid)) { std::wcerr << L"Failed to lookup privilege." << std::endl; CloseHandle(hToken); return false; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { std::wcerr << L"Failed to adjust token privileges." << std::endl; CloseHandle(hToken); return false; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { std::wcerr << L"The privilege was not assigned." << std::endl; CloseHandle(hToken); return false; } CloseHandle(hToken); return true; } // 检查是否以管理员权限运行 bool IsRunAsAdmin() { BOOL isAdmin = FALSE; PSID administratorsGroup = NULL; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsGroup)) { CheckTokenMembership(NULL, administratorsGroup, &isAdmin); FreeSid(administratorsGroup); } return isAdmin == TRUE; } // 重新启动并请求管理员权限 bool RelaunchAsAdmin() { wchar_t szPath[MAX_PATH]; if (!GetModuleFileNameW(NULL, szPath, MAX_PATH)) { std::wcerr << L"Failed to get module file name." << std::endl; return false; } SHELLEXECUTEINFOW sei = { sizeof(sei) }; sei.lpVerb = L"runas"; // 请求管理员权限 sei.lpFile = szPath; sei.hwnd = NULL; sei.nShow = SW_NORMAL; if (!ShellExecuteExW(&sei)) { std::wcerr << L"Failed to relaunch as administrator." << std::endl; return false; } return true; } // 获取 winlogon 进程的 SYSTEM 令牌 HANDLE GetSystemTokenFromWinlogon() { HANDLE hToken = NULL; HANDLE hProcess = NULL; // 获取 Winlogon 进程的进程ID DWORD winlogonPid = 0; PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnapshot == INVALID_HANDLE_VALUE) { std::wcerr << L"Failed to create process snapshot!" << std::endl; return NULL; } if (Process32First(hProcessSnapshot, &pe32)) { do { if (_wcsicmp(pe32.szExeFile, L"winlogon.exe") == 0) { winlogonPid = pe32.th32ProcessID; break; } } while (Process32Next(hProcessSnapshot, &pe32)); } CloseHandle(hProcessSnapshot); if (winlogonPid == 0) { std::wcerr << L"Failed to find winlogon.exe process!" << std::endl; return NULL; } // 打开 Winlogon 进程 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, winlogonPid); if (!hProcess) { std::wcerr << L"Failed to open winlogon process!" << std::endl; return NULL; } // 打开该进程的令牌 if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token!" << std::endl; CloseHandle(hProcess); return NULL; } CloseHandle(hProcess); return hToken; } // 创建具有 SYSTEM 权限的进程 bool CreateSystemProcess(LPCWSTR applicationName, LPCWSTR commandLine) { HANDLE hToken = GetSystemTokenFromWinlogon(); if (!hToken) { std::wcerr << L"Failed to get SYSTEM token!" << std::endl; return false; } // 使用 SYSTEM 权限创建进程 STARTUPINFOW si = { sizeof(STARTUPINFOW) }; PROCESS_INFORMATION pi = { 0 }; if (!CreateProcessAsUserW(hToken, applicationName, const_cast<LPWSTR>(commandLine), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { std::wcerr << L"Failed to create process as SYSTEM!" << std::endl; CloseHandle(hToken); return false; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hToken); return true; } // 检查当前进程是否具有 SYSTEM 权限 bool IsSystem() { HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { return false; } DWORD tokenInfoLength = 0; GetTokenInformation(hToken, TokenUser, NULL, 0, &tokenInfoLength); PTOKEN_USER tokenUser = (PTOKEN_USER)malloc(tokenInfoLength); if (!GetTokenInformation(hToken, TokenUser, tokenUser, tokenInfoLength, &tokenInfoLength)) { CloseHandle(hToken); free(tokenUser); return false; } LPWSTR sidString = NULL; ConvertSidToStringSidW(tokenUser->User.Sid, &sidString); bool isSystem = (_wcsicmp(sidString, L"S-1-5-18") == 0); LocalFree(sidString); CloseHandle(hToken); free(tokenUser); return isSystem; } // 递归获取窗口层次,使用宽字符 API void EnumChildWindowsRecursive(HWND hwndParent, std::vector<WindowInfo>& windowList) { wchar_t className[256]; wchar_t windowTitle[256]; ZeroMemory(className, sizeof(className)); ZeroMemory(windowTitle, sizeof(windowTitle)); // 获取窗口类名和标题 GetClassNameW(hwndParent, className, sizeof(className) / sizeof(wchar_t)); GetWindowTextW(hwndParent, windowTitle, sizeof(windowTitle) / sizeof(wchar_t)); if (className[0] == L'\0') { wcscpy_s(className, L"(None)"); } if (windowTitle[0] == L'\0') { wcscpy_s(windowTitle, L"(None)"); } WindowInfo windowInfo = { className, windowTitle, hwndParent }; // 枚举子窗口 EnumChildWindows(hwndParent, [](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* children = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *children); return TRUE; }, reinterpret_cast<LPARAM>(&windowInfo.children)); windowList.push_back(windowInfo); } // 获取当前桌面窗口层次,使用宽字符 API std::vector<WindowInfo> GetWindowHierarchy() { std::vector<WindowInfo> windows; EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* windows = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *windows); return TRUE; }, reinterpret_cast<LPARAM>(&windows)); return windows; } // 格式化时间为宽字符格式 std::wstring FormatTime(const std::time_t& time) { wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &time); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); return std::wstring(timeBuffer); } // 转换宽字符到 UTF-8 std::string WideToUTF8(const std::wstring& wideStr) { std::wstring_convert<std::codecvt_utf8<wchar_t>> conv; return conv.to_bytes(wideStr); } // 保存窗口层次信息到文件,使用 UTF-8 编码 void SaveWindowHierarchy(const std::vector<WindowInfo>& windows, const std::time_t& changeTime, const std::wstring& filename) { std::ofstream file(WideToUTF8(filename), std::ios::app | std::ios::binary); // 使用 binary 防止换行符的意外转换 if (!file.is_open()) { std::wcerr << L"Unable to open file for writing!" << std::endl; return; } // 在文件开头写入 BOM,标识为 UTF-8 编码 static bool bomWritten = false; if (!bomWritten) { const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; // UTF-8 BOM file.write(reinterpret_cast<const char*>(bom), sizeof(bom)); bomWritten = true; } // 格式化时间为字符串 wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &changeTime); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); // 写入时间戳 file << WideToUTF8(std::wstring(timeBuffer)) << "\n"; // 递归写入窗口信息 std::function<void(const std::vector<WindowInfo>&, int)> WriteHierarchy; WriteHierarchy = [&file, &WriteHierarchy](const std::vector<WindowInfo>& windows, int indent) { for (const auto& window : windows) { file << std::string(indent, ' ') // 使用空格进行缩进 << "Class Name: " << WideToUTF8(window.className) << ", Title: " << WideToUTF8(window.windowTitle) << ", HWND: " << std::hex << window.hwnd << "\n"; if (!window.children.empty()) { WriteHierarchy(window.children, indent + 4); // 递归写入子窗口信息 } } }; WriteHierarchy(windows, 0); file << "\n"; file.close(); } // 获取当前桌面的名称 std::wstring GetDesktopName(HDESK hDesktop) { wchar_t desktopName[256]; DWORD neededLength = 0; if (!GetUserObjectInformationW(hDesktop, UOI_NAME, desktopName, sizeof(desktopName), &neededLength)) { std::wcerr << L"Failed to get desktop name." << std::endl; return L""; } return std::wstring(desktopName); } // 切换到指定桌面并枚举窗口,任务结束后恢复到原始桌面 std::vector<WindowInfo> GetWindowsFromDesktop(HDESK hDesktop) { // 获取原始桌面句柄 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到目标桌面 if (!SetThreadDesktop(hDesktop)) { std::wcerr << L"Failed to set thread desktop!" << std::endl; return {}; } // 切换后获取窗口层次 std::vector<WindowInfo> windows = GetWindowHierarchy(); 恢复到原始桌面 //if (!SetThreadDesktop(hOriginalDesktop)) { // std::wcerr << L"Failed to restore original desktop!" << std::endl; //} return windows; } // 使用辅助线程进行桌面切换和窗口枚举 DWORD WINAPI MonitorDesktopThread(LPVOID param) { _wsetlocale(LC_ALL, L"zh-CN"); MYTHREADINFO* threadInfo = static_cast<MYTHREADINFO*>(param); threadInfo->wndInfo = GetWindowsFromDesktop(threadInfo->hDesktop); return 0; } // 打开窗口工作站并切换到桌面 HDESK OpenDesktopWithWindowStation(LPCWSTR desktopName, HWINSTA hWinsta) { // 打开桌面 HDESK hDesktop = OpenDesktopW(desktopName, 0, FALSE, GENERIC_ALL); if (!hDesktop) { std::wcerr << L"Failed to open desktop! name = " << desktopName << std::endl; CloseWindowStation(hWinsta); } return hDesktop; } // 获取当前活动桌面 HDESK GetActiveDesktop() { return OpenInputDesktop(0, FALSE, GENERIC_ALL); } // 监控桌面切换 void MonitorDesktop() { // 打开窗口工作站 HWINSTA hWinsta = OpenWindowStationW(L"WinSta0", FALSE, GENERIC_READ | GENERIC_WRITE); if (!hWinsta) { std::wcerr << L"Failed to open window station!" << std::endl; return; } // 将当前线程关联到工作站 if (!SetProcessWindowStation(hWinsta)) { std::wcerr << L"Failed to set process window station!" << std::endl; CloseWindowStation(hWinsta); return; } HDESK hDefaultDesk = OpenDesktopWithWindowStation(L"Default", hWinsta); HDESK hWinlogonDesk = OpenDesktopWithWindowStation(L"Winlogon", hWinsta); if (!hDefaultDesk || !hWinlogonDesk) { std::wcerr << L"Failed to open desktops!" << std::endl; return; } std::wcout << L"Monitoring desktop changes (SYSTEM privileges detected)..." << std::endl; std::wcout << L"Desktops: Winlogon(" << std::hex << (UINT64)hWinlogonDesk << L"), Default(" << std::hex << (UINT64)hDefaultDesk << L")." << std::endl; // 桌面监控代码 std::vector<WindowInfo> windowHistory; bool monitoring = false; while (true) { // 检查指定按键是否被按下(比如 ESC 键,键码 0x1B) if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) { std::wcout << L"Escape key pressed. Exiting monitoring..." << std::endl; break; // 退出循环,结束监控 } HDESK hCurrentDesk = GetActiveDesktop(); // 获取活动桌面句柄 std::wstring desktopName = GetDesktopName(hCurrentDesk); // 获取桌面名称 //std::wcout << L"Current Desktop: " << desktopName << std::endl; if (desktopName == L"Winlogon" && !monitoring) { std::wcout << L"Switched to Winlogon desktop. Start monitoring window hierarchy..." << std::endl; monitoring = true; windowHistory.clear(); // 清空之前的记录 // 切换到 winlogon 桌面并枚举窗口 MYTHREADINFO info = { hWinlogonDesk }; HANDLE hThread = CreateThread(NULL, 0, MonitorDesktopThread, &info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } windowHistory = info.wndInfo; } else if (desktopName == L"Default" && monitoring) { std::wcout << L"Switched back to Default desktop. Stopping monitoring..." << std::endl; std::time_t currentTime = std::time(nullptr); SaveWindowHierarchy(windowHistory, currentTime, L"D:\\window_hierarchy.log"); monitoring = false; // 显示历史记录 MessageBoxW(NULL, L"The window hierarchy log has been saved. Check window_hierarchy.log for details.", L"History Saved", MB_OK | MB_ICONINFORMATION | MB_SYSTEMMODAL); } if (monitoring) { // 切换到 winlogon 桌面枚举窗口 MYTHREADINFO info = { hWinlogonDesk }; HANDLE hThread = CreateThread(NULL, 0, MonitorDesktopThread, &info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } if (!info.wndInfo.empty()) { windowHistory = info.wndInfo; } } Sleep(1000); // 每秒检查一次 } CloseDesktop(hDefaultDesk); CloseDesktop(hWinlogonDesk); } int wmain(int argc, wchar_t* argv[]) { _wsetlocale(LC_ALL, L"zh-CN"); // 检查是否为管理员权限运行 if (!IsRunAsAdmin()) { std::wcout << L"Attempting to restart with administrator privileges..." << std::endl; if (RelaunchAsAdmin()) { return 0; // 提升后进程将重新启动,当前进程结束 } else { std::wcerr << L"Failed to relaunch as administrator." << std::endl; return 1; } } // 启用 SeDebugPrivilege if (!EnablePrivilege(SE_DEBUG_NAME)) { std::wcerr << L"Failed to enable SeDebugPrivilege." << std::endl; return 1; } // 检查 SYSTEM 权限 if (!IsSystem()) { std::wcout << L"Attempting to restart with SYSTEM privileges..." << std::endl; // 检查命令行参数,避免无限递归 if (argc < 2 || _wcsicmp(argv[1], L"system") != 0) { // 重新启动自身并传递 "system" 参数 wchar_t commandLine[MAX_PATH]; swprintf(commandLine, MAX_PATH, L"%s system", argv[0]); if (CreateSystemProcess(argv[0], commandLine)) { std::wcout << L"Restarted with SYSTEM privileges." << std::endl; } else { std::wcerr << L"Failed to restart with SYSTEM privileges." << std::endl; } return 0; } else { std::wcerr << L"Already tried to elevate privileges but failed." << std::endl; return 1; } } // 如果当前进程已经是 SYSTEM 权限,则继续执行桌面监控 MonitorDesktop(); return 0; }

版本 Beta 2 修复内容:

  1. 非主用户账户缺少 SE_ASSIGNPRIMARYTOKEN_NAME 特权导致无法创建 SYSTEM 权限的进程(强制修改安全配置并在重启时生效);
  2. 多用户账户登陆下获取到错误的 Winlogon 进程句柄;
  3. 窗口关闭快捷键异常,现在改用注册热键 Ctrl + Shitf + F1 完成;
  4. 在监视窗口时,新增高亮边框效果;
  5. 日志记录完成时,显示当前日志记录的内容;
  6. 使用特权账户启动失败时,给出更多信息;

Beta 2 代码:

#include <windows.h> #include <iostream> #include <lmcons.h> #include <fstream> #include <string> #include <vector> #include <ctime> #include <sstream> #include <codecvt> #include <functional> #include <tlhelp32.h> #include <userenv.h> #include <sddl.h> #include "resource.h" #pragma comment(linker,"\"/manifestdependency:type='win32' "\ "name='Microsoft.Windows.Common-Controls' "\ "version='6.0.0.0' processorArchitecture='*' "\ "publicKeyToken='6595b64144ccf1df' language='*'\"") // 记录窗口信息的结构体,使用宽字符 struct WindowInfo { std::wstring className; std::wstring windowTitle; HWND hwnd; std::vector<WindowInfo> children; }; struct MYTHREADINFO { HDESK hDesktop; std::vector<WindowInfo> wndInfo; }; // 文件编码格式 enum class FileEncoding { ANSI, UTF8, UTF16LE, UTF16BE, UNKNOWN }; // 用于边框绘制的全局变量 HWND hBorderWnd = NULL; // 边框窗口句柄 bool borderRunning = false; // 边框是否正在显示 // 全局变量保存主窗口句柄 HWND hMainWindow = NULL; HWND GetConsoleHwnd(void) { // https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/obtain-console-window-handle #define MY_BUFSIZE 1024 // Buffer size for console window titles. HWND hwndFound; // This is what is returned to the caller. wchar_t pszNewWindowTitle[MY_BUFSIZE]; // Contains fabricated // WindowTitle. // Format a "unique" NewWindowTitle. wsprintfW(pszNewWindowTitle, L"WinlogonWindowsMonitor - %d/%d", GetTickCount(), GetCurrentProcessId()); // Change current window title. SetConsoleTitleW(pszNewWindowTitle); // Ensure window title has been updated. Sleep(40); // Look for NewWindowTitle. hwndFound = FindWindowW(L"ConsoleWindowClass", pszNewWindowTitle); if(!hwndFound) hwndFound = FindWindowW(L"CASCADIA_HOSTING_WINDOW_CLASS", pszNewWindowTitle); #undef MY_BUFSIZE return(hwndFound); } // 获取当前进程的主窗口 HWND GetMainWindowHandle() { return GetConsoleHwnd(); } // 检查当前活动窗口是否是主窗口 bool IsMainWindowActive() { HWND hForeground = GetForegroundWindow(); // 获取当前活动窗口 return hForeground == hMainWindow; // 比较是否是主窗口 } // 边框窗口过程 LRESULT CALLBACK BorderWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static int borderWidth = 10; // 默认边框宽度 switch (message) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT rect; GetClientRect(hWnd, &rect); // 设置边框颜色为蓝色 HBRUSH brush = CreateSolidBrush(RGB(119, 228, 155)); // 填充外边框到内边框之间的区域 RECT outerRect = rect; RECT innerRect = rect; // 根据边框宽度调整内边框位置 InflateRect(&innerRect, -borderWidth, -borderWidth); // 填充外边框和内边框之间的区域 if (borderWidth > 0) { // 填充四个部分:上边、下边、左边、右边 RECT topRect = { outerRect.left, outerRect.top, outerRect.right, innerRect.top }; RECT bottomRect = { outerRect.left, innerRect.bottom, outerRect.right, outerRect.bottom }; RECT leftRect = { outerRect.left, innerRect.top, innerRect.left, innerRect.bottom }; RECT rightRect = { innerRect.right, innerRect.top, outerRect.right, innerRect.bottom }; FillRect(hdc, &topRect, brush); FillRect(hdc, &bottomRect, brush); FillRect(hdc, &leftRect, brush); FillRect(hdc, &rightRect, brush); } DeleteObject(brush); EndPaint(hWnd, &ps); break; } case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProcW(hWnd, message, wParam, lParam); } return 0; } DWORD WINAPI CloseBorderThread(LPVOID lpParam) { HDESK hWinlogonDesk = static_cast<HDESK>(lpParam); // 传递的桌面句柄 // 获取当前线程的桌面句柄,后面恢复用 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到 Winlogon 桌面 if (!SetThreadDesktop(hWinlogonDesk)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } // 发送关闭消息 if (hBorderWnd) { PostMessageW(hBorderWnd, WM_DESTROY, 0, 0); } std::wcout << L"An exit monitoring message has been sent." << std::endl; // 恢复到原始桌面 SetThreadDesktop(hOriginalDesktop); return 0; } // 边框绘制线程 DWORD WINAPI BorderThread(LPVOID lpParam) { std::wcout << L"Monitoring the target desktop..." << std::endl; HDESK hWinlogonDesk = static_cast<HDESK>(lpParam); // 传递的桌面句柄 // 切换到 Winlogon 桌面 if (!SetThreadDesktop(hWinlogonDesk)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } #define MY_BUFSIZE 1024 // Buffer size for console window titles. wchar_t pszWindowClass[MY_BUFSIZE]; // Contains fabricated // WindowClass. // Format a "unique" WindowClass. wsprintfW(pszWindowClass, L"WinlogonMonitorBorderWindowClass[%d::%d]", GetTickCount(), GetCurrentProcessId()); HINSTANCE hInstance = GetModuleHandleW(NULL); WNDCLASS wc = { 0 }; wc.lpfnWndProc = BorderWndProc; wc.hInstance = hInstance; wc.lpszClassName = pszWindowClass; wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); if (!RegisterClassW(&wc)) { std::wcerr << L"Failed to RegisterClass." << std::endl; return 1; } // 获取屏幕大小并创建无边框窗口 int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); hBorderWnd = CreateWindowExW(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, L"WinlogonMonitorBorderWindow", WS_POPUP, 0, 0, screenWidth, screenHeight, NULL, NULL, hInstance, NULL); if (!hBorderWnd) { std::wcerr << L"Failed to CreateWindow." << std::endl; return 1; } // 设置窗口透明度 SetLayeredWindowAttributes(hBorderWnd, RGB(255, 255, 255), 0, LWA_COLORKEY); ShowWindow(hBorderWnd, SW_SHOW); UpdateWindow(hBorderWnd); // 消息循环,等待关闭事件 MSG msg; while (true) { // 检查并处理窗口消息 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } } // 销毁窗口 DestroyWindow(hBorderWnd); #undef MY_BUFSIZE return 0; } // 启用特定的权限(例如 SeDebugPrivilege) bool EnablePrivilege(LPCWSTR privilege) { HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token." << std::endl; return false; } if (!LookupPrivilegeValueW(NULL, privilege, &luid)) { std::wcerr << L"Failed to lookup privilege." << std::endl; CloseHandle(hToken); return false; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { std::wcerr << L"Failed to adjust token privileges." << std::endl; CloseHandle(hToken); return false; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { std::wcerr << L"The privilege was not assigned." << std::endl; CloseHandle(hToken); return false; } CloseHandle(hToken); return true; } // 检查是否以管理员权限运行 bool IsRunAsAdmin() { BOOL isAdmin = FALSE; PSID administratorsGroup = NULL; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsGroup)) { CheckTokenMembership(NULL, administratorsGroup, &isAdmin); FreeSid(administratorsGroup); } return isAdmin == TRUE; } // 重新启动并请求管理员权限 bool RelaunchAsAdmin() { wchar_t szPath[MAX_PATH]; if (!GetModuleFileNameW(NULL, szPath, MAX_PATH)) { std::wcerr << L"Failed to get module file name." << std::endl; return false; } SHELLEXECUTEINFOW sei = { sizeof(sei) }; sei.lpVerb = L"runas"; // 请求管理员权限 sei.lpFile = szPath; sei.hwnd = NULL; sei.nShow = SW_NORMAL; if (!ShellExecuteExW(&sei)) { std::wcerr << L"Failed to relaunch as administrator." << std::endl; return false; } return true; } DWORD WINAPI GetActiveConsoleSessionId() { return WTSGetActiveConsoleSessionId(); } BOOL WINAPI IsProcessInSession(DWORD processId, DWORD sessionId) { DWORD session; if (!ProcessIdToSessionId(processId, &session)) { printf("Error: ProcessIdToSessionId failed.\n"); return FALSE; } return session == sessionId; } DWORD WINAPI FindWinlogonProcessId() { DWORD dwProcessId = 0; DWORD activeSessionId = GetActiveConsoleSessionId(); if (activeSessionId == 0xFFFFFFFF) { printf("Error: Unable to retrieve active console session ID.\n"); return 0; } HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { printf("Error: CreateToolhelp32Snapshot failed.\n"); return 0; } PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(snapshot, &entry)) { printf("Error: Process32First failed.\n"); CloseHandle(snapshot); return 0; } do { if (entry.cntThreads <= 1u) continue; // 跳过僵尸进程 if (_wcsicmp(entry.szExeFile, L"winlogon.exe") == 0) { if (IsProcessInSession(entry.th32ProcessID, activeSessionId)) { dwProcessId = entry.th32ProcessID; break; } } } while (Process32Next(snapshot, &entry)); CloseHandle(snapshot); return dwProcessId; } // 获取 winlogon 进程的 SYSTEM 令牌 HANDLE GetSystemTokenFromWinlogon() { HANDLE hToken = NULL; HANDLE hProcess = NULL; HANDLE hSystemToken = NULL; // 存储复制后的 SYSTEM 令牌 // 获取 Winlogon 进程的进程ID DWORD winlogonPid = FindWinlogonProcessId(); if (winlogonPid == 0) { std::wcerr << L"Failed to find winlogon.exe process!" << std::endl; return NULL; } std::wcout << L"winlogon.exe PID: " << winlogonPid << std::endl; // 打开 Winlogon 进程 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, winlogonPid); if (!hProcess) { std::wcerr << L"Failed to open winlogon process!" << std::endl; return NULL; } // 打开该进程的令牌 if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token!" << std::endl; CloseHandle(hProcess); return NULL; } // 复制令牌以使其可用于新进程 if (!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hSystemToken)) { std::wcerr << L"Failed to duplicate SYSTEM token!" << std::endl; CloseHandle(hToken); CloseHandle(hProcess); return NULL; } // 关闭原始的令牌和进程句柄 CloseHandle(hToken); CloseHandle(hProcess); return hSystemToken; } // 创建具有 SYSTEM 权限的进程 bool CreateSystemProcess(LPCWSTR applicationName, LPCWSTR commandLine) { HANDLE hToken = GetSystemTokenFromWinlogon(); if (!hToken) { std::wcerr << L"Failed to get SYSTEM token!" << std::endl; return false; } // 使用 SYSTEM 权限创建进程 STARTUPINFOW si = { sizeof(STARTUPINFOW) }; PROCESS_INFORMATION pi = { 0 }; if (!CreateProcessAsUserW(hToken, applicationName, const_cast<LPWSTR>(commandLine), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { std::wcerr << L"Failed to create process as SYSTEM! err = " << GetLastError() << std::endl; CloseHandle(hToken); return false; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hToken); return true; } // 检查当前进程是否具有 SYSTEM 权限 bool IsSystem() { HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { return false; } DWORD tokenInfoLength = 0; GetTokenInformation(hToken, TokenUser, NULL, 0, &tokenInfoLength); PTOKEN_USER tokenUser = (PTOKEN_USER)malloc(tokenInfoLength); if (!GetTokenInformation(hToken, TokenUser, tokenUser, tokenInfoLength, &tokenInfoLength)) { CloseHandle(hToken); free(tokenUser); return false; } LPWSTR sidString = NULL; ConvertSidToStringSidW(tokenUser->User.Sid, &sidString); bool isSystem = (_wcsicmp(sidString, L"S-1-5-18") == 0); LocalFree(sidString); CloseHandle(hToken); free(tokenUser); return isSystem; } // 递归获取窗口层次,使用宽字符 API void EnumChildWindowsRecursive(HWND hwndParent, std::vector<WindowInfo>& windowList) { wchar_t className[256]; wchar_t windowTitle[256]; ZeroMemory(className, sizeof(className)); ZeroMemory(windowTitle, sizeof(windowTitle)); // 获取窗口类名和标题 GetClassNameW(hwndParent, className, sizeof(className) / sizeof(wchar_t)); GetWindowTextW(hwndParent, windowTitle, sizeof(windowTitle) / sizeof(wchar_t)); if (className[0] == L'\0') { wcscpy_s(className, L"(None)"); } if (windowTitle[0] == L'\0') { wcscpy_s(windowTitle, L"(None)"); } WindowInfo windowInfo = { className, windowTitle, hwndParent }; // 枚举子窗口 EnumChildWindows(hwndParent, [](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* children = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *children); return TRUE; }, reinterpret_cast<LPARAM>(&windowInfo.children)); windowList.push_back(windowInfo); } // 获取当前桌面窗口层次,使用宽字符 API std::vector<WindowInfo> GetWindowHierarchy() { std::vector<WindowInfo> windows; EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* windows = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *windows); return TRUE; }, reinterpret_cast<LPARAM>(&windows)); return windows; } // 格式化时间为宽字符格式 std::wstring FormatTime(const std::time_t& time) { wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &time); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); return std::wstring(timeBuffer); } // 转换宽字符到 UTF-8 std::string WideToUTF8(const std::wstring& wideStr) { std::wstring_convert<std::codecvt_utf8<wchar_t>> conv; return conv.to_bytes(wideStr); } // 保存窗口层次信息到文件,使用 UTF-8 编码 void SaveWindowHierarchy(const std::vector<WindowInfo>& windows, const std::time_t& changeTime, const std::wstring& filename) { std::ofstream file(WideToUTF8(filename), std::ios::app | std::ios::binary); // 使用 binary 防止换行符的意外转换 if (!file.is_open()) { std::wcerr << L"Unable to open file for writing!" << std::endl; return; } // 在文件开头写入 BOM,标识为 UTF-8 编码 static bool bomWritten = false; if (!bomWritten) { const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; // UTF-8 BOM file.write(reinterpret_cast<const char*>(bom), sizeof(bom)); bomWritten = true; } // 格式化时间为字符串 wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &changeTime); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); // 写入时间戳 file << WideToUTF8(std::wstring(timeBuffer)) << "\n"; // 递归写入窗口信息 std::function<void(const std::vector<WindowInfo>&, int)> WriteHierarchy; WriteHierarchy = [&file, &WriteHierarchy](const std::vector<WindowInfo>& windows, int indent) { for (const auto& window : windows) { file << std::string(indent, ' ') // 使用空格进行缩进 << "Class Name: " << WideToUTF8(window.className) << ", Title: " << WideToUTF8(window.windowTitle) << ", HWND: " << std::hex << window.hwnd << "\n"; if (!window.children.empty()) { WriteHierarchy(window.children, indent + 4); // 递归写入子窗口信息 } } }; WriteHierarchy(windows, 0); file << "\n"; file.close(); } // 获取当前桌面的名称 std::wstring GetDesktopName(HDESK hDesktop) { wchar_t desktopName[256]; DWORD neededLength = 0; if (!GetUserObjectInformationW(hDesktop, UOI_NAME, desktopName, sizeof(desktopName), &neededLength)) { std::wcerr << L"Failed to get desktop name." << std::endl; return L""; } return std::wstring(desktopName); } // 切换到指定桌面并枚举窗口,任务结束后恢复到原始桌面 std::vector<WindowInfo> GetWindowsFromDesktop(HDESK hDesktop) { // 获取原始桌面句柄 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到目标桌面 if (!SetThreadDesktop(hDesktop)) { std::wcerr << L"Failed to set thread desktop!" << std::endl; return {}; } // 切换后获取窗口层次 std::vector<WindowInfo> windows = GetWindowHierarchy(); 恢复到原始桌面 //if (!SetThreadDesktop(hOriginalDesktop)) { // std::wcerr << L"Failed to restore original desktop!" << std::endl; //} return windows; } // 使用辅助线程进行桌面切换和窗口枚举 DWORD WINAPI MonitorDesktopThread(LPVOID param) { _wsetlocale(LC_ALL, L"zh-CN"); MYTHREADINFO* threadInfo = static_cast<MYTHREADINFO*>(param); threadInfo->wndInfo = GetWindowsFromDesktop(threadInfo->hDesktop); return 0; } // 打开窗口工作站并切换到桌面 HDESK OpenDesktopWithWindowStation(LPCWSTR desktopName, HWINSTA hWinsta) { // 打开桌面 HDESK hDesktop = OpenDesktopW(desktopName, 0, FALSE, GENERIC_ALL); if (!hDesktop) { std::wcerr << L"Failed to open desktop! name = " << desktopName << std::endl; CloseWindowStation(hWinsta); } return hDesktop; } // 获取当前活动桌面 HDESK GetActiveDesktop() { return OpenInputDesktop(0, FALSE, GENERIC_ALL); } // 对话框过程函数 INT_PTR CALLBACK HistoryDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static std::wstring* historyText = nullptr; switch (message) { case WM_INITDIALOG: { historyText = reinterpret_cast<std::wstring*>(lParam); // 获取编辑控件句柄 HWND hEdit = GetDlgItem(hDlg, IDC_EDIT1); if (hEdit) { // 设置文本 SetWindowTextW(hEdit, historyText->c_str()); } } return (INT_PTR)TRUE; case WM_SIZE: { // 获取对话框的新尺寸 int width = LOWORD(lParam); // 新的宽度 int height = HIWORD(lParam); // 新的高度 // 调整编辑控件大小 HWND hEdit = GetDlgItem(hDlg, IDC_EDIT1); if (hEdit) { int margin = 25; MoveWindow(hEdit, margin, margin, width - 2 * margin, height - 80, TRUE); } // 调整OK按钮的位置 HWND hButtonOK = GetDlgItem(hDlg, IDOK); if (hButtonOK) { int buttonWidth = 120; int buttonHeight = 35; int margin = 15; // 按钮位于对话框底部,右边留一定的边距 MoveWindow(hButtonOK, width - buttonWidth - margin, height - buttonHeight - margin, buttonWidth, buttonHeight, TRUE); } } break; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; default: return FALSE; } return FALSE; } // 监控桌面切换 void MonitorDesktop() { // 注册 Ctrl + Shift + F1 组合键为全局热键 (ID = 1) if (!RegisterHotKey(NULL, 1, MOD_CONTROL | MOD_SHIFT, VK_F1)) { std::wcerr << L"Failed to register hotkey for Ctrl + Shift + F1." << std::endl; return; } // 打开窗口工作站 HWINSTA hWinsta = OpenWindowStationW(L"WinSta0", FALSE, GENERIC_READ | GENERIC_WRITE); if (!hWinsta) { std::wcerr << L"Failed to open window station!" << std::endl; return; } // 将当前线程关联到工作站 if (!SetProcessWindowStation(hWinsta)) { std::wcerr << L"Failed to set process window station!" << std::endl; CloseWindowStation(hWinsta); return; } HDESK hDefaultDesk = OpenDesktopWithWindowStation(L"Default", hWinsta); HDESK hWinlogonDesk = OpenDesktopWithWindowStation(L"Winlogon", hWinsta); if (!hDefaultDesk || !hWinlogonDesk) { std::wcerr << L"Failed to open desktops!" << std::endl; CloseWindowStation(hWinsta); return; } std::wcout << L"Monitoring desktop changes (SYSTEM privileges detected)..." << std::endl; std::wcout << L"Desktops: Winlogon(" << std::hex << (UINT64)hWinlogonDesk << L"), Default(" << std::hex << (UINT64)hDefaultDesk << L")." << std::endl; // 桌面监控代码 std::vector<WindowInfo> windowHistory; bool monitoring = false; HANDLE hBorderThread = NULL; // 边框线程句柄 while (true) { MSG msg; // 非阻塞的消息检查 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (IsMainWindowActive() && msg.message == WM_HOTKEY && msg.wParam == 1) { // Ctrl + Shift + F1 热键触发,退出循环 std::wcout << L"Ctrl + Shift + F1 pressed. Exiting monitoring..." << std::endl; Sleep(1000); goto exit_monitoring; } TranslateMessage(&msg); DispatchMessageW(&msg); } HDESK hCurrentDesk = GetActiveDesktop(); // 获取活动桌面句柄 std::wstring desktopName = GetDesktopName(hCurrentDesk); // 获取桌面名称 //std::wcout << L"Current Desktop: " << desktopName << std::endl; if (desktopName == L"Winlogon" && !monitoring) { std::wcout << L"Switched to Winlogon desktop. Start monitoring window hierarchy..." << std::endl; monitoring = true; windowHistory.clear(); // 清空之前的记录 // 启动边框绘制线程,并传递 Winlogon 桌面句柄 if (!borderRunning) { hBorderThread = CreateThread(NULL, 0, BorderThread, (LPVOID)hWinlogonDesk, 0, NULL); borderRunning = true; } // 切换到 winlogon 桌面并枚举窗口 MYTHREADINFO info = { hWinlogonDesk }; HANDLE hThread = CreateThread(NULL, 0, MonitorDesktopThread, &info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } windowHistory = info.wndInfo; } else if (desktopName == L"Default" && monitoring) { std::wcout << L"Switched back to Default desktop. Stopping monitoring..." << std::endl; std::time_t currentTime = std::time(nullptr); SaveWindowHierarchy(windowHistory, currentTime, L"D:\\window_hierarchy.log"); monitoring = false; // 启动关闭边框的线程 if (borderRunning) { HANDLE hCloseThread = CreateThread(NULL, 0, CloseBorderThread, (LPVOID)hWinlogonDesk, 0, NULL); WaitForSingleObject(hCloseThread, INFINITE); // 等待线程完成 CloseHandle(hCloseThread); borderRunning = false; } // 构建窗口层次信息文本 std::wstringstream ss; for (const auto& win : windowHistory) { ss << L"Class Name: " << win.className << L", Title: " << win.windowTitle << L", HWND: " << std::hex << win.hwnd << L"\r\n"; } std::wstring historyText = ss.str(); // 显示历史记录对话框 DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, HistoryDialogProc, (LPARAM)&historyText); } //if (monitoring) { // // 切换到 winlogon 桌面枚举窗口 // MYTHREADINFO info = { hWinlogonDesk }; // HANDLE hThread = CreateThread(NULL, 0, MonitorDesktopThread, &info, 0, NULL); // if (hThread) { // WaitForSingleObject(hThread, INFINITE); // CloseHandle(hThread); // } // if (!info.wndInfo.empty()) { // windowHistory = info.wndInfo; // } //} Sleep(1000); // 每秒检查一次 } exit_monitoring: // 注销热键 UnregisterHotKey(NULL, 1); // 清理 CloseDesktop(hDefaultDesk); CloseDesktop(hWinlogonDesk); CloseWindowStation(hWinsta); } // 检测文件编码 FileEncoding DetectFileEncoding(const std::wstring& filePath) { std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for encoding detection: " << filePath << std::endl; return FileEncoding::UNKNOWN; } unsigned char bom[3] = { 0 }; file.read(reinterpret_cast<char*>(bom), 3); file.close(); // Check BOM (Byte Order Mark) if (bom[0] == 0xFF && bom[1] == 0xFE) { return FileEncoding::UTF16LE; // UTF-16 Little Endian } else if (bom[0] == 0xFE && bom[1] == 0xFF) { return FileEncoding::UTF16BE; // UTF-16 Big Endian } else if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) { return FileEncoding::UTF8; // UTF-8 with BOM } else { return FileEncoding::ANSI; // Default to ANSI } } // 读取文件内容,处理不同编码格式 bool ReadFileWithEncoding(const std::wstring& filePath, std::wstring& content) { FileEncoding encoding = DetectFileEncoding(filePath); if (encoding == FileEncoding::UTF16LE || encoding == FileEncoding::UTF16BE) { // Use wide string stream to read UTF-16 encoded file std::wifstream wfile(filePath, std::ios::binary); if (!wfile.is_open()) { std::wcerr << L"Failed to open UTF-16 file: " << filePath << std::endl; return false; } wfile.imbue(std::locale(wfile.getloc(), new std::codecvt_utf16<wchar_t, 0x10FFFF, std::little_endian>)); std::wstringstream wss; wss << wfile.rdbuf(); content = wss.str(); wfile.close(); } else if (encoding == FileEncoding::UTF8) { // Use standard string stream to read UTF-8 encoded file std::ifstream file(filePath); if (!file.is_open()) { std::wcerr << L"Failed to open UTF-8 file: " << filePath << std::endl; return false; } std::stringstream ss; ss << file.rdbuf(); std::string utf8Content = ss.str(); // Convert UTF-8 string to wide string std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; content = converter.from_bytes(utf8Content); file.close(); } else if (encoding == FileEncoding::ANSI) { // Use standard string stream to read ANSI encoded file std::ifstream file(filePath); if (!file.is_open()) { std::wcerr << L"Failed to open ANSI file: " << filePath << std::endl; return false; } std::stringstream ss; ss << file.rdbuf(); std::string ansiContent = ss.str(); // Convert ANSI string to wide string (using current locale) content = std::wstring(ansiContent.begin(), ansiContent.end()); file.close(); } else { std::wcerr << L"Unknown file encoding." << std::endl; return false; } return true; } // 宽字符到多字节字符的转换 (ANSI) std::string WideStringToString(const std::wstring& wstr) { if (wstr.empty()) return std::string(); // 使用系统默认代码页 (CP_ACP) 将宽字符转换为多字节字符 int size_needed = WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); std::string str(size_needed, 0); WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), &str[0], size_needed, NULL, NULL); return str; } // 根据编码保存修改后的文件内容 bool ModifyAndSaveFileWithEncoding(const std::wstring& filePath, const std::wstring& content) { FileEncoding encoding = DetectFileEncoding(filePath); if (encoding == FileEncoding::UTF16LE) { // Save as UTF-16 Little Endian std::wofstream wfile(filePath, std::ios::binary); if (!wfile.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } wfile.imbue(std::locale(wfile.getloc(), new std::codecvt_utf16<wchar_t, 0x10FFFF, std::little_endian>)); wfile << content; wfile.close(); } else if (encoding == FileEncoding::UTF8) { // Save as UTF-8 std::ofstream file(filePath); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; std::string utf8Content = converter.to_bytes(content); file << utf8Content; file.close(); } else if (encoding == FileEncoding::ANSI) { // Save as ANSI std::ofstream file(filePath); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } std::string ansiContent = WideStringToString(content); file << ansiContent; file.close(); } else { std::wcerr << L"Unknown file encoding, cannot save file." << std::endl; return false; } return true; } // 修改 SeAssignPrimaryTokenPrivilege 权限 bool ModifySeAssignPrimaryTokenPrivilege(const std::wstring& configFilePath, const std::wstring& userName) { std::wstring content; // 读取文件内容 if (!ReadFileWithEncoding(configFilePath, content)) { std::wcerr << L"Failed to read configuration file with correct encoding." << std::endl; return false; } // 查找并修改 SeAssignPrimaryTokenPrivilege size_t pos = content.find(L"SeAssignPrimaryTokenPrivilege"); if (pos != std::wstring::npos) { size_t endPos = content.find(L"\n", pos) - 1; std::wstring line = content.substr(pos, endPos - pos); // 检查是否包含当前用户 if (line.find(userName) == std::wstring::npos) { line += L"," + userName; content.replace(pos, endPos - pos, line); } } // 保存修改后的文件 if (!ModifyAndSaveFileWithEncoding(configFilePath, content)) { std::wcerr << L"Failed to save modified configuration file." << std::endl; return false; } return true; } // 执行命令 bool ExecuteCommandWithOutput(const std::wstring& command, std::wstring& output) { STARTUPINFOW si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; HANDLE hRead, hWrite; // Initialize security attributes for pipe handles ZeroMemory(&sa, sizeof(sa)); sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; // Create a pipe for the child process's STDOUT and STDERR if (!CreatePipe(&hRead, &hWrite, &sa, 0)) { std::wcerr << L"Failed to create pipe." << std::endl; return false; } // Ensure the read handle to the pipe is not inherited if (!SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0)) { std::wcerr << L"Failed to set handle information." << std::endl; return false; } // Set up the STARTUPINFO structure for the child process ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.hStdError = hWrite; si.hStdOutput = hWrite; si.dwFlags |= STARTF_USESTDHANDLES; // Redirect STDOUT and STDERR to the pipe // Set up the PROCESS_INFORMATION structure ZeroMemory(&pi, sizeof(pi)); // Create the child process with CREATE_NO_WINDOW to avoid showing a new console window if (!CreateProcessW(NULL, const_cast<LPWSTR>(command.c_str()), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { std::wcerr << L"Failed to execute command: " << command << std::endl; CloseHandle(hRead); CloseHandle(hWrite); return false; } // Close the write end of the pipe in the parent process CloseHandle(hWrite); // Read the output from the child process DWORD bytesRead; CHAR buffer[4096]; std::string ansiResult; BOOL success = FALSE; ZeroMemory(buffer, sizeof(buffer) * sizeof(CHAR)); // Continuously read from the pipe until there's no more data while (true) { success = ReadFile(hRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL); if (!success || bytesRead == 0) { break; // No more data to read } buffer[bytesRead] = '\0'; // Null-terminate the buffer ansiResult += buffer; // Collect the ANSI result } // Close handles CloseHandle(hRead); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); // If the result is empty, return "Output is empty" if (ansiResult[0] == '\0') { output = L"\n(no message)\n"; return true; } // Convert the ANSI result to wide string (UTF-16) for proper Unicode handling int wideSize = MultiByteToWideChar(CP_ACP, 0, ansiResult.c_str(), -1, NULL, 0); if (wideSize == 0) { std::wcerr << L"Failed to convert output to wide string." << std::endl; return false; } std::wstring wideResult(wideSize, 0); MultiByteToWideChar(CP_ACP, 0, ansiResult.c_str(), -1, &wideResult[0], wideSize); output = wideResult; return true; } // 判断文件是否存在 BOOL IsFileExist(const std::wstring& wsFile) { DWORD dwAttrib = GetFileAttributesW(wsFile.c_str()); return INVALID_FILE_ATTRIBUTES != dwAttrib && 0 == (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); } // 导出安全策略 bool ExportSecurityPolicy(const std::wstring& outputFile, std::wstring& output) { // https://blog.csdn.net/u0/article/details/ if (IsFileExist(outputFile)) { // 判断配置文件是否已经存在 std::wcout << L"The configuration file already exists. Attempting to delete it." << std::endl; if (!DeleteFileW(outputFile.c_str())) { std::wcerr << L"Failed to delete the configuration file." << std::endl; return false; } } std::wstring command = L"secedit /export /cfg " + outputFile; return ExecuteCommandWithOutput(command, output); } // 应用修改后的安全策略 bool ApplySecurityPolicy(const std::wstring& configFilePath, const std::wstring& dbFilePath, std::wstring& output) { std::wstring command = L"secedit /configure /db " + dbFilePath + L" /cfg " + configFilePath + L" /overwrite /quiet"; return ExecuteCommandWithOutput(command, output); } // 获取系统盘符 std::wstring GetSystemDrive() { wchar_t systemPath[MAX_PATH]; if (GetSystemDirectoryW(systemPath, MAX_PATH)) { return std::wstring(systemPath).substr(0, 3); // 返回盘符部分,例如 C: } return L"C:\\"; // 如果获取失败,返回默认 C 盘符 } // 获取当前用户账户名称 std::wstring GetCurrentUserName() { wchar_t userName[UNLEN + 1]; DWORD size = UNLEN + 1; if (GetUserNameW(userName, &size)) { return std::wstring(userName); } return L""; // 返回空字符串表示获取失败 } // 提示用户注销 void PromptUserToLogout() { // Ask the user if they want to log off and re-log in int result = MessageBoxW(NULL, L"The security settings have been updated. Would you like to log off now and apply the changes?", L"Log Off Confirmation", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL); if (result == IDYES) { // Log off the user if (!ExitWindowsEx(EWX_LOGOFF | EWX_FORCE, SHTDN_REASON_MAJOR_OTHER)) { std::wcerr << L"Failed to log off the user." << std::endl; } } else { std::wcout << L"User chose not to log off." << std::endl; } } bool ChangeAssignPrimaryTokenPrivilege() { std::wstring systemDrive = GetSystemDrive(); std::wstring configFilePath = systemDrive + L"gp.inf"; std::wstring dbFilePath = systemDrive + L"test.sdb"; std::wstring userName = GetCurrentUserName(); std::wstring output; if (userName.empty()) { std::wcerr << L"Failed to retrieve the current user name." << std::endl; return false; } if (!ExportSecurityPolicy(configFilePath, output)) { std::wcerr << L"Failed to export security policy." << std::endl; return false; } std::wcout << L"\nExportSecurityPolicy message: \n" << output << std::endl; if (!ModifySeAssignPrimaryTokenPrivilege(configFilePath, userName)) { std::wcerr << L"Failed to modify SeAssignPrimaryTokenPrivilege." << std::endl; return false; } if (!ApplySecurityPolicy(configFilePath, dbFilePath, output)) { std::wcerr << L"Failed to apply security policy." << std::endl; return false; } std::wcout << L"\nApplySecurityPolicy message: \n" << output << std::endl; PromptUserToLogout(); return true; } int wmain(int argc, wchar_t* argv[]) { // 宽字符中文支持 _wsetlocale(LC_ALL, L"zh-CN"); // 获取当前进程的主窗口 hMainWindow = GetMainWindowHandle(); if (!hMainWindow) { std::cerr << "Failed to find main window." << std::endl; system("pause > nul 2 > nul"); return 1; } std::cout << "Main window handle: " << std::hex << hMainWindow << std::endl; // 检查是否为管理员权限运行 if (!IsRunAsAdmin()) { std::wcout << L"Attempting to restart with administrator privileges..." << std::endl; if (RelaunchAsAdmin()) { return 0; // 提升后进程将重新启动,当前进程结束 } else { std::wcerr << L"Failed to relaunch as administrator." << std::endl; system("pause > nul 2 > nul"); return 1; } } // 启用 SeDebugPrivilege if (!EnablePrivilege(SE_DEBUG_NAME)) { std::wcerr << L"Failed to enable SeDebugPrivilege." << std::endl; system("pause > nul 2 > nul"); return 1; } // 启用 SeDebugPrivilege if (!EnablePrivilege(SE_INCREASE_QUOTA_NAME)) { std::wcerr << L"Failed to enable SeIncreaseQuotaPrivilege." << std::endl; system("pause > nul 2 > nul"); return 1; } // 启用 SeDebugPrivilege if (!EnablePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME)) { std::wcerr << L"Failed to enable SeAssignPrimaryTokenPrivilege." << std::endl; if (!ChangeAssignPrimaryTokenPrivilege()) { std::wcerr << L"Failed to enable primary token assignment privilege" << L" by modifying security configuration." << std::endl; } std::wcout << L"Press any key to close app."; system("pause > nul 2 > nul"); return 1; } // 检查 SYSTEM 权限 if (!IsSystem()) { std::wcout << L"Attempting to restart with SYSTEM privileges..." << std::endl; // 检查命令行参数,避免无限递归 if (argc < 2 || _wcsicmp(argv[1], L"system") != 0) { // 重新启动自身并传递 "system" 参数 wchar_t commandLine[MAX_PATH]; swprintf(commandLine, MAX_PATH, L"%s system", argv[0]); if (CreateSystemProcess(argv[0], commandLine)) { std::wcout << L"Restarted with SYSTEM privileges." << std::endl; } else { std::wcerr << L"Failed to restart with SYSTEM privileges." << std::endl; system("pause > nul 2 > nul"); } return 0; } else { std::wcerr << L"Already tried to elevate privileges but failed." << std::endl; system("pause > nul 2 > nul"); return 1; } } // 如果当前进程已经是 SYSTEM 权限,则继续执行桌面监控 MonitorDesktop(); std::wcout << L"Press any key to close app."; system("pause > nul 2 > nul"); return 0; }

版本 Beta 3 修复内容: 

  1. 修复了进程退出时不检查主窗口是否关闭的问题(但异步对话框的关闭未完成,将在后续更新中修复);
  2. 修复了不能连续监视的问题,现在在监视 Winlogon 桌面时,每隔 1 秒扫描一次窗口层次并产生日志哈希。当窗口层次发生变化时,记录新的窗口层次信息到日志文件;
  3. 只在返回 Default 桌面时弹窗显示最近一次记录的窗口层次,其他由日志记录保存;
  4. 新增在监视窗口时显示状态信息,状态信息默认对齐在屏幕左下角;

已知未修复问题:

  1.  DialogBox 的文本框中文本过长或者行数过多时不支持滚动条滑动翻页;
  2. 部分资源释放和指针的检查代码不够健硕;

Beta 3 代码:

#include <windows.h> #include <iostream> #include <lmcons.h> #include <fstream> #include <string> #include <vector> #include <ctime> #include <sstream> #include <codecvt> #include <functional> #include <tlhelp32.h> #include <userenv.h> #include <sddl.h> #include "resource.h" #pragma comment(linker,"\"/manifestdependency:type='win32' "\ "name='Microsoft.Windows.Common-Controls' "\ "version='6.0.0.0' processorArchitecture='*' "\ "publicKeyToken='6595b64144ccf1df' language='*'\"") constexpr auto WM_UPDATE_STATUS = WM_APP + 10; // 传递新的状态信息的用户自定义消息 constexpr auto STATUS_MSG_HASH = 0x; // 内部校验码(防止外部程序模拟消息) constexpr auto LOGING_STATUS_INFO = L"Now Monitoring..."; // 显示提示:正在扫描窗口 constexpr auto COMPLETE_STATUS_INFO = L"Monitoring completed!"; // 显示提示:窗口扫描已完成 constexpr auto LogDir = L"D:\\window_hierarchy.log"; // 默认日志记录保存位置 constexpr auto MAX_STSTUS_BUFFER = 350; // 状态信息的最大缓冲区大小 // 记录窗口信息的结构体,使用宽字符 struct WindowInfo { std::wstring className; std::wstring windowTitle; HWND hwnd; std::vector<WindowInfo> children; }; // 监视器线程存储 struct MYMONITOR_THREAD_INFO { HDESK hDesktop; std::vector<WindowInfo> wndInfo; }; // 状态信息参数传递结构 struct MYSTATUS_UPDATE_THREAD_INFO { HDESK hChangeToDesktop; WCHAR wcsBuffer[MAX_STSTUS_BUFFER]; }; // 对话框(异步)参数结构(在线程堆栈上传递) struct DIALOGBOX_PARAM_LIST { _In_opt_ HINSTANCE hInstance; _In_ LPCWSTR lpTemplateName; _In_opt_ HWND hWndParent; _In_opt_ DLGPROC lpDialogFunc; _In_ LPARAM dwInitParam; _In_ size_t cbSize; _Out_ INT_PTR intResponse; }; // 文件编码格式 enum class FileEncoding { ANSI, UTF8, UTF16LE, UTF16BE, UNKNOWN }; // 用于边框绘制的全局变量 HWND hBorderWnd = NULL; // 边框窗口句柄 bool borderRunning = false; // 边框是否正在显示 // 全局变量保存主窗口句柄 HWND hMainWindow = NULL; HANDLE hBroderWndCreateEvent; // 启用特定的权限(例如 SeDebugPrivilege) bool EnablePrivilege(LPCWSTR privilege) { HANDLE hToken; TOKEN_PRIVILEGES tp{}; LUID luid; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token." << std::endl; return false; } if (!LookupPrivilegeValueW(NULL, privilege, &luid)) { std::wcerr << L"Failed to lookup privilege." << std::endl; CloseHandle(hToken); return false; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { std::wcerr << L"Failed to adjust token privileges." << std::endl; CloseHandle(hToken); return false; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { std::wcerr << L"The privilege was not assigned." << std::endl; CloseHandle(hToken); return false; } CloseHandle(hToken); return true; } // 检查是否以管理员权限运行 bool IsRunAsAdmin() { BOOL isAdmin = FALSE; PSID administratorsGroup = NULL; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsGroup)) { CheckTokenMembership(NULL, administratorsGroup, &isAdmin); FreeSid(administratorsGroup); } return isAdmin == TRUE; } // 重新启动并请求管理员权限 bool RelaunchAsAdmin() { wchar_t szPath[MAX_PATH]; if (!GetModuleFileNameW(NULL, szPath, MAX_PATH)) { std::wcerr << L"Failed to get module file name." << std::endl; return false; } SHELLEXECUTEINFOW sei = { sizeof(sei) }; sei.lpVerb = L"runas"; // 请求管理员权限 sei.lpFile = szPath; sei.hwnd = NULL; sei.nShow = SW_NORMAL; if (!ShellExecuteExW(&sei)) { std::wcerr << L"Failed to relaunch as administrator." << std::endl; return false; } return true; } DWORD WINAPI GetActiveConsoleSessionId() { return WTSGetActiveConsoleSessionId(); } BOOL WINAPI IsProcessInSession(DWORD processId, DWORD sessionId) { DWORD session; if (!ProcessIdToSessionId(processId, &session)) { printf("Error: ProcessIdToSessionId failed.\n"); return FALSE; } return session == sessionId; } DWORD WINAPI FindWinlogonProcessId() { DWORD dwProcessId = 0; DWORD activeSessionId = GetActiveConsoleSessionId(); if (activeSessionId == 0xFFFFFFFF) { printf("Error: Unable to retrieve active console session ID.\n"); return 0; } HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { printf("Error: CreateToolhelp32Snapshot failed.\n"); return 0; } PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(snapshot, &entry)) { printf("Error: Process32First failed.\n"); CloseHandle(snapshot); return 0; } do { if (entry.cntThreads <= 1u) continue; // 跳过僵尸进程 if (_wcsicmp(entry.szExeFile, L"winlogon.exe") == 0) { if (IsProcessInSession(entry.th32ProcessID, activeSessionId)) { dwProcessId = entry.th32ProcessID; break; } } } while (Process32Next(snapshot, &entry)); CloseHandle(snapshot); return dwProcessId; } // 获取 winlogon 进程的 SYSTEM 令牌 HANDLE GetSystemTokenFromWinlogon() { HANDLE hToken = NULL; HANDLE hProcess = NULL; HANDLE hSystemToken = NULL; // 存储复制后的 SYSTEM 令牌 // 获取 Winlogon 进程的进程ID DWORD winlogonPid = FindWinlogonProcessId(); if (winlogonPid == 0) { std::wcerr << L"Failed to find winlogon.exe process!" << std::endl; return NULL; } std::wcout << L"winlogon.exe PID: " << winlogonPid << std::endl; // 打开 Winlogon 进程 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, winlogonPid); if (!hProcess) { std::wcerr << L"Failed to open winlogon process!" << std::endl; return NULL; } // 打开该进程的令牌 if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token!" << std::endl; CloseHandle(hProcess); return NULL; } // 复制令牌以使其可用于新进程 if (!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hSystemToken)) { std::wcerr << L"Failed to duplicate SYSTEM token!" << std::endl; CloseHandle(hToken); CloseHandle(hProcess); return NULL; } // 关闭原始的令牌和进程句柄 CloseHandle(hToken); CloseHandle(hProcess); return hSystemToken; } // 创建具有 SYSTEM 权限的进程 bool CreateSystemProcess(LPCWSTR applicationName, LPCWSTR commandLine) { HANDLE hToken = GetSystemTokenFromWinlogon(); if (!hToken) { std::wcerr << L"Failed to get SYSTEM token!" << std::endl; return false; } // 使用 SYSTEM 权限创建进程 STARTUPINFOW si = { sizeof(STARTUPINFOW) }; PROCESS_INFORMATION pi = { 0 }; if (!CreateProcessAsUserW(hToken, applicationName, const_cast<LPWSTR>(commandLine), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { std::wcerr << L"Failed to create process as SYSTEM! err = " << GetLastError() << std::endl; CloseHandle(hToken); return false; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hToken); return true; } // 检查当前进程是否具有 SYSTEM 权限 bool IsSystem() { HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { return false; } DWORD tokenInfoLength = 0; GetTokenInformation(hToken, TokenUser, NULL, 0, &tokenInfoLength); PTOKEN_USER tokenUser = (PTOKEN_USER)malloc(tokenInfoLength); if (!GetTokenInformation(hToken, TokenUser, tokenUser, tokenInfoLength, &tokenInfoLength)) { CloseHandle(hToken); free(tokenUser); return false; } LPWSTR sidString = NULL; ConvertSidToStringSidW(tokenUser->User.Sid, &sidString); bool isSystem = (_wcsicmp(sidString, L"S-1-5-18") == 0); LocalFree(sidString); CloseHandle(hToken); free(tokenUser); return isSystem; } HWND GetConsoleHwnd(void) { // https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/obtain-console-window-handle #define MY_BUFSIZE 1024 // Buffer size for console window titles. HWND hwndFound; // This is what is returned to the caller. wchar_t pszNewWindowTitle[MY_BUFSIZE]; // Contains fabricated // WindowTitle. // Format a "unique" NewWindowTitle. wsprintfW(pszNewWindowTitle, L"WinlogonWindowsMonitor - %d/%d", GetTickCount(), GetCurrentProcessId()); // Change current window title. SetConsoleTitleW(pszNewWindowTitle); // Ensure window title has been updated. Sleep(40); // Look for NewWindowTitle. hwndFound = FindWindowW(L"ConsoleWindowClass", pszNewWindowTitle); if(!hwndFound) hwndFound = FindWindowW(L"CASCADIA_HOSTING_WINDOW_CLASS", pszNewWindowTitle); #undef MY_BUFSIZE return(hwndFound); } // 获取当前进程的主窗口 HWND GetMainWindowHandle() { return GetConsoleHwnd(); } // 检查当前活动窗口是否是主窗口 bool IsMainWindowActive() { HWND hForeground = GetForegroundWindow(); // 获取当前活动窗口 return hForeground == hMainWindow; // 比较是否是主窗口 } // 边框窗口过程 LRESULT CALLBACK BorderWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static int borderWidth = 10; // 默认边框宽度 static wchar_t statusText[MAX_STSTUS_BUFFER]; switch (message) { case WM_CREATE: { memset(statusText, 0, sizeof(statusText)); //memcpy(statusText, LOGING_STATUS_INFO, sizeof(statusText) - 5); // 窗口创建成功后,发出信号 SetEvent(hBroderWndCreateEvent); break; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT rect; GetClientRect(hWnd, &rect); // 设置边框颜色为蓝色 HBRUSH brush = CreateSolidBrush(RGB(119, 228, 155)); // 填充外边框到内边框之间的区域 RECT outerRect = rect; RECT innerRect = rect; // 根据边框宽度调整内边框位置 InflateRect(&innerRect, -borderWidth, -borderWidth); // 填充外边框和内边框之间的区域 if (borderWidth > 0) { // 填充四个部分:上边、下边、左边、右边 RECT topRect = { outerRect.left, outerRect.top, outerRect.right, innerRect.top }; RECT bottomRect = { outerRect.left, innerRect.bottom, outerRect.right, outerRect.bottom }; RECT leftRect = { outerRect.left, innerRect.top, innerRect.left, innerRect.bottom }; RECT rightRect = { innerRect.right, innerRect.top, outerRect.right, innerRect.bottom }; FillRect(hdc, &topRect, brush); FillRect(hdc, &bottomRect, brush); FillRect(hdc, &leftRect, brush); FillRect(hdc, &rightRect, brush); } DeleteObject(brush); // 设置字体 HFONT hFont = CreateFontW(36, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Arial"); SelectObject(hdc, hFont); // 设置文本颜色为高亮(例如绿色) SetTextColor(hdc, RGB(0, 255, 0)); SetBkMode(hdc, TRANSPARENT); // 透明背景 // 获取窗口文本并绘制在左下方,间隔 50 像素 rect.left += 50; // 左侧间隔 50 像素 rect.bottom -= 50; // 底部间隔 50 像素 DrawTextW(hdc, statusText, -1, &rect, DT_LEFT | DT_BOTTOM | DT_SINGLELINE); DeleteObject(hFont); EndPaint(hWnd, &ps); break; } case WM_UPDATE_STATUS: { if (wParam == STATUS_MSG_HASH) { // 获取窗口文本并绘制 memset(statusText, 0, sizeof(statusText)); memcpy(statusText, (LPVOID)lParam, sizeof(statusText) - 5); InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hWnd); } return 0; } case WM_DESTROY: PostQuitMessage(0); return 0; default: break; //return DefWindowProcW(hWnd, message, wParam, lParam); } return DefWindowProcW(hWnd, message, wParam, lParam); } DWORD WINAPI CloseBorderThread(LPVOID lpParam) { HDESK hWinlogonDesk = static_cast<HDESK>(lpParam); // 传递的桌面句柄 // 获取当前线程的桌面句柄,后面恢复用 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到 Winlogon 桌面 if (!SetThreadDesktop(hWinlogonDesk)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } // 发送关闭消息 if (hBorderWnd) { PostMessageW(hBorderWnd, WM_DESTROY, 0, 0); } std::wcout << L"An exit monitoring message has been sent." << std::endl; // 恢复到原始桌面 SetThreadDesktop(hOriginalDesktop); return 0; } // 更新状态窗口显示的文本信息 DWORD WINAPI UpdateStatusWindowTextThread(LPVOID lpParam) { if (lpParam == nullptr) { std::wcerr << L"Invalid thread parameters." << std::endl; return ERROR_INVALID_PARAMETER; } __try { MYSTATUS_UPDATE_THREAD_INFO* info = static_cast<MYSTATUS_UPDATE_THREAD_INFO*>(lpParam); if (info->hChangeToDesktop == nullptr || info->wcsBuffer == nullptr) { std::wcerr << L"Invalid thread parameters." << std::endl; return ERROR_INVALID_PARAMETER; } // 获取当前线程的桌面句柄,后面恢复用 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到 Winlogon 桌面 if (!SetThreadDesktop(info->hChangeToDesktop)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } // 设置新的信息 if (hBorderWnd) { //InvalidateRect(hBorderWnd, NULL, TRUE); SendMessageW(hBorderWnd, WM_UPDATE_STATUS, STATUS_MSG_HASH, (LPARAM)info->wcsBuffer); //UpdateWindow(hBorderWnd); } std::wcout << L"UpdateStatusWindowText successfully." << std::endl; // 恢复到原始桌面 SetThreadDesktop(hOriginalDesktop); } __except (EXCEPTION_EXECUTE_HANDLER) { std::wcerr << L"Memory access violation." << std::endl; return ERROR_INVALID_ACCESS; } return 0; } // 在监视过程开始时显示提示信息 BOOL StartMonitoringUI(HDESK hWinlogonDesk) { // 切换到 winlogon 桌面并发送状态更新消息 MYSTATUS_UPDATE_THREAD_INFO info = { hWinlogonDesk }; wcscpy_s(info.wcsBuffer, LOGING_STATUS_INFO); HANDLE hThread = CreateThread(NULL, 0, UpdateStatusWindowTextThread, &info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, 7000); CloseHandle(hThread); Sleep(1000); // 刻意的延时 return TRUE; } return FALSE; } // 在监视过程完成时更新提示信息 BOOL CompleteMonitoringUI(HDESK hWinlogonDesk) { // 切换到 winlogon 桌面并发送状态更新消息 MYSTATUS_UPDATE_THREAD_INFO info = { hWinlogonDesk }; wcscpy_s(info.wcsBuffer, COMPLETE_STATUS_INFO); HANDLE hThread = CreateThread(NULL, 0, UpdateStatusWindowTextThread, &info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, 7000); CloseHandle(hThread); Sleep(1000); // 刻意的延时 return TRUE; } return FALSE; } // 检查并关闭监视器 BOOL CloseMonitoringUI(HDESK hWinlogonDesk) { // 启动关闭边框的线程 if (borderRunning) { HANDLE hCloseThread = CreateThread(NULL, 0, CloseBorderThread, (LPVOID)hWinlogonDesk, 0, NULL); WaitForSingleObject(hCloseThread, INFINITE); // 等待线程完成 CloseHandle(hCloseThread); borderRunning = false; } return FALSE; } // 边框绘制线程 DWORD WINAPI BorderThread(LPVOID lpParam) { std::wcout << L"Monitoring the target desktop..." << std::endl; HDESK hWinlogonDesk = static_cast<HDESK>(lpParam); // 传递的桌面句柄 // 切换到 Winlogon 桌面 if (!SetThreadDesktop(hWinlogonDesk)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } #define MY_BUFSIZE 1024 // Buffer size for console window titles. wchar_t pszWindowClass[MY_BUFSIZE]; // Contains fabricated // WindowClass. // Format a "unique" WindowClass. wsprintfW(pszWindowClass, L"WinlogonMonitorBorderWindowClass[%d::%d]", GetTickCount(), GetCurrentProcessId()); HINSTANCE hInstance = GetModuleHandleW(NULL); WNDCLASS wc = { 0 }; wc.lpfnWndProc = BorderWndProc; wc.hInstance = hInstance; wc.lpszClassName = pszWindowClass; wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); if (!RegisterClassW(&wc)) { std::wcerr << L"Failed to RegisterClass." << std::endl; return 1; } // 获取屏幕大小并创建无边框窗口 int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); hBorderWnd = CreateWindowExW(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, L"WinlogonMonitorBorderWindow", WS_POPUP, 0, 0, screenWidth, screenHeight, NULL, NULL, hInstance, NULL); if (!hBorderWnd) { std::wcerr << L"Failed to CreateWindow." << std::endl; return 1; } // 设置窗口透明度 SetLayeredWindowAttributes(hBorderWnd, RGB(255, 255, 255), 0, LWA_COLORKEY); ShowWindow(hBorderWnd, SW_SHOW); UpdateWindow(hBorderWnd); // 消息循环,等待关闭事件 MSG msg; while (true) { // 检查并处理窗口消息 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } } // 销毁窗口 DestroyWindow(hBorderWnd); #undef MY_BUFSIZE return 0; } // 递归获取窗口层次,使用宽字符 API void EnumChildWindowsRecursive(HWND hwndParent, std::vector<WindowInfo>& windowList) { wchar_t className[256]; wchar_t windowTitle[256]; ZeroMemory(className, sizeof(className)); ZeroMemory(windowTitle, sizeof(windowTitle)); // 获取窗口类名和标题 GetClassNameW(hwndParent, className, sizeof(className) / sizeof(wchar_t)); GetWindowTextW(hwndParent, windowTitle, sizeof(windowTitle) / sizeof(wchar_t)); if (className[0] == L'\0') { wcscpy_s(className, L"(None)"); } if (windowTitle[0] == L'\0') { wcscpy_s(windowTitle, L"(None)"); } WindowInfo windowInfo = { className, windowTitle, hwndParent }; // 枚举子窗口 EnumChildWindows(hwndParent, [](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* children = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *children); return TRUE; }, reinterpret_cast<LPARAM>(&windowInfo.children)); windowList.push_back(windowInfo); } // 获取当前桌面窗口层次,使用宽字符 API std::vector<WindowInfo> GetWindowHierarchy() { std::vector<WindowInfo> windows; EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* windows = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *windows); return TRUE; }, reinterpret_cast<LPARAM>(&windows)); return windows; } // 获取当前窗口层次的哈希值,用于检测窗口层次是否发生变化 std::size_t GetWindowHierarchyHash(const std::vector<WindowInfo>& windows) { std::hash<std::wstring> hash_fn; std::size_t hash = 0; std::function<void(const std::vector<WindowInfo>&)> computeHash; computeHash = [&hash_fn, &hash, &computeHash](const std::vector<WindowInfo>& windows) { for (const auto& window : windows) { hash ^= hash_fn(window.className); hash ^= hash_fn(window.windowTitle); computeHash(window.children); // 递归计算子窗口的哈希值 } }; computeHash(windows); return hash; } // 格式化时间为宽字符格式 std::wstring FormatTime(const std::time_t& time) { wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &time); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); return std::wstring(timeBuffer); } // 转换宽字符到 UTF-8 std::string WideToUTF8(const std::wstring& wideStr) { std::wstring_convert<std::codecvt_utf8<wchar_t>> conv; return conv.to_bytes(wideStr); } // 保存窗口层次信息到文件,使用 UTF-8 编码 void SaveWindowHierarchy(const std::vector<WindowInfo>& windows, const std::time_t& changeTime, const std::wstring& filename) { std::ofstream file(WideToUTF8(filename), std::ios::app | std::ios::binary); // 使用 binary 防止换行符的意外转换 if (!file.is_open()) { std::wcerr << L"Unable to open file for writing!" << std::endl; return; } // 在文件开头写入 BOM,标识为 UTF-8 编码 static bool bomWritten = false; if (!bomWritten) { const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; // UTF-8 BOM file.write(reinterpret_cast<const char*>(bom), sizeof(bom)); bomWritten = true; } // 格式化时间为字符串 wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &changeTime); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); // 写入时间戳 file << WideToUTF8(std::wstring(timeBuffer)) << "\n"; // 递归写入窗口信息 std::function<void(const std::vector<WindowInfo>&, int)> WriteHierarchy; WriteHierarchy = [&file, &WriteHierarchy](const std::vector<WindowInfo>& windows, int indent) { for (const auto& window : windows) { file << std::string(indent, ' ') // 使用空格进行缩进 << "Class Name: " << WideToUTF8(window.className) << ", Title: " << WideToUTF8(window.windowTitle) << ", HWND: " << std::hex << window.hwnd << "\n"; if (!window.children.empty()) { WriteHierarchy(window.children, indent + 4); // 递归写入子窗口信息 } } }; WriteHierarchy(windows, 0); file << "\n"; file.close(); } // 获取当前桌面的名称 std::wstring GetDesktopName(HDESK hDesktop) { wchar_t desktopName[256]; DWORD neededLength = 0; if (!GetUserObjectInformationW(hDesktop, UOI_NAME, desktopName, sizeof(desktopName), &neededLength)) { std::wcerr << L"Failed to get desktop name." << std::endl; return L""; } return std::wstring(desktopName); } // 切换到指定桌面并枚举窗口,任务结束后恢复到原始桌面 std::vector<WindowInfo> GetWindowsFromDesktop(HDESK hDesktop) { // 获取原始桌面句柄 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到目标桌面 if (!SetThreadDesktop(hDesktop)) { std::wcerr << L"Failed to set thread desktop!" << std::endl; return {}; } // 切换后获取窗口层次 std::vector<WindowInfo> windows = GetWindowHierarchy(); // 恢复到原始桌面 if (!SetThreadDesktop(hOriginalDesktop)) { std::wcerr << L"Failed to restore original desktop!" << std::endl; } return windows; } // 使用辅助线程进行桌面切换和窗口枚举 DWORD WINAPI MonitorDesktopThread(LPVOID param) { _wsetlocale(LC_ALL, L"zh-CN"); MYMONITOR_THREAD_INFO* threadInfo = static_cast<MYMONITOR_THREAD_INFO*>(param); threadInfo->wndInfo = GetWindowsFromDesktop(threadInfo->hDesktop); return 0; } // 打开窗口工作站并切换到桌面 HDESK OpenDesktopWithWindowStation(LPCWSTR desktopName, HWINSTA hWinsta) { // 打开桌面 HDESK hDesktop = OpenDesktopW(desktopName, 0, FALSE, GENERIC_ALL); if (!hDesktop) { std::wcerr << L"Failed to open desktop! name = " << desktopName << std::endl; CloseWindowStation(hWinsta); } return hDesktop; } // 获取当前活动桌面 HDESK GetActiveDesktop() { return OpenInputDesktop(0, FALSE, GENERIC_ALL); } // 对话框过程函数 INT_PTR CALLBACK HistoryDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static std::wstring* historyText = nullptr; switch (message) { case WM_INITDIALOG: { historyText = reinterpret_cast<std::wstring*>(lParam); // 获取编辑控件句柄 HWND hEdit = GetDlgItem(hDlg, IDC_EDIT1); if (hEdit) { // 设置文本 SetWindowTextW(hEdit, historyText->c_str()); } } return (INT_PTR)TRUE; case WM_SIZE: { // 获取对话框的新尺寸 int width = LOWORD(lParam); // 新的宽度 int height = HIWORD(lParam); // 新的高度 // 调整编辑控件大小 HWND hEdit = GetDlgItem(hDlg, IDC_EDIT1); if (hEdit) { int margin = 25; MoveWindow(hEdit, margin, margin, width - 2 * margin, height - 80, TRUE); } // 调整OK按钮的位置 HWND hButtonOK = GetDlgItem(hDlg, IDOK); if (hButtonOK) { int buttonWidth = 120; int buttonHeight = 35; int margin = 15; // 按钮位于对话框底部,右边留一定的边距 MoveWindow(hButtonOK, width - buttonWidth - margin, height - buttonHeight - margin, buttonWidth, buttonHeight, TRUE); } } break; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; default: return FALSE; } return FALSE; } // 使用辅助线程进行桌面切换和窗口枚举 DWORD WINAPI DialogBoxParamThread(LPVOID param) { _wsetlocale(LC_ALL, L"zh-CN"); if (param == nullptr) { return ERROR_INVALID_PARAMETER; } DIALOGBOX_PARAM_LIST* dialogParam = static_cast<DIALOGBOX_PARAM_LIST*>(param); if (dialogParam->lpDialogFunc == nullptr || dialogParam->dwInitParam == NULL) { return ERROR_INVALID_PARAMETER; } dialogParam->intResponse = DialogBoxParamW(dialogParam->hInstance, dialogParam->lpTemplateName, dialogParam->hWndParent, dialogParam->lpDialogFunc, dialogParam->dwInitParam); Sleep(1000); memset(*(WCHAR)dialogParam->dwInitParam, 0, dialogParam->cbSize); delete[] *(WCHAR)dialogParam->dwInitParam; return 0; } // 异步显示历史记录对话框 void CreateDialogBoxAsyncW(std::wstring text) { WCHAR* wcsText = new (std::nothrow) WCHAR[text.length() + 1]; const size_t len = (text.length() + 1) * sizeof(WCHAR); memset(wcsText, 0, len); memcpy(wcsText, text.c_str(), len); // 显示历史记录对话框 DIALOGBOX_PARAM_LIST dialogParam = { GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, HistoryDialogProc,(LPARAM)&wcsText, len , 0 }; HANDLE hThread = CreateThread(NULL, 0, DialogBoxParamThread, &dialogParam, 0, NULL); } // 监控桌面切换 void MonitorDesktop() { // 注册 Ctrl + Shift + F1 组合键为全局热键 (ID = 1) if (!RegisterHotKey(NULL, 1, MOD_CONTROL | MOD_SHIFT, VK_F1)) { std::wcerr << L"Failed to register hotkey for Ctrl + Shift + F1." << std::endl; return; } // 打开窗口工作站 HWINSTA hWinsta = OpenWindowStationW(L"WinSta0", FALSE, GENERIC_READ | GENERIC_WRITE); if (!hWinsta) { std::wcerr << L"Failed to open window station!" << std::endl; return; } // 将当前线程关联到工作站 if (!SetProcessWindowStation(hWinsta)) { std::wcerr << L"Failed to set process window station!" << std::endl; CloseWindowStation(hWinsta); return; } HDESK hDefaultDesk = OpenDesktopWithWindowStation(L"Default", hWinsta); HDESK hWinlogonDesk = OpenDesktopWithWindowStation(L"Winlogon", hWinsta); if (!hDefaultDesk || !hWinlogonDesk) { std::wcerr << L"Failed to open desktops!" << std::endl; CloseWindowStation(hWinsta); return; } std::wcout << L"Monitoring desktop changes (SYSTEM privileges detected)..." << std::endl; std::wcout << L"Desktops: Winlogon(" << std::hex << (UINT64)hWinlogonDesk << L"), Default(" << std::hex << (UINT64)hDefaultDesk << L")." << std::endl; // 桌面监控代码 std::vector<WindowInfo> windowHistory; std::size_t prevHash = 0; // 保存上一次的哈希值 bool monitoring = false; MYMONITOR_THREAD_INFO monitor_info = { hWinlogonDesk }; HANDLE hBorderThread = NULL; // 边框线程句柄 while (true) { MSG msg; // 非阻塞的消息检查 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (IsMainWindowActive() && msg.message == WM_HOTKEY && msg.wParam == 1) { // Ctrl + Shift + F1 热键触发,退出循环 std::wcout << L"Ctrl + Shift + F1 pressed. Exiting monitoring..." << std::endl; Sleep(1000); // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); goto exit_monitoring; } TranslateMessage(&msg); DispatchMessageW(&msg); } HDESK hCurrentDesk = GetActiveDesktop(); // 获取活动桌面句柄 std::wstring desktopName = GetDesktopName(hCurrentDesk); // 获取桌面名称 //std::wcout << L"Current Desktop: " << desktopName << std::endl; if (desktopName == L"Winlogon" && !monitoring) { std::wcout << L"Switched to Winlogon desktop. Start monitoring window hierarchy..." << std::endl; monitoring = true; windowHistory.clear(); // 清空之前的记录 // 创建事件对象 hBroderWndCreateEvent = CreateEventW(NULL, TRUE, FALSE, NULL); // 启动边框绘制线程,并传递 Winlogon 桌面句柄 if (!borderRunning) { hBorderThread = CreateThread(NULL, 0, BorderThread, (LPVOID)hWinlogonDesk, 0, NULL); borderRunning = true; } // 在主线程等待窗口创建完成 WaitForSingleObject(hBroderWndCreateEvent, INFINITE); // 设置状态为:记录中 StartMonitoringUI(hWinlogonDesk); // 切换到 winlogon 桌面并枚举窗口 HANDLE hThread = CreateThread(NULL, 0, MonitorDesktopThread, &monitor_info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } windowHistory = monitor_info.wndInfo; // 计算当前窗口层次的哈希值 prevHash = GetWindowHierarchyHash(windowHistory); // 设置状态为:已完成 CompleteMonitoringUI(hWinlogonDesk); } else if (desktopName == L"Default" && monitoring) { std::wcout << L"Switched back to Default desktop. Stopping monitoring..." << std::endl; std::time_t currentTime = std::time(nullptr); SaveWindowHierarchy(windowHistory, currentTime, LogDir); monitoring = false; // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); // 格式化时间为字符串 wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &currentTime); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); // 构建窗口层次信息文本 std::wstringstream ss; ss << L"Most Recent History Call ( " << timeBuffer << L" ): \r\n"; for (const auto& win : windowHistory) { ss << L"Class Name: " << win.className << L", Title: " << win.windowTitle << L", HWND: " << std::hex << win.hwnd << L"\r\n"; } std::wstring historyText = ss.str(); // 显示历史记录对话框 //DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, HistoryDialogProc, (LPARAM)&historyText); CreateDialogBoxAsyncW(historyText); } // 检查窗口层次变化 if (monitoring) { HANDLE hThread = CreateThread(NULL, 0, MonitorDesktopThread, &monitor_info, 0, NULL); if (hThread) { // 设置状态为:记录中 StartMonitoringUI(hWinlogonDesk); WaitForSingleObject(hThread, INFINITE); // 等待线程结束 CloseHandle(hThread); // 设置状态为:已完成 CompleteMonitoringUI(hWinlogonDesk); } std::size_t currentHash = GetWindowHierarchyHash(monitor_info.wndInfo); if (currentHash != prevHash) { std::wcout << L"Window hierarchy changed. Updating records..." << std::endl; windowHistory = monitor_info.wndInfo; std::time_t currentTime = std::time(nullptr); SaveWindowHierarchy(windowHistory, currentTime, LogDir); prevHash = currentHash; } } Sleep(1000); // 每秒检查一次 } exit_monitoring: // 注销热键 UnregisterHotKey(NULL, 1); // 清理 CloseDesktop(hDefaultDesk); CloseDesktop(hWinlogonDesk); CloseWindowStation(hWinsta); } // 检测文件编码 FileEncoding DetectFileEncoding(const std::wstring& filePath) { std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for encoding detection: " << filePath << std::endl; return FileEncoding::UNKNOWN; } unsigned char bom[3] = { 0 }; file.read(reinterpret_cast<char*>(bom), 3); file.close(); // Check BOM (Byte Order Mark) if (bom[0] == 0xFF && bom[1] == 0xFE) { return FileEncoding::UTF16LE; // UTF-16 Little Endian } else if (bom[0] == 0xFE && bom[1] == 0xFF) { return FileEncoding::UTF16BE; // UTF-16 Big Endian } else if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) { return FileEncoding::UTF8; // UTF-8 with BOM } else { return FileEncoding::ANSI; // Default to ANSI } } // 读取文件内容,处理不同编码格式 bool ReadFileWithEncoding(const std::wstring& filePath, std::wstring& content) { FileEncoding encoding = DetectFileEncoding(filePath); if (encoding == FileEncoding::UTF16LE || encoding == FileEncoding::UTF16BE) { // Use wide string stream to read UTF-16 encoded file std::wifstream wfile(filePath, std::ios::binary); if (!wfile.is_open()) { std::wcerr << L"Failed to open UTF-16 file: " << filePath << std::endl; return false; } wfile.imbue(std::locale(wfile.getloc(), new std::codecvt_utf16<wchar_t, 0x10FFFF, std::little_endian>)); std::wstringstream wss; wss << wfile.rdbuf(); content = wss.str(); wfile.close(); } else if (encoding == FileEncoding::UTF8) { // Use standard string stream to read UTF-8 encoded file std::ifstream file(filePath); if (!file.is_open()) { std::wcerr << L"Failed to open UTF-8 file: " << filePath << std::endl; return false; } std::stringstream ss; ss << file.rdbuf(); std::string utf8Content = ss.str(); // Convert UTF-8 string to wide string std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; content = converter.from_bytes(utf8Content); file.close(); } else if (encoding == FileEncoding::ANSI) { // Use standard string stream to read ANSI encoded file std::ifstream file(filePath); if (!file.is_open()) { std::wcerr << L"Failed to open ANSI file: " << filePath << std::endl; return false; } std::stringstream ss; ss << file.rdbuf(); std::string ansiContent = ss.str(); // Convert ANSI string to wide string (using current locale) content = std::wstring(ansiContent.begin(), ansiContent.end()); file.close(); } else { std::wcerr << L"Unknown file encoding." << std::endl; return false; } return true; } // 宽字符到多字节字符的转换 (ANSI) std::string WideStringToString(const std::wstring& wstr) { if (wstr.empty()) return std::string(); // 使用系统默认代码页 (CP_ACP) 将宽字符转换为多字节字符 int size_needed = WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); std::string str(size_needed, 0); WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), &str[0], size_needed, NULL, NULL); return str; } // 根据编码保存修改后的文件内容 bool ModifyAndSaveFileWithEncoding(const std::wstring& filePath, const std::wstring& content) { FileEncoding encoding = DetectFileEncoding(filePath); if (encoding == FileEncoding::UTF16LE) { // Save as UTF-16 Little Endian std::wofstream wfile(filePath, std::ios::binary); if (!wfile.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } wfile.imbue(std::locale(wfile.getloc(), new std::codecvt_utf16<wchar_t, 0x10FFFF, std::little_endian>)); wfile << content; wfile.close(); } else if (encoding == FileEncoding::UTF8) { // Save as UTF-8 std::ofstream file(filePath); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; std::string utf8Content = converter.to_bytes(content); file << utf8Content; file.close(); } else if (encoding == FileEncoding::ANSI) { // Save as ANSI std::ofstream file(filePath); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } std::string ansiContent = WideStringToString(content); file << ansiContent; file.close(); } else { std::wcerr << L"Unknown file encoding, cannot save file." << std::endl; return false; } return true; } // 修改 SeAssignPrimaryTokenPrivilege 权限 bool ModifySeAssignPrimaryTokenPrivilege(const std::wstring& configFilePath, const std::wstring& userName) { std::wstring content; // 读取文件内容 if (!ReadFileWithEncoding(configFilePath, content)) { std::wcerr << L"Failed to read configuration file with correct encoding." << std::endl; return false; } // 查找并修改 SeAssignPrimaryTokenPrivilege size_t pos = content.find(L"SeAssignPrimaryTokenPrivilege"); if (pos != std::wstring::npos) { size_t endPos = content.find(L"\n", pos) - 1; std::wstring line = content.substr(pos, endPos - pos); // 检查是否包含当前用户 if (line.find(userName) == std::wstring::npos) { line += L"," + userName; content.replace(pos, endPos - pos, line); } } // 保存修改后的文件 if (!ModifyAndSaveFileWithEncoding(configFilePath, content)) { std::wcerr << L"Failed to save modified configuration file." << std::endl; return false; } return true; } // 执行命令 bool ExecuteCommandWithOutput(const std::wstring& command, std::wstring& output) { STARTUPINFOW si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; HANDLE hRead, hWrite; // Initialize security attributes for pipe handles ZeroMemory(&sa, sizeof(sa)); sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; // Create a pipe for the child process's STDOUT and STDERR if (!CreatePipe(&hRead, &hWrite, &sa, 0)) { std::wcerr << L"Failed to create pipe." << std::endl; return false; } // Ensure the read handle to the pipe is not inherited if (!SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0)) { std::wcerr << L"Failed to set handle information." << std::endl; return false; } // Set up the STARTUPINFO structure for the child process ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.hStdError = hWrite; si.hStdOutput = hWrite; si.dwFlags |= STARTF_USESTDHANDLES; // Redirect STDOUT and STDERR to the pipe // Set up the PROCESS_INFORMATION structure ZeroMemory(&pi, sizeof(pi)); // Create the child process with CREATE_NO_WINDOW to avoid showing a new console window if (!CreateProcessW(NULL, const_cast<LPWSTR>(command.c_str()), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { std::wcerr << L"Failed to execute command: " << command << std::endl; CloseHandle(hRead); CloseHandle(hWrite); return false; } // Close the write end of the pipe in the parent process CloseHandle(hWrite); // Read the output from the child process DWORD bytesRead; CHAR buffer[4096]; std::string ansiResult; BOOL success = FALSE; ZeroMemory(buffer, sizeof(buffer) * sizeof(CHAR)); // Continuously read from the pipe until there's no more data while (true) { success = ReadFile(hRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL); if (!success || bytesRead == 0) { break; // No more data to read } buffer[bytesRead] = '\0'; // Null-terminate the buffer ansiResult += buffer; // Collect the ANSI result } // Close handles CloseHandle(hRead); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); // If the result is empty, return "Output is empty" if (ansiResult[0] == '\0') { output = L"\n(no message)\n"; return true; } // Convert the ANSI result to wide string (UTF-16) for proper Unicode handling int wideSize = MultiByteToWideChar(CP_ACP, 0, ansiResult.c_str(), -1, NULL, 0); if (wideSize == 0) { std::wcerr << L"Failed to convert output to wide string." << std::endl; return false; } std::wstring wideResult(wideSize, 0); MultiByteToWideChar(CP_ACP, 0, ansiResult.c_str(), -1, &wideResult[0], wideSize); output = wideResult; return true; } // 判断文件是否存在 BOOL IsFileExist(const std::wstring& wsFile) { DWORD dwAttrib = GetFileAttributesW(wsFile.c_str()); return INVALID_FILE_ATTRIBUTES != dwAttrib && 0 == (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); } // 导出安全策略 bool ExportSecurityPolicy(const std::wstring& outputFile, std::wstring& output) { // https://blog.csdn.net/u0/article/details/ if (IsFileExist(outputFile)) { // 判断配置文件是否已经存在 std::wcout << L"The configuration file already exists. Attempting to delete it." << std::endl; if (!DeleteFileW(outputFile.c_str())) { std::wcerr << L"Failed to delete the configuration file." << std::endl; return false; } } std::wstring command = L"secedit /export /cfg " + outputFile; return ExecuteCommandWithOutput(command, output); } // 应用修改后的安全策略 bool ApplySecurityPolicy(const std::wstring& configFilePath, const std::wstring& dbFilePath, std::wstring& output) { std::wstring command = L"secedit /configure /db " + dbFilePath + L" /cfg " + configFilePath + L" /overwrite /quiet"; return ExecuteCommandWithOutput(command, output); } // 获取系统盘符 std::wstring GetSystemDrive() { wchar_t systemPath[MAX_PATH]; if (GetSystemDirectoryW(systemPath, MAX_PATH)) { return std::wstring(systemPath).substr(0, 3); // 返回盘符部分,例如 C: } return L"C:\\"; // 如果获取失败,返回默认 C 盘符 } // 获取当前用户账户名称 std::wstring GetCurrentUserName() { wchar_t userName[UNLEN + 1]; DWORD size = UNLEN + 1; if (GetUserNameW(userName, &size)) { return std::wstring(userName); } return L""; // 返回空字符串表示获取失败 } // 提示用户注销 void PromptUserToLogout() { // Ask the user if they want to log off and re-log in int result = MessageBoxW(NULL, L"The security settings have been updated. Would you like to log off now and apply the changes?", L"Log Off Confirmation", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL); if (result == IDYES) { // Log off the user if (!ExitWindowsEx(EWX_LOGOFF | EWX_FORCE, SHTDN_REASON_MAJOR_OTHER)) { std::wcerr << L"Failed to log off the user." << std::endl; } } else { std::wcout << L"User chose not to log off." << std::endl; } } bool ChangeAssignPrimaryTokenPrivilege() { std::wstring systemDrive = GetSystemDrive(); std::wstring configFilePath = systemDrive + L"gp.inf"; std::wstring dbFilePath = systemDrive + L"test.sdb"; std::wstring userName = GetCurrentUserName(); std::wstring output; if (userName.empty()) { std::wcerr << L"Failed to retrieve the current user name." << std::endl; return false; } if (!ExportSecurityPolicy(configFilePath, output)) { std::wcerr << L"Failed to export security policy." << std::endl; return false; } std::wcout << L"\nExportSecurityPolicy message: \n" << output << std::endl; if (!ModifySeAssignPrimaryTokenPrivilege(configFilePath, userName)) { std::wcerr << L"Failed to modify SeAssignPrimaryTokenPrivilege." << std::endl; return false; } if (!ApplySecurityPolicy(configFilePath, dbFilePath, output)) { std::wcerr << L"Failed to apply security policy." << std::endl; return false; } std::wcout << L"\nApplySecurityPolicy message: \n" << output << std::endl; PromptUserToLogout(); return true; } int wmain(int argc, wchar_t* argv[]) { // 宽字符中文支持 _wsetlocale(LC_ALL, L"zh-CN"); // 获取当前进程的主窗口 hMainWindow = GetMainWindowHandle(); if (!hMainWindow) { std::cerr << "Failed to find main window." << std::endl; system("pause > nul 2 > nul"); return 1; } std::cout << "Main window handle: " << std::hex << hMainWindow << std::endl; // 检查是否为管理员权限运行 if (!IsRunAsAdmin()) { std::wcout << L"Attempting to restart with administrator privileges..." << std::endl; if (RelaunchAsAdmin()) { return 0; // 提升后进程将重新启动,当前进程结束 } else { std::wcerr << L"Failed to relaunch as administrator." << std::endl; system("pause > nul 2 > nul"); return 1; } } // 启用 SeDebugPrivilege if (!EnablePrivilege(SE_DEBUG_NAME)) { std::wcerr << L"Failed to enable SeDebugPrivilege." << std::endl; system("pause > nul 2 > nul"); return 1; } // 启用 SeDebugPrivilege if (!EnablePrivilege(SE_INCREASE_QUOTA_NAME)) { std::wcerr << L"Failed to enable SeIncreaseQuotaPrivilege." << std::endl; system("pause > nul 2 > nul"); return 1; } // 启用 SeDebugPrivilege if (!EnablePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME)) { std::wcerr << L"Failed to enable SeAssignPrimaryTokenPrivilege." << std::endl; if (!ChangeAssignPrimaryTokenPrivilege()) { std::wcerr << L"Failed to enable primary token assignment privilege" << L" by modifying security configuration." << std::endl; } std::wcout << L"Press any key to close app."; system("pause > nul 2 > nul"); return 1; } // 检查 SYSTEM 权限 if (!IsSystem()) { std::wcout << L"Attempting to restart with SYSTEM privileges..." << std::endl; // 检查命令行参数,避免无限递归 if (argc < 2 || _wcsicmp(argv[1], L"system") != 0) { // 重新启动自身并传递 "system" 参数 wchar_t commandLine[MAX_PATH]; swprintf(commandLine, MAX_PATH, L"%s system", argv[0]); if (CreateSystemProcess(argv[0], commandLine)) { std::wcout << L"Restarted with SYSTEM privileges." << std::endl; } else { std::wcerr << L"Failed to restart with SYSTEM privileges." << std::endl; system("pause > nul 2 > nul"); } return 0; } else { std::wcerr << L"Already tried to elevate privileges but failed." << std::endl; system("pause > nul 2 > nul"); return 1; } } // 如果当前进程已经是 SYSTEM 权限,则继续执行桌面监控 MonitorDesktop(); std::wcout << L"Press any key to close app."; system("pause > nul 2 > nul"); return 0; }

版本 1.0.0.3 修复内容:

  1. 修复了在创建历史记录对话框时,在堆栈上传递共享地址导致的内存访问问题;
  2. 修复了发送窗口关闭消息但但有时候不能够完成退出的问题;
  3. 修复了多线程的等待和检查机制(部分地方之前采用了无限等待或者不安全的检查);
  4. 根据 C++20 ,取消了对 codecvt 头文件及其 API 的使用;
  5. 使用静态链接和 Win32 的绝对路径缓解使用当前目录的模块劫持漏洞;
  6. 修复在部分系统上,日志文本不正确回车换行的问题;

版本 1.0.0.3 代码:

#include <windows.h> #include <iostream> #include <lmcons.h> #include <fstream> #include <string> #include <vector> #include <ctime> #include <sstream> #include <functional> #include <tlhelp32.h> #include <userenv.h> #include <sddl.h> #include "resource.h" #pragma comment(lib, "user32.lib") #pragma comment(lib, "shell32.lib") #pragma comment(lib, "gdi32.lib") #pragma comment(lib, "advapi32.lib") #pragma comment(linker,"\"/manifestdependency:type='win32' "\ "name='Microsoft.Windows.Common-Controls' "\ "version='6.0.0.0' processorArchitecture='*' "\ "publicKeyToken='6595b64144ccf1df' language='*'\"") constexpr auto WM_UPDATE_STATUS = WM_APP + 10; // 传递新的状态信息的用户自定义消息 constexpr auto STATUS_MSG_HASH = 0x; // 内部校验码(防止外部程序模拟消息) constexpr auto LOGING_STATUS_INFO = L"Now Monitoring..."; // 显示提示:正在扫描窗口 constexpr auto COMPLETE_STATUS_INFO = L"Monitoring completed!"; // 显示提示:窗口扫描已完成 constexpr auto LOG_FILE_NAME = L"window_hierarchy.log"; // 默认日志记录保存位置 constexpr auto MAX_STSTUS_BUFFER = 350; // 状态信息的最大缓冲区大小 constexpr auto MONITOR_THREAD_TIMEOUT = 25000; // 窗口监视器线程超时(默认 25 秒) constexpr auto CREATE_BORDER_WINDOW_TIMEOUT = 0x493E0; // 边框窗口创建超时(默认 5 分钟) constexpr auto SYSTEM32_NAME = L"System32"; // 记录窗口信息的结构体,使用宽字符 struct WindowInfo { std::wstring className; std::wstring windowTitle; HWND hwnd; std::vector<WindowInfo> children; }; // 监视器线程存储 struct MYMONITOR_THREAD_INFO { HDESK hDesktop; std::vector<WindowInfo> wndInfo; }; // 状态信息参数传递结构 struct MYSTATUS_UPDATE_THREAD_INFO { HDESK hChangeToDesktop; WCHAR wcsBuffer[MAX_STSTUS_BUFFER]; }; // 文件编码格式(处理系统安全配置文件时用到) enum class FileEncoding { ANSI, UTF8, UTF16LE, UTF16BE, UNKNOWN }; // 对话框(异步)参数结构(使用深拷贝) struct DIALOGBOX_PARAM_LIST { HINSTANCE hInstance; // 使用默认初始化值 LPCWSTR lpTemplateName; // 模板名称 HWND hWndParent; // 父窗口句柄 DLGPROC lpDialogFunc; // 对话框过程 LPARAM dwInitParam; // 初始化参数 size_t cbSize; // 参数大小 INT_PTR intResponse; // 响应值 DIALOGBOX_PARAM_LIST() : hInstance(nullptr), lpTemplateName(nullptr), hWndParent(nullptr), lpDialogFunc(nullptr), dwInitParam(0), cbSize(0), intResponse(0) {} // 复制构造函数,用于深拷贝 DIALOGBOX_PARAM_LIST(const DIALOGBOX_PARAM_LIST& other) { hInstance = other.hInstance; lpTemplateName = other.lpTemplateName; hWndParent = other.hWndParent; lpDialogFunc = other.lpDialogFunc; cbSize = other.cbSize; intResponse = other.intResponse; // 深拷贝 dwInitParam if (other.dwInitParam != 0 && other.cbSize > 0) { dwInitParam = (LPARAM)new BYTE[other.cbSize]; std::memcpy((void*)dwInitParam, (void*)other.dwInitParam, other.cbSize); } else { dwInitParam = 0; } } // 析构函数,释放深拷贝的内存 ~DIALOGBOX_PARAM_LIST() { if (dwInitParam != 0) { delete[](BYTE*)dwInitParam; dwInitParam = 0; } } // 赋值运算符重载,确保安全的深拷贝 DIALOGBOX_PARAM_LIST& operator=(const DIALOGBOX_PARAM_LIST& other) { if (this == &other) return *this; hInstance = other.hInstance; lpTemplateName = other.lpTemplateName; hWndParent = other.hWndParent; lpDialogFunc = other.lpDialogFunc; cbSize = other.cbSize; intResponse = other.intResponse; if (dwInitParam != 0) { delete[](BYTE*)dwInitParam; } // 深拷贝 dwInitParam if (other.dwInitParam != 0 && other.cbSize > 0) { dwInitParam = (LPARAM)new BYTE[other.cbSize]; std::memcpy((void*)dwInitParam, (void*)other.dwInitParam, other.cbSize); } else { dwInitParam = 0; } return *this; } }; // 用于边框绘制的全局变量 HWND hBorderWnd = NULL; // 边框窗口句柄 bool borderRunning = false; // 边框是否正在显示 // 全局变量保存主窗口句柄 HWND hMainWindow = NULL; HANDLE hBroderWndCreateEvent; // 获取当前进程完整文件路径的函数 std::wstring GetCurrentProcessFullPath() { // 初始缓冲区大小 DWORD size = MAX_PATH; std::vector<wchar_t> buffer(size); // 获取当前进程句柄 HANDLE hProcess = GetCurrentProcess(); // 查询完整的进程路径,可能需要多次尝试 while (true) { if (QueryFullProcessImageNameW(hProcess, 0, buffer.data(), &size)) { return std::wstring(buffer.data(), size); } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // 缓冲区大小不足,扩大缓冲区 size += MAX_PATH; buffer.resize(size); } else { // 其他错误情况,返回空字符串 std::wcerr << L"Failed to get full process image name. Error: " << GetLastError() << std::endl; return L""; } } } // 获取当前进程所在目录的函数 std::wstring GetCurrentProcessDirectory() { std::wstring fullPath = GetCurrentProcessFullPath(); if (fullPath.empty()) { std::wcerr << L"Failed to get process full path." << std::endl; return L""; } size_t lastBackslashPos = fullPath.find_last_of(L"\\/"); if (lastBackslashPos != std::wstring::npos) { return fullPath.substr(0, lastBackslashPos); // 提取目录部分 } else { std::wcerr << L"Failed to find directory in path." << std::endl; return L""; } } // 启用特定的权限(例如 SeDebugPrivilege) bool EnablePrivilege(LPCWSTR privilege) { HANDLE hToken; TOKEN_PRIVILEGES tp{}; LUID luid; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token." << std::endl; return false; } if (!LookupPrivilegeValueW(NULL, privilege, &luid)) { std::wcerr << L"Failed to lookup privilege." << std::endl; CloseHandle(hToken); return false; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { std::wcerr << L"Failed to adjust token privileges." << std::endl; CloseHandle(hToken); return false; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { std::wcerr << L"The privilege was not assigned." << std::endl; CloseHandle(hToken); return false; } CloseHandle(hToken); return true; } // 检查是否以管理员权限运行 bool IsRunAsAdmin() { BOOL isAdmin = FALSE; PSID administratorsGroup = NULL; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsGroup)) { CheckTokenMembership(NULL, administratorsGroup, &isAdmin); FreeSid(administratorsGroup); } return isAdmin == TRUE; } // 重新启动并请求管理员权限 bool RelaunchAsAdmin() { std::wstring szPath = GetCurrentProcessFullPath(); if (szPath.empty()) { std::wcerr << L"Failed to get module file name." << std::endl; return false; } SHELLEXECUTEINFOW sei = { sizeof(sei) }; sei.lpVerb = L"runas"; // 请求管理员权限 sei.lpFile = szPath.c_str(); sei.hwnd = NULL; sei.nShow = SW_NORMAL; if (!ShellExecuteExW(&sei)) { std::wcerr << L"Failed to relaunch as administrator." << std::endl; return false; } return true; } // 获取当前控制台的活动会话 DWORD WINAPI GetActiveConsoleSessionId() { return WTSGetActiveConsoleSessionId(); } // 判断进程是否在给定的会话中 BOOL WINAPI IsProcessInSession(DWORD processId, DWORD sessionId) { DWORD session; if (!ProcessIdToSessionId(processId, &session)) { printf("Error: ProcessIdToSessionId failed.\n"); return FALSE; } return session == sessionId; } // 获取当前会话的 Winlogon 进程的 PID DWORD WINAPI FindWinlogonProcessId() { DWORD dwProcessId = 0; DWORD activeSessionId = GetActiveConsoleSessionId(); if (activeSessionId == 0xFFFFFFFF) { printf("Error: Unable to retrieve active console session ID.\n"); return 0; } HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { printf("Error: CreateToolhelp32Snapshot failed.\n"); return 0; } PROCESSENTRY32W entry{}; entry.dwSize = sizeof(PROCESSENTRY32W); if (!Process32FirstW(snapshot, &entry)) { printf("Error: Process32First failed.\n"); CloseHandle(snapshot); return 0; } do { if (entry.cntThreads <= 1u) continue; // 跳过僵尸进程 if (_wcsicmp(entry.szExeFile, L"winlogon.exe") == 0) { if (IsProcessInSession(entry.th32ProcessID, activeSessionId)) { dwProcessId = entry.th32ProcessID; break; } } } while (Process32NextW(snapshot, &entry)); CloseHandle(snapshot); return dwProcessId; } // 获取 winlogon 进程的 SYSTEM 令牌 HANDLE GetSystemTokenFromWinlogon() { HANDLE hToken = nullptr; HANDLE hProcess = nullptr; HANDLE hSystemToken = nullptr; // 存储复制后的 SYSTEM 令牌 // 获取 Winlogon 进程的进程ID DWORD winlogonPid = FindWinlogonProcessId(); if (winlogonPid == 0) { std::wcerr << L"Failed to find winlogon.exe process!" << std::endl; return nullptr; } std::wcout << L"winlogon.exe PID: " << winlogonPid << std::endl; // 打开 Winlogon 进程 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, winlogonPid); if (!hProcess) { std::wcerr << L"Failed to open winlogon process!" << std::endl; return nullptr; } // 打开该进程的令牌 if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token!" << std::endl; CloseHandle(hProcess); return nullptr; } // 复制令牌以使其可用于新进程 if (!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, nullptr, SecurityImpersonation, TokenPrimary, &hSystemToken)) { std::wcerr << L"Failed to duplicate SYSTEM token!" << std::endl; CloseHandle(hToken); CloseHandle(hProcess); return nullptr; } // 关闭原始的令牌和进程句柄 CloseHandle(hToken); CloseHandle(hProcess); return hSystemToken; } // 创建具有 SYSTEM 权限的进程 bool CreateSystemProcess(LPCWSTR applicationName, LPCWSTR commandLine) { HANDLE hToken = GetSystemTokenFromWinlogon(); if (!hToken) { std::wcerr << L"Failed to get SYSTEM token!" << std::endl; return false; } // 使用 SYSTEM 权限创建进程 STARTUPINFOW si = { sizeof(STARTUPINFOW) }; PROCESS_INFORMATION pi = { 0 }; if (!CreateProcessAsUserW(hToken, applicationName, const_cast<LPWSTR>(commandLine), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { std::wcerr << L"Failed to create process as SYSTEM! err = " << GetLastError() << std::endl; CloseHandle(hToken); return false; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hToken); return true; } // 检查当前进程是否至少在 SYSTEM 用户组(Local System) bool IsSystem() { HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { return false; } DWORD tokenInfoLength = 0; GetTokenInformation(hToken, TokenUser, NULL, 0, &tokenInfoLength); PTOKEN_USER tokenUser = (PTOKEN_USER)malloc(tokenInfoLength); if (!GetTokenInformation(hToken, TokenUser, tokenUser, tokenInfoLength, &tokenInfoLength)) { CloseHandle(hToken); free(tokenUser); return false; } LPWSTR sidString = NULL; ConvertSidToStringSidW(tokenUser->User.Sid, &sidString); bool isSystem = (_wcsicmp(sidString, L"S-1-5-18") == 0); LocalFree(sidString); CloseHandle(hToken); free(tokenUser); return isSystem; } // 获取当前控制台窗口句柄 HWND GetConsoleHwnd(void) { // https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/obtain-console-window-handle #define MY_BUFSIZE 1024 // 控制台窗口标题的缓冲区大小。 HWND hwndFound; // 这是返回给调用方的内容。 wchar_t pszNewWindowTitle[MY_BUFSIZE]; // 包含编造的 // 窗口标题 // 构造一个“独特的”NewWindowTitle。 wsprintfW(pszNewWindowTitle, L"WinlogonWindowsMonitor - %d/%d", GetTickCount(), GetCurrentProcessId()); // 更改当前窗口标题。 SetConsoleTitleW(pszNewWindowTitle); // 确保窗口标题已更新。 Sleep(40); // 查找 NewWindowTitle。 hwndFound = FindWindowW(L"ConsoleWindowClass", pszNewWindowTitle); if(!hwndFound) hwndFound = FindWindowW(L"CASCADIA_HOSTING_WINDOW_CLASS", pszNewWindowTitle); #undef MY_BUFSIZE return(hwndFound); } // 获取当前进程的主窗口 HWND GetMainWindowHandle() { return GetConsoleHwnd(); } // 检查当前活动窗口是否是主窗口 bool IsMainWindowActive() { HWND hForeground = GetForegroundWindow(); // 获取当前活动窗口 return hForeground == hMainWindow; // 比较是否是主窗口 } // 边框窗口过程 LRESULT CALLBACK BorderWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static int borderWidth = 10; // 默认边框宽度 static wchar_t statusText[MAX_STSTUS_BUFFER]; switch (message) { case WM_CREATE: { // 清空存储状态提示信息的缓冲区 memset(statusText, 0, sizeof(statusText)); //memcpy(statusText, LOGING_STATUS_INFO, sizeof(statusText) - 5); // 窗口创建成功后,发出信号 SetEvent(hBroderWndCreateEvent); break; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT rect; GetClientRect(hWnd, &rect); // 设置边框颜色为蓝色 HBRUSH brush = CreateSolidBrush(RGB(119, 228, 155)); // 填充外边框到内边框之间的区域 RECT outerRect = rect; RECT innerRect = rect; // 根据边框宽度调整内边框位置 InflateRect(&innerRect, -borderWidth, -borderWidth); // 填充外边框和内边框之间的区域 if (borderWidth > 0) { // 填充四个部分:上边、下边、左边、右边 RECT topRect = { outerRect.left, outerRect.top, outerRect.right, innerRect.top }; RECT bottomRect = { outerRect.left, innerRect.bottom, outerRect.right, outerRect.bottom }; RECT leftRect = { outerRect.left, innerRect.top, innerRect.left, innerRect.bottom }; RECT rightRect = { innerRect.right, innerRect.top, outerRect.right, innerRect.bottom }; FillRect(hdc, &topRect, brush); FillRect(hdc, &bottomRect, brush); FillRect(hdc, &leftRect, brush); FillRect(hdc, &rightRect, brush); } DeleteObject(brush); // 设置字体 HFONT hFont = CreateFontW(36, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Arial"); SelectObject(hdc, hFont); // 设置文本颜色为高亮(例如绿色) SetTextColor(hdc, RGB(0, 255, 0)); SetBkMode(hdc, TRANSPARENT); // 透明背景 // 获取窗口文本并绘制在左下方,间隔 50 像素 rect.left += 50; // 左侧间隔 50 像素 rect.bottom -= 50; // 底部间隔 50 像素 DrawTextW(hdc, statusText, -1, &rect, DT_LEFT | DT_BOTTOM | DT_SINGLELINE); DeleteObject(hFont); EndPaint(hWnd, &ps); break; } case WM_UPDATE_STATUS: { if (wParam == STATUS_MSG_HASH) { // 获取窗口文本并绘制 memset(statusText, 0, sizeof(statusText)); memcpy(statusText, (LPVOID)lParam, sizeof(statusText) - 5); InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hWnd); } return 0; } case WM_CLOSE: DestroyWindow(hWnd); // 其他:用户已取消。什么都不做。 return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: break; } return DefWindowProcW(hWnd, message, wParam, lParam); } DWORD WINAPI CloseBorderThread(LPVOID lpParam) { HDESK hWinlogonDesk = static_cast<HDESK>(lpParam); // 传递的桌面句柄 HWND hBorderWndBack = hBorderWnd; //std::wcout << L"hWinlogonDesk in thread: " << hWinlogonDesk << std::endl; // 获取当前线程的桌面句柄,后面恢复用 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到 Winlogon 桌面 if (!SetThreadDesktop(hWinlogonDesk)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } // 发送关闭消息 if (hBorderWndBack != nullptr && IsWindow(hBorderWndBack)) { // 最小化窗口 CloseWindow(hBorderWndBack); // 关闭窗口 SendMessageW(hBorderWndBack, WM_CLOSE, 0, 0); // 销毁窗口(发送 WM_DESTROY 和 WM_NCDESTROY 消息) DestroyWindow(hBorderWndBack); } std::wcout << L"An exit monitoring message has been sent." << std::endl; // 恢复到原始桌面 SetThreadDesktop(hOriginalDesktop); return 0; } // 更新状态窗口显示的文本信息 DWORD WINAPI UpdateStatusWindowTextThread(LPVOID lpParam) { if (lpParam == nullptr) { std::wcerr << L"Invalid thread parameters." << std::endl; return ERROR_INVALID_PARAMETER; } __try { MYSTATUS_UPDATE_THREAD_INFO* info = static_cast<MYSTATUS_UPDATE_THREAD_INFO*>(lpParam); if (info->hChangeToDesktop == nullptr || info->wcsBuffer == nullptr) { std::wcerr << L"Invalid thread parameters." << std::endl; return ERROR_INVALID_PARAMETER; } // 获取当前线程的桌面句柄,后面恢复用 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到 Winlogon 桌面 if (!SetThreadDesktop(info->hChangeToDesktop)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } // 设置新的信息 if (hBorderWnd) { // 发送 WM_APP 类型消息给目标窗口过程 SendMessageW(hBorderWnd, WM_UPDATE_STATUS, STATUS_MSG_HASH, (LPARAM)info->wcsBuffer); } std::wcout << L"Update Status Text: " << info->wcsBuffer << std::endl; // 恢复到原始桌面 SetThreadDesktop(hOriginalDesktop); } __except (EXCEPTION_EXECUTE_HANDLER) { std::wcerr << L"Memory access violation." << std::endl; return ERROR_INVALID_ACCESS; } return 0; } // 在监视过程开始时显示提示信息 BOOL StartMonitoringUI(HDESK hWinlogonDesk) { // 切换到 winlogon 桌面并发送状态更新消息 MYSTATUS_UPDATE_THREAD_INFO info = { hWinlogonDesk }; wcscpy_s(info.wcsBuffer, LOGING_STATUS_INFO); HANDLE hThread = CreateThread(NULL, 0, UpdateStatusWindowTextThread, &info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, 7000); CloseHandle(hThread); Sleep(300); // 刻意的延时, TODO: 未来仅在绘制部 // 分做延时动画而不是对发消息的线程做延时 return TRUE; } return FALSE; } // 在监视过程完成时更新提示信息 BOOL CompleteMonitoringUI(HDESK hWinlogonDesk) { // 切换到 winlogon 桌面并发送状态更新消息 MYSTATUS_UPDATE_THREAD_INFO info = { hWinlogonDesk }; wcscpy_s(info.wcsBuffer, COMPLETE_STATUS_INFO); HANDLE hThread = CreateThread(NULL, 0, UpdateStatusWindowTextThread, &info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, 7000); CloseHandle(hThread); Sleep(300); // 刻意的延时 return TRUE; } return FALSE; } // 检查并关闭监视器 BOOL CloseMonitoringUI(HDESK hWinlogonDesk) { // 启动关闭边框的线程 if (borderRunning) { //std::wcout << L"hWinlogonDesk call: " << hWinlogonDesk << std::endl; HANDLE hCloseThread = CreateThread(NULL, 0, CloseBorderThread, (LPVOID)hWinlogonDesk, 0, NULL); WaitForSingleObject(hCloseThread, INFINITE); // 等待线程完成 CloseHandle(hCloseThread); borderRunning = false; } return FALSE; } // 边框绘制线程 DWORD WINAPI BorderThread(LPVOID lpParam) { std::wcout << L"Monitoring the target desktop..." << std::endl; HDESK hWinlogonDesk = static_cast<HDESK>(lpParam); // 传递的桌面句柄 // 切换到 Winlogon 桌面 if (!SetThreadDesktop(hWinlogonDesk)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } #define MY_BUFSIZE 1024 wchar_t pszWindowClass[MY_BUFSIZE]; // 构造一个特殊的窗口类名 wsprintfW(pszWindowClass, L"WinlogonMonitorBorderWindowClass[%d::%d]", GetTickCount(), GetCurrentProcessId()); HINSTANCE hInstance = GetModuleHandleW(NULL); WNDCLASS wc = { 0 }; wc.lpfnWndProc = BorderWndProc; wc.hInstance = hInstance; wc.lpszClassName = pszWindowClass; wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); if (!RegisterClassW(&wc)) { std::wcerr << L"Failed to RegisterClass." << std::endl; return 1; } // 获取屏幕大小并创建无边框窗口 int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); hBorderWnd = CreateWindowExW(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, L"WinlogonMonitorBorderWindow", WS_POPUP, 0, 0, screenWidth, screenHeight, NULL, NULL, hInstance, NULL); if (!hBorderWnd) { std::wcerr << L"Failed to CreateWindow." << std::endl; return 1; } // 设置窗口透明度 SetLayeredWindowAttributes(hBorderWnd, RGB(255, 255, 255), 0, LWA_COLORKEY); ShowWindow(hBorderWnd, SW_SHOW); UpdateWindow(hBorderWnd); // 消息循环,等待关闭事件 MSG msg; while (true) { // 检查并处理窗口消息 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } } // 销毁窗口 DestroyWindow(hBorderWnd); #undef MY_BUFSIZE return 0; } // 递归获取窗口层次,使用宽字符 API void EnumChildWindowsRecursive(HWND hwndParent, std::vector<WindowInfo>& windowList) { wchar_t className[256]; wchar_t windowTitle[256]; ZeroMemory(className, sizeof(className)); ZeroMemory(windowTitle, sizeof(windowTitle)); // 获取窗口类名和标题 GetClassNameW(hwndParent, className, sizeof(className) / sizeof(wchar_t)); GetWindowTextW(hwndParent, windowTitle, sizeof(windowTitle) / sizeof(wchar_t)); if (className[0] == L'\0') { wcscpy_s(className, L"(None)"); } if (windowTitle[0] == L'\0') { wcscpy_s(windowTitle, L"(None)"); } WindowInfo windowInfo = { className, windowTitle, hwndParent }; // 枚举子窗口 EnumChildWindows(hwndParent, [](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* children = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *children); return TRUE; }, reinterpret_cast<LPARAM>(&windowInfo.children)); windowList.push_back(windowInfo); } // 获取当前桌面窗口层次,使用宽字符 API std::vector<WindowInfo> GetWindowHierarchy() { std::vector<WindowInfo> windows; EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* windows = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *windows); return TRUE; }, reinterpret_cast<LPARAM>(&windows)); return windows; } // 获取当前窗口层次的哈希值,用于检测窗口层次是否发生变化 std::size_t GetWindowHierarchyHash(const std::vector<WindowInfo>& windows) { std::hash<std::wstring> hash_fn; std::size_t hash = 0; std::function<void(const std::vector<WindowInfo>&)> computeHash; computeHash = [&hash_fn, &hash, &computeHash](const std::vector<WindowInfo>& windows) { for (const auto& window : windows) { hash ^= hash_fn(window.className); hash ^= hash_fn(window.windowTitle); computeHash(window.children); // 递归计算子窗口的哈希值 } }; computeHash(windows); return hash; } // 格式化时间为宽字符格式 std::wstring FormatTime(const std::time_t& time) { wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &time); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); return std::wstring(timeBuffer); } // 宽字符到 UTF-8 的转换(使用 UTF-8 目标编码) std::string WideStringToUTF8(const std::wstring& wideStr) { if (wideStr.empty()) return std::string(); // 计算转换所需的缓冲区大小 int size_needed = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), static_cast<int>(wideStr.size()), NULL, 0, NULL, NULL); if (size_needed <= 0) { throw std::runtime_error("WideCharToMultiByte failed to calculate buffer size."); } // 分配缓冲区并进行转换 std::string str(size_needed, 0); int bytes_written = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), static_cast<int>(wideStr.size()), &str[0], size_needed, NULL, NULL); if (bytes_written <= 0) { throw std::runtime_error("WideCharToMultiByte failed to convert string to UTF-8."); } return str; } // UTF-8 到宽字符的转换 std::wstring UTF8ToWideString(const std::string& utf8Str) { if (utf8Str.empty()) return std::wstring(); // 获取所需的宽字符缓冲区大小(包括空终止符) int size_needed = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), static_cast<int>(utf8Str.size()), NULL, 0); if (size_needed <= 0) { throw std::runtime_error("MultiByteToWideChar failed to calculate buffer size."); } std::wstring wideStr(size_needed, 0); int chars_written = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), static_cast<int>(utf8Str.size()), &wideStr[0], size_needed); if (chars_written <= 0) { throw std::runtime_error("MultiByteToWideChar failed to convert UTF-8 string to wide string."); } return wideStr; } // 将 ANSI 字符串转换为宽字符串(根据当前系统代码页) std::wstring ANSIToWideString(const std::string& ansiStr, UINT codePage = CP_ACP) { if (ansiStr.empty()) return std::wstring(); int size_needed = MultiByteToWideChar(codePage, 0, ansiStr.c_str(), static_cast<int>(ansiStr.size()), NULL, 0); if (size_needed <= 0) { throw std::runtime_error("MultiByteToWideChar failed to calculate buffer size."); } std::wstring wideStr(size_needed, 0); MultiByteToWideChar(codePage, 0, ansiStr.c_str(), static_cast<int>(ansiStr.size()), &wideStr[0], size_needed); return wideStr; } // 将宽字符转换为 ANSI 编码的字符串(基于当前系统的代码页) std::string WideStringToANSI(const std::wstring& wideStr, UINT codePage = CP_ACP) { if (wideStr.empty()) return std::string(); int size_needed = WideCharToMultiByte(codePage, 0, wideStr.c_str(), static_cast<int>(wideStr.size()), NULL, 0, NULL, NULL); if (size_needed <= 0) { throw std::runtime_error("WideCharToMultiByte failed to calculate buffer size."); } std::string ansiStr(size_needed, 0); WideCharToMultiByte(codePage, 0, wideStr.c_str(), static_cast<int>(wideStr.size()), &ansiStr[0], size_needed, NULL, NULL); return ansiStr; } // 读取 UTF-8 文件内容并转换为宽字符串 bool ReadUTF8File(const std::wstring& filePath, std::wstring& content) { std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open UTF-8 file: " << filePath << std::endl; return false; } std::stringstream ss; ss << file.rdbuf(); std::string utf8Content = ss.str(); file.close(); // 将 UTF-8 内容转换为宽字符内容 content = UTF8ToWideString(utf8Content); return true; } // 手动读取 UTF-16 LE 编码的文件 bool ReadUTF16LEFile(const std::wstring& filePath, std::wstring& content) { std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open UTF-16 LE file: " << filePath << std::endl; return false; } // 跳过 BOM (2 bytes) file.seekg(2, std::ios::beg); std::wstringstream wss; char buffer[2]; while (file.read(buffer, 2)) { // Little Endian 解码为 wchar_t wchar_t wchar = (static_cast<unsigned char>(buffer[1]) << 8) | static_cast<unsigned char>(buffer[0]); wss << wchar; } content = wss.str(); file.close(); return true; } // 手动转换 UTF-16 BE 到宽字符 std::wstring ConvertUTF16BEToWideString(const std::vector<char>& buffer) { if (buffer.size() % 2 != 0) { throw std::runtime_error("Invalid UTF-16 BE byte stream length."); } std::wstring result; for (size_t i = 0; i < buffer.size(); i += 2) { // 将两个字节转换为 wchar_t,并转换字节序 wchar_t wchar = (static_cast<unsigned char>(buffer[i]) << 8) | static_cast<unsigned char>(buffer[i + 1]); result += wchar; } return result; } // 保存窗口层次信息到文件,使用 UTF-8 编码 void SaveWindowHierarchy( const std::vector<WindowInfo>& windows, const std::time_t& changeTime, const std::wstring& filename ) { std::ofstream file(WideStringToUTF8(filename), std::ios::app | std::ios::binary); // 使用 binary 防止换行符的意外转换 if (!file.is_open()) { std::wcerr << L"Unable to open file for writing!" << std::endl; return; } // 在文件开头写入 BOM,标识为 UTF-8 编码 static bool bomWritten = false; if (!bomWritten) { const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; // UTF-8 BOM file.write(reinterpret_cast<const char*>(bom), sizeof(bom)); bomWritten = true; } // 格式化时间为字符串 wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &changeTime); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); // 写入时间戳 file << WideStringToUTF8(std::wstring(timeBuffer)) << "\r\n"; // 递归写入窗口信息 std::function<void(const std::vector<WindowInfo>&, int)> WriteHierarchy; WriteHierarchy = [&file, &WriteHierarchy](const std::vector<WindowInfo>& windows, int indent) { for (const auto& window : windows) { file << std::string(indent, ' ') // 使用空格进行缩进 << "Class Name: " << WideStringToUTF8(window.className) << ", Title: " << WideStringToUTF8(window.windowTitle) << ", HWND: " << std::hex << window.hwnd << "\r\n"; if (!window.children.empty()) { WriteHierarchy(window.children, indent + 4); // 递归写入子窗口信息 } } }; WriteHierarchy(windows, 0); file << "\r\n"; file.close(); } // 获取当前桌面的名称 std::wstring GetDesktopName(HDESK hDesktop) { wchar_t desktopName[256]{}; DWORD neededLength = 0; if (!GetUserObjectInformationW(hDesktop, UOI_NAME, desktopName, sizeof(desktopName), &neededLength)) { std::wcerr << L"Failed to get desktop name." << std::endl; return L""; } return std::wstring(desktopName); } // 切换到指定桌面并枚举窗口,任务结束后恢复到原始桌面 std::vector<WindowInfo> GetWindowsFromDesktop(HDESK hDesktop) { // 获取原始桌面句柄 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到目标桌面 if (!SetThreadDesktop(hDesktop)) { std::wcerr << L"Failed to set thread desktop!" << std::endl; return {}; } // 切换后获取窗口层次 std::vector<WindowInfo> windows = GetWindowHierarchy(); // 恢复到原始桌面 if (!SetThreadDesktop(hOriginalDesktop)) { std::wcerr << L"Failed to restore original desktop!" << std::endl; } return windows; } // 使用辅助线程进行桌面切换和窗口枚举 DWORD WINAPI MonitorDesktopThread(LPVOID param) { _wsetlocale(LC_ALL, L"zh-CN"); MYMONITOR_THREAD_INFO* threadInfo = static_cast<MYMONITOR_THREAD_INFO*>(param); threadInfo->wndInfo = GetWindowsFromDesktop(threadInfo->hDesktop); return 0; } // 打开窗口工作站并切换到桌面 HDESK OpenDesktopWithWindowStation(LPCWSTR desktopName, HWINSTA hWinsta) { // 打开桌面 HDESK hDesktop = OpenDesktopW(desktopName, 0, FALSE, GENERIC_ALL); if (!hDesktop) { std::wcerr << L"Failed to open desktop! name = " << desktopName << std::endl; CloseWindowStation(hWinsta); } return hDesktop; } // 获取当前活动桌面 HDESK GetActiveDesktop() { return OpenInputDesktop(0, FALSE, GENERIC_ALL); } // 对话框过程函数 INT_PTR CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { static std::wstring* historyText = nullptr; switch (message) { case WM_INITDIALOG: { historyText = reinterpret_cast<std::wstring*>(lParam); // 获取编辑控件句柄 HWND hEdit = GetDlgItem(hDlg, IDC_EDIT1); if (hEdit) { // 设置文本 SetWindowTextW(hEdit, historyText->c_str()); } } return (INT_PTR)TRUE; case WM_SIZE: { // 获取对话框的新尺寸 int width = LOWORD(lParam); // 新的宽度 int height = HIWORD(lParam); // 新的高度 // 调整编辑控件大小 HWND hEdit = GetDlgItem(hDlg, IDC_EDIT1); if (hEdit) { int margin = 25; MoveWindow(hEdit, margin, margin, width - 2 * margin, height - 80, TRUE); } // 调整OK按钮的位置 HWND hButtonOK = GetDlgItem(hDlg, IDOK); if (hButtonOK) { int buttonWidth = 120; int buttonHeight = 35; int margin = 15; // 按钮位于对话框底部,右边留一定的边距 MoveWindow(hButtonOK, width - buttonWidth - margin, height - buttonHeight - margin, buttonWidth, buttonHeight, TRUE); } } break; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; default: return FALSE; } return FALSE; } // 使用辅助线程进行桌面切换和窗口枚举 DWORD WINAPI DialogBoxParamThread(LPVOID param) { _wsetlocale(LC_ALL, L"zh-CN"); if (param == nullptr) { return ERROR_INVALID_PARAMETER; } DIALOGBOX_PARAM_LIST* dialogParam = static_cast<DIALOGBOX_PARAM_LIST*>(param); // 使用副本进行操作 DIALOGBOX_PARAM_LIST localCopy = *dialogParam; delete dialogParam; // 删除传入的原始参数 if (localCopy.lpDialogFunc == nullptr || localCopy.dwInitParam == 0) { return ERROR_INVALID_PARAMETER; } //std::wcout << L"localCopy.dwInitParam " << std::hex << localCopy.dwInitParam << std::endl; localCopy.intResponse = DialogBoxParamW(localCopy.hInstance, localCopy.lpTemplateName, localCopy.hWndParent, localCopy.lpDialogFunc, (LPARAM)&(localCopy.dwInitParam)); Sleep(1000); // 清理 dwInitParam 的内容,不删除外部指针 memset((void*)localCopy.dwInitParam, 0, localCopy.cbSize); return 0; } // 异步显示历史记录对话框 void CreateDialogBoxAsyncW(std::wstring text) { const size_t cbSize = text.length() + 1; WCHAR* wcsText = new (std::nothrow) WCHAR[cbSize]; wcscpy_s(wcsText, cbSize, text.c_str()); // 显示历史记录对话框 DIALOGBOX_PARAM_LIST* param = new DIALOGBOX_PARAM_LIST(); // 动态分配结构体 param->hInstance = GetModuleHandleW(NULL); param->lpTemplateName = MAKEINTRESOURCE(IDD_DIALOG1); param->hWndParent = NULL; param->lpDialogFunc = HistoryDialogProc; param->cbSize = cbSize * sizeof(WCHAR); param->dwInitParam = (LPARAM)wcsText; // 动态分配内存并赋值 //std::wcout << L"WindowsLog: " << text << std::endl; HANDLE hThread = CreateThread(NULL, 0, DialogBoxParamThread, param, 0, NULL); } // 监控桌面切换 void MonitorDesktop() { // 注册 Ctrl + Shift + F1 组合键为全局热键 (ID = 1) if (!RegisterHotKey(NULL, 1, MOD_CONTROL | MOD_SHIFT, VK_F1)) { std::wcerr << L"Failed to register hotkey for Ctrl + Shift + F1." << std::endl; return; } // 打开窗口工作站 HWINSTA hWinsta = OpenWindowStationW(L"WinSta0", FALSE, GENERIC_READ | GENERIC_WRITE); if (!hWinsta) { std::wcerr << L"Failed to open window station!" << std::endl; return; } // 将当前线程关联到工作站 if (!SetProcessWindowStation(hWinsta)) { std::wcerr << L"Failed to set process window station!" << std::endl; CloseWindowStation(hWinsta); return; } HDESK hDefaultDesk = OpenDesktopWithWindowStation(L"Default", hWinsta); HDESK hWinlogonDesk = OpenDesktopWithWindowStation(L"Winlogon", hWinsta); if (!hDefaultDesk || !hWinlogonDesk) { std::wcerr << L"Failed to open desktops!" << std::endl; CloseWindowStation(hWinsta); return; } std::wcout << L"Monitoring desktop changes (SYSTEM privileges detected)..." << std::endl; std::wcout << L"Desktops: Winlogon(" << std::hex << (UINT64)hWinlogonDesk << L"), Default(" << std::hex << (UINT64)hDefaultDesk << L")." << std::endl; // 桌面监控代码 // 获取文件所在目录 std::wstring szLogPath = GetCurrentProcessDirectory(); if (szLogPath.empty()) { std::wcerr << L"Failed to get module file path." << std::endl; szLogPath = L".\\"; szLogPath += LOG_FILE_NAME; } else { // 追加日志文件名,构成完整的日志路径 szLogPath += L"\\"; szLogPath += LOG_FILE_NAME; } std::vector<WindowInfo> windowHistory; std::size_t prevHash = 0; // 保存上一次的哈希值 bool monitoring = false; MYMONITOR_THREAD_INFO monitor_info = { hWinlogonDesk }; HANDLE hBorderThread = nullptr; // 边框线程句柄 DWORD errCnt = 0; // 循环中发生非致命错误次数 while (errCnt <= 5u) { MSG msg; // 非阻塞的消息检查 while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { if (IsMainWindowActive() && msg.message == WM_HOTKEY && msg.wParam == 1) { // Ctrl + Shift + F1 热键触发,退出循环 std::wcout << L"Ctrl + Shift + F1 pressed. Exiting monitoring..." << std::endl; Sleep(1000); // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); goto exit_monitoring; } TranslateMessage(&msg); DispatchMessageW(&msg); } HDESK hCurrentDesk = GetActiveDesktop(); // 获取活动桌面句柄 std::wstring desktopName = GetDesktopName(hCurrentDesk); // 获取桌面名称 //std::wcout << L"Current Desktop: " << desktopName << std::endl; if (desktopName == L"Winlogon" && !monitoring) { std::wcout << L"Switched to Winlogon desktop. Start monitoring window hierarchy..." << std::endl; monitoring = true; windowHistory.clear(); // 清空之前的记录 // 创建事件对象 hBroderWndCreateEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); // 启动边框绘制线程,并传递 Winlogon 桌面句柄 if (!borderRunning) { hBorderThread = CreateThread(nullptr, 0, BorderThread, (LPVOID)hWinlogonDesk, 0, nullptr); borderRunning = true; } // 在主线程等待窗口创建完成 WaitForSingleObject(hBroderWndCreateEvent, CREATE_BORDER_WINDOW_TIMEOUT); DWORD dwCreateBwndRet = 0; SetLastError(0); // 错误代码清零 dwCreateBwndRet == WaitForSingleObject(hBroderWndCreateEvent, CREATE_BORDER_WINDOW_TIMEOUT); if (dwCreateBwndRet != WAIT_OBJECT_0) { std::wcerr << L"WaitForSingleObject signal a failed: " << dwCreateBwndRet << L", GetLastError() == " << GetLastError() << std::endl; CloseHandle(hBroderWndCreateEvent); ++errCnt; // 增加错误计数 monitoring = false; // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); TerminateThread(hBorderThread, 0); continue; } // 设置状态为:记录中 StartMonitoringUI(hWinlogonDesk); // 切换到 winlogon 桌面并枚举窗口 HANDLE hThread = CreateThread(nullptr, 0, MonitorDesktopThread, &monitor_info, 0, nullptr); if (hThread) { DWORD waitRet = 0; SetLastError(0); // 错误代码清零 waitRet == WaitForSingleObject(hThread, MONITOR_THREAD_TIMEOUT); if (waitRet != WAIT_OBJECT_0) { std::wcerr << L"WaitForSingleObject signal a failed: " << waitRet << L", GetLastError() == " << GetLastError() << std::endl; CloseHandle(hThread); ++errCnt; // 增加错误计数 monitoring = false; // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); TerminateThread(hBorderThread, 0); continue; } CloseHandle(hThread); errCnt = 0; // 错误计数归零 } windowHistory = monitor_info.wndInfo; // 计算当前窗口层次的哈希值 prevHash = GetWindowHierarchyHash(windowHistory); // 设置状态为:已完成 CompleteMonitoringUI(hWinlogonDesk); } else if (desktopName == L"Default" && monitoring) { std::wcout << L"Switched back to Default desktop. Stopping monitoring..." << std::endl; std::time_t currentTime = std::time(nullptr); SaveWindowHierarchy(windowHistory, currentTime, szLogPath.c_str()); monitoring = false; // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); // 格式化时间为字符串 wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &currentTime); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); // 构建窗口层次信息文本 std::wstringstream ss; ss << L"Most Recent History Call ( " << timeBuffer << L" ): \r\n"; for (const auto& win : windowHistory) { ss << L"Class Name: " << win.className << L", Title: " << win.windowTitle << L", HWND: " << std::hex << win.hwnd << L"\r\n"; } std::wstring historyText = ss.str(); // 显示历史记录对话框(使用线程实现非阻滞) CreateDialogBoxAsyncW(historyText); } // 检查窗口层次变化 if (monitoring) { SetLastError(0); HANDLE hThread = CreateThread(nullptr, 0, MonitorDesktopThread, &monitor_info, 0, nullptr); if (hThread) { // 设置状态为:记录中 StartMonitoringUI(hWinlogonDesk); //WaitForSingleObject(hThread, INFINITE); // 等待线程结束 //CloseHandle(hThread); 设置状态为:已完成 //CompleteMonitoringUI(hWinlogonDesk); DWORD waitRet = 0; SetLastError(0); waitRet == WaitForSingleObject(hThread, MONITOR_THREAD_TIMEOUT); if (waitRet != WAIT_OBJECT_0) { std::wcerr << L"WaitForSingleObject signal a failed: " << waitRet << L", GetLastError() == " << GetLastError() << std::endl; CloseHandle(hThread); ++errCnt; // 增加错误计数 monitoring = false; // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); TerminateThread(hBorderThread, 0); continue; } errCnt = 0; // 错误计数归零 CloseHandle(hThread); // 设置状态为:已完成 CompleteMonitoringUI(hWinlogonDesk); // 检查并更新窗口层次 std::size_t currentHash = GetWindowHierarchyHash(monitor_info.wndInfo); if (currentHash != prevHash) { std::wcout << L"Window hierarchy changed. Updating records..." << std::endl; windowHistory = monitor_info.wndInfo; std::time_t currentTime = std::time(nullptr); SaveWindowHierarchy(windowHistory, currentTime, szLogPath.c_str()); prevHash = currentHash; } } else { // CreateThread failed std::wcerr << L"Create Monitor-Desktop thread failed, GetLastError() == " << GetLastError() << std::endl; } } Sleep(1000); // 每秒检查一次 } exit_monitoring: // 注销热键 UnregisterHotKey(NULL, 1); // 清理 CloseDesktop(hDefaultDesk); CloseDesktop(hWinlogonDesk); CloseWindowStation(hWinsta); } // 检测文件编码 FileEncoding DetectFileEncoding(const std::wstring& filePath) { std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for encoding detection: " << filePath << std::endl; return FileEncoding::UNKNOWN; } unsigned char bom[3] = { 0 }; file.read(reinterpret_cast<char*>(bom), 3); file.close(); // 检查 BOM(字节顺序标记, Byte Order Mark) if (bom[0] == 0xFF && bom[1] == 0xFE) { return FileEncoding::UTF16LE; // UTF-16 LE } else if (bom[0] == 0xFE && bom[1] == 0xFF) { return FileEncoding::UTF16BE; // UTF-16 BE } else if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) { return FileEncoding::UTF8; // UTF-8 (BOM) } else { return FileEncoding::ANSI; // 默认解析为 ANSI } } // 读取文件内容,处理不同编码格式 bool ReadFileWithEncoding(const std::wstring& filePath, std::wstring& content) { FileEncoding encoding = DetectFileEncoding(filePath); if (encoding == FileEncoding::UTF16LE) { // 读取 UTF-16 LE 编码的文件 //std::wcout << L"UTF16-LE" << std::endl; return ReadUTF16LEFile(filePath, content); } else if (encoding == FileEncoding::UTF16BE) { // 读取 UTF-16 BE 编码的文件,手动处理字节序 std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open UTF-16 file: " << filePath << std::endl; return false; } // 跳过 BOM(2 个字节) file.seekg(2, std::ios::beg); // 读取文件到缓冲区 std::vector<char> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); file.close(); // 转换为宽字符 content = ConvertUTF16BEToWideString(buffer); } else if (encoding == FileEncoding::UTF8) { return ReadUTF8File(filePath, content); } else if (encoding == FileEncoding::ANSI) { // 读取 ANSI 编码的文件 std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open ANSI file: " << filePath << std::endl; return false; } std::stringstream ss; ss << file.rdbuf(); std::string ansiContent = ss.str(); content = ANSIToWideString(ansiContent); file.close(); } else { std::wcerr << L"Unknown file encoding." << std::endl; return false; } return true; } // 将宽字符内容保存为 UTF-8 文件 bool SaveAsUTF8(const std::wstring& filePath, const std::wstring& content) { std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } // 写入 UTF-8 BOM const char bom[] = { '\xEF', '\xBB', '\xBF' }; file.write(bom, sizeof(bom)); // 将宽字符转换为 UTF-8 编码 std::string utf8Content = WideStringToUTF8(content); file.write(utf8Content.c_str(), utf8Content.size()); file.close(); return true; } // 将宽字符内容保存为 UTF-16 LE 文件(不使用 codecvt) bool SaveAsUTF16LE(const std::wstring& filePath, const std::wstring& content) { std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } // 写入 BOM const char bom[] = { '\xFF', '\xFE' }; file.write(bom, sizeof(bom)); // 将宽字符转换为 UTF-16 Little Endian 编码字节流 for (wchar_t wc : content) { char16_t utf16LE = static_cast<char16_t>(wc); file.write(reinterpret_cast<const char*>(&utf16LE), sizeof(utf16LE)); } file.close(); return true; } // 将宽字符内容保存为 UTF-16 BE 文件 bool SaveAsUTF16BE(const std::wstring& filePath, const std::wstring& content) { std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } // 写入 BOM const char bom[] = { '\xFE', '\xFF' }; file.write(bom, sizeof(bom)); // 将宽字符转换为 UTF-16 Big Endian 编码字节流 for (wchar_t wc : content) { char16_t utf16BE = (static_cast<unsigned char>(wc >> 8) & 0xFF) | (static_cast<unsigned char>(wc & 0xFF) << 8); file.write(reinterpret_cast<const char*>(&utf16BE), sizeof(utf16BE)); } file.close(); return true; } // 根据编码保存修改后的文件内容 bool ModifyAndSaveFileWithEncoding(const std::wstring& filePath, const std::wstring& content) { FileEncoding encoding = DetectFileEncoding(filePath); if (encoding == FileEncoding::UTF16LE) { // 保存为 UTF-16 Little Endian 编码 return SaveAsUTF16LE(filePath, content); } else if (encoding == FileEncoding::UTF16BE) { // 保存为 UTF-16 Big Endian 编码 return SaveAsUTF16BE(filePath, content); } else if (encoding == FileEncoding::UTF8) { // 保存为 UTF-8 编码 std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } // 写入 UTF-8 BOM const char bom[] = { '\xEF', '\xBB', '\xBF' }; file.write(bom, sizeof(bom)); std::string utf8Content = WideStringToUTF8(content); file.write(utf8Content.c_str(), utf8Content.size()); file.close(); } else if (encoding == FileEncoding::ANSI) { // 保存为 ANSI 编码 std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } std::string ansiContent = WideStringToANSI(content); file.write(ansiContent.c_str(), ansiContent.size()); file.close(); } else { std::wcerr << L"Unknown file encoding, cannot save file." << std::endl; return false; } return true; } // 修改 SeAssignPrimaryTokenPrivilege 权限 bool ModifySeAssignPrimaryTokenPrivilege(const std::wstring& configFilePath, const std::wstring& userName) { std::wstring content; // 读取文件内容 if (!ReadFileWithEncoding(configFilePath, content)) { std::wcerr << L"Failed to read configuration file with correct encoding." << std::endl; return false; } // 查找并修改 SeAssignPrimaryTokenPrivilege size_t pos = content.find(L"SeAssignPrimaryTokenPrivilege"); if (pos != std::wstring::npos) { size_t endPos = content.find(L"\n", pos) - 1; std::wstring line = content.substr(pos, endPos - pos); // 检查是否包含当前用户 if (line.find(userName) == std::wstring::npos) { line += L"," + userName; content.replace(pos, endPos - pos, line); } } // 保存修改后的文件 if (!ModifyAndSaveFileWithEncoding(configFilePath, content)) { std::wcerr << L"Failed to save modified configuration file." << std::endl; return false; } return true; } // 执行命令 bool ExecuteCommandWithOutput(const std::wstring& command, std::wstring& output) { STARTUPINFOW si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; HANDLE hRead, hWrite; // 初始化管道句柄的安全属性 ZeroMemory(&sa, sizeof(sa)); sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = nullptr; // 为子进程的 STDOUT 和 STDERR 创建管道 if (!CreatePipe(&hRead, &hWrite, &sa, 0)) { std::wcerr << L"Failed to create pipe." << std::endl; return false; } // 确保管道的读取句柄未被继承 if (!SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0)) { std::wcerr << L"Failed to set handle information." << std::endl; return false; } // 为子进程设置 STARTUPINFO 结构 ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.hStdError = hWrite; si.hStdOutput = hWrite; si.dwFlags |= STARTF_USESTDHANDLES; // 将 STDOUT 和 STDERR 重定向到管道 // 设置 PROCESS_INFORMATION 结构 ZeroMemory(&pi, sizeof(pi)); // 使用 Create_NO_WINDOW 创建子进程,以避免显示新的控制台窗口 if (!CreateProcessW(NULL, const_cast<LPWSTR>(command.c_str()), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { std::wcerr << L"Failed to execute command: " << command << std::endl; CloseHandle(hRead); CloseHandle(hWrite); return false; } // 关闭父进程中管道的写入端 CloseHandle(hWrite); // 读取子进程的输出 DWORD bytesRead; CHAR buffer[4096]; std::string ansiResult; BOOL success = FALSE; ZeroMemory(buffer, sizeof(buffer) * sizeof(CHAR)); // 从管道中连续读取,直到没有更多数据 while (true) { success = ReadFile(hRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL); if (!success || bytesRead == 0) { break; // 没有更多数据要读取 } buffer[bytesRead] = '\0'; // 缓冲区结束标记 ansiResult += buffer; // 收集 ANSI 编码的结果 } // 关闭手柄 CloseHandle(hRead); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); // 如果结果为空,则返回“no message” if (ansiResult[0] == '\0') { output = L"\n(no message)\n"; return true; } // 将 ANSI 结果转换为宽字符串以进行正确的 Unicode 处理 int wideSize = MultiByteToWideChar(CP_ACP, 0, ansiResult.c_str(), -1, NULL, 0); if (wideSize == 0) { std::wcerr << L"Failed to convert output to wide string." << std::endl; return false; } std::wstring wideResult(wideSize, 0); MultiByteToWideChar(CP_ACP, 0, ansiResult.c_str(), -1, &wideResult[0], wideSize); output = wideResult; return true; } // 判断文件是否存在 BOOL IsFileExist(const std::wstring& wsFile) { DWORD dwAttrib = GetFileAttributesW(wsFile.c_str()); return INVALID_FILE_ATTRIBUTES != dwAttrib && 0 == (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); } // 导出安全策略 // secedit /export /cfg C:\gp.inf bool ExportSecurityPolicy(const std::wstring& system32Dir, const std::wstring& outputFile, std::wstring& output) { // https://blog.csdn.net/u0/article/details/ if (IsFileExist(outputFile)) { // 判断配置文件是否已经存在 std::wcout << L"The configuration file already exists. Attempting to delete it." << std::endl; if (!DeleteFileW(outputFile.c_str())) { std::wcerr << L"Failed to delete the configuration file." << std::endl; return false; } } std::wstring command = system32Dir + L"\\secedit.exe /export /cfg " + outputFile; return ExecuteCommandWithOutput(command, output); } // 应用修改后的安全策略 // secedit /configure /db test.sdb /cfg C:\gp.inf /overwrite /log hisecws.log /quiet bool ApplySecurityPolicy(const std::wstring& system32Dir, const std::wstring& configFilePath, const std::wstring& dbFilePath, std::wstring& output) { std::wstring command = system32Dir + L"\\secedit.exe /configure /db " + dbFilePath + L" /cfg " + configFilePath + L" /overwrite /quiet"; return ExecuteCommandWithOutput(command, output); } // 获取系统盘符 std::wstring GetSystemDrive() { wchar_t systemPath[MAX_PATH]; if (GetSystemDirectoryW(systemPath, MAX_PATH)) { return std::wstring(systemPath).substr(0, 3); // 返回盘符部分,例如 C:/ } return L""; // 如果获取失败,返回默认 C 盘符 } // 获取当前用户账户名称 std::wstring GetCurrentUserName() { wchar_t userName[UNLEN + 1]; DWORD size = UNLEN + 1; if (GetUserNameW(userName, &size)) { return std::wstring(userName); } return L""; // 返回空字符串表示获取失败 } // 提示用户注销 void PromptUserToLogout() { // 询问用户是否要注销并重新登录 int result = MessageBoxW(NULL, L"The security settings have been updated. Would you like to log off now and apply the changes?", L"Log Off Confirmation", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL); if (result == IDYES) { // 注销用户 if (!ExitWindowsEx(EWX_LOGOFF | EWX_FORCE, SHTDN_REASON_MAJOR_OTHER)) { std::wcerr << L"Failed to log off the user." << std::endl; } } else { std::wcout << L"User chose not to log off." << std::endl; } } // 解除用户获取 AssignPrimaryTokenPrivilege 的限制 // (修改系统配置文件,需要注销并重新登陆) bool ChangeAssignPrimaryTokenPrivilege() { std::wstring systemDrive = GetSystemDrive(); std::wstring system32Dir = systemDrive + L"Windows\\" + SYSTEM32_NAME; std::wstring configFilePath = systemDrive + L"gpSvcInfo.inf"; std::wstring dbFilePath = systemDrive + L"gpSvcdata.sdb"; std::wstring userName = GetCurrentUserName(); std::wstring output; if (systemDrive.empty()) { std::wcerr << L"Failed to retrieve the current system root path." << std::endl; return false; } if (userName.empty()) { std::wcerr << L"Failed to retrieve the current user name." << std::endl; return false; } if (!ExportSecurityPolicy(system32Dir, configFilePath, output)) { std::wcerr << L"Failed to export security policy." << std::endl; return false; } std::wcout << L"\nExportSecurityPolicy message: \n" << output << std::endl; if (!ModifySeAssignPrimaryTokenPrivilege(configFilePath, userName)) { std::wcerr << L"Failed to modify SeAssignPrimaryTokenPrivilege." << std::endl; return false; } if (!ApplySecurityPolicy(system32Dir, configFilePath, dbFilePath, output)) { std::wcerr << L"Failed to apply security policy." << std::endl; return false; } std::wcout << L"\nApplySecurityPolicy message: \n" << output << std::endl; PromptUserToLogout(); return true; } int wmain(int argc, wchar_t* argv[]) { // 宽字符中文支持 _wsetlocale(LC_ALL, L"zh-CN"); // 获取当前进程的主窗口 hMainWindow = GetMainWindowHandle(); if (!hMainWindow) { std::cerr << "Failed to find main window." << std::endl; system("pause > nul 2 > nul"); return 1; } std::cout << "Main window handle: " << std::hex << hMainWindow << std::endl; // 检查是否为管理员权限运行 if (!IsRunAsAdmin()) { std::wcout << L"Attempting to restart with administrator privileges..." << std::endl; if (RelaunchAsAdmin()) { return 0; // 提升后进程将重新启动,当前进程结束 } else { std::wcerr << L"Fatal Error." << std::endl; system("pause > nul 2 > nul"); return 1; } } // 启用 SeDebugPrivilege if (!EnablePrivilege(SE_DEBUG_NAME)) { std::wcerr << L"Failed to enable SeDebugPrivilege." << std::endl; system("pause > nul 2 > nul"); return 1; } // 启用 SeIncreaseQuotaPrivilege if (!EnablePrivilege(SE_INCREASE_QUOTA_NAME)) { std::wcerr << L"Failed to enable SeIncreaseQuotaPrivilege." << std::endl; system("pause > nul 2 > nul"); return 1; } // 启用 SeAssignPrimaryTokenPrivilege if (!EnablePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME)) { std::wcerr << L"Failed to enable SeAssignPrimaryTokenPrivilege." << std::endl; if (!ChangeAssignPrimaryTokenPrivilege()) { std::wcerr << L"Failed to enable primary token assignment privilege" << L" by modifying security configuration." << std::endl; } std::wcout << L"Press any key to close app."; system("pause > nul 2 > nul"); return 1; } // 检查 SYSTEM 权限 if (!IsSystem()) { std::wcout << L"Attempting to restart with SYSTEM privileges..." << std::endl; // 检查命令行参数,避免无限递归 if (argc < 2 || _wcsicmp(argv[1], L"system") != 0) { // 重新启动自身并传递 "system" 参数 wchar_t commandLine[MAX_PATH]; swprintf(commandLine, MAX_PATH, L"%s system", argv[0]); if (CreateSystemProcess(argv[0], commandLine)) { std::wcout << L"Restarted with SYSTEM privileges." << std::endl; } else { std::wcerr << L"Failed to restart with SYSTEM privileges." << std::endl; system("pause > nul 2 > nul"); } return 0; } else { std::wcerr << L"Already tried to elevate privileges but failed." << std::endl; system("pause > nul 2 > nul"); return 1; } } // 如果当前进程已经是 SYSTEM 权限,则继续执行桌面监控 MonitorDesktop(); std::wcout << L"Press any key to close app."; system("pause > nul 2 > nul"); return 0; }

1.0.0.4 版本更新内容:

  1. 修复部分句柄未释放和判断错误问题;
  2. 新增对一种特定桌面的安全描述符信息的解析功能(所有者和用户组信息解析存在问题);
  3. 修复在中文路径下日志保存失败的问题。
  4. 发生桌面切换时,回显当前活动桌面的名称。

1.0.0.4 版本代码:

#include <windows.h> #include <iostream> #include <lmcons.h> #include <fstream> #include <string> #include <vector> #include <ctime> #include <cstdio> #include <sstream> #include <functional> #include <tlhelp32.h> #include <userenv.h> #include <sddl.h> #include "resource.h" #include <regex> #pragma warning(push) #pragma warning(disable : 6258) #pragma warning(disable : 28159) #pragma comment(lib, "user32.lib") #pragma comment(lib, "shell32.lib") #pragma comment(lib, "gdi32.lib") #pragma comment(lib, "advapi32.lib") #pragma comment(linker,"\"/manifestdependency:type='win32' "\ "name='Microsoft.Windows.Common-Controls' "\ "version='6.0.0.0' processorArchitecture='*' "\ "publicKeyToken='6595b64144ccf1df' language='*'\"") constexpr auto WM_UPDATE_STATUS = WM_APP + 10; // 传递新的状态信息的用户自定义消息 constexpr auto STATUS_MSG_HASH = 0x; // 内部校验码(防止外部程序模拟消息) constexpr auto LOGING_STATUS_INFO = L"Now Monitoring..."; // 显示提示:正在扫描窗口 constexpr auto COMPLETE_STATUS_INFO = L"Monitoring completed!"; // 显示提示:窗口扫描已完成 constexpr auto LOG_FILE_NAME = L"window_hierarchy.log"; // 默认日志记录保存位置 constexpr auto MAX_STSTUS_BUFFER = 350; // 状态信息的最大缓冲区大小 constexpr auto MONITOR_THREAD_TIMEOUT = 25000; // 窗口监视器线程超时(默认 25 秒) constexpr auto CREATE_BORDER_WINDOW_TIMEOUT = 0x493E0; // 边框窗口创建超时(默认 5 分钟) constexpr auto SYSTEM32_NAME = L"System32"; // 记录窗口信息的结构体,使用宽字符 struct WindowInfo { std::wstring className; std::wstring windowTitle; HWND hwnd; std::vector<WindowInfo> children; }; // 监视器线程存储 struct MYMONITOR_THREAD_INFO { HDESK hDesktop; std::vector<WindowInfo> wndInfo; }; // 状态信息参数传递结构 struct MYSTATUS_UPDATE_THREAD_INFO { HDESK hChangeToDesktop; WCHAR wcsBuffer[MAX_STSTUS_BUFFER]; }; // 文件编码格式(处理系统安全配置文件时用到) enum class FileEncoding { ANSI, UTF8, UTF16LE, UTF16BE, UNKNOWN }; // 对话框(异步)参数结构(使用深拷贝) struct DIALOGBOX_PARAM_LIST { HINSTANCE hInstance; // 使用默认初始化值 LPCWSTR lpTemplateName; // 模板名称 HWND hWndParent; // 父窗口句柄 DLGPROC lpDialogFunc; // 对话框过程 LPARAM dwInitParam; // 初始化参数 size_t cbSize; // 参数大小 INT_PTR intResponse; // 响应值 DIALOGBOX_PARAM_LIST() : hInstance(nullptr), lpTemplateName(nullptr), hWndParent(nullptr), lpDialogFunc(nullptr), dwInitParam(0), cbSize(0), intResponse(0) {} // 复制构造函数,用于深拷贝 DIALOGBOX_PARAM_LIST(const DIALOGBOX_PARAM_LIST& other) { hInstance = other.hInstance; lpTemplateName = other.lpTemplateName; hWndParent = other.hWndParent; lpDialogFunc = other.lpDialogFunc; cbSize = other.cbSize; intResponse = other.intResponse; // 深拷贝 dwInitParam if (other.dwInitParam != 0 && other.cbSize > 0) { dwInitParam = (LPARAM)new BYTE[other.cbSize]; std::memcpy((void*)dwInitParam, (void*)other.dwInitParam, other.cbSize); } else { dwInitParam = 0; } } // 析构函数,释放深拷贝的内存 ~DIALOGBOX_PARAM_LIST() { if (dwInitParam != 0) { delete[](BYTE*)dwInitParam; dwInitParam = 0; } } // 赋值运算符重载,确保安全的深拷贝 DIALOGBOX_PARAM_LIST& operator=(const DIALOGBOX_PARAM_LIST& other) { if (this == &other) return *this; hInstance = other.hInstance; lpTemplateName = other.lpTemplateName; hWndParent = other.hWndParent; lpDialogFunc = other.lpDialogFunc; cbSize = other.cbSize; intResponse = other.intResponse; if (dwInitParam != 0) { delete[](BYTE*)dwInitParam; } // 深拷贝 dwInitParam if (other.dwInitParam != 0 && other.cbSize > 0) { dwInitParam = (LPARAM)new BYTE[other.cbSize]; std::memcpy((void*)dwInitParam, (void*)other.dwInitParam, other.cbSize); } else { dwInitParam = 0; } return *this; } }; // 用于边框绘制的全局变量 HWND hBorderWnd = NULL; // 边框窗口句柄 bool borderRunning = false; // 边框是否正在显示 // 全局变量保存主窗口句柄 HWND hMainWindow = NULL; HANDLE hBroderWndCreateEvent; // 获取当前进程完整文件路径的函数 std::wstring GetCurrentProcessFullPath() { // 初始缓冲区大小 DWORD size = MAX_PATH; std::vector<wchar_t> buffer(size); // 获取当前进程句柄 HANDLE hProcess = GetCurrentProcess(); // 查询完整的进程路径,可能需要多次尝试 while (true) { if (QueryFullProcessImageNameW(hProcess, 0, buffer.data(), &size)) { return std::wstring(buffer.data(), size); } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // 缓冲区大小不足,扩大缓冲区 size += MAX_PATH; buffer.resize(size); } else { // 其他错误情况,返回空字符串 std::wcerr << L"Failed to get full process image name. Error: " << GetLastError() << std::endl; return L""; } } } // 获取当前进程所在目录的函数 std::wstring GetCurrentProcessDirectory() { std::wstring fullPath = GetCurrentProcessFullPath(); if (fullPath.empty()) { std::wcerr << L"Failed to get process full path." << std::endl; return L""; } size_t lastBackslashPos = fullPath.find_last_of(L"\\/"); if (lastBackslashPos != std::wstring::npos) { return fullPath.substr(0, lastBackslashPos); // 提取目录部分 } else { std::wcerr << L"Failed to find directory in path." << std::endl; return L""; } } // 启用特定的权限(例如 SeDebugPrivilege) bool EnablePrivilege(LPCWSTR privilege) { HANDLE hToken; TOKEN_PRIVILEGES tp{}; LUID luid; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token." << std::endl; return false; } if (!LookupPrivilegeValueW(NULL, privilege, &luid)) { std::wcerr << L"Failed to lookup privilege." << std::endl; CloseHandle(hToken); return false; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { std::wcerr << L"Failed to adjust token privileges." << std::endl; CloseHandle(hToken); return false; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { std::wcerr << L"The privilege was not assigned." << std::endl; CloseHandle(hToken); return false; } CloseHandle(hToken); return true; } // 检查是否以管理员权限运行 bool IsRunAsAdmin() { BOOL isAdmin = FALSE; PSID administratorsGroup = NULL; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsGroup)) { CheckTokenMembership(NULL, administratorsGroup, &isAdmin); FreeSid(administratorsGroup); } return isAdmin == TRUE; } // 重新启动并请求管理员权限 bool RelaunchAsAdmin() { std::wstring szPath = GetCurrentProcessFullPath(); if (szPath.empty()) { std::wcerr << L"Failed to get module file name." << std::endl; return false; } SHELLEXECUTEINFOW sei = { sizeof(sei) }; sei.lpVerb = L"runas"; // 请求管理员权限 sei.lpFile = szPath.c_str(); sei.hwnd = NULL; sei.nShow = SW_NORMAL; if (!ShellExecuteExW(&sei)) { std::wcerr << L"Failed to relaunch as administrator." << std::endl; return false; } return true; } // 获取当前控制台的活动会话 DWORD WINAPI GetActiveConsoleSessionId() { return WTSGetActiveConsoleSessionId(); } // 判断进程是否在给定的会话中 BOOL WINAPI IsProcessInSession(DWORD processId, DWORD sessionId) { DWORD session; if (!ProcessIdToSessionId(processId, &session)) { printf("Error: ProcessIdToSessionId failed.\n"); return FALSE; } return session == sessionId; } // 获取当前会话的 Winlogon 进程的 PID DWORD WINAPI FindWinlogonProcessId() { DWORD dwProcessId = 0; DWORD activeSessionId = GetActiveConsoleSessionId(); if (activeSessionId == 0xFFFFFFFF) { printf("Error: Unable to retrieve active console session ID.\n"); return 0; } HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { printf("Error: CreateToolhelp32Snapshot failed.\n"); return 0; } PROCESSENTRY32W entry{}; entry.dwSize = sizeof(PROCESSENTRY32W); if (!Process32FirstW(snapshot, &entry)) { printf("Error: Process32First failed.\n"); CloseHandle(snapshot); return 0; } do { if (entry.cntThreads <= 1u) continue; // 跳过僵尸进程 if (_wcsicmp(entry.szExeFile, L"winlogon.exe") == 0) { if (IsProcessInSession(entry.th32ProcessID, activeSessionId)) { dwProcessId = entry.th32ProcessID; break; } } } while (Process32NextW(snapshot, &entry)); CloseHandle(snapshot); return dwProcessId; } // 获取 winlogon 进程的 SYSTEM 令牌 HANDLE GetSystemTokenFromWinlogon() { HANDLE hToken = nullptr; HANDLE hProcess = nullptr; HANDLE hSystemToken = nullptr; // 存储复制后的 SYSTEM 令牌 // 获取 Winlogon 进程的进程ID DWORD winlogonPid = FindWinlogonProcessId(); if (winlogonPid == 0) { std::wcerr << L"Failed to find winlogon.exe process!" << std::endl; return nullptr; } std::wcout << L"winlogon.exe PID: " << winlogonPid << std::endl; // 打开 Winlogon 进程 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, winlogonPid); if (!hProcess) { std::wcerr << L"Failed to open winlogon process!" << std::endl; return nullptr; } // 打开该进程的令牌 if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &hToken)) { std::wcerr << L"Failed to open process token!" << std::endl; CloseHandle(hProcess); return nullptr; } // 复制令牌以使其可用于新进程 if (!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, nullptr, SecurityImpersonation, TokenPrimary, &hSystemToken)) { std::wcerr << L"Failed to duplicate SYSTEM token!" << std::endl; CloseHandle(hToken); CloseHandle(hProcess); return nullptr; } // 关闭原始的令牌和进程句柄 CloseHandle(hToken); CloseHandle(hProcess); return hSystemToken; } // 创建具有 SYSTEM 权限的进程 bool CreateSystemProcess(LPCWSTR applicationName, LPCWSTR commandLine) { HANDLE hToken = GetSystemTokenFromWinlogon(); if (!hToken) { std::wcerr << L"Failed to get SYSTEM token!" << std::endl; return false; } // 使用 SYSTEM 权限创建进程 STARTUPINFOW si = { sizeof(STARTUPINFOW) }; PROCESS_INFORMATION pi = { 0 }; if (!CreateProcessAsUserW(hToken, applicationName, const_cast<LPWSTR>(commandLine), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { std::wcerr << L"Failed to create process as SYSTEM! err = " << GetLastError() << std::endl; CloseHandle(hToken); return false; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hToken); return true; } // 检查当前进程是否至少在 SYSTEM 用户组(Local System) bool IsSystem() { HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { return false; } DWORD tokenInfoLength = 0; GetTokenInformation(hToken, TokenUser, NULL, 0, &tokenInfoLength); PTOKEN_USER tokenUser = (PTOKEN_USER)malloc(tokenInfoLength); if (!GetTokenInformation(hToken, TokenUser, tokenUser, tokenInfoLength, &tokenInfoLength)) { CloseHandle(hToken); free(tokenUser); return false; } LPWSTR sidString = NULL; ConvertSidToStringSidW(tokenUser->User.Sid, &sidString); bool isSystem = (_wcsicmp(sidString, L"S-1-5-18") == 0); LocalFree(sidString); CloseHandle(hToken); free(tokenUser); return isSystem; } // 获取当前控制台窗口句柄 HWND GetConsoleHwnd(void) { // https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/obtain-console-window-handle #define MY_BUFSIZE 1024 // 控制台窗口标题的缓冲区大小。 HWND hwndFound; // 这是返回给调用方的内容。 wchar_t pszNewWindowTitle[MY_BUFSIZE]; // 包含编造的 // 窗口标题 // 构造一个“独特的”NewWindowTitle。 wsprintfW(pszNewWindowTitle, L"WinlogonWindowsMonitor - %I64u/%d", GetTickCount64(), GetCurrentProcessId()); // 更改当前窗口标题。 SetConsoleTitleW(pszNewWindowTitle); // 确保窗口标题已更新。 Sleep(40); // 查找 NewWindowTitle。 hwndFound = FindWindowW(L"ConsoleWindowClass", pszNewWindowTitle); if(!hwndFound) hwndFound = FindWindowW(L"CASCADIA_HOSTING_WINDOW_CLASS", pszNewWindowTitle); #undef MY_BUFSIZE return(hwndFound); } // 获取当前进程的主窗口 HWND GetMainWindowHandle() { return GetConsoleHwnd(); } // 检查当前活动窗口是否是主窗口 bool IsMainWindowActive() { HWND hForeground = GetForegroundWindow(); // 获取当前活动窗口 return hForeground == hMainWindow; // 比较是否是主窗口 } // 边框窗口过程 LRESULT CALLBACK BorderWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static int borderWidth = 10; // 默认边框宽度 static wchar_t statusText[MAX_STSTUS_BUFFER]; switch (message) { case WM_CREATE: { // 清空存储状态提示信息的缓冲区 memset(statusText, 0, sizeof(statusText)); //memcpy(statusText, LOGING_STATUS_INFO, sizeof(statusText) - 5); // 窗口创建成功后,发出信号 SetEvent(hBroderWndCreateEvent); break; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT rect; GetClientRect(hWnd, &rect); // 设置边框颜色为蓝色 HBRUSH brush = CreateSolidBrush(RGB(119, 228, 155)); // 填充外边框到内边框之间的区域 RECT outerRect = rect; RECT innerRect = rect; // 根据边框宽度调整内边框位置 InflateRect(&innerRect, -borderWidth, -borderWidth); // 填充外边框和内边框之间的区域 if (borderWidth > 0) { // 填充四个部分:上边、下边、左边、右边 RECT topRect = { outerRect.left, outerRect.top, outerRect.right, innerRect.top }; RECT bottomRect = { outerRect.left, innerRect.bottom, outerRect.right, outerRect.bottom }; RECT leftRect = { outerRect.left, innerRect.top, innerRect.left, innerRect.bottom }; RECT rightRect = { innerRect.right, innerRect.top, outerRect.right, innerRect.bottom }; FillRect(hdc, &topRect, brush); FillRect(hdc, &bottomRect, brush); FillRect(hdc, &leftRect, brush); FillRect(hdc, &rightRect, brush); } DeleteObject(brush); // 设置字体 HFONT hFont = CreateFontW(36, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Arial"); SelectObject(hdc, hFont); // 设置文本颜色为高亮(例如绿色) SetTextColor(hdc, RGB(0, 255, 0)); SetBkMode(hdc, TRANSPARENT); // 透明背景 // 获取窗口文本并绘制在左下方,间隔 50 像素 rect.left += 50; // 左侧间隔 50 像素 rect.bottom -= 50; // 底部间隔 50 像素 DrawTextW(hdc, statusText, -1, &rect, DT_LEFT | DT_BOTTOM | DT_SINGLELINE); DeleteObject(hFont); EndPaint(hWnd, &ps); break; } case WM_UPDATE_STATUS: { if (wParam == STATUS_MSG_HASH) { // 获取窗口文本并绘制 memset(statusText, 0, sizeof(statusText)); memcpy(statusText, (LPVOID)lParam, sizeof(statusText) - 5); InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hWnd); } return 0; } case WM_CLOSE: DestroyWindow(hWnd); // 其他:用户已取消。什么都不做。 return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: break; } return DefWindowProcW(hWnd, message, wParam, lParam); } DWORD WINAPI CloseBorderThread(LPVOID lpParam) { HDESK hWinlogonDesk = static_cast<HDESK>(lpParam); // 传递的桌面句柄 HWND hBorderWndBack = hBorderWnd; //std::wcout << L"hWinlogonDesk in thread: " << hWinlogonDesk << std::endl; // 获取当前线程的桌面句柄,后面恢复用 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到 Winlogon 桌面 if (!SetThreadDesktop(hWinlogonDesk)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } // 发送关闭消息 if (hBorderWndBack != nullptr && IsWindow(hBorderWndBack)) { // 最小化窗口 CloseWindow(hBorderWndBack); // 关闭窗口 SendMessageW(hBorderWndBack, WM_CLOSE, 0, 0); // 销毁窗口(发送 WM_DESTROY 和 WM_NCDESTROY 消息) DestroyWindow(hBorderWndBack); } std::wcout << L"An exit monitoring message has been sent." << std::endl; // 恢复到原始桌面 SetThreadDesktop(hOriginalDesktop); return 0; } // 更新状态窗口显示的文本信息 DWORD WINAPI UpdateStatusWindowTextThread(LPVOID lpParam) { if (lpParam == nullptr) { std::wcerr << L"Invalid thread parameters." << std::endl; return ERROR_INVALID_PARAMETER; } __try { MYSTATUS_UPDATE_THREAD_INFO* info = static_cast<MYSTATUS_UPDATE_THREAD_INFO*>(lpParam); if (info->hChangeToDesktop == nullptr || info->wcsBuffer == nullptr) { std::wcerr << L"Invalid thread parameters." << std::endl; return ERROR_INVALID_PARAMETER; } // 获取当前线程的桌面句柄,后面恢复用 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到 Winlogon 桌面 if (!SetThreadDesktop(info->hChangeToDesktop)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } // 设置新的信息 if (hBorderWnd) { // 发送 WM_APP 类型消息给目标窗口过程 SendMessageW(hBorderWnd, WM_UPDATE_STATUS, STATUS_MSG_HASH, (LPARAM)info->wcsBuffer); } std::wcout << L"Update Status Text: " << info->wcsBuffer << std::endl; // 恢复到原始桌面 SetThreadDesktop(hOriginalDesktop); } __except (EXCEPTION_EXECUTE_HANDLER) { std::wcerr << L"Memory access violation." << std::endl; return ERROR_INVALID_ACCESS; } return 0; } // 在监视过程开始时显示提示信息 BOOL StartMonitoringUI(HDESK hWinlogonDesk) { // 切换到 winlogon 桌面并发送状态更新消息 MYSTATUS_UPDATE_THREAD_INFO info = { hWinlogonDesk }; wcscpy_s(info.wcsBuffer, LOGING_STATUS_INFO); HANDLE hThread = CreateThread(NULL, 0, UpdateStatusWindowTextThread, &info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, 7000); CloseHandle(hThread); Sleep(300); // 刻意的延时, TODO: 未来仅在绘制部 // 分做延时动画而不是对发消息的线程做延时 return TRUE; } return FALSE; } // 在监视过程完成时更新提示信息 BOOL CompleteMonitoringUI(HDESK hWinlogonDesk) { // 切换到 winlogon 桌面并发送状态更新消息 MYSTATUS_UPDATE_THREAD_INFO info = { hWinlogonDesk }; wcscpy_s(info.wcsBuffer, COMPLETE_STATUS_INFO); HANDLE hThread = CreateThread(NULL, 0, UpdateStatusWindowTextThread, &info, 0, NULL); if (hThread) { WaitForSingleObject(hThread, 7000); CloseHandle(hThread); Sleep(300); // 刻意的延时 return TRUE; } return FALSE; } // 检查并关闭监视器 BOOL CloseMonitoringUI(HDESK hWinlogonDesk) { // 启动关闭边框的线程 if (borderRunning) { //std::wcout << L"hWinlogonDesk call: " << hWinlogonDesk << std::endl; HANDLE hCloseThread = CreateThread(NULL, 0, CloseBorderThread, (LPVOID)hWinlogonDesk, 0, NULL); if (!hCloseThread || hCloseThread == INVALID_HANDLE_VALUE) return ERROR_ACCESS_DENIED; WaitForSingleObject(hCloseThread, INFINITE); // 等待线程完成 CloseHandle(hCloseThread); borderRunning = false; } return FALSE; } // 边框绘制线程 DWORD WINAPI BorderThread(LPVOID lpParam) { std::wcout << L"Monitoring the target desktop..." << std::endl; HDESK hWinlogonDesk = static_cast<HDESK>(lpParam); // 传递的桌面句柄 // 切换到 Winlogon 桌面 if (!SetThreadDesktop(hWinlogonDesk)) { std::wcerr << L"Failed to set thread desktop to Winlogon." << std::endl; return 1; } #define MY_BUFSIZE 1024 wchar_t pszWindowClass[MY_BUFSIZE]; // 构造一个特殊的窗口类名 wsprintfW(pszWindowClass, L"WinlogonMonitorBorderWindowClass[%I64u::%d]", GetTickCount64(), GetCurrentProcessId()); HINSTANCE hInstance = GetModuleHandleW(NULL); WNDCLASS wc = { 0 }; wc.lpfnWndProc = BorderWndProc; wc.hInstance = hInstance; wc.lpszClassName = pszWindowClass; wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); if (!RegisterClassW(&wc)) { std::wcerr << L"Failed to RegisterClass." << std::endl; return 1; } // 获取屏幕大小并创建无边框窗口 int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); hBorderWnd = CreateWindowExW(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, L"WinlogonMonitorBorderWindow", WS_POPUP, 0, 0, screenWidth, screenHeight, NULL, NULL, hInstance, NULL); if (!hBorderWnd) { std::wcerr << L"Failed to CreateWindow." << std::endl; return 1; } // 设置窗口透明度 SetLayeredWindowAttributes(hBorderWnd, RGB(255, 255, 255), 0, LWA_COLORKEY); ShowWindow(hBorderWnd, SW_SHOW); UpdateWindow(hBorderWnd); // 消息循环,等待关闭事件 MSG msg; while (true) { // 检查并处理窗口消息 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } } // 销毁窗口 DestroyWindow(hBorderWnd); #undef MY_BUFSIZE return 0; } // 递归获取窗口层次,使用宽字符 API void EnumChildWindowsRecursive(HWND hwndParent, std::vector<WindowInfo>& windowList) { wchar_t className[256]; wchar_t windowTitle[256]; ZeroMemory(className, sizeof(className)); ZeroMemory(windowTitle, sizeof(windowTitle)); // 获取窗口类名和标题 GetClassNameW(hwndParent, className, sizeof(className) / sizeof(wchar_t)); GetWindowTextW(hwndParent, windowTitle, sizeof(windowTitle) / sizeof(wchar_t)); if (className[0] == L'\0') { wcscpy_s(className, L"(None)"); } if (windowTitle[0] == L'\0') { wcscpy_s(windowTitle, L"(None)"); } WindowInfo windowInfo = { className, windowTitle, hwndParent }; // 枚举子窗口 EnumChildWindows(hwndParent, [](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* children = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *children); return TRUE; }, reinterpret_cast<LPARAM>(&windowInfo.children)); windowList.push_back(windowInfo); } // 获取当前桌面窗口层次,使用宽字符 API std::vector<WindowInfo> GetWindowHierarchy() { std::vector<WindowInfo> windows; EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL { std::vector<WindowInfo>* windows = reinterpret_cast<std::vector<WindowInfo>*>(lParam); EnumChildWindowsRecursive(hwnd, *windows); return TRUE; }, reinterpret_cast<LPARAM>(&windows)); return windows; } // 获取当前窗口层次的哈希值,用于检测窗口层次是否发生变化 std::size_t GetWindowHierarchyHash(const std::vector<WindowInfo>& windows) { std::hash<std::wstring> hash_fn; std::size_t hash = 0; std::function<void(const std::vector<WindowInfo>&)> computeHash; computeHash = [&hash_fn, &hash, &computeHash](const std::vector<WindowInfo>& windows) { for (const auto& window : windows) { hash ^= hash_fn(window.className); hash ^= hash_fn(window.windowTitle); computeHash(window.children); // 递归计算子窗口的哈希值 } }; computeHash(windows); return hash; } // 格式化时间为宽字符格式 std::wstring FormatTime(const std::time_t& time) { wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &time); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); return std::wstring(timeBuffer); } // 宽字符到 UTF-8 的转换(使用 UTF-8 目标编码) std::string WideStringToUTF8(const std::wstring& wideStr) { if (wideStr.empty()) return std::string(); // 计算转换所需的缓冲区大小 int size_needed = WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), static_cast<int>(wideStr.size()), NULL, 0, NULL, NULL); if (size_needed <= 0) { throw std::runtime_error("WideCharToMultiByte failed to calculate buffer size."); } // 分配缓冲区并进行转换 std::string str(size_needed, 0); int bytes_written = WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), static_cast<int>(wideStr.size()), &str[0], size_needed, NULL, NULL); if (bytes_written <= 0) { throw std::runtime_error("WideCharToMultiByte failed to convert string to UTF-8."); } return str; } // UTF-8 到宽字符的转换 std::wstring UTF8ToWideString(const std::string& utf8Str) { if (utf8Str.empty()) return std::wstring(); // 获取所需的宽字符缓冲区大小(包括空终止符) int size_needed = MultiByteToWideChar(CP_ACP, 0, utf8Str.c_str(), static_cast<int>(utf8Str.size()), NULL, 0); if (size_needed <= 0) { throw std::runtime_error("MultiByteToWideChar failed to calculate buffer size."); } std::wstring wideStr(size_needed, 0); int chars_written = MultiByteToWideChar(CP_ACP, 0, utf8Str.c_str(), static_cast<int>(utf8Str.size()), &wideStr[0], size_needed); if (chars_written <= 0) { throw std::runtime_error("MultiByteToWideChar failed to convert UTF-8 string to wide string."); } return wideStr; } // 将 ANSI 字符串转换为宽字符串(根据当前系统代码页) std::wstring ANSIToWideString(const std::string& ansiStr, UINT codePage = CP_ACP) { if (ansiStr.empty()) return std::wstring(); int size_needed = MultiByteToWideChar(codePage, 0, ansiStr.c_str(), static_cast<int>(ansiStr.size()), NULL, 0); if (size_needed <= 0) { throw std::runtime_error("MultiByteToWideChar failed to calculate buffer size."); } std::wstring wideStr(size_needed, 0); MultiByteToWideChar(codePage, 0, ansiStr.c_str(), static_cast<int>(ansiStr.size()), &wideStr[0], size_needed); return wideStr; } // 将宽字符转换为 ANSI 编码的字符串(基于当前系统的代码页) std::string WideStringToANSI(const std::wstring& wideStr, UINT codePage = CP_ACP) { if (wideStr.empty()) return std::string(); int size_needed = WideCharToMultiByte(codePage, 0, wideStr.c_str(), static_cast<int>(wideStr.size()), NULL, 0, NULL, NULL); if (size_needed <= 0) { throw std::runtime_error("WideCharToMultiByte failed to calculate buffer size."); } std::string ansiStr(size_needed, 0); WideCharToMultiByte(codePage, 0, wideStr.c_str(), static_cast<int>(wideStr.size()), &ansiStr[0], size_needed, NULL, NULL); return ansiStr; } // 读取 UTF-8 文件内容并转换为宽字符串 bool ReadUTF8File(const std::wstring& filePath, std::wstring& content) { std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open UTF-8 file: " << filePath << std::endl; return false; } std::stringstream ss; ss << file.rdbuf(); std::string utf8Content = ss.str(); file.close(); // 将 UTF-8 内容转换为宽字符内容 content = UTF8ToWideString(utf8Content); return true; } // 手动读取 UTF-16 LE 编码的文件 bool ReadUTF16LEFile(const std::wstring& filePath, std::wstring& content) { std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open UTF-16 LE file: " << filePath << std::endl; return false; } // 跳过 BOM (2 bytes) file.seekg(2, std::ios::beg); std::wstringstream wss; char buffer[2]; while (file.read(buffer, 2)) { // Little Endian 解码为 wchar_t wchar_t wchar = (static_cast<unsigned char>(buffer[1]) << 8) | static_cast<unsigned char>(buffer[0]); wss << wchar; } content = wss.str(); file.close(); return true; } // 手动转换 UTF-16 BE 到宽字符 std::wstring ConvertUTF16BEToWideString(const std::vector<char>& buffer) { if (buffer.size() % 2 != 0) { throw std::runtime_error("Invalid UTF-16 BE byte stream length."); } std::wstring result; for (size_t i = 0; i < buffer.size(); i += 2) { // 将两个字节转换为 wchar_t,并转换字节序 wchar_t wchar = (static_cast<unsigned char>(buffer[i]) << 8) | static_cast<unsigned char>(buffer[i + 1]); result += wchar; } return result; } // 保存窗口层次信息到文件,使用 UTF-8 编码 void SaveWindowHierarchy( const std::vector<WindowInfo>& windows, const std::time_t& changeTime, const std::wstring& filename ) { // 宽字符中文支持 _wsetlocale(LC_ALL, L"zh-CN"); setlocale(LC_ALL, "zh-CN"); std::string filePath = WideStringToUTF8(filename); std::ofstream file(filePath, std::ios::app | std::ios::binary); // 使用 binary 防止换行符的意外转换 if (!file.is_open()) { std::wcerr << L"Unable to open file for writing!" << std::endl; return; } // 在文件开头写入 BOM,标识为 UTF-8 编码 static bool bomWritten = false; if (!bomWritten) { const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; // UTF-8 BOM file.write(reinterpret_cast<const char*>(bom), sizeof(bom)); bomWritten = true; } // 格式化时间为字符串 wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &changeTime); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); // 写入时间戳 file << WideStringToUTF8(std::wstring(timeBuffer)) << "\r\n"; // 递归写入窗口信息 std::function<void(const std::vector<WindowInfo>&, int)> WriteHierarchy; WriteHierarchy = [&file, &WriteHierarchy](const std::vector<WindowInfo>& windows, int indent) { for (const auto& window : windows) { file << std::string(indent, ' ') // 使用空格进行缩进 << "Class Name: " << WideStringToUTF8(window.className) << ", Title: " << WideStringToUTF8(window.windowTitle) << ", HWND: " << std::hex << window.hwnd << "\r\n"; if (!window.children.empty()) { WriteHierarchy(window.children, indent + 4); // 递归写入子窗口信息 } } }; WriteHierarchy(windows, 0); file << "\r\n"; file.close(); } // 获取当前桌面的名称 std::wstring GetDesktopName(HDESK hDesktop) { wchar_t desktopName[256]{}; DWORD neededLength = 0; if (!GetUserObjectInformationW(hDesktop, UOI_NAME, desktopName, sizeof(desktopName), &neededLength)) { std::wcerr << L"Failed to get desktop name." << std::endl; return L""; } return std::wstring(desktopName); } // 切换到指定桌面并枚举窗口,任务结束后恢复到原始桌面 std::vector<WindowInfo> GetWindowsFromDesktop(HDESK hDesktop) { // 获取原始桌面句柄 HDESK hOriginalDesktop = GetThreadDesktop(GetCurrentThreadId()); // 切换到目标桌面 if (!SetThreadDesktop(hDesktop)) { std::wcerr << L"Failed to set thread desktop!" << std::endl; return {}; } // 切换后获取窗口层次 std::vector<WindowInfo> windows = GetWindowHierarchy(); // 恢复到原始桌面 if (!SetThreadDesktop(hOriginalDesktop)) { std::wcerr << L"Failed to restore original desktop!" << std::endl; } return windows; } // 使用辅助线程进行桌面切换和窗口枚举 DWORD WINAPI MonitorDesktopThread(LPVOID param) { _wsetlocale(LC_ALL, L"zh-CN"); MYMONITOR_THREAD_INFO* threadInfo = static_cast<MYMONITOR_THREAD_INFO*>(param); threadInfo->wndInfo = GetWindowsFromDesktop(threadInfo->hDesktop); return 0; } // 打开窗口工作站并切换到桌面 HDESK OpenDesktopWithWindowStation(LPCWSTR desktopName, HWINSTA hWinsta) { // 打开桌面 HDESK hDesktop = OpenDesktopW(desktopName, 0, FALSE, GENERIC_ALL); if (!hDesktop) { std::wcerr << L"Failed to open desktop! name = " << desktopName << std::endl; CloseWindowStation(hWinsta); } return hDesktop; } // 获取当前活动桌面 HDESK GetActiveDesktop() { return OpenInputDesktop(0, FALSE, GENERIC_ALL); } // 对话框过程函数 INT_PTR CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { static std::wstring* historyText = nullptr; switch (message) { case WM_INITDIALOG: { historyText = reinterpret_cast<std::wstring*>(lParam); // 获取编辑控件句柄 HWND hEdit = GetDlgItem(hDlg, IDC_EDIT1); if (hEdit) { // 设置文本 SetWindowTextW(hEdit, historyText->c_str()); } } return (INT_PTR)TRUE; case WM_SIZE: { // 获取对话框的新尺寸 int width = LOWORD(lParam); // 新的宽度 int height = HIWORD(lParam); // 新的高度 // 调整编辑控件大小 HWND hEdit = GetDlgItem(hDlg, IDC_EDIT1); if (hEdit) { int margin = 25; MoveWindow(hEdit, margin, margin, width - 2 * margin, height - 80, TRUE); } // 调整OK按钮的位置 HWND hButtonOK = GetDlgItem(hDlg, IDOK); if (hButtonOK) { int buttonWidth = 120; int buttonHeight = 35; int margin = 15; // 按钮位于对话框底部,右边留一定的边距 MoveWindow(hButtonOK, width - buttonWidth - margin, height - buttonHeight - margin, buttonWidth, buttonHeight, TRUE); } } break; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; default: return FALSE; } return FALSE; } // 使用辅助线程进行桌面切换和窗口枚举 DWORD WINAPI DialogBoxParamThread(LPVOID param) { _wsetlocale(LC_ALL, L"zh-CN"); if (param == nullptr) { return ERROR_INVALID_PARAMETER; } DIALOGBOX_PARAM_LIST* dialogParam = static_cast<DIALOGBOX_PARAM_LIST*>(param); // 使用副本进行操作 DIALOGBOX_PARAM_LIST localCopy = *dialogParam; delete dialogParam; // 删除传入的原始参数 if (localCopy.lpDialogFunc == nullptr || localCopy.dwInitParam == 0) { return ERROR_INVALID_PARAMETER; } //std::wcout << L"localCopy.dwInitParam " << std::hex << localCopy.dwInitParam << std::endl; localCopy.intResponse = DialogBoxParamW(localCopy.hInstance, localCopy.lpTemplateName, localCopy.hWndParent, localCopy.lpDialogFunc, (LPARAM)&(localCopy.dwInitParam)); Sleep(1000); // 清理 dwInitParam 的内容,不删除外部指针 memset((void*)localCopy.dwInitParam, 0, localCopy.cbSize); return 0; } // 异步显示历史记录对话框 void CreateDialogBoxAsyncW(std::wstring text) { const size_t cbSize = text.length() + 1; WCHAR* wcsText = new (std::nothrow) WCHAR[cbSize]; if (!wcsText) { return; } wcscpy_s(wcsText, cbSize, text.c_str()); // 显示历史记录对话框 DIALOGBOX_PARAM_LIST* param = new DIALOGBOX_PARAM_LIST(); // 动态分配结构体 param->hInstance = GetModuleHandleW(NULL); param->lpTemplateName = MAKEINTRESOURCE(IDD_DIALOG1); param->hWndParent = NULL; param->lpDialogFunc = HistoryDialogProc; param->cbSize = cbSize * sizeof(WCHAR); param->dwInitParam = (LPARAM)wcsText; // 动态分配内存并赋值 //std::wcout << L"WindowsLog: " << text << std::endl; HANDLE hThread = CreateThread(NULL, 0, DialogBoxParamThread, param, 0, NULL); } bool IsGuidDesktopName(const std::wstring& deskName) { // 正则表达式匹配 GUID 格式 std::wregex guidPattern(L"\\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\}"); return std::regex_match(deskName, guidPattern); } // TODO: 此处还存在问题 void PrintSecurityDescriptor(HANDLE hDesk) { PSECURITY_DESCRIPTOR pSD = nullptr; DWORD dwSize = 0; // 获取桌面的安全描述符 SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION; if (GetUserObjectSecurity(hDesk, &si, nullptr, 0, &dwSize) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { std::cerr << "Unable to obtain security descriptor size: " << GetLastError() << std::endl; return; } pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSize); if (!pSD) { std::cerr << "Memory allocation failed." << std::endl; return; } DWORD lpfnLengthNeeded = 0; if (!GetUserObjectSecurity(hDesk, &si, pSD, dwSize, &lpfnLengthNeeded)) { std::cerr << "Failed to obtain security descriptor: " << GetLastError() << std::endl; LocalFree(pSD); return; } // 检查安全描述符是否有效 if (pSD == nullptr) { std::cerr << "The security descriptor is empty." << std::endl; LocalFree(pSD); return; } // 将安全描述符转换为 SDDL 字符串 LPSTR sddlString; if (ConvertSecurityDescriptorToStringSecurityDescriptorA(pSD, SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddlString, nullptr)) { std::cout << "Security descriptor: " << sddlString << std::endl; LocalFree(sddlString); } else { std::cerr << "Failed to convert security descriptor: " << GetLastError() << std::endl; } // 获取所有者 BOOL bOwnerDefaulted; PSID pOwner; if (GetSecurityDescriptorOwner(pSD, &pOwner, &bOwnerDefaulted) && pOwner) { LPWSTR ownerName = nullptr; LPWSTR domainName = nullptr; DWORD ownerNameSize = 0; DWORD domainNameSize = 0; SID_NAME_USE sidType; // 第一次调用获取缓冲区大小 LookupAccountSidW(nullptr, pOwner, nullptr, &ownerNameSize, nullptr, &domainNameSize, &sidType); ownerName = (LPWSTR)LocalAlloc(LPTR, ownerNameSize * sizeof(WCHAR)); domainName = (LPWSTR)LocalAlloc(LPTR, domainNameSize * sizeof(WCHAR)); if (LookupAccountSidW(nullptr, pOwner, ownerName, &ownerNameSize, domainName, &domainNameSize, &sidType)) { std::wcout << L"Owner: " << domainName << L"\\" << ownerName << std::endl; } else { std::cerr << "Failed to find owner: " << GetLastError() << std::endl; } LocalFree(ownerName); LocalFree(domainName); } else { std::cerr << "Failed to obtain owner: " << GetLastError() << std::endl; } // 获取组 BOOL bGroupDefaulted; PSID pGroup; if (GetSecurityDescriptorGroup(pSD, &pGroup, &bGroupDefaulted) && pGroup) { LPWSTR groupName = nullptr; LPWSTR domainName = nullptr; DWORD groupNameSize = 0; DWORD domainNameSize = 0; SID_NAME_USE sidType; // 第一次调用获取缓冲区大小 LookupAccountSid(nullptr, pGroup, nullptr, &groupNameSize, nullptr, &domainNameSize, &sidType); groupName = (LPWSTR)LocalAlloc(LPTR, groupNameSize * sizeof(WCHAR)); domainName = (LPWSTR)LocalAlloc(LPTR, domainNameSize * sizeof(WCHAR)); if (LookupAccountSid(nullptr, pGroup, groupName, &groupNameSize, domainName, &domainNameSize, &sidType)) { std::wcout << L"Group: " << domainName << L"\\" << groupName << std::endl; } else { std::cerr << "Search group failed: " << GetLastError() << std::endl; } LocalFree(groupName); LocalFree(domainName); } else { std::cerr << "Failed to retrieve group: " << GetLastError() << std::endl; } // 获取 DACL BOOL bDaclPresent; BOOL bDaclDefaulted; PACL pDACL; if (GetSecurityDescriptorDacl(pSD, &bDaclPresent, &pDACL, &bDaclDefaulted)) { if (bDaclPresent) { std::wcout << L"DACL: " << std::endl; for (DWORD i = 0; i < pDACL->AceCount; i++) { PACCESS_ALLOWED_ACE pAce; if (GetAce(pDACL, i, (LPVOID*)&pAce)) { // 获取 SID PSID pSid = &pAce->SidStart; LPWSTR sidName = nullptr; LPWSTR domainName = nullptr; DWORD sidNameSize = 0; DWORD domainNameSize = 0; SID_NAME_USE sidType; LookupAccountSidW(nullptr, pSid, sidName, &sidNameSize, domainName, &domainNameSize, &sidType); sidName = (LPWSTR)LocalAlloc(LPTR, sidNameSize * sizeof(WCHAR)); domainName = (LPWSTR)LocalAlloc(LPTR, domainNameSize * sizeof(WCHAR)); if (LookupAccountSidW(nullptr, pSid, sidName, &sidNameSize, domainName, &domainNameSize, &sidType)) { std::wcout << L" Allow: " << domainName << L"\\" << sidName << std::endl; } else { std::cerr << "Finding SID failed: " << GetLastError() << std::endl; } LocalFree(sidName); LocalFree(domainName); } } } else { std::wcout << L"There is no DACL." << std::endl; } } else { std::cerr << "Failed to obtain DACL: " << GetLastError() << std::endl; } // 清理 LocalFree(pSD); } // 监控桌面切换 void MonitorDesktop() { // 注册 Ctrl + Shift + F1 组合键为全局热键 (ID = 1) if (!RegisterHotKey(NULL, 1, MOD_CONTROL | MOD_SHIFT, VK_F1)) { std::wcerr << L"Failed to register hotkey for Ctrl + Shift + F1." << std::endl; return; } // 打开窗口工作站 HWINSTA hWinsta = OpenWindowStationW(L"WinSta0", FALSE, GENERIC_READ | GENERIC_WRITE); if (!hWinsta) { std::wcerr << L"Failed to open window station!" << std::endl; return; } // 将当前线程关联到工作站 if (!SetProcessWindowStation(hWinsta)) { std::wcerr << L"Failed to set process window station!" << std::endl; CloseWindowStation(hWinsta); return; } HDESK hDefaultDesk = OpenDesktopWithWindowStation(L"Default", hWinsta); HDESK hWinlogonDesk = OpenDesktopWithWindowStation(L"Winlogon", hWinsta); if (!hDefaultDesk || !hWinlogonDesk) { std::wcerr << L"Failed to open desktops!" << std::endl; CloseWindowStation(hWinsta); return; } std::wcout << L"Monitoring desktop changes (SYSTEM privileges detected)..." << std::endl; std::wcout << L"Desktops: Winlogon(" << std::hex << (UINT64)hWinlogonDesk << L"), Default(" << std::hex << (UINT64)hDefaultDesk << L")." << std::endl; // 桌面监控代码 // 获取文件所在目录 std::wstring szLogPath = GetCurrentProcessDirectory(); if (szLogPath.empty()) { std::wcerr << L"Failed to get module file path." << std::endl; szLogPath = L".\\"; szLogPath += LOG_FILE_NAME; } else { // 追加日志文件名,构成完整的日志路径 szLogPath += L"\\"; szLogPath += LOG_FILE_NAME; } std::wcout << L"LogFilePath: " << szLogPath << std::endl; std::vector<WindowInfo> windowHistory; std::size_t prevHash = 0; // 保存上一次的哈希值 bool monitoring = false; MYMONITOR_THREAD_INFO monitor_info = { hWinlogonDesk }; HANDLE hBorderThread = nullptr; // 边框线程句柄 DWORD errCnt = 0; // 循环中发生非致命错误次数 std::wstring oldDesktopName; while (errCnt <= 5u) { MSG msg; // 非阻塞的消息检查 while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { if (IsMainWindowActive() && msg.message == WM_HOTKEY && msg.wParam == 1) { // Ctrl + Shift + F1 热键触发,退出循环 std::wcout << L"Ctrl + Shift + F1 pressed. Exiting monitoring..." << std::endl; Sleep(1000); // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); goto exit_monitoring; } TranslateMessage(&msg); DispatchMessageW(&msg); } HDESK hCurrentDesk = GetActiveDesktop(); // 获取活动桌面句柄 std::wstring desktopName = GetDesktopName(hCurrentDesk); // 获取桌面名称 if (oldDesktopName != desktopName) { std::wcout << L"Current Desktop: " << desktopName << std::endl; } if (desktopName == L"Winlogon" && !monitoring) { std::wcout << L"Switched to Winlogon desktop. Start monitoring window hierarchy..." << std::endl; monitoring = true; windowHistory.clear(); // 清空之前的记录 // 创建事件对象 hBroderWndCreateEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); if (!hBroderWndCreateEvent) { std::wcout << L"Failed to create event." << std::endl; CloseMonitoringUI(hWinlogonDesk); goto exit_monitoring; } // 启动边框绘制线程,并传递 Winlogon 桌面句柄 if (!borderRunning) { hBorderThread = CreateThread(nullptr, 0, BorderThread, (LPVOID)hWinlogonDesk, 0, nullptr); borderRunning = true; } // 在主线程等待窗口创建完成 WaitForSingleObject(hBroderWndCreateEvent, CREATE_BORDER_WINDOW_TIMEOUT); DWORD dwCreateBwndRet = 0; SetLastError(0); // 错误代码清零 dwCreateBwndRet = WaitForSingleObject(hBroderWndCreateEvent, CREATE_BORDER_WINDOW_TIMEOUT); if (dwCreateBwndRet != WAIT_OBJECT_0) { std::wcerr << L"WaitForSingleObject signal a failed: " << dwCreateBwndRet << L", GetLastError() == " << GetLastError() << std::endl; if (hBroderWndCreateEvent) CloseHandle(hBroderWndCreateEvent); ++errCnt; // 增加错误计数 monitoring = false; // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); TerminateThread(hBorderThread, 0); continue; } // 设置状态为:记录中 StartMonitoringUI(hWinlogonDesk); // 切换到 winlogon 桌面并枚举窗口 HANDLE hThread = CreateThread(nullptr, 0, MonitorDesktopThread, &monitor_info, 0, nullptr); if (hThread) { DWORD waitRet = 0; SetLastError(0); // 错误代码清零 waitRet = WaitForSingleObject(hThread, MONITOR_THREAD_TIMEOUT); if (waitRet != WAIT_OBJECT_0) { std::wcerr << L"WaitForSingleObject signal a failed: " << waitRet << L", GetLastError() == " << GetLastError() << std::endl; CloseHandle(hThread); ++errCnt; // 增加错误计数 monitoring = false; // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); TerminateThread(hBorderThread, 0); continue; } CloseHandle(hThread); errCnt = 0; // 错误计数归零 } windowHistory = monitor_info.wndInfo; // 计算当前窗口层次的哈希值 prevHash = GetWindowHierarchyHash(windowHistory); // 设置状态为:已完成 CompleteMonitoringUI(hWinlogonDesk); } else if (desktopName == L"Default" && monitoring) { std::wcout << L"Switched back to Default desktop. Stopping monitoring..." << std::endl; std::time_t currentTime = std::time(nullptr); SaveWindowHierarchy(windowHistory, currentTime, szLogPath.c_str()); monitoring = false; // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); // 格式化时间为字符串 wchar_t timeBuffer[100]; tm ti = {}; localtime_s(&ti, &currentTime); std::wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), L"%Y-%m-%d %H:%M:%S", &ti); // 构建窗口层次信息文本 std::wstringstream ss; ss << L"Most Recent History Call ( " << timeBuffer << L" ): \r\n"; for (const auto& win : windowHistory) { ss << L"Class Name: " << win.className << L", Title: " << win.windowTitle << L", HWND: " << std::hex << win.hwnd << L"\r\n"; } std::wstring historyText = ss.str(); // 显示历史记录对话框(使用线程实现非阻滞) CreateDialogBoxAsyncW(historyText); } else if(IsGuidDesktopName(desktopName) && !monitoring) { if (oldDesktopName != desktopName) { std::wcout << L"The current desktop is a special desktop, " << "which may be related to anti-virus software..." << std::endl; PrintSecurityDescriptor(hCurrentDesk); } } // 检查窗口层次变化 if (monitoring) { SetLastError(0); HANDLE hThread = CreateThread(nullptr, 0, MonitorDesktopThread, &monitor_info, 0, nullptr); if (hThread) { // 设置状态为:记录中 StartMonitoringUI(hWinlogonDesk); //WaitForSingleObject(hThread, INFINITE); // 等待线程结束 //CloseHandle(hThread); 设置状态为:已完成 //CompleteMonitoringUI(hWinlogonDesk); DWORD waitRet = 0; SetLastError(0); waitRet = WaitForSingleObject(hThread, MONITOR_THREAD_TIMEOUT); if (waitRet != WAIT_OBJECT_0) { std::wcerr << L"WaitForSingleObject signal a failed: " << waitRet << L", GetLastError() == " << GetLastError() << std::endl; CloseHandle(hThread); ++errCnt; // 增加错误计数 monitoring = false; // 启动关闭边框的线程 CloseMonitoringUI(hWinlogonDesk); if(hBorderThread) TerminateThread(hBorderThread, 0); continue; } errCnt = 0; // 错误计数归零 CloseHandle(hThread); // 设置状态为:已完成 CompleteMonitoringUI(hWinlogonDesk); // 检查并更新窗口层次 std::size_t currentHash = GetWindowHierarchyHash(monitor_info.wndInfo); if (currentHash != prevHash) { std::wcout << L"Window hierarchy changed. Updating records..." << std::endl; windowHistory = monitor_info.wndInfo; std::time_t currentTime = std::time(nullptr); SaveWindowHierarchy(windowHistory, currentTime, szLogPath.c_str()); prevHash = currentHash; } } else { // CreateThread failed std::wcerr << L"Create Monitor-Desktop thread failed, GetLastError() == " << GetLastError() << std::endl; } } oldDesktopName = desktopName; Sleep(1000); // 每秒检查一次 } exit_monitoring: // 注销热键 UnregisterHotKey(NULL, 1); // 清理 CloseDesktop(hDefaultDesk); CloseDesktop(hWinlogonDesk); CloseWindowStation(hWinsta); } // 检测文件编码 FileEncoding DetectFileEncoding(const std::wstring& filePath) { std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for encoding detection: " << filePath << std::endl; return FileEncoding::UNKNOWN; } unsigned char bom[3] = { 0 }; file.read(reinterpret_cast<char*>(bom), 3); file.close(); // 检查 BOM(字节顺序标记, Byte Order Mark) if (bom[0] == 0xFF && bom[1] == 0xFE) { return FileEncoding::UTF16LE; // UTF-16 LE } else if (bom[0] == 0xFE && bom[1] == 0xFF) { return FileEncoding::UTF16BE; // UTF-16 BE } else if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) { return FileEncoding::UTF8; // UTF-8 (BOM) } else { return FileEncoding::ANSI; // 默认解析为 ANSI } } // 读取文件内容,处理不同编码格式 bool ReadFileWithEncoding(const std::wstring& filePath, std::wstring& content) { FileEncoding encoding = DetectFileEncoding(filePath); if (encoding == FileEncoding::UTF16LE) { // 读取 UTF-16 LE 编码的文件 //std::wcout << L"UTF16-LE" << std::endl; return ReadUTF16LEFile(filePath, content); } else if (encoding == FileEncoding::UTF16BE) { // 读取 UTF-16 BE 编码的文件,手动处理字节序 std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open UTF-16 file: " << filePath << std::endl; return false; } // 跳过 BOM(2 个字节) file.seekg(2, std::ios::beg); // 读取文件到缓冲区 std::vector<char> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); file.close(); // 转换为宽字符 content = ConvertUTF16BEToWideString(buffer); } else if (encoding == FileEncoding::UTF8) { return ReadUTF8File(filePath, content); } else if (encoding == FileEncoding::ANSI) { // 读取 ANSI 编码的文件 std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open ANSI file: " << filePath << std::endl; return false; } std::stringstream ss; ss << file.rdbuf(); std::string ansiContent = ss.str(); content = ANSIToWideString(ansiContent); file.close(); } else { std::wcerr << L"Unknown file encoding." << std::endl; return false; } return true; } // 将宽字符内容保存为 UTF-8 文件 bool SaveAsUTF8(const std::wstring& filePath, const std::wstring& content) { std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } // 写入 UTF-8 BOM const char bom[] = { '\xEF', '\xBB', '\xBF' }; file.write(bom, sizeof(bom)); // 将宽字符转换为 UTF-8 编码 std::string utf8Content = WideStringToUTF8(content); file.write(utf8Content.c_str(), utf8Content.size()); file.close(); return true; } // 将宽字符内容保存为 UTF-16 LE 文件(不使用 codecvt) bool SaveAsUTF16LE(const std::wstring& filePath, const std::wstring& content) { std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } // 写入 BOM const char bom[] = { '\xFF', '\xFE' }; file.write(bom, sizeof(bom)); // 将宽字符转换为 UTF-16 Little Endian 编码字节流 for (wchar_t wc : content) { char16_t utf16LE = static_cast<char16_t>(wc); file.write(reinterpret_cast<const char*>(&utf16LE), sizeof(utf16LE)); } file.close(); return true; } // 将宽字符内容保存为 UTF-16 BE 文件 bool SaveAsUTF16BE(const std::wstring& filePath, const std::wstring& content) { std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } // 写入 BOM const char bom[] = { '\xFE', '\xFF' }; file.write(bom, sizeof(bom)); // 将宽字符转换为 UTF-16 Big Endian 编码字节流 for (wchar_t wc : content) { char16_t utf16BE = (static_cast<unsigned char>(wc >> 8) & 0xFF) | (static_cast<unsigned char>(wc & 0xFF) << 8); file.write(reinterpret_cast<const char*>(&utf16BE), sizeof(utf16BE)); } file.close(); return true; } // 根据编码保存修改后的文件内容 bool ModifyAndSaveFileWithEncoding(const std::wstring& filePath, const std::wstring& content) { FileEncoding encoding = DetectFileEncoding(filePath); if (encoding == FileEncoding::UTF16LE) { // 保存为 UTF-16 Little Endian 编码 return SaveAsUTF16LE(filePath, content); } else if (encoding == FileEncoding::UTF16BE) { // 保存为 UTF-16 Big Endian 编码 return SaveAsUTF16BE(filePath, content); } else if (encoding == FileEncoding::UTF8) { // 保存为 UTF-8 编码 std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } // 写入 UTF-8 BOM const char bom[] = { '\xEF', '\xBB', '\xBF' }; file.write(bom, sizeof(bom)); std::string utf8Content = WideStringToUTF8(content); file.write(utf8Content.c_str(), utf8Content.size()); file.close(); } else if (encoding == FileEncoding::ANSI) { // 保存为 ANSI 编码 std::ofstream file(filePath, std::ios::binary); if (!file.is_open()) { std::wcerr << L"Failed to open file for writing: " << filePath << std::endl; return false; } std::string ansiContent = WideStringToANSI(content); file.write(ansiContent.c_str(), ansiContent.size()); file.close(); } else { std::wcerr << L"Unknown file encoding, cannot save file." << std::endl; return false; } return true; } // 修改 SeAssignPrimaryTokenPrivilege 权限 bool ModifySeAssignPrimaryTokenPrivilege(const std::wstring& configFilePath, const std::wstring& userName) { std::wstring content; // 读取文件内容 if (!ReadFileWithEncoding(configFilePath, content)) { std::wcerr << L"Failed to read configuration file with correct encoding." << std::endl; return false; } // 查找并修改 SeAssignPrimaryTokenPrivilege size_t pos = content.find(L"SeAssignPrimaryTokenPrivilege"); if (pos != std::wstring::npos) { size_t endPos = content.find(L"\n", pos) - 1; std::wstring line = content.substr(pos, endPos - pos); // 检查是否包含当前用户 if (line.find(userName) == std::wstring::npos) { line += L"," + userName; content.replace(pos, endPos - pos, line); } } // 保存修改后的文件 if (!ModifyAndSaveFileWithEncoding(configFilePath, content)) { std::wcerr << L"Failed to save modified configuration file." << std::endl; return false; } return true; } // 执行命令 bool ExecuteCommandWithOutput(const std::wstring& command, std::wstring& output) { STARTUPINFOW si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; HANDLE hRead, hWrite; // 初始化管道句柄的安全属性 ZeroMemory(&sa, sizeof(sa)); sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = nullptr; // 为子进程的 STDOUT 和 STDERR 创建管道 if (!CreatePipe(&hRead, &hWrite, &sa, 0)) { std::wcerr << L"Failed to create pipe." << std::endl; return false; } // 确保管道的读取句柄未被继承 if (!SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0)) { std::wcerr << L"Failed to set handle information." << std::endl; return false; } // 为子进程设置 STARTUPINFO 结构 ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.hStdError = hWrite; si.hStdOutput = hWrite; si.dwFlags |= STARTF_USESTDHANDLES; // 将 STDOUT 和 STDERR 重定向到管道 // 设置 PROCESS_INFORMATION 结构 ZeroMemory(&pi, sizeof(pi)); // 使用 Create_NO_WINDOW 创建子进程,以避免显示新的控制台窗口 if (!CreateProcessW(NULL, const_cast<LPWSTR>(command.c_str()), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { std::wcerr << L"Failed to execute command: " << command << std::endl; CloseHandle(hRead); CloseHandle(hWrite); return false; } // 关闭父进程中管道的写入端 CloseHandle(hWrite); // 读取子进程的输出 DWORD bytesRead; CHAR buffer[4096]; std::string ansiResult; BOOL success = FALSE; ZeroMemory(buffer, sizeof(buffer)); // 从管道中连续读取,直到没有更多数据 while (true) { success = ReadFile(hRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL); if (!success || bytesRead == 0) { break; // 没有更多数据要读取 } buffer[bytesRead] = '\0'; // 缓冲区结束标记 ansiResult += buffer; // 收集 ANSI 编码的结果 } // 关闭手柄 CloseHandle(hRead); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); // 如果结果为空,则返回“no message” if (ansiResult[0] == '\0') { output = L"\n(no message)\n"; return true; } // 将 ANSI 结果转换为宽字符串以进行正确的 Unicode 处理 int wideSize = MultiByteToWideChar(CP_ACP, 0, ansiResult.c_str(), -1, NULL, 0); if (wideSize == 0) { std::wcerr << L"Failed to convert output to wide string." << std::endl; return false; } std::wstring wideResult(wideSize, 0); MultiByteToWideChar(CP_ACP, 0, ansiResult.c_str(), -1, &wideResult[0], wideSize); output = wideResult; return true; } // 判断文件是否存在 BOOL IsFileExist(const std::wstring& wsFile) { DWORD dwAttrib = GetFileAttributesW(wsFile.c_str()); return INVALID_FILE_ATTRIBUTES != dwAttrib && 0 == (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); } // 导出安全策略 // secedit /export /cfg C:\gp.inf bool ExportSecurityPolicy(const std::wstring& system32Dir, const std::wstring& outputFile, std::wstring& output) { // https://blog.csdn.net/u0/article/details/ if (IsFileExist(outputFile)) { // 判断配置文件是否已经存在 std::wcout << L"The configuration file already exists. Attempting to delete it." << std::endl; if (!DeleteFileW(outputFile.c_str())) { std::wcerr << L"Failed to delete the configuration file." << std::endl; return false; } } std::wstring command = system32Dir + L"\\secedit.exe /export /cfg " + outputFile; return ExecuteCommandWithOutput(command, output); } // 应用修改后的安全策略 // secedit /configure /db test.sdb /cfg C:\gp.inf /overwrite /log hisecws.log /quiet bool ApplySecurityPolicy(const std::wstring& system32Dir, const std::wstring& configFilePath, const std::wstring& dbFilePath, std::wstring& output) { std::wstring command = system32Dir + L"\\secedit.exe /configure /db " + dbFilePath + L" /cfg " + configFilePath + L" /overwrite /quiet"; return ExecuteCommandWithOutput(command, output); } // 获取系统盘符 std::wstring GetSystemDrive() { wchar_t systemPath[MAX_PATH]; if (GetSystemDirectoryW(systemPath, MAX_PATH)) { return std::wstring(systemPath).substr(0, 3); // 返回盘符部分,例如 C:/ } return L""; // 如果获取失败,返回默认 C 盘符 } // 获取当前用户账户名称 std::wstring GetCurrentUserName() { wchar_t userName[UNLEN + 1]; DWORD size = UNLEN + 1; if (GetUserNameW(userName, &size)) { return std::wstring(userName); } return L""; // 返回空字符串表示获取失败 } // 提示用户注销 void PromptUserToLogout() { // 询问用户是否要注销并重新登录 int result = MessageBoxW(NULL, L"The security settings have been updated. Would you like to log off now and apply the changes?", L"Log Off Confirmation", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL); if (result == IDYES) { // 注销用户 if (!ExitWindowsEx(EWX_LOGOFF | EWX_FORCE, SHTDN_REASON_MAJOR_OTHER)) { std::wcerr << L"Failed to log off the user." << std::endl; } } else { std::wcout << L"User chose not to log off." << std::endl; } } // 解除用户获取 AssignPrimaryTokenPrivilege 的限制 // (修改系统配置文件,需要注销并重新登陆) bool ChangeAssignPrimaryTokenPrivilege() { std::wstring systemDrive = GetSystemDrive(); std::wstring system32Dir = systemDrive + L"Windows\\" + SYSTEM32_NAME; std::wstring configFilePath = systemDrive + L"gpSvcInfo.inf"; std::wstring dbFilePath = systemDrive + L"gpSvcdata.sdb"; std::wstring userName = GetCurrentUserName(); std::wstring output; if (systemDrive.empty()) { std::wcerr << L"Failed to retrieve the current system root path." << std::endl; return false; } if (userName.empty()) { std::wcerr << L"Failed to retrieve the current user name." << std::endl; return false; } if (!ExportSecurityPolicy(system32Dir, configFilePath, output)) { std::wcerr << L"Failed to export security policy." << std::endl; return false; } std::wcout << L"\nExportSecurityPolicy message: \n" << output << std::endl; if (!ModifySeAssignPrimaryTokenPrivilege(configFilePath, userName)) { std::wcerr << L"Failed to modify SeAssignPrimaryTokenPrivilege." << std::endl; return false; } if (!ApplySecurityPolicy(system32Dir, configFilePath, dbFilePath, output)) { std::wcerr << L"Failed to apply security policy." << std::endl; return false; } std::wcout << L"\nApplySecurityPolicy message: \n" << output << std::endl; PromptUserToLogout(); return true; } int wmain(int argc, wchar_t* argv[]) { // 宽字符中文支持 _wsetlocale(LC_ALL, L"zh-CN"); // 获取当前进程的主窗口 hMainWindow = GetMainWindowHandle(); if (!hMainWindow) { std::cerr << "Failed to find main window." << std::endl; system("pause > nul 2 > nul"); return 1; } std::cout << "Main window handle: " << std::hex << hMainWindow << std::endl; // 检查是否为管理员权限运行 if (!IsRunAsAdmin()) { std::wcout << L"Attempting to restart with administrator privileges..." << std::endl; if (RelaunchAsAdmin()) { return 0; // 提升后进程将重新启动,当前进程结束 } else { std::wcerr << L"Fatal Error." << std::endl; system("pause > nul 2 > nul"); return 1; } } // 启用 SeDebugPrivilege if (!EnablePrivilege(SE_DEBUG_NAME)) { std::wcerr << L"Failed to enable SeDebugPrivilege." << std::endl; system("pause > nul 2 > nul"); return 1; } // 启用 SeIncreaseQuotaPrivilege if (!EnablePrivilege(SE_INCREASE_QUOTA_NAME)) { std::wcerr << L"Failed to enable SeIncreaseQuotaPrivilege." << std::endl; system("pause > nul 2 > nul"); return 1; } // 启用 SeAssignPrimaryTokenPrivilege if (!EnablePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME)) { std::wcerr << L"Failed to enable SeAssignPrimaryTokenPrivilege." << std::endl; if (!ChangeAssignPrimaryTokenPrivilege()) { std::wcerr << L"Failed to enable primary token assignment privilege" << L" by modifying security configuration." << std::endl; } std::wcout << L"Press any key to close app."; system("pause > nul 2 > nul"); return 1; } // 检查 SYSTEM 权限 if (!IsSystem()) { std::wcout << L"Attempting to restart with SYSTEM privileges..." << std::endl; // 检查命令行参数,避免无限递归 if (argc < 2 || _wcsicmp(argv[1], L"system") != 0) { // 重新启动自身并传递 "system" 参数 wchar_t commandLine[MAX_PATH]; swprintf(commandLine, MAX_PATH, L"%s system", argv[0]); if (CreateSystemProcess(argv[0], commandLine)) { std::wcout << L"Restarted with SYSTEM privileges." << std::endl; } else { std::wcerr << L"Failed to restart with SYSTEM privileges." << std::endl; system("pause > nul 2 > nul"); } return 0; } else { std::wcerr << L"Already tried to elevate privileges but failed." << std::endl; system("pause > nul 2 > nul"); return 1; } } // 如果当前进程已经是 SYSTEM 权限,则继续执行桌面监控 MonitorDesktop(); std::wcout << L"Press any key to close app."; system("pause > nul 2 > nul"); return 0; }

编译时设置监视器高 DPI 识别,和以管理员身份启动。

运行效果如下:

正确枚举 Winlogon 桌面窗口层次
测试截图

初次应用修改配置(提示需要注销用户后重启):

正确枚举 Winlogon 桌面窗口层次
系统配置自动化修改

Winlogon 桌面屏幕描边 + 提示信息:

正确枚举 Winlogon 桌面窗口层次
全屏覆盖提示窗口(透明)

监视器窗口最近一次历史记录信息回放:

正确枚举 Winlogon 桌面窗口层次
最近一次历史记录信息

1.0.0.2 版本开始日志记录存在放在当前目录 “window_hierarchy.log” 文件中。

正确枚举 Winlogon 桌面窗口层次
日志记录样例

1.0.0.4 特性:

(1)枚举特定桌面的安全描述符信息:

正确枚举 Winlogon 桌面窗口层次
桌面的安全描述符信息

 Winlogon 桌面窗口层次

Winlogon 桌面默认是没有窗口的,所以是黑色背景。当执行任务时,具有窗口。当在 UAC 界面时,背景窗口是 Credential Dialog Xaml Host 窗口;当在 CAD 或登陆界面时,背景窗口是 LogonUI Logon Window。

备注:由于博主时间有限,更多信息请自行探索。

参考文献:

  • CreateProcessAsUserW 函数 – Win32 apps | Microsoft Learn
  • CreateProcessAsUser 失败,错误码:1314 – CSDNCreateProcessAsUser 失败,错误码:1314 
  • Windows平台下的 Session0 创建进程的问题与解决办法 – CSDN
  • 颜色查找器 – 在线颜色工具 – PhotoKit.com

本文出处链接:正确枚举 Winlogon 桌面窗口层次。 

本文发布于:2024.08.27;更新于:2024.08.27 / 29, 2024.09.26,2024.10.13。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/135238.html

(0)
上一篇 2025-07-06 12:33
下一篇 2025-07-06 13:00

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信