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

KMDF驱动能否判断IOCTL是否来自管理员进程并限制非管理员请求?

判断发送IOCTL_MY_TEST的用户态进程是否属于内置管理员组的实现方案

完全可以实现这个需求,核心思路是在内核中获取发起IOCTL请求的用户态进程的访问令牌,验证令牌是否包含内置管理员组的固定SID(S-1-5-32-544),具体实现步骤如下:

核心实现步骤

  • 获取发起请求的进程对象:在Queue.c的IOCTL处理函数中,通过IoGetCurrentProcess()直接获取当前进程对象(内核处理IRP时,当前进程上下文就是发起请求的用户态进程)。
  • 打开进程的访问令牌:调用ZwOpenProcessToken函数,传入进程对象和TOKEN_QUERY权限,获取令牌句柄用于后续查询。
  • 查询令牌的组信息:调用ZwQueryInformationToken,指定TokenGroups信息类,先调用一次获取所需缓冲区大小,用ExAllocatePoolWithTag分配内存后,再次调用获取完整的组SID列表。
  • 校验内置管理员组SID:用RtlInitSid初始化内置管理员组的SID,遍历令牌的组列表,对比每个组的SID是否匹配。
  • 拦截无权限请求:如果未找到匹配的SID,直接返回STATUS_ACCESS_DENIED,终止IOCTL请求处理。

关键代码示例

NTSTATUS HandleMyTestIoctl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS status = STATUS_SUCCESS;
    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
    HANDLE tokenHandle = NULL;
    PTOKEN_GROUPS tokenGroups = NULL;
    ULONG bufferSize = 0;
    SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
    PSID adminSid = NULL;

    // 仅处理目标IOCTL
    if (irpSp->Parameters.DeviceIoControl.IoControlCode != IOCTL_MY_TEST)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
        goto Cleanup;
    }

    // 1. 打开进程令牌
    status = ZwOpenProcessToken(IoGetCurrentProcess(), TOKEN_QUERY, &tokenHandle);
    if (!NT_SUCCESS(status))
    {
        status = STATUS_ACCESS_DENIED;
        goto Cleanup;
    }

    // 2. 获取令牌组信息所需缓冲区大小
    status = ZwQueryInformationToken(tokenHandle, TokenGroups, NULL, 0, &bufferSize);
    if (status != STATUS_BUFFER_TOO_SMALL)
    {
        goto Cleanup;
    }

    // 分配缓冲区
    tokenGroups = (PTOKEN_GROUPS)ExAllocatePoolWithTag(NonPagedPoolNx, bufferSize, 'tGpS');
    if (!tokenGroups)
    {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }

    // 3. 查询令牌组信息
    status = ZwQueryInformationToken(tokenHandle, TokenGroups, tokenGroups, bufferSize, &bufferSize);
    if (!NT_SUCCESS(status))
    {
        goto Cleanup;
    }

    // 4. 初始化内置管理员组SID(S-1-5-32-544)
    adminSid = ExAllocatePoolWithTag(NonPagedPoolNx, SECURITY_MAX_SID_SIZE, 'AdmS');
    if (!adminSid)
    {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }
    if (!RtlInitSid(&adminSid, &ntAuthority, 2, 5, 32, 544))
    {
        status = STATUS_UNSUCCESSFUL;
        goto Cleanup;
    }

    // 5. 遍历组列表,检查是否包含管理员组SID
    BOOLEAN isAdmin = FALSE;
    for (ULONG i = 0; i < tokenGroups->GroupCount; i++)
    {
        if (RtlEqualSid(tokenGroups->Groups[i].Sid, adminSid))
        {
            // 检查组是否启用(UAC过滤下管理员组可能被禁用)
            if (tokenGroups->Groups[i].Attributes & SE_GROUP_ENABLED)
            {
                isAdmin = TRUE;
                break;
            }
        }
    }

    if (!isAdmin)
    {
        status = STATUS_ACCESS_DENIED;
        goto Cleanup;
    }

    // 权限验证通过,执行你的IOCTL处理逻辑
    // ...

Cleanup:
    // 资源清理
    if (tokenHandle) ZwClose(tokenHandle);
    if (tokenGroups) ExFreePoolWithTag(tokenGroups, 'tGpS');
    if (adminSid) ExFreePoolWithTag(adminSid, 'AdmS');

    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return status;
}

注意事项

  • UAC过滤处理:默认管理员账号发起的用户态进程会受UAC令牌过滤,此时管理员组SID的SE_GROUP_ENABLED属性会被清除,若需兼容该场景,可调整判断逻辑(仅检查SID存在),但会降低安全性。
  • 内存安全:内核中必须使用NonPagedPoolNx分配非可执行内存,避免漏洞风险,所有分配的资源必须在退出前释放。
  • 错误处理:每一步内核API调用都要检查返回状态,避免因调用失败导致资源泄漏或异常。

内容的提问来源于stack exchange,提问作者c00000fd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 00:45:10