以用户账户运行的服务如何在非0会话启动子进程?
如何在用户账户运行的Session 0服务中跨会话启动子进程?
可行,但普通用户账户运行的服务跨会话创建进程会比System账户受限更多。你当前遇到的ERROR_ACCESS_DENIED(错误码5)核心原因是:直接修改自身令牌的Session ID后,Windows会话隔离机制会阻止普通用户账户的令牌在非原会话中创建进程,且部分特权即使启用也无法突破会话权限检查。
核心解决方案思路
不要修改当前服务的令牌会话ID,而是获取目标会话中已登录用户的合法令牌,用这个令牌来创建进程。具体步骤如下:
获取目标会话的用户令牌
- 用
WTSEnumerateSessions枚举系统中的所有会话,定位到你要启动进程的目标会话ID。 - 调用
WTSQueryUserToken获取该会话中登录用户的令牌(这个API需要SE_TCB_NAME特权,必须提前给服务账户分配该权限)。
- 用
复制并配置令牌权限
- 用
DuplicateTokenEx将获取到的用户令牌复制为Primary类型令牌,确保复制时包含TOKEN_ASSIGN_PRIMARY、TOKEN_QUERY、TOKEN_ADJUST_PRIVILEGES等必要权限。 - 启用三个关键特权:
SE_TCB_NAME、SE_ASSIGNPRIMARYTOKEN_NAME、SE_INCREASE_QUOTA_NAME,这些特权需要在本地安全策略的「用户权利指派」中分配给服务账户。
- 用
创建进程时的关键参数设置
- 用
CreateEnvironmentBlock从目标用户令牌生成对应会话的环境变量块,避免使用服务的环境变量。 - 设置
STARTUPINFO的lpDesktop为"winsta0\\default",确保进程能正确关联到目标会话的桌面。 - 指定目标用户的默认目录作为
CreateProcessAsUserW的lpCurrentDirectory参数。
- 用
修正后的代码示例
// 替换为你的目标会话ID DWORD targetSessionID = 1; HANDLE hUserToken = NULL; // 1. 获取目标会话的用户令牌 if (!WTSQueryUserToken(targetSessionID, &hUserToken)) { // 处理错误:比如检查SE_TCB_NAME特权是否已分配 return GetLastError(); } // 2. 复制为Primary令牌 HANDLE hPrimaryToken = NULL; if (!DuplicateTokenEx( hUserToken, TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_DEFAULT, NULL, SecurityIdentification, TokenPrimary, &hPrimaryToken )) { CloseHandle(hUserToken); return GetLastError(); } CloseHandle(hUserToken); // 3. 启用必要特权 EnablePrivilege(SE_TCB_NAME, hPrimaryToken); EnablePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME, hPrimaryToken); EnablePrivilege(SE_INCREASE_QUOTA_NAME, hPrimaryToken); // 4. 创建目标会话的环境块 LPVOID lpEnv = NULL; if (!CreateEnvironmentBlock(&lpEnv, hPrimaryToken, TRUE)) { CloseHandle(hPrimaryToken); return GetLastError(); } // 5. 配置STARTUPINFO,指定目标桌面 STARTUPINFOW si = { sizeof(si) }; si.lpDesktop = L"winsta0\\default"; PROCESS_INFORMATION pi = { 0 }; // 6. 跨会话创建进程 BOOL success = CreateProcessAsUserW( hPrimaryToken, L"C:\\your\\path\\myproc2.exe", // 替换为实际进程路径 NULL, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, lpEnv, L"C:\\Users\\TargetUserName\\Documents", // 替换为目标用户的默认目录 &si, &pi ); // 清理资源 if (lpEnv) DestroyEnvironmentBlock(lpEnv); CloseHandle(hPrimaryToken); if (success) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else { return GetLastError(); }
关键注意事项
- 必须给服务账户分配以下用户权利:「替换进程级令牌」、「调整进程内存配额」、「作为操作系统的一部分操作」(通过本地安全策略->本地策略->用户权利指派配置)。
- 目标会话必须是已登录的活跃用户会话,
WTSQueryUserToken无法获取未登录会话的令牌。 - 不要尝试直接修改自身服务令牌的Session ID,普通用户账户的令牌不允许跨会话使用,这是Windows会话隔离的安全限制。
内容的提问来源于stack exchange,提问作者user3406792
相关产品推荐
相关产品推荐

