CheckTokenMembership 用于判断指定 SID 是否在访问令牌中启用;若检查 BUILTIN\Administrators 组,需用 AllocateAndInitializeSid 构造 SID 并传 nullptr 令牌句柄,结果存于输出参数,调用后必须 FreeSid 防内存泄漏。

最可靠的方式是调用 CheckTokenMembership 检查当前线程令牌是否属于 BUILTIN\Administrators 组,而不是看 UAC 提权状态或调用已过时的 IsUserAnAdmin。
为什么不能用 IsUserAnAdmin
这个函数在 Windows Vista 及以后已被微软标记为过时,且行为不可靠:启用了 UAC 的系统上,标准用户启动的程序可能因兼容性 shim 返回 TRUE,即使没真正提权;它也不区分“属于管理员组”和“当前令牌已提升”。实际项目中一旦依赖它,上线后容易在客户环境出权限误判。
-
IsUserAnAdmin不检查访问令牌,只查注册表或缓存的旧策略项 - 在 Windows 10/11 上默认返回
FALSE(除非 manifest 强制要求requireAdministrator) - 无法用于判断远程进程或服务进程的权限归属
CheckTokenMembership(nullptr, adminSid, &bIsAdmin) 怎么写才安全
这是 Windows XP SP2 起就稳定支持的轻量方案,不打开令牌句柄、不触发 UAC 弹窗、不依赖 manifest。关键点全在 SID 构造和资源释放上:
- 必须用
AllocateAndInitializeSid构造SECURITY_BUILTIN_DOMAIN_SID+DOMAIN_ALIAS_RID_ADMINS,硬编码 RID 值(0x220)不可靠,不同平台可能变化 - 第一个参数传
nullptr表示使用当前线程令牌,省去OpenProcessToken和错误处理 -
CheckTokenMembership返回值只表示 API 调用是否成功,真实结果存在输出参数&bIsAdmin中 —— 很多人漏读文档,直接拿返回值当结果 - 每次调用后必须
FreeSid(pAdminSid),否则每调一次泄漏 28 字节(x64 下),长期运行的服务会缓慢耗尽内存
想查远程进程或判断是否“真正提权”怎么办
如果目标不是“是否属于 Administrators 组”,而是“该进程的令牌是否已完成 UAC 提升”,就得换路子:GetTokenInformation + TokenElevation 是唯一正解。但注意它仅适用于 Vista+ 系统,且需先用 OpenProcessToken 获取目标进程令牌:
立即学习“C++免费学习笔记(深入)”;
- 对当前进程:传
GetCurrentProcess();对远程进程:需PROCESS_QUERY_INFORMATION权限,否则OpenProcess失败 -
TokenElevation的结构体定义未公开,必须按TOKEN_ELEVATION使用(字段名TokenIsElevated,类型DWORD) - 该方式无法判断“是否属于管理员组”,只反映当前令牌是否被 UAC 提升过 —— 比如一个以标准用户登录、但被父进程继承了高完整性令牌的进程,
TokenIsElevated为TRUE,但它未必能写C:\Windows
真正难处理的是混合场景:比如服务进程以 LocalSystem 运行(天然高于 Administrators),或沙盒进程被限制了令牌组——这时连 CheckTokenMembership 都可能返回 FALSE,但实际权限远超管理员。这种边界情况必须结合 GetTokenInformation + TokenGroups 手动遍历,代价高、易出错,一般应用应避免依赖。


















