[转载]关于驱动中的ObReferenceObjectByName 和 IoGetDeviceObjectPointer

本文探讨了ObReferenceObjectByName与IoGetDeviceObjectPointer两个函数的区别,尤其是在获取Device类型对象时的不同表现。揭示了ObReferenceObjectByName在获取Device对象时返回STATUS_OBJECT_TYPE_MISMATCH错误的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天huhu0013问我为什么ObReferenceObjectByName不能得到Device类型的对象,讨论一番,最终得到如下解释,记录一下:

=================================================================================
原文:http://www.boxcounter.org/?action=show&id=13

 

前些阵子学写过滤驱动,遇到个问题,在网上找到了相应的资料,Blog下来。

 

我写的程序需要挂在文件系统上,我看《Programming the Windows Driver Model 》中用IoGetDeviceObjectPointer

 

 

 

NTSTATUS 
  IoGetDeviceObjectPointer(
    IN PUNICODE_STRING   ObjectName ,
    IN ACCESS_MASK   DesiredAccess ,
    OUT PFILE_OBJECT   * FileObject ,
    OUT PDEVICE_OBJECT   * DeviceObject
    );

 

 

来获取要挂载的驱动对象,于是我也采用了相同的方法,但是却发现始终无法获取到"//FileSystem//Ntfs" 和//FileSystem//FastFat " , 参考了sinister的WssHookFS驱动程序,发现是用ObReferenceObjectByName 来获取的,试了下,果然成功了。于是很想 知道这两个函数的区别,自己琢磨了下win2K的源码,无奈功力不够,看来半天,也没发现什么蹊跷,上网搜现成的,得到下面的资料,感谢原作者 flyingkisser

 


 

    有时,我们需要得到像"/Device/XXX"或"/Driver/XXX"这种对象的地址,一般想到的最方便的方法是就是用 ObReferenceObjectByName .

 

 

NTKERNELAPI
NTSTATUS
ObReferenceObjectByName(
    IN PUNICODE_STRING ObjectName,
    IN ULONG Attributes,
    IN PACCESS_STATE PassedAccessState OPTIONAL,
    IN ACCESS_MASK DesiredAccess OPTIONAL,
    IN POBJECT_TYPE ObjectType,
    IN KPROCESSOR_MODE AccessMode,
    IN OUT PVOID ParseContext OPTIONAL,
    OUT PVOID *Object
    );

 

这个函数没有公开,但是被导出了。其参数也比较简单,没什么多说的。比如我们想得到"/Driver /KbdClass"对象的地址,调用这个函数可以轻易获得。然而,当我们用这个API去获/Device/KeyboardClass0"对象的地址 时,总是返回0xC0000024,即STATUS_OBJECT_TYPE_MIMATCH对象的类型不匹配,这是哪门子错误,虽然,此路不通时,还有 别的方法可以获得这个对象的地址,但是,我总想把这个问题搞清楚,于是在xp下面逆向了一个这个函数,并结合win2k源代码,最后终于找到了问题所在。


    ObReferenceObjectByName是ObpLookupObjectName 的包裹函数,对于给定的"/X1/X2"这样的名称,
ObpLookupObjectName会通过ObpLookupDirectoryEntry得到"/X1"对象的地址,再定位到对象头,由对象头再找 到对象类型的对象的地址,然后再判断一下类型对象->TypeInfo->ParseProcedure有没有值,如果不为空,则调 用。    如果为空,则不调用(嗯,的确是费话...).对于"/Device"或是"/Driver",他们的类型对象的ParseProcdedure都是空, 所以代码继续往下走,再通过ObpLookupDirectoryEntry得到"/X1/X2"对象的地址,然后,和上面是同样的过程,先定位到对象 头,再找到对象类型对象的地址,再判断类型对象->TypeInfo->ParseProcedure是不是为空.如果是Driver类型的 对象,它的Type对象,名称为Driver,其ParseProcedure为空,没有被调用,成功返回如果是Device类型的对象,它的Type对 象,名称为Device,其ParseProcedure不为空,指向IopParseDevice,而在IopParseDevice内部,会对传入的 一个参数进行检测,这个参数就是我们调用ObReferenceObjectByName()时给定的第7个参数:ParseContext.先把这个指 针转换成指向OPEN_PACKET类型的指针如果这个指针为空,或是这个结构体的Type成员不等于O_TYPE_OPEN_PACKET或者Size 成员不等于sizeof(OPEN_PACKET)就返回0xC0000024错误,即STATUS_OBJECT_TYPE_MIMATCH.而这个参 数一般我们给定的都是空。所以,ObReferenceObjectByName最终返回了STATUS_OBJECT_TYPE_MIMATCH。

    其实这里win2k源代码中是有一段注释的,意思是保证这个Routine的调用是从NtCreateFile()发起的,而不是其实任意的对象打开或创 建操作发起的,尽是这个参数被标记为"可选的".所以,看到这里大家应该清楚了吧,说到底,还是我们被M$玩弄了。


