在 Windows 操作系统中,服务进程通常运行在 Session 0,而用户界面应用程序运行在用户会话中。为了在用户会话中启动 GUI 应用程序,可以使用 CreateProcessAsUser
函数。以下是一个示例代码,展示了如何在服务进程中使用 CreateProcessAsUser
函数穿透 Session 0 并在用户会话中启动 GUI 应用程序:
#include <windows.h>
#include <wtsapi32.h>
#include <stdio.h>
#pragma comment(lib, "wtsapi32.lib")
BOOL CreateProcessAsUserInSession(DWORD dwSessionId, LPCTSTR lpApplicationName, LPTSTR lpCommandLine) {
HANDLE hToken = NULL;
HANDLE hNewToken = NULL;
STARTUPINFO si;
PROCESS_INFORMATION pi;
BOOL bResult = FALSE;
// 打开指定会话的用户令牌
if (!WTSQueryUserToken(dwSessionId, &hToken)) {
printf("WTSQueryUserToken failed (%d)\n", GetLastError());
goto Cleanup;
}
// 复制令牌
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hNewToken)) {
printf("DuplicateTokenEx failed (%d)\n", GetLastError());
goto Cleanup;
}
// 初始化 STARTUPINFO 结构
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.lpDesktop = TEXT("winsta0\\default"); // 指定桌面
// 创建进程
if (!CreateProcessAsUser(hNewToken, lpApplicationName, lpCommandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
printf("CreateProcessAsUser failed (%d)\n", GetLastError());
goto Cleanup;
}
// 关闭进程和线程句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
bResult = TRUE;
Cleanup:
if (hToken) CloseHandle(hToken);
if (hNewToken) CloseHandle(hNewToken);
return bResult;
}
int main() {
DWORD dwSessionId = WTSGetActiveConsoleSessionId(); // 获取当前活动会话ID
TCHAR szCommandLine[] = TEXT("notepad.exe");
if (CreateProcessAsUserInSession(dwSessionId, NULL, szCommandLine)) {
printf("Process created successfully in session %d\n", dwSessionId);
} else {
printf("Failed to create process in session %d\n", dwSessionId);
}
return 0;
}
说明
- WTSQueryUserToken: 这个函数用于获取指定会话的用户令牌。需要注意的是,这个函数只能在服务进程中调用,并且需要有足够的权限。
- DuplicateTokenEx: 复制令牌以便后续使用。
- CreateProcessAsUser: 使用复制的令牌在指定会话中创建进程。
- WTSGetActiveConsoleSessionId: 获取当前活动控制台会话的 ID。