目录
本文出处链接:https://blog.csdn.net/qq_59075481/article/details/141608316。
前言
众所周知,从 Windows 7 开始,Winlogon 桌面不再使用 SASWindow 作为背景窗口,而是采用了一套新的安全桌面模式。我发现 CSDN 上在这方面的研究很少。
在我前面的几篇文章里面已经详细分析并实现了,编程化拦截与 Winlogon 有关的登陆事件(如 Ctrl + Alt + Delete 快捷键)。在之前,我浅谈过一些有关 LogonUI Interface 登陆 UI 界面的内容,但并未做详细的解释。这篇文章将就如何动态枚举 Winlogon 桌面的窗口层次并进行记录进行详细的讲解。如有错误,敬请点拨。
专栏文章:
快捷键机制系列文章 | 涟幽 516https://blog.csdn.net/qq_59075481/category_12568641.html
原理解释
首先,我们需要知道 Winlogon 桌面(登陆桌面,运行在控制台会话)和 Default 桌面(用户默认桌面,运行在 UI 会话)均是由 Winlogon.exe 所创建的。
Winlogon.exe 是 Windows 操作系统中负责处理用户登录和注销等会话管理功能的进程。通常情况下,每个控制台会话(即本地会话)会有一个对应的 Winlogon.exe 实例,用于处理用户认证等任务。
UI 会话 是指用户交互的会话,通常指的是图形用户界面(GUI)环境下的桌面会话。每个用户登录到系统时,Windows 会为该用户分配一个会话 ID,并启动一个桌面环境来处理用户的输入和输出。
在多用户环境中,每个用户的 UI 会话可以与一个特定的控制台会话关联。控制台会话是指直接连接到物理屏幕、键盘和鼠标的会话(如通过直接本地登录),而其他 UI 会话则可能是通过远程桌面或其他方式登录的。
所以,用户一般接触最多的就是交互式用户会话和桌面,对于 Winlogon 桌面可能知之甚少。其实用户所熟知的 UAC 对话框,就是运行在 Winlogon 桌面下的系统程序所创建的窗口。
LogonUI.exe 进程:这是登陆用户交互式界面进程,Winlogon.exe 进程会在需要在登陆桌面下显示交互式界面时,启动该进程。并通过进程间通信(IPC)技术,如管道、RPC 等完成多进程同步事务。用户看到的登陆界面、CAD 界面、UAC 界面均最终由它启动。
LogonUI 运行时模块:LogonUI.exe 实质上是一个加载容器,用于装载运行时需要的接口模块。第一个加载的模块为:LogonUIController.dll。其他运行时模块通过延迟加载技术完成加载。
窗口工作站:窗口工作站是与进程关联的安全对象,包含一个或多个绑定的桌面对象、剪贴板等等。Winlogon 桌面和 Default 桌面默认运行在 Winsta0 窗口工作站下。进程必须以合适的访问权限打开窗口工作站,才能够打开指定的桌面访问句柄。进程每次获取的窗口工作站的访问句柄不同,桌面的访问句柄就不同。如果需要获知对象句柄的名称,则需要通过 WINAPI GetUserObjectInformation。
工作线程:进程连接到窗口工作站后,系统会将桌面分配给建立连接的线程。
为了在运行时研究清楚 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 修复内容:
- 非主用户账户缺少 SE_ASSIGNPRIMARYTOKEN_NAME 特权导致无法创建 SYSTEM 权限的进程(强制修改安全配置并在重启时生效);
- 多用户账户登陆下获取到错误的 Winlogon 进程句柄;
- 窗口关闭快捷键异常,现在改用注册热键 Ctrl + Shitf + F1 完成;
- 在监视窗口时,新增高亮边框效果;
- 日志记录完成时,显示当前日志记录的内容;
- 使用特权账户启动失败时,给出更多信息;
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/u012494876/article/details/51204615
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 修复内容:
- 修复了进程退出时不检查主窗口是否关闭的问题(但异步对话框的关闭未完成,将在后续更新中修复);
- 修复了不能连续监视的问题,现在在监视 Winlogon 桌面时,每隔 1 秒扫描一次窗口层次并产生日志哈希。当窗口层次发生变化时,记录新的窗口层次信息到日志文件;
- 只在返回 Default 桌面时弹窗显示最近一次记录的窗口层次,其他由日志记录保存;
- 新增在监视窗口时显示状态信息,状态信息默认对齐在屏幕左下角;
已知未修复问题:
- DialogBox 的文本框中文本过长或者行数过多时不支持滚动条滑动翻页;
- 部分资源释放和指针的检查代码不够健硕;
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 = 0x3456789; // 内部校验码(防止外部程序模拟消息)
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, ¤tTime);
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/u012494876/article/details/51204615
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 修复内容:
- 修复了在创建历史记录对话框时,在堆栈上传递共享地址导致的内存访问问题;
- 修复了发送窗口关闭消息但但有时候不能够完成退出的问题;
- 修复了多线程的等待和检查机制(部分地方之前采用了无限等待或者不安全的检查);
- 根据 C++20 ,取消了对 codecvt 头文件及其 API 的使用;
- 使用静态链接和 Win32 的绝对路径缓解使用当前目录的模块劫持漏洞;
- 修复在部分系统上,日志文本不正确回车换行的问题;
版本 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 = 0x3456789; // 内部校验码(防止外部程序模拟消息)
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, ¤tTime);
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/u012494876/article/details/51204615
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 桌面屏幕描边 + 提示信息:
监视器窗口最近一次历史记录信息回放:
1.0.0.2 版本开始日志记录存在放在当前目录 "window_hierarchy.log" 文件中。
Winlogon 桌面窗口层次
Winlogon 桌面默认是没有窗口的,所以是黑色背景。当执行任务时,具有窗口。当在 UAC 界面时,背景窗口是 Credential Dialog Xaml Host 窗口;当在 CAD 或登陆界面时,背景窗口是 LogonUI Logon Window。
备注:由于博主时间有限,更多信息请自行探索。
参考文献:
本文出处链接:https://blog.csdn.net/qq_59075481/article/details/141608316。
本文发布于:2024.08.27;更新于:2024.08.27 / 29, 2024.09.26。