那么,如何解决这个问题呢?
我本想搞清楚这个ParseContext指向的OPEN_PACKET类型的结构体如何初始化的,不过在IopParseDevice里面会看到大量使 用了这个结构体的成员,所以,我觉得分析下去挺没意思了,体力活,实在不想干。其实解决这个问题也很简单,调用 IoGetDeviceObjectPointer()就是了,当然,从它的名称也能看出,它只能得到Device类型的对象的地址 。其实 比较稳妥的方法是调用ZwCreateFile()得到给定名称的对象的句柄,再用ObReferenceObjectByHandle就能得到对象的地 址了,然后句柄就可以ZwClose掉了,最后对象用完以后可别忘了ObDereferenceObject一下。

#include <ntddk.h> #include <wdm.h> #include <ntddndis.h> // 调试级别定义(必须放在所有include之后) #define DPFLTR_IHVDRIVER_ID 0x63 // 自定义驱动标识(0-63) #define MYDRIVER_TRACE_LEVEL_INFO DPFLTR_INFO_LEVEL // 0x0 #define MYDRIVER_TRACE_LEVEL_WARNING DPFLTR_WARNING_LEVEL // 0x1 #define MYDRIVER_TRACE_LEVEL_ERROR DPFLTR_ERROR_LEVEL // 0x2 #define MYDRIVER_TRACE_LEVEL_VERBOSE 0x3 // 自定义详细级别 #define IOCTL_PROTOCOL_CONTROL CTL_CODE(FILE_DEVICE_NETWORK, 0x800, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) typedef struct _PROTOCOL_DRIVER { PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; KEVENT CompletionEvent; IO_STATUS_BLOCK IoStatus; } PROTOCOL_DRIVER; // 协议特征结构 typedef struct _PROTOCOL_CHARACTERISTICS { ULONG Magic; USHORT HeaderSize; UCHAR Checksum; } PROTOCOL_CHARACTERISTICS; // 全局变量 PROTOCOL_DRIVER g_ProtocolDriver; PROTOCOL_CHARACTERISTICS g_ProtocolChars = { 0x4E455449, sizeof(PROTOCOL_CHARACTERISTICS), 0xAA }; // IRP完成回调 NTSTATUS ProtocolIoCompletion( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID Context) { UNREFERENCED_PARAMETER(DeviceObject); PKEVENT event = (PKEVENT)Context; KeSetEvent(event, IO_NO_INCREMENT, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } // 绑定到协议驱动 NTSTATUS AttachToProtocolDriver() { UNICODE_STRING protoName; RtlInitUnicodeString(&protoName, L"\\Device\\Tcp"); NTSTATUS status = IoGetDeviceObjectPointer( &protoName, FILE_ALL_ACCESS, &g_ProtocolDriver.FileObject, &g_ProtocolDriver.DeviceObject); if (!NT_SUCCESS(status)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DBG_LEVEL_ERROR, "[%s] Failed to attach to protocol driver: 0x%X\n", __FUNCTION__, status); return status; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DBG_LEVEL_INFO, "[%s] Successfully attached to protocol driver at 0x%p\n", __FUNCTION__, g_ProtocolDriver.DeviceObject); return STATUS_SUCCESS; }
最新发布
03-31
<think>好的,用户想了解如何通过Windows驱动程序附加到协议驱动并处理IRP完成回调,并需要一个示例。我需要先回忆相关知识点,并结合提供的引用内容来构建答案。 首先,用户提到的“附加到协议驱动”可能是指将驱动程序附加到现有的设备堆栈上,以便拦截处理IRP。这在Windows驱动开发中是常见的操作,例如过滤驱动程序。根据引用[3]提到的文件系统或过滤器驱动安装时的问题,可能涉及到驱动签名加载的问题,但用户的问题更关注技术实现。 接下来,处理IRP完成回调需要了解IRP的生命周期。当IRP被发送到底层驱动处理后,在完成时会触发完成例程。开发者可以注册一个完成回调函数来在此处进行处理。引用[4]提到了NTSTATUS代码,这可能与处理IRP时返回的状态有关,例如在回调中检查操作是否成功。 然后,我需要考虑如何正确附加到协议驱动。通常,这涉及到调用IoAttachDeviceToDeviceStackSafe函数来将过滤设备附加到目标设备堆栈。这需要先找到目标设备对象,可能通过设备名称或类型来定位。例如,协议驱动可能有一个特定的设备名称,需要在代码中指定。 然后,编写驱动程序的入口点,如DriverEntry函数,在其中创建设备对象并附加到目标设备。在创建设备对象时,需要设置适当的设备特征标志,确保正确处理IRP。 处理IRP完成回调时,需要创建一个完成例程函数,并在派遣函数中调用IoSetCompletionRoutineEx来注册它。在完成例程中,可以访问IRP的结果数据,并进行处理。需要注意的是,完成例程中必须正确管理IRP的状态,避免内存泄漏或系统崩溃。 此外,引用[1]提到驱动程序加载失败的问题,可能是因为之前的驱动版本仍在内存中。这提示在开发过程中需要正确卸载驱动,避免残留。引用[2]涉及证书错误,可能驱动签名有关,但用户的问题可能暂时不需要关注这点,除非示例代码运行时出现签名问题。 最后,确保示例代码符合WDK的规范,使用正确的函数参数。例如,使用IoCopyCurrentIrpStackLocationToNext来复制IRP栈位置,然后调用IoCallDriver传递IRP。在完成例程中,检查IRP的状态,并执行必要的处理。 总结步骤:附加到协议驱动、设置派遣函数、注册完成回调、处理IRP完成后的操作。同时,注意驱动卸载时的清理工作,如分离设备堆栈删除设备对象。</think>以下是关于如何通过Windows驱动程序附加到协议驱动并处理IRP完成回调的逐步说明: --- ### 1. **驱动附加到协议驱动的核心步骤** #### (1) 获取目标设备对象 通过设备名称或设备类型定位协议驱动的设备对象。例如,使用`IoGetDeviceObjectPointer`获取目标设备对象指针: ```c UNICODE_STRING targetDeviceName; RtlInitUnicodeString(&targetDeviceName, L"\\Device\\ProtocolDriver"); PFILE_OBJECT fileObject; PDEVICE_OBJECT targetDeviceObject; NTSTATUS status = IoGetDeviceObjectPointer(&targetDeviceName, FILE_ALL_ACCESS, &fileObject, &targetDeviceObject); ``` #### (2) 创建设备对象并附加 创建过滤设备对象,并将其附加到目标设备堆栈: ```c PDEVICE_OBJECT filterDeviceObject; status = IoCreateDevice( DriverObject, 0, NULL, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &filterDeviceObject ); // 附加到协议驱动的设备堆栈 PDEVICE_OBJECT attachedDevice = IoAttachDeviceToDeviceStackSafe(filterDeviceObject, targetDeviceObject); ``` --- ### 2. **IRP完成回调的实现** #### (1) 定义完成回调函数 在IRP完成时触发的回调函数中处理结果: ```c NTSTATUS CompletionRoutine( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context ) { if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } // 处理IRP完成后的数据(例如读取缓冲区) if (NT_SUCCESS(Irp->IoStatus.Status)) { KdPrint(("IRP completed successfully, Bytes transferred: %d", Irp->IoStatus.Information)); } return STATUS_SUCCESS; } ``` #### (2) 在派遣函数中注册回调 在IRP派遣函数中设置完成回调: ```c NTSTATUS DispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp) { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); // 复制当前IRP栈到下一层驱动 IoCopyCurrentIrpStackLocationToNext(Irp); // 设置完成回调 IoSetCompletionRoutineEx( DeviceObject, Irp, CompletionRoutine, NULL, // Context TRUE, // InvokeOnSuccess TRUE, // InvokeOnError TRUE // InvokeOnCancel ); // 传递IRP到下层驱动 return IoCallDriver(((PFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->AttachedDevice, Irp); } ``` --- ### 3. **关键注意事项** - **设备扩展结构**:建议使用设备扩展(`DEVICE_EXTENSION`)存储附加设备对象指针等上下文信息。 - **IRP状态管理**:在完成回调中必须正确处理`Irp->PendingReturned`,否则可能导致系统崩溃[^4]。 - **卸载处理**:在驱动卸载例程中分离设备堆栈并删除设备对象: ```c VOID DriverUnload(PDRIVER_OBJECT DriverObject) { PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; IoDetachDevice(((PFILTER_DEVICE_EXTENSION)deviceObject->DeviceExtension)->AttachedDevice); IoDeleteDevice(deviceObject); } ``` --- ### 示例代码框架 ```c #include <ntddk.h> typedef struct _FILTER_DEVICE_EXTENSION { PDEVICE_OBJECT AttachedDevice; } FILTER_DEVICE_EXTENSION, *PFILTER_DEVICE_EXTENSION; NTSTATUS CompletionRoutine(PDEVICE_OBJECT, PIRP, PVOID); NTSTATUS DispatchRead(PDEVICE_OBJECT, PIRP); VOID DriverUnload(PDRIVER_OBJECT); NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { DriverObject->DriverUnload = DriverUnload; DriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead; // 创建设备并附加到协议驱动(此处需补充设备创建附加代码) return STATUS_SUCCESS; } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值