虫趣:空指针引用(SYSTEM_SERVICE_EXCEPTION)

作者:张佩】【原文: http://www.yiiyee.cn/Blog/bsod-0x3b-1/

这是一个BSOD 0x3B的dump文件,BSOD 0x3B所代表错误的描述是:SYSTEM_SERVICE_EXCEPTION,表示在一个系统线程里面发生了不可解决的系统异常,导致的系统错误。

系统线程是一个笼统的概念,凡是通过内核函数IoCreateSystemThread创建的线程,都是系统线程。内核是一个混沌的整体,内核里面不存在进程的概念。但是它却需要分配一些独立执行的任务,线程是执行这些独立任务的最佳载体。但是线程必须依附在进程中。为了解决这种矛盾,系统专门创建了一个SYSTEM进程,这个进程没有用户模式的代码,专门作为内核线程的载体存在。系统为SYTEM进程分配固定的进程号4。在Windbg内核调试会话中运行命令!process 4将列印SYSTEM进程信息:

0: kd> !process 4 0
Searching for Process with Cid == 4
Cid handle table at fffff8a000004e80 with 754 entries in use

PROCESS fffffa8003699040
    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00187000  ObjectTable: fffff8a0000019b0  HandleCount: 1559.
    Image: System

打开dump文件之后,执行kv; .echo; r命令:

