我建议您先阅读
文档。我猜您可以打开窗口工作站并将您的进程附加到它,但我对Windows的这个领域不是很熟悉。
编辑1:
在Windows XP中,当以SYSTEM身份运行时,我能够通过OpenDesktop访问安全桌面("winlogon");安全桌面的ACL仅允许SYSTEM帐户访问。打开后,我可以枚举其上的窗口,尽管只有少数几个。也许您可以设置一个窗口挂钩并监听特定对话框的创建。我不确定Vista是否改变了这个模型,所以也许它不起作用;我没有一台Vista机器来测试。
编辑2:
好的,我找到了基本可行的方法(在Windows 7上测试过)。首先,您必须拥有一个以SYSTEM身份运行的服务。从该服务中,您需要在用户会话中启动一个单独的应用程序。为此,枚举所有进程,查找winlogon.exe,打开其令牌,并使用CreateProcessAsUser创建进程。将“WinSta0\Winlogon”指定为STARTUPINFO的lpDesktop参数。现在,在“Winlogon”桌面上有一个以SYSTEM身份运行的进程在用户会话中运行。在新进程中,您可以做任何想做的事情;我使用EnumDesktopWindows进行了快速测试,并能够获取各种与UAC相关的窗口的窗口类和文本(“$$$Secure UAP Background Window”,“$$$Secure UAP Background Fake Client Window”等)。但我不确定如何确定何时显示UAC提示;作为一个快速的hack,您可以每100毫秒运行一次循环,查找UAC窗口或其他内容。如果需要,我可以贴上一些代码。
编辑3:
好的。我编写了一个Win32服务,它接受以下参数:
/install-安装服务
/uninstall-卸载服务
/service-作为服务运行;通过SCM调用
/client-作为客户端运行;通过CreateProcessAsUser调用
唯一有趣的代码位于/service和/client模式中。
在/service模式下,它通过EnumProcesses和GetModuleFileNameEx枚举运行的进程,寻找“winlogon.exe”。当找到时,它打开其令牌并通过CreateProcessAsUser以/client模式启动自身。
HANDLE hProcess = ...;
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);
_tcscat_s(szCommandLine, MAX_PATH, _T(" /client"));
STARTUPINFO StartupInfo;
ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
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;
}
在客户端模式下,它通过一系列的FindWindow和FindWindowEx调用来点击UAC提示上的"Yes"按钮。您可以使用Spy++来确定窗口层次结构。
HWND hWnd = ...;
HWND hWndButton = FindWindowEx(hWnd, NULL, _T("Button"), NULL);
if(hWndButton != NULL)
{
TCHAR szText[32];
if(GetWindowText(hWndButton, szText, 32) && _tcsicmp(szText, _T("&Yes")) == 0)
{
SendMessage(hWndButton, BM_CLICK, 0, 0);
}
}
我测试这个的方法是在/client代码中加入Sleep(5000);。然后我启动服务并立即执行一些会触发UAC提示(例如运行regedit)的操作。5秒后,/client代码将唤醒并找到并点击“Yes”按钮。您可以在Winlogon桌面上运行其他进程;cmd.exe和spyxx.exe(Spy ++)最有用。不幸的是,当在Winlogon桌面上运行时,explorer.exe会出现许多问题,并且不太有用。要进入Winlogon桌面,您可以运行regedit,然后Alt+Tab切换到另一个应用程序。如果您想变得花哨一些,您可以编写自己的桌面切换实用程序(使用SwitchDesktop函数),以便无需触发UAC提示即可进入Winlogon桌面。如果您想变得非常花哨,您可以设置全局窗口钩子来监视窗口创建;当即将显示UAC对话框时,您可以准备好点击其“Yes”按钮。不过,我没有那么深入。