当然,它是一个有效的句柄!(不是吗?)
调用方传递给驱动程序的句柄不会经过 I/O 管理器,因此 I/O 管理器不对这类句柄执行任何验证检查。决不要假设一个句柄有效;始终确保句柄拥有正确的对象类型、对于所需任务的合适的访问权、正确的访问模式,并且访问模式与请求的访问兼容。
驱动程序应该谨慎使用句柄,特别是那些从用户模式应用程序接收到的句柄。第一,这种句柄特定于进程上下文,因此它们仅在打开句柄的进程中有效。当从不同的进程上下文或工作线程使用时,句柄可以引用不同的对象或者只是变得无效。第二,在驱动程序使用句柄期间,攻击者可以关闭和重新打开句柄来改变其引用的内容。第三,攻击者可以传入这样一个句柄来引诱驱动程序执行对于应用程序非法的操作,例如调用 ZwXxx 函数。对于这些函数的内核模式调用方,访问检查被跳过,因此攻击者可以使用这种机制绕过验证。
驱动程序还应该确保用户模式应用程序不能误用驱动程序创建的句柄。为一个句柄设置 OBJ_KERNEL_HANDLE 属性使其成为内核句柄,内核句柄可以在任何进程上下文中使用,但是只能从内核模式进行访问(对于传递给 ZwXxx 例程的句柄,这特别重要)。用户模式的进程不能访问、关闭或替换内核句柄。
您应该做什么?
| • | 接收到任何句柄之后,立即调用 ObReferenceObjectByHandle 来为对象指针交换用户模式句柄: | • | 始终指定期望的对象类型,从而您可以使用 ObReferenceObjectByHandle 提供的类型检查。 | | • | 对于用户模式句柄,将 AccessMode 指定为 UserMode(假设期望用户对文件对象的访问权限与您的驱动程序相同)。 | | • | 始终检查 ObReferenceObjectByHandle 返回的状态码,并且当它为 STATUS_SUCCESS 时进行处理。 | | • | 当您完成对 ObReferenceObjectByHandle 提供的对象指针的使用时,调用 ObDereferenceObject 来释放指针并避免资源泄漏。 |
|
| • | 创建在内核模式中使用的句柄之前,调用 InitializeObjectAttributes 来初始化一个 OBJECT_ATTRIBUTES 结构,其中 Attributes 值设置为 OBJ_KERNEL_HANDLE。 |
下列代码片段显示 ObReferenceObjectByHandle 的正确用法,在这个例子中为一个事件的句柄。
NTSTATUS status; PKEVENT userEvent; HANDLE handle;
handle = RetrieveHandleFromIrpBuffer(…);
status = ObReferenceObjectByHandle(handle, EVENT_MODIFY_STATE, *ExEventObjectType, UserMode, (PVOID*) &userEvent, NULL);
if (NT_SUCCESS(status)) { // do something interesting here KeSetEvent(userEvent, IO_NO_INCREMENT, FALSE);
ObDereferenceObject(userEvent);
}更多信息:
常见的驱动程序可靠性问题
用户模式交互:内核模式驱动程序指南
Windows DDK:
InitializeObjectAttributes
ObReferenceObjectByHandle
ObDeferenceObject
Writing Secure CodeChapter 23: General Good Practices