You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows微过滤驱动中无法获取I/O发起线程主令牌问题求助

问题分析与解决方案

错误原因

你调用ZwOpenThreadTokenEx返回STATUS_NO_TOKEN的核心原因是:该API的作用是获取线程的模拟令牌,只有当线程处于客户端模拟状态(比如执行了ImpersonateNamedPipeClient、ImpersonateLoggedOnUser等模拟操作)时,线程才会存在这个令牌。而你需要的是线程所属进程的主令牌——主令牌属于进程而非线程的模拟上下文,因此用这个API完全找错了对象。

正确的主令牌获取方法

要获取发起I/O操作的线程所属进程的主令牌,需从线程关联的进程对象入手,步骤如下:

  1. 获取线程所属的进程对象:通过PsGetThreadProcess(callback_data->Thread)(兼容性更好)或直接使用callback_data->Thread->Process获取。
  2. 调用ObOpenObjectByPointer打开进程对象的内核句柄。
  3. 调用ZwOpenProcessToken获取进程的主令牌句柄。
  4. 后续通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 08:42:20