提升权限场景下获取SHGetFolderPath所需的当前用户登录安全令牌
管理员权限进程获取当前登录用户文件夹路径解决方案
原有代码故障原因
WTSQueryUserToken接口要求调用进程持有SeTcbPrivilege特权(即作为操作系统的一部分特权),默认提升的管理员权限进程未启用该特权,会导致调用失败- 直接对
PWTS_SESSION_INFO指针做自增操作,后续释放内存时会因指针偏移产生异常 - 未做API返回值校验,无法定位具体错误点
- 未释放WTS接口分配的内存,存在内存泄漏
完整可行实现代码
#include <windows.h> #include <wtsapi32.h> #include <shlobj.h> #include <userenv.h> #pragma comment(lib, "wtsapi32.lib") #pragma comment(lib, "userenv.lib") #pragma comment(lib, "shell32.lib") // 调整进程特权的辅助函数 BOOL EnablePrivilege(LPCWSTR szPrivName, BOOL bEnable) { HANDLE hToken = NULL; LUID luid; TOKEN_PRIVILEGES tp = {0}; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return FALSE; if (!LookupPrivilegeValueW(NULL, szPrivName, &luid)) { CloseHandle(hToken); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0; BOOL bRet = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); CloseHandle(hToken); return bRet && (GetLastError() == ERROR_SUCCESS); } BOOL GetCurrentLoginUserFolder(REFKNOWNFOLDERID rfid, PWSTR* ppszPath) { BOOL bRet = FALSE; HANDLE hUserToken = NULL; PWTS_SESSION_INFO pSessions = NULL; DWORD dwSessionCount = 0; DWORD dwActiveSessionId = 0xFFFFFFFF; // 先开启需要的特权 if (!EnablePrivilege(SE_TCB_NAME, TRUE)) return FALSE; // 枚举所有会话 if (!WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessions, &dwSessionCount)) goto Cleanup; // 查找当前活动的会话 for (DWORD i = 0; i < dwSessionCount; i++) { if (pSessions[i].State == WTSActive) { dwActiveSessionId = pSessions[i].SessionId; break; } } if (dwActiveSessionId == 0xFFFFFFFF) goto Cleanup; // 查询活动会话的用户令牌 if (!WTSQueryUserToken(dwActiveSessionId, &hUserToken)) goto Cleanup; // 通过用户令牌获取对应路径 if (SHGetKnownFolderPath(rfid, 0, hUserToken, ppszPath) == S_OK) bRet = TRUE; Cleanup: if (hUserToken) CloseHandle(hUserToken); if (pSessions) WTSFreeMemory(pSessions); // 关闭之前开启的特权 EnablePrivilege(SE_TCB_NAME, FALSE); return bRet; } // 调用示例 int main() { PWSTR pszDesktopPath = NULL; // 可替换为其他FOLDERID_*常量获取对应系统文件夹路径 if (GetCurrentLoginUserFolder(FOLDERID_Desktop, &pszDesktopPath)) { // 业务逻辑处理 CoTaskMemFree(pszDesktopPath); } return 0; }
注意事项
- 该实现适配单用户登录的常规桌面场景,多用户同时登录的服务器场景需要根据业务需求调整会话选择逻辑
- 路径获取成功后需要调用
CoTaskMemFree释放SHGetKnownFolderPath分配的内存 - 所有内核对象句柄、WTS分配的内存都需要按示例完成释放,避免资源泄漏
- 如果需要调用其他用户上下文的接口,可以拿到
hUserToken后调用ImpersonateLoggedOnUser模拟用户身份,调用完成后执行RevertToSelf回到原进程上下文即可
内容的提问来源于stack exchange,提问作者mrzacek mrzacek
相关产品推荐
相关产品推荐

