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
相关产品推荐
相关产品推荐

