Windows微过滤驱动中无法获取I/O发起线程主令牌问题求助
问题分析与解决方案
错误原因
你调用ZwOpenThreadTokenEx返回STATUS_NO_TOKEN的核心原因是:该API的作用是获取线程的模拟令牌,只有当线程处于客户端模拟状态(比如执行了ImpersonateNamedPipeClient、ImpersonateLoggedOnUser等模拟操作)时,线程才会存在这个令牌。而你需要的是线程所属进程的主令牌——主令牌属于进程而非线程的模拟上下文,因此用这个API完全找错了对象。
正确的主令牌获取方法
要获取发起I/O操作的线程所属进程的主令牌,需从线程关联的进程对象入手,步骤如下:
- 获取线程所属的进程对象:通过
PsGetThreadProcess(callback_data->Thread)(兼容性更好)或直接使用callback_data->Thread->Process获取。 - 调用
ObOpenObjectByPointer打开进程对象的内核句柄。 - 调用
ZwOpenProcessToken获取进程的主令牌句柄。 - 后续通过
ZwQueryInformationToken和SecLookupAccountSid获取用户名,这部分你的原有逻辑是正确的。
修正后的代码示例
PEPROCESS pProcess = PsGetThreadProcess(callback_data->Thread); HANDLE hProcess = NULL; NTSTATUS status = ObOpenObjectByPointer( pProcess, OBJ_KERNEL_HANDLE, NULL, TOKEN_QUERY, // 仅需查询令牌的最小权限,比READ_CONTROL更精准 *PsProcessType, KernelMode, &hProcess ); if (NT_SUCCESS(status)) { HANDLE hToken = NULL; status = ZwOpenProcessToken( hProcess, TOKEN_QUERY, &hToken ); if (NT_SUCCESS(status)) { // 执行ZwQueryInformationToken获取TokenUser,再调用SecLookupAccountSid转换用户名 // ... 你的原有逻辑代码 ... ZwClose(hToken); } ZwClose(hProcess); }
更优方案:直接获取进程SID
在内核模式下,若仅需获取用户名,可跳过打开令牌的步骤,直接调用PsGetProcessSid从EPROCESS对象中提取SID,再转换为账户名,效率更高:
PEPROCESS pProcess = PsGetThreadProcess(callback_data->Thread); PSID pSid = PsGetProcessSid(pProcess); if (pSid != NULL) { UNICODE_STRING userName = {0}; UNICODE_STRING domainName = {0}; SID_NAME_USE sidType; NTSTATUS status = SecLookupAccountSid( NULL, // 本地系统 pSid, &userName, NULL, &domainName, NULL, &sidType ); if (NT_SUCCESS(status)) { // 使用userName和domainName // ... // 记得释放分配的内存 RtlFreeUnicodeString(&userName); RtlFreeUnicodeString(&domainName); } }
注意事项
- 内核模式下打开的句柄必须用
ZwClose关闭,避免资源泄漏。 - 权限设置遵循最小化原则:比如获取令牌仅需
TOKEN_QUERY权限,不要使用过大权限。 SecLookupAccountSid会分配内存,使用后必须调用RtlFreeUnicodeString释放。- 对于系统进程(如System进程),
SecLookupAccountSid可能无法转换为常规用户名,需额外处理这类场景。
内容的提问来源于stack exchange,提问作者zbx0310
相关产品推荐
相关产品推荐

