如何从SYSTEM权限进程可靠获取登录用户名/SID?适配Win10/11全版本
SYSTEM权限应用以当前登录用户启动程序的可靠方案及API兼容性说明
核心结论
WTSQuerySessionInformationW 在Win10/11所有版本(包括家庭版)中均可正常工作,无需依赖远程桌面服务运行。微软文档的描述侧重远程桌面会话主机(RD Session Host)场景,但该API完全支持本地交互式会话的信息查询,家庭版只是不支持作为RD会话主机提供远程服务,本地会话的WTS相关API调用不受限制。
具体实现步骤
获取活跃交互式会话ID
使用WTSGetActiveConsoleSessionId()获取当前用户登录的控制台会话ID,该API全Windows版本通用,且不受远程桌面服务状态影响:DWORD sessionId = WTSGetActiveConsoleSessionId();查询会话用户信息
传入获取到的会话ID,调用WTSQuerySessionInformationW获取用户名和域名:LPWSTR pUserName = nullptr; DWORD dwUserNameLen = 0; LPWSTR pDomainName = nullptr; DWORD dwDomainNameLen = 0; if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, &pUserName, &dwUserNameLen) && WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSDomainName, &pDomainName, &dwDomainNameLen)) { // 后续处理用户名与域名 } // 释放API分配的内存 if (pUserName) WTSFreeMemory(pUserName); if (pDomainName) WTSFreeMemory(pDomainName);转换用户名到SID
通过LookupAccountNameW结合域名和用户名获取用户SID:PSID pSid = nullptr; DWORD dwSidSize = 0; LPWSTR pReferencedDomainName = nullptr; DWORD dwReferencedDomainNameSize = 0; SID_NAME_USE sidType; // 先获取所需内存大小 LookupAccountNameW(nullptr, pUserName, nullptr, &dwSidSize, nullptr, &dwReferencedDomainNameSize, &sidType); pSid = (PSID)LocalAlloc(LPTR, dwSidSize); pReferencedDomainName = (LPWSTR)LocalAlloc(LPTR, dwReferencedDomainNameSize * sizeof(WCHAR)); if (LookupAccountNameW(nullptr, pUserName, pSid, &dwSidSize, pReferencedDomainName, &dwReferencedDomainNameSize, &sidType)) { // SID获取成功,可用于后续令牌操作 } // 释放分配的资源 if (pSid) LocalFree(pSid); if (pReferencedDomainName) LocalFree(pReferencedDomainName);创建用户令牌并启动目标程序
通过WTSQueryUserToken获取用户主令牌,复制后用CreateProcessAsUserW启动浏览器或邮件客户端,需指定用户桌面确保可见:HANDLE hUserToken = nullptr; if (WTSQueryUserToken(sessionId, &hUserToken)) { HANDLE hDuplicateToken = nullptr; if (DuplicateTokenEx(hUserToken, MAXIMUM_ALLOWED, nullptr, SecurityIdentification, TokenPrimary, &hDuplicateToken)) { STARTUPINFOW si = { sizeof(si) }; PROCESS_INFORMATION pi = { 0 }; si.lpDesktop = L"WinSta0\\Default"; // 指定用户可见桌面 // 示例:启动Edge浏览器 if (CreateProcessAsUserW(hDuplicateToken, L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } CloseHandle(hDuplicateToken); } CloseHandle(hUserToken); }
关键注意事项
- 会话ID准确性:优先使用
WTSGetActiveConsoleSessionId()而非WTS_CURRENT_SESSION,SYSTEM权限下WTS_CURRENT_SESSION可能指向Session 0系统会话,无法获取登录用户信息。 - 桌面指定:启动进程时必须指定
WinSta0\\Default桌面,否则进程会在Session 0的不可见后台运行,用户无法交互。 - 资源释放:所有通过WTS或
LocalAlloc分配的资源必须手动释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Scott Smith
相关产品推荐
相关产品推荐

