0: kd> uf nt!IofCallDriver
Flow analysis was incomplete, some code may be missing
nt!IofCallDriver:
fffff800`03ed1c10 4883ec28 sub rsp,28h
fffff800`03ed1c14 488b05fda62200 mov rax,qword ptr [nt!pIofCallDriver (fffff800`040fc318)]
fffff800`03ed1c1b 4885c0 test rax,rax
fffff800`03ed1c1e 0f85b8050700 jne nt! ?? ::FNODOBFM::`string'+0x3f3d0 (fffff800`03f421dc)
栈指针移动5字节位置
将nt!pIofCallDriver地址赋值给RAX
判断nt!pIofCallDriver地址是否是0
如果不相等(判断的是ZF标志位是否为1,如果test结果是0,那么ZF为1),跳转到这个地址,(test是and操作,所以只要不是0就跳)
正常是要跳转的,也就是说执行到下面这些代码肯定出问题了。
nt!IofCallDriver+0x14:
fffff800`03ed1c24 fe4a43 dec byte ptr [rdx+43h]
fffff800`03ed1c27 384243 cmp byte ptr [rdx+43h],al
fffff800`03ed1c2a 0f8eb8050700 jle nt! ?? ::FNODOBFM::`string'+0x3f3dc (fffff800`03f421e8)
RDX是IRP的栈地址指针,rdx+43h是保存当IRP前的栈地址指针,所以减一后jle必定跳转,导致蓝屏
所以如果nt!pIofCallDriver地址是0,肯定蓝屏
执行到这里是有问题的,nt!pIofCallDriver地址是0
这个地址的值 堆栈状态信息-当前位置 irp->CurrentLocation-1得到下一个irp堆栈信息(1字节,8位 0000 0000)减少1
判断这个值和RDX的低8位的值的大小(80000000)
如果小于或等于,就跳转(判断IRP->当前位置 是不是当前位置?这里不能跳,跳了就蓝屏)(没有下一个IRP栈信息)
nt!IofCallDriver+0x20:
fffff800`03ed1c30 488b82b8000000 mov rax,qword ptr [rdx+0B8h]
fffff800`03ed1c37 4883e848 sub rax,48h
fffff800`03ed1c3b 488982b8000000 mov qword ptr [rdx+0B8h],rax
fffff800`03ed1c42 440fb608 movzx r9d,byte ptr [rax]
fffff800`03ed1c46 48894828 mov qword ptr [rax+28h],rcx
fffff800`03ed1c4a 4180f916 cmp r9b,16h
fffff800`03ed1c4e 0f84a4050700 je nt! ?? ::FNODOBFM::`string'+0x3f3ec (fffff800`03f421f8)
B8是什么?将这个地址赋值给RAX(应该是IRP地址偏移184字节处 位于tail)
减去48h
然后再将结果赋值回这个地址
无符号将RAX地址处的第一个字节扩展到32位,然后赋值到r9的低32位
将RCX的值移动到RAX+28h字节处
判断r9的值和16h,如果r9的值是16h就跳转
nt!IofCallDriver+0x44:
fffff800`03ed1c54 4c8b4108 mov r8,qword ptr [rcx+8]
fffff800`03ed1c58 410fb6c1 movzx eax,r9b
fffff800`03ed1c5c 4883c428 add rsp,28h
fffff800`03ed1c60 49ff64c070 jmp qword ptr [r8+rax*8+70h]
将该驱动对象地址赋值给r8
无符号的将r9的低8位扩展到32位,然后赋值给EAX
恢复栈顶指针到原来的位置
跳转到设备对象的设备所对象结构 DISPATCHER_HEADER
nt! ?? ::FNODOBFM::`string'+0x3f3d0:
fffff800`03f421dc 4c8b442428 mov r8,qword ptr [rsp+28h]
fffff800`03f421e1 4883c428 add rsp,28h
fffff800`03f421e5 48ffe0 jmp rax
正常情况下如果IofCallDriver正确就会在第一个判断时跳转到这里
将栈顶5字节处的值赋值给R8,然后栈地址加40字节
最后跳转到nt!pIofCallDriver执行,本函数是nt!IofCallDriver
nt! ?? ::FNODOBFM::`string'+0x3f3dc:
fffff800`03f421e8 4533c9 xor r9d,r9d
fffff800`03f421eb 4533c0 xor r8d,r8d
fffff800`03f421ee 418d4935 lea ecx,[r9+35h]
fffff800`03f421f2 e8e93cf8ff call nt!KiBugCheck3 (fffff800`03ec5ee0)
fffff800`03f421f7 cc int 3
如果IofCallDriver地址为0,则跳转到这里,蓝屏了。
首先清空r8和r9的低32位为0
然后获得r9+53字节处的值到ECX(这里保存的应该是用户显示的错误信息吧)
调用nt!KiBugCheck3函数,错误原因:没有更多的IRP堆栈位置,最底层驱动对象是没有IofCallDriver的。
nt! ?? ::FNODOBFM::`string'+0x3f3ec:
fffff800`03f421f8 440fb64001 movzx r8d,byte ptr [rax+1]
fffff800`03f421fd 4180f802 cmp r8b,2
fffff800`03f42201 7417 je nt! ?? ::FNODOBFM::`string'+0x3f40e (fffff800`03f4221a)
这个函数都是跳到执行nt!IopPoHandleIrp函数代码段处。[rax+1]大概是执行完nt!pIofCallDriver的结果
然后根据返回值进行处理
nt! ?? ::FNODOBFM::`string'+0x3f3f7:
fffff800`03f42203 4180f803 cmp r8b,3
fffff800`03f42207 7411 je nt! ?? ::FNODOBFM::`string'+0x3f40e (fffff800`03f4221a)
这个函数都是跳到执行nt!IopPoHandleIrp函数代码段处
nt! ?? ::FNODOBFM::`string'+0x3f3fd:
fffff800`03f42209 4c8b4108 mov r8,qword ptr [rcx+8]
fffff800`03f4220d 410fb6c1 movzx eax,r9b
fffff800`03f42211 4883c428 add rsp,28h
fffff800`03f42215 49ff64c070 jmp qword ptr [r8+rax*8+70h]
如果返回值不是2或者3,就执行这里
设备对象调度对象 DISPATCHER_HEADER
首先获得驱动对象指针,赋值给R8
将R9低8位扩展到32位,赋值到EAX
栈地址加40字节
跳转DISPATCHER_HEADER
nt! ?? ::FNODOBFM::`string'+0x3f40e:
fffff800`03f4221a 488bca mov rcx,rdx
fffff800`03f4221d 4883c428 add rsp,28h
fffff800`03f42221 e9aa520800 jmp nt!IopPoHandleIrp (fffff800`03fc74d0)
这部分是用来执行nt!IopPoHandleIrp函数的了
将RDX赋值给RCX,RDX是IRP指针
栈地址加40字节
跳转执行nt!IopPoHandleIrp
nt!IopPoHandleIrp:
fffff800`03fc74d0 fff3 push rbx
fffff800`03fc74d2 4883ec20 sub rsp,20h
fffff800`03fc74d6 488d542438 lea rdx,[rsp+38h]
fffff800`03fc74db 488bd9 mov rbx,rcx
fffff800`03fc74de e8ddfeffff call nt!PoHandleIrp (fffff800`03fc73c0)
fffff800`03fc74e3 84c0 test al,al
fffff800`03fc74e5 751d jne nt!IopPoHandleIrp+0x34 (fffff800`03fc7504)
将RBX压栈
栈顶减少32字节
获得栈顶+56字节的内容到RDX
将RCX赋值给RDX
跳转到PoHandleIrp
判断返回值是否是0,如果不是0,短跳
nt!IopPoHandleIrp+0x17:
fffff800`03fc74e7 488b83b8000000 mov rax,qword ptr [rbx+0B8h]
fffff800`03fc74ee 488bd3 mov rdx,rbx
fffff800`03fc74f1 488b4828 mov rcx,qword ptr [rax+28h]
fffff800`03fc74f5 440fb600 movzx r8d,byte ptr [rax]
fffff800`03fc74f9 4c8b4908 mov r9,qword ptr [rcx+8]
fffff800`03fc74fd 43ff54c170 call qword ptr [r9+r8*8+70h]
fffff800`03fc7502 eb04 jmp nt!IopPoHandleIrp+0x38 (fffff800`03fc7508)
否则如果返回值是0,RBX不知道是什么参数,反正是赋值给RAX,这里假定是IRP地址?
RBX=RCX=RDX
[rax+28h] 可能是设备对象的Timer?
...
nt!IopPoHandleIrp+0x34:
fffff800`03fc7504 8b442438 mov eax,dword ptr [rsp+38h]
将栈中的结果赋值到RAX返回值中
nt!IopPoHandleIrp+0x38:
fffff800`03fc7508 4883c420 add rsp,20h
fffff800`03fc750c 5b pop rbx
fffff800`03fc750d c3 ret
恢复栈指针
RBX出栈
返回函数
看来IofCallDriver并不是函数的实现,而是调用pIofCallDriver执行,并做一些错误处理。
kd> uf nt!pIofCallDriver
Flow analysis was incomplete, some code may be missing
nt!pIofCallDriver:
fffff800`04105318 0000 add byte ptr [rax],al
fffff800`0410531a 0000 add byte ptr [rax],al
fffff800`0410531c 0000 add byte ptr [rax],al
fffff800`0410531e 0000 add byte ptr [rax],al
fffff800`04105320 001e add byte ptr [rsi],bl
fffff800`04105322 0000 add byte ptr [rax],al
fffff800`04105324 0000 add byte ptr [rax],al
fffff800`04105326 0000 add byte ptr [rax],al
fffff800`04105328 8047f130 add byte ptr [rdi-0Fh],30h
fffff800`0410532c 80faff cmp dl,0FFh
fffff800`0410532f ff00 inc dword ptr [rax]
fffff800`04105331 d9f0 f2xm1
fffff800`04105333 3080faffff08 xor byte ptr [rax+8FFFFFAh],al
fffff800`04105339 420040fc add byte ptr [rax-4],al
fffff800`0410533d f6ff idiv bh
fffff800`0410533f ff00 inc dword ptr [rax]
fffff800`04105341 0001 add byte ptr [rcx],al
fffff800`04105343 0000 add byte ptr [rax],al
fffff800`04105345 f9 stc
这个明显是错的。。