我建议您从阅读documentation 开始。我想也许你可以打开窗口站并将你的进程附加到它,但我对 Windows 的这方面不是很熟悉。
编辑 1:
在 Windows XP 中,当以 SYSTEM 身份运行时,我能够通过 OpenDesktop 访问安全桌面(“winlogon”);安全桌面上的 ACL 只允许访问 SYSTEM 帐户。打开它后,我可以列举它上面的窗户,虽然只有少数几个。也许您可以设置一个窗口挂钩并监听特定对话框的创建。我不确定 Vista 是否改变了这个模型,所以它可能行不通;我面前没有要测试的 Vista 机器。
编辑 2:
好的,我得到了最有效的东西(在 Windows 7 上测试)。首先,您必须有一个以 SYSTEM 身份运行的服务。从该服务中,您需要在用户会话中启动一个单独的应用程序。为此,请枚举所有查找 winlogon.exe 的进程,打开其令牌,然后 CreateProcessAsUser。为 STARTUPINFO 的 lpDesktop 参数指定“WinSta0\Winlogon”。现在,您在“Winlogon”桌面上的用户会话中以 SYSTEM 身份运行了一个进程。在新的流程中,你可以为所欲为;我使用 EnumDesktopWindows 进行了快速测试,我能够获取各种 UAC 相关窗口的窗口类和文本(“$$$Secure UAP 背景窗口”、“$$$Secure UAP 背景假客户端窗口”等)。不过,我不确定如何确定何时显示 UAC 提示;作为一种快速破解,您可以每 100 毫秒运行一次循环以查找 UAC 窗口或其他东西。如果有帮助,我可以粘贴一些代码。
编辑 3:
好的。我编写了一个 Win32 服务,它采用以下参数:
/install - 安装服务
/uninstall - 卸载服务
/service - 作为服务运行;通过 SCM 调用
/client - 作为客户端运行;通过 CreateProcessAsUser 调用
唯一有趣的代码是 /service 和 /client 模式。
在 /service 模式下,它通过 EnumProcesses 和 GetModuleFileNameEx 枚举正在运行的进程以查找“winlogon.exe”。当它找到一个时,它会打开它的令牌并通过 CreateProcessAsUser 以 /client 模式启动自己:
HANDLE hProcess = ...;
// winlogon.exe runs as SYSTEM in user's session; we need to run the same way
HANDLE hToken = NULL;
if(OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, &hToken))
{
TCHAR szCommandLine[MAX_PATH];
GetModuleFileName(NULL, szCommandLine, MAX_PATH);
PathQuoteSpaces(szCommandLine);
// run in /client mode
_tcscat_s(szCommandLine, MAX_PATH, _T(" /client"));
STARTUPINFO StartupInfo;
ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
// run on the Winlogon desktop
StartupInfo.lpDesktop = _T("WinSta0\\Winlogon");
PROCESS_INFORMATION ProcessInformation;
ZeroMemory(&ProcessInformation, sizeof(PROCESS_INFORMATION));
if(CreateProcessAsUser(hToken, NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInformation))
{
CloseHandle(ProcessInformation.hThread);
ProcessInformation.hThread = NULL;
CloseHandle(ProcessInformation.hProcess);
ProcessInformation.hProcess = NULL;
}
CloseHandle(hToken);
hToken = NULL;
}
在 /client 模式下,它通过一堆 FindWindow 和 FindWindowEx 调用单击 UAC 提示符上的“是”按钮。您可以使用 Spy++ 来确定窗口层次结构。
HWND hWnd = ...;
HWND hWndButton = FindWindowEx(hWnd, NULL, _T("Button"), NULL);
if(hWndButton != NULL)
{
// see if this is the "Yes" button
TCHAR szText[32];
if(GetWindowText(hWndButton, szText, 32) && _tcsicmp(szText, _T("&Yes")) == 0)
{
// click it
SendMessage(hWndButton, BM_CLICK, 0, 0);
}
}
我测试这个的方法是坚持 Sleep(5000);在 /client 代码中。然后我启动服务并立即执行一些触发 UAC 提示的操作(即运行 regedit)。 5 秒后 /client 代码将唤醒并找到并单击“是”按钮。您可以在 Winlogon 桌面上运行其他进程; cmd.exe 和 spyxx.exe (Spy++) 是最有用的。不幸的是,explorer.exe 在 Winlogon 桌面上运行时会出现很多问题并且不是很有用。要进入 Winlogon 桌面,您可以运行 regedit 然后 Alt+Tab 切换到其他应用程序。如果您想变得花哨,您可以编写自己的桌面切换实用程序(使用 SwitchDesktop 函数),这样您就不必触发 UAC 提示符即可进入 Winlogon 桌面。如果你想变得非常花哨,你可以设置一个全局窗口挂钩来监控窗口的创建;当 UAC 对话框即将显示时,您可以准备单击其“是”按钮。不过,我并没有走那么远。