0: kd> kv;.echo;r
Child-SP          RetAddr           : Args to Child                                                           : Call Site
0>fffff880`0afdb108 fffff800`02c76469 : 00000000`0000003b 00000000`c0000005 fffff880`03d1bf8f fffff880`0afdb9d0 : nt!KeBugCheckEx [d:\w7rtm\minkernel\ntos\ke\amd64\procstat.asm @ 177]
1>fffff880`0afdb110 fffff800`02c75dbc : fffff960`002d8888 fffff960`002bc74f 00000000`000d784c 00000000`000d784d : nt!KiBugCheckDispatch+0x69 [d:\w7rtm\minkernel\ntos\ke\amd64\trap.asm @ 2244]
2>fffff880`0afdb250 fffff800`02c9cbed : fffff960`00305314 fffff960`002d0420 fffff960`00020000 fffff880`0afdc178 : nt!KiSystemServiceHandler+0x7c [d:\w7rtm\minkernel\ntos\ke\amd64\trap.asm @ 1695]
3>fffff880`0afdb290 fffff800`02ca4250 : fffff800`02dc51e8 fffff880`0afdb308 fffff880`0afdc178 fffff800`02c05000 : nt!RtlpExecuteHandlerForException+0xd [d:\w7rtm\minkernel\ntos\rtl\amd64\xcptmisc.asm @ 131]
4>fffff880`0afdb2c0 fffff800`02cb11b5 : fffff880`0afdc178 fffff880`0afdb9d0 fffff880`00000000 fffff880`00000004 : nt!RtlDispatchException+0x410 [d:\w7rtm\minkernel\ntos\rtl\amd64\exdsptch.c @ 425]
5>fffff880`0afdb9a0 fffff800`02c76542 : fffff880`0afdc178 00000000`00000003 fffff880`0afdc220 fffffa80`0558a000 : nt!KiDispatchException+0x135 [d:\w7rtm\minkernel\ntos\ke\amd64\exceptn.c @ 724]
6>fffff880`0afdc040 fffff800`02c750ba : 00000000`00000000 00000000`00000003 00000000`000d8600 00000000`000d8647 : nt!KiExceptionDispatch+0xc2 [d:\w7rtm\minkernel\ntos\ke\amd64\trap.asm @ 2308]
7>fffff880`0afdc220 fffff880`03d1bf8f : fffff880`03d1c255 00000000`00000003 fffffa80`0558a000 00000000`ec5fa311 : nt!KiPageFault+0x23a (TrapFrame @ fffff880`0afdc220) [d:\w7rtm\minkernel\ntos\ke\amd64\trap.asm @ 1051]
8>fffff880`0afdc3b8 fffff880`03d1c255 : 00000000`00000003 fffffa80`0558a000 00000000`ec5fa311 00000000`00000000 : 0xfffff880`03d1bf8f
9>fffff880`0afdc3c0 00000000`00000003 : fffffa80`0558a000 00000000`ec5fa311 00000000`00000000 fffff880`0afdc470 : 0xfffff880`03d1c255
10>fffff880`0afdc3c8 fffffa80`0558a000 : 00000000`ec5fa311 00000000`00000000 fffff880`0afdc470 fffffa80`0558a000 : 0x3
11>fffff880`0afdc3d0 00000000`ec5fa311 : 00000000`00000000 fffff880`0afdc470 fffffa80`0558a000 011e0000`011e0000 : 0xfffffa80`0558a000
12>fffff880`0afdc3d8 00000000`00000000 : fffff880`0afdc470 fffffa80`0558a000 011e0000`011e0000 011e0000`00000004 : 0xec5fa311

rax=fffff8800afdb210 rbx=fffff80002dc51e8 rcx=000000000000003b
rdx=00000000c0000005 rsi=fffff80002c05000 rdi=0000000000000000
rip=fffff80002c76f00 rsp=fffff8800afdb108 rbp=0000000000000000
 r8=fffff88003d1bf8f  r9=fffff8800afdb9d0 r10=0000000000000000
r11=fffff8800afdb308 r12=fffff80002c76153 r13=fffff80002e83354
r14=fffff80002c75d40 r15=0000000000000000
iopl=0         nv up ei ng nz na pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00000282
nt!KeBugCheckEx:
fffff800`02c76f00 48894c2408      mov     qword ptr [rsp+8],rcx ss:0018:fffff880`0afdb110=3b00000000000000

第8帧是第三方驱动执行的最后一个函数,发生异常后,系统的异常处理函数接管了执行权,并产生一个陷阱帧。在第7帧上可以看到TrapFrame关键字。这是一个什么异常呢?从函数名KiPageFault可以看出,系统认为发生了页错误异常。也就是第8帧的函数访问了一个无效的虚拟地址,系统视图解决这个虚拟地址。页错误对应的是0xE号中断,而KiPageFault是系统为64位系统注册的异常处理函数:

0: kd> !idt 0xe
Dumping IDT: fffff8011807f080
0e:	fffff80116a8c980 nt!KiPageFault

下面一个步骤是切换到问题发生时候的场景,通过陷阱帧来实现:

0: kd> kv; .echo; r
  *** Stack trace for last set context - .thread/.cxr resets it
Child-SP          RetAddr           : Args to Child                                                           : Call Site
fffff880`0afdc3b8 fffff880`03d1c255 : 00000000`00000003 fffffa80`0558a000 00000000`ec5fa311 00000000`00000000 : 0xfffff880`03d1bf8f
fffff880`0afdc3c0 00000000`00000003 : fffffa80`0558a000 00000000`ec5fa311 00000000`00000000 fffff880`0afdc470 : 0xfffff880`03d1c255
fffff880`0afdc3c8 fffffa80`0558a000 : 00000000`ec5fa311 00000000`00000000 fffff880`0afdc470 fffffa80`0558a000 : 0x3
fffff880`0afdc3d0 00000000`ec5fa311 : 00000000`00000000 fffff880`0afdc470 fffffa80`0558a000 011e0000`011e0000 : 0xfffffa80`0558a000
fffff880`0afdc3d8 00000000`00000000 : fffff880`0afdc470 fffffa80`0558a000 011e0000`011e0000 011e0000`00000004 : 0xec5fa311

Last set context:
rax=fffffa800558a000 rbx=0000000000000000 rcx=0000000000000000
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000000000
rip=fffff88003d1bf8f rsp=fffff8800afdc3b8 rbp=0000000000000011
 r8=fffff8800afdc3e0  r9=0000000000000003 r10=fffff8800afdc3e0
r11=00000000011e0080 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz ac po cy
cs=0010  ss=0018  ds=0000  es=0000  fs=0000  gs=0000             efl=00010297
fffff880`03d1bf8f 8a040a          mov     al,byte ptr [rdx+rcx] ds:00000000`00000001=??

最后一行指出了当前执行的指令。因为这是出问题时候的场景,所以这一个指令就和所发生的问题息息相关。

mov     al,byte ptr [rdx+rcx] ds:00000000`00000001=??

指令的意思是把寄存在rcx所在地址处加上偏移rdx后所在地址处的一个字节的值赋给dl。问题出在哪里呢?问题出在rcx的当前值是0。这相当于下面的操作:

mov al, byte ptr [1] // error!

所以这是个空指针引用错误。有了这个分析之后,拿到符号文件后,发现rcx对应的是一个结构体,它的前面两个变量都是boolean类型。这个操作是要获取第二个boolean变量的值。类似下面的代码:

void funcNotSafeWithInputPara (myStruct* pStruct)
{
  // 没有检测指针有效性
  if (pStruct->bDo){…}
}

这个函数的错误在于它没有检查输入参数的有效性。如果输入的是空指针,就会引发蓝屏。

操作系统对空指针错误有一个聪明的预防办法,就是永远让从0开始的一个页面(page)无效,这个措施针对所有的虚拟地址空间。所以所有引用地址0到0xFFF的指令都是错误的,并且会立刻引发错误,引起编程者和使用者的注意。如果没有系统的这个措施,那么有空指针错误的指令将会得到到一个有效但内容不正确的值,接下来的错误会越走越远,不可收拾。

0: kd> db 0
00000000`00000000  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000000`00000010  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000000`00000020  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000000`00000030  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000000`00000040  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000000`00000050  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000000`00000060  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000000`00000070  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

6,907 total views, 3 views today

《虫趣:空指针引用(SYSTEM_SERVICE_EXCEPTION)》有2个想法

  1. 指令的意思是把寄存在rcx所在地址處加上偏移rdx後所在地址處的一個字節的值賦給dl。
    這裡dl看起來應該是al才對??

发表评论

电子邮件地址不会被公开。