一个差点被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了的对象。这种对象总可以通过文件名来访问。永久对象可以被转化为暂时对象