首页
社区
课程
招聘
[原创]页切换引起的蓝屏之大公司的程序也不靠谱
发表于: 2017-9-19 19:37 5940

[原创]页切换引起的蓝屏之大公司的程序也不靠谱

2017-9-19 19:37
5940
 注意:这文章是 2013-12-9 写的,留了很久现在自己也不做TO C的安全了,就把文章整理发出来,留在硬盘也是烂了而已

      今晚回来的时候,刚开电脑启动到用户选择界面处,我还没有输入密码,过了一会就莫名的蓝屏然后重启了,幸好我的电脑设置了核心转储的设置,于是电脑很顺利的把DUMP保存下来了,之后再重启电脑,打开WINDBG看下这个DUMP到底是什么原因倒致蓝屏的。    

 

一般分析内核DUMP,WINDBG会提示你用!anaylze -v来看DUMP的相关信息.在执行上述指令后我们得到了相关的信息

1: kd> !analyze -v

*******************************************************************************

*                                                                             *

*                        Bugcheck Analysis                                    *

*                                                                             *

*******************************************************************************

PAGE_FAULT_IN_NONPAGED_AREA (50)

Invalid system memory was referenced.  This cannot be protected by try-except,

it must be protected by a Probe.  Typically the address is just plain bad or it

is pointing at freed memory.

Arguments:

Arg1: fffff8a002408000, memory referenced.

Arg2: 0000000000000000, value 0 = read operation, 1 = write operation.

Arg3: fffff88001aed3f0, If non-zero, the instruction address which referenced the bad memory

address.

Arg4: 0000000000000000, (reserved)

Debugging Details:

------------------

READ_ADDRESS:  fffff8a002408000 Paged pool

FAULTING_IP: 

nvpciflt+13f0

fffff880`01aed3f0 6642391c49      cmp     word ptr [rcx+r9*2],bx

MM_INTERNAL_CODE:  0

IMAGE_NAME:  nvpciflt.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  52314e29

MODULE_NAME: nvpciflt

FAULTING_MODULE: fffff88001aec000 nvpciflt

DEFAULT_BUCKET_ID:  WIN7_DRIVER_FAULT

BUGCHECK_STR:  0x50

PROCESS_NAME:  svchost.exe

CURRENT_IRQL:  0

TRAP_FRAME:  fffff880043bd720 -- (.trap 0xfffff880043bd720)

NOTE: The trap frame does not contain all registers.

Some register values may be zeroed or incorrect.

rax=fffff8a002407f70 rbx=0000000000000000 rcx=fffff8a002407f80

rdx=fffffa800ab04b50 rsi=0000000000000000 rdi=0000000000000000

rip=fffff88001aed3f0 rsp=fffff880043bd8b0 rbp=fffff880043bdb60

 r8=0000000000000000  r9=0000000000000040 r10=fffffa8003f0f148

r11=fffffa800ab04b50 r12=0000000000000000 r13=0000000000000000

r14=0000000000000000 r15=0000000000000000

iopl=0         nv up ei pl nz ac pe nc

nvpciflt+0x13f0:

fffff880`01aed3f0 6642391c49      cmp     word ptr [rcx+r9*2],bx ds:fffff8a0`02408000=????

Resetting default scope

LAST_CONTROL_TRANSFER:  from fffff80004301be0 to fffff80004283b80

STACK_TEXT:  

fffff880`043bd5b8 fffff800`04301be0 : 00000000`00000050 fffff8a0`02408000 00000000`00000000 fffff880`043bd720 : nt!KeBugCheckEx

fffff880`043bd5c0 fffff800`04281cae : 00000000`00000000 fffff8a0`02408000 00000000`00000000 00000000`00000000 : nt! ?? ::FNODOBFM::`string'+0x4518f

fffff880`043bd720 fffff880`01aed3f0 : 00000000`00000000 fffff880`043bdb60 fffff880`043bda60 fffff880`043bd8f8 : nt!KiPageFault+0x16e

fffff880`043bd8b0 fffff800`0462f460 : 00000000`00000000 00000000`00000000 00000000`00000000 fffff8a0`02407f70 : nvpciflt+0x13f0

fffff880`043bd910 fffff800`04504b34 : 00000000`00000002 fffff880`043bda60 00000000`00000001 00000000`00000011 : nt!CmpCallCallBacks+0x1c0

fffff880`043bd9e0 fffff800`04282e13 : 00000000`00000624 fffffa80`0ab04b50 00000000`00000000 00000000`00000000 : nt!NtDeleteValueKey+0x2b3

fffff880`043bdae0 00000000`77c91e4a : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13

00000000`01d3e538 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x77c91e4a

STACK_COMMAND:  kb

FOLLOWUP_IP: 

nvpciflt+13f0

fffff880`01aed3f0 6642391c49      cmp     word ptr [rcx+r9*2],bx

SYMBOL_STACK_INDEX:  3

SYMBOL_NAME:  nvpciflt+13f0

FOLLOWUP_NAME:  MachineOwner

FAILURE_BUCKET_ID:  X64_0x50_nvpciflt+13f0

BUCKET_ID:  X64_0x50_nvpciflt+13f0

Followup: MachineOwner

---------

从这些信息来看是因为CPU在读取一个无效的地址倒致引起一个PAGE FAULT从而系统挂掉了.
这个PAGE_FAULT_IN_NONPAGED_AREA的代号相关描述MS是这样定义的:
The PAGE_FAULT_IN_NONPAGED_AREA bug check has a value of 0x00000050.This indicates that invalid system memory has been referenced.

上述的说明也很清楚的说到了是引用到了无效的系统内存.

好了有了这些基本的信息我们再进一步去获取更有用的信息.接下来我们想知道到底是什么进程引起这个蓝屏的.我们可以通过!pcr这个命令获取当前CPU的相关信息.由于我的电脑是双核的, 从上面的DUMP信息我们也看到了1: kd>.这里的1就是当前是我的CPU 1正在运行.于是我们输入!pcr 1看下当前这个CPU的信息

1: kd> !pcr 1

KPCR for Processor 1 at fffff88004900000:

    Major 1 Minor 1

NtTib.ExceptionList: fffff8800490b540

   NtTib.StackBase: fffff88004904f40

  NtTib.StackLimit: 0000000001d3e538

NtTib.SubSystemTib: fffff88004900000

     NtTib.Version: 0000000004900180

 NtTib.UserPointer: fffff880049007f0

     NtTib.SelfTib: 000007fffff86000

           SelfPcr: 0000000000000000

              Prcb: fffff88004900180

              Irql: 0000000000000000

               IRR: 0000000000000000

               IDR: 0000000000000000

     InterruptMode: 0000000000000000

               IDT: 0000000000000000

               GDT: 0000000000000000

               TSS: 0000000000000000

    CurrentThread: fffffa800ab04b50

        NextThread: 0000000000000000

        IdleThread: fffff8800490afc0

         DpcQueue: 

上面的信息记录了当前CPU的很多有用的状态,其中CurrentThread这个字段是我们要关心的,这个说明当前CPU正在某个线程里面跑.这个线程里面有什么信息呢?我们可以通过输入!thread fffffa800ab04b50,来得到当前CPU正在执行的栈的信息.
1: kd> !thread fffffa800ab04b50

THREAD fffffa800ab04b50  Cid 0364.050c  Teb: 000007fffff86000 Win32Thread: 0000000000000000 RUNNING on processor 1

Not impersonating

DeviceMap                 fffff8a000008aa0

Owning Process            fffffa80078b4b30      Image:        svchost.exe

Attached Process          N/A            Image:         N/A

Wait Start TickCount      4313           Ticks: 0

Context Switch Count      3239           IdealProcessor: 2             

UserTime                  00:00:00.046

KernelTime                00:00:00.109

Win32 Start Address 0x0000000077c5fbf0

Stack Init fffff880043bdc70 Current fffff880043bd680

Base fffff880043be000 Limit fffff880043b8000 Call 0

Priority 8 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5

Child-SP          RetAddr           : Args to Child                                                           : Call Site

fffff880`043bd5b8 fffff800`04301be0 : 00000000`00000050 fffff8a0`02408000 00000000`00000000 fffff880`043bd720 : nt!KeBugCheckEx

fffff880`043bd5c0 fffff800`04281cae : 00000000`00000000 fffff8a0`02408000 00000000`00000000 00000000`00000000 : nt! ?? ::FNODOBFM::`string'+0x4518f

fffff880`043bd720 fffff880`01aed3f0 : 00000000`00000000 fffff880`043bdb60 fffff880`043bda60 fffff880`043bd8f8 : nt!KiPageFault+0x16e (TrapFrame @ fffff880`043bd720)

fffff880`043bd8b0 fffff800`0462f460 : 00000000`00000000 00000000`00000000 00000000`00000000 fffff8a0`02407f70 : nvpciflt+0x13f0

fffff880`043bd910 fffff800`04504b34 : 00000000`00000002 fffff880`043bda60 00000000`00000001 00000000`00000011 : nt!CmpCallCallBacks+0x1c0

fffff880`043bd9e0 fffff800`04282e13 : 00000000`00000624 fffffa80`0ab04b50 00000000`00000000 00000000`00000000 : nt!NtDeleteValueKey+0x2b3

fffff880`043bdae0 00000000`77c91e4a : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`043bdae0)

00000000`01d3e538 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x77c91e4a


确实这个过程和用!analyze -v分析的栈信息一致.如果我们还在怀疑的话我们可以读取下当前CPU的CR3寄存器,看下当前CR3这个寄存器里的值是否和这个进程一致

1: kd> r cr3

cr3=000000007ac7e0001: kd> !process fffffa80078b4b30 2

PROCESS fffffa80078b4b30

    SessionId: 0  Cid: 0364    Peb: 7fffffdf000  ParentCid: 02a8

    DirBase:7ac7e000  ObjectTable: fffff8a001530870  HandleCount: 388.

    Image: svchost.exe

可以看到当前CPU确实在响应这个svchost.exe这个进程的操作.这个栈是可信的.接下我们就要看这个进程为什么引起蓝屏?我们可以看下整个栈回溯,我们可以通过kv这个命令来看下

1: kd> kv

Child-SP          RetAddr           : Args to Child                                                           : Call Site

fffff880`043bd5b8 fffff800`04301be0 : 00000000`00000050 fffff8a0`02408000 00000000`00000000 fffff880`043bd720 : nt!KeBugCheckEx

fffff880`043bd5c0 fffff800`04281cae : 00000000`00000000 fffff8a0`02408000 00000000`00000000 00000000`00000000 : nt! ?? ::FNODOBFM::`string'+0x4518f

fffff880`043bd720 fffff880`01aed3f0 : 00000000`00000000 fffff880`043bdb60 fffff880`043bda60 fffff880`043bd8f8 : nt!KiPageFault+0x16e (TrapFrame @ fffff880`043bd720)

fffff880`043bd8b0 fffff800`0462f460 : 00000000`00000000 00000000`00000000 00000000`00000000 fffff8a0`02407f70 : nvpciflt+0x13f0

fffff880`043bd910 fffff800`04504b34 : 00000000`00000002 fffff880`043bda60 00000000`00000001 00000000`00000011 : nt!CmpCallCallBacks+0x1c0

fffff880`043bd9e0 fffff800`04282e13 : 00000000`00000624 fffffa80`0ab04b50 00000000`00000000 00000000`00000000 : nt!NtDeleteValueKey+0x2b3

fffff880`043bdae0 00000000`77c91e4a : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`043bdae0)

00000000`01d3e538 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x77c91e4a

从这个栈来看svchost.exe进程在执行一个删除注册表键值的操作.于时我们看下这个操作触发错误是指令是什么。

我们如果仔细的话我们可以注意到上面的回溯中有

fffff880`043bd720 fffff880`01aed3f0 : 00000000`00000000 fffff880`043bdb60 fffff880`043bda60 fffff880`043bd8f8 : nt!KiPageFault+0x16e (TrapFrame @ fffff880`043bd720)这么一个语句,这个语句后面有个TrapFrame的提示,这个结构里面才是保存当时CPU执行的指令引起崩溃的地方,我们看下里面是什么内容,我们可以用.trap  fffff880`043bd720 查看下.

1: kd> .trap fffff880`043bd720

NOTE: The trap frame does not contain all registers.

Some register values may be zeroed or incorrect.

rax=fffff8a002407f70 rbx=0000000000000000 rcx=fffff8a002407f80

rdx=fffffa800ab04b50 rsi=0000000000000000 rdi=0000000000000000

rip=fffff88001aed3f0 rsp=fffff880043bd8b0 rbp=fffff880043bdb60

 r8=0000000000000000  r9=0000000000000040 r10=fffffa8003f0f148

r11=fffffa800ab04b50 r12=0000000000000000 r13=0000000000000000

r14=0000000000000000 r15=0000000000000000

iopl=0         nv up ei pl nz ac pe nc

nvpciflt+0x13f0:

fffff880`01aed3f0 6642391c49      cmp     word ptr [rcx+r9*2],bx ds:fffff8a0`02408000=????
我们发现这个崩溃是因为rcx+r9*2存放的地址是无效的.我们可以dd看下是否真的无效

1: kd> dd rcx+r9*2

fffff8a0`02408000  ???????? ???????? ???????? ????????

fffff8a0`02408010  ???????? ???????? ???????? ????????

fffff8a0`02408020  ???????? ???????? ???????? ????????

确实,这个内存地址不存在,而上面的语句还要执行到这里。当然就会蓝屏了,通过反汇编我们看到当前这个语句所在汇编上下文知道CPU此时在获取这个注册表键路径的长度,但是不幸的时它读取到了无效的系统内存地址,从而倒致CPU在读取的时候触发#PF

1: kd> ub eip

nvpciflt+0x13c9:

fffff880`01aed3c9 488d0d682f0000  lea     rcx,[nvpciflt+0x4338 (fffff880`01af0338)]

fffff880`01aed3d0 ff15821c0000    call    qword ptr [nvpciflt+0x3058 (fffff880`01aef058)]

fffff880`01aed3d6 3d0d0000c0      cmp     eax,0C000000Dh

fffff880`01aed3db 0f84f2000000    je      nvpciflt+0x14d3 (fffff880`01aed4d3)

fffff880`01aed3e1 488b442478      mov     rax,qword ptr [rsp+78h]

fffff880`01aed3e6 488b4808         mov     rcx,qword ptr [rax+8]

fffff880`01aed3ea 4d8bce              mov     r9,r14

fffff880`01aed3ed49ffc1                inc     r9

1: kd> u eip

nvpciflt+0x13f0:

fffff880`01aed3f0 6642391c49      cmp     word ptr [rcx+r9*2],bx  //bx = 0

fffff880`01aed3f5 75f6                    jne     nvpciflt+0x13ed (fffff880`01aed3ed)

 从反汇编代码看出在执行一个循环。我们可以du下rcx看下内里是什么内容 

1: kd> du rcx

fffff8a0`02407f80  "\REGISTRY\MACHINE\SYSTEM\Control"

fffff8a0`02407fc0  "Set001\Enum\ROOT\*ISATAP\0003獥.."

fffff8a0`02408000 "????????????????????????????????"

fffff8a0`02408040  "????????????????????????????????"

我们看下IDA里面是怎么写的 

可以知道rcx指向CmCallbackGetKeyObjectID函数返回的PUNICODE_STRING类型参数的buffer,由于这个值是存放在栈上面的且没有被修改过我们可以通过WINDBG去读取并格式化显示它

1: kd> dQ rsp+78

fffff880`043bd928  fffff8a0`02407f7000000000`00020000

fffff880`043bd938  fffff8a0`00337470 00000000`01b29901

fffff880`043bd948  fffffa80`0ab04b50 fffff8a0`0277c3b0

fffff880`043bd958  00000000`00000000 00000000`00000001

fffff880`043bd968  fffff880`043bdb60 00000000`00000000

fffff880`043bd978  fffff800`0453a5e1 fffff880`043bd900

fffff880`043bd988  00000000`00000002 fffffa80`03f82080

fffff880`043bd998  00000000`00240001 00000000`00000000

1: kd> dt _UNICODE_STRINGfffff8a0`02407f70 

nt!_UNICODE_STRING

 "\REGISTRY\MACHINE\SYSTEM\ControlSet001\Enum\ROOT\*ISATAP\0003"

  +0x000 Length           : 0x7a

   +0x002 MaximumLength    : 0x7a

   +0x008 Buffer           : 0xfffff8a0`02407f80  "\REGISTRY\MACHINE\SYSTEM\ControlSet001\Enum\ROOT\*ISATAP\0003"

我们可以看到这个注册表路径长度为0x7a,而崩溃时读取的长度为0x40*2 =0x80,崩溃时r9执行了0x40次

1: kd> r r9

Last set context:

r9=0000000000000040
显然读取的长度超过了0x7a。为了更明显的看出问题根源,我们用db显示下buffer里面的内容

1: kd> db 0xfffff8a0`02407f80

fffff8a0`02407f80  5c 00 52 00 45 00 47 00-49 00 53 00 54 00 52 00  \.R.E.G.I.S.T.R.

fffff8a0`02407f90  59 00 5c 00 4d 00 41 00-43 00 48 00 49 00 4e 00  Y.\.M.A.C.H.I.N.

fffff8a0`02407fa0  45 00 5c 00 53 00 59 00-53 00 54 00 45 00 4d 00  E.\.S.Y.S.T.E.M.

fffff8a0`02407fb0  5c 00 43 00 6f 00 6e 00-74 00 72 00 6f 00 6c 00  \.C.o.n.t.r.o.l.

fffff8a0`02407fc0  53 00 65 00 74 00 30 00-30 00 31 00 5c 00 45 00  S.e.t.0.0.1.\.E.

fffff8a0`02407fd0  6e 00 75 00 6d 00 5c 00-52 00 4f 00 4f 00 54 00  n.u.m.\.R.O.O.T.

fffff8a0`02407fe0  5c 00 2a 00 49 00 53 00-41 00 54 00 41 00 50 00  \.*.I.S.A.T.A.P.

fffff8a0`02407ff0  5c 00 30 00 30 00 30 00-33 00 65 73 04 f0 ff ff  \.0.0.0.3.es....

注意下后面的es  很明显这个BUFFER并不是以NULL为结束的。在这里我们不得不提下UNICODE_STRING这个结构。
typedef struct _LSA_UNICODE_STRING {   
 USHORT Length;    
USHORT MaximumLength;    
PWSTR  Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
其中Buffer这个字段并不一定是以NULL为结束的,这和我们平时在RING3环写的字符串数组还不一样。MSDN上有相关描述:http://msdn.microsoft.com/zh-cn/aa380518(v=vs.85)Buffer

Pointer to a wide-character string.Note that the strings returned by the various LSA functions might not be null-terminated.

其中文档上写得清清楚楚,这个Buffer里面可以不包含空字符。在RIG3层我们的程序员比较喜欢用下面的方法去定义字符串:
char* str="test by robin!";
wchar_t* str = L"test by robin!";

实际上这种字符串相当的不安全,很容易倒致缓冲区溢出漏洞。这是因为没有任何地方确切地表明一个字符串的长度,仅仅用一个'\0'字符来表示这个字符串的结束。一旦碰到根本没有空结束的字符串,程序可能就会陷入崩溃。

在驱动开发中,一般不再使用空来表示一个字符串的结束,而是定义了UNICODE_STRING这么一个结构来存放字符串。如果要判断这个字符串的长度,可以通过结构里面的Length来确定。

而在这个崩溃的DUMP中,我们看到写该驱动的开发人员直接想通过Buffer的空结束符来确定该注册表路径的长度,而这个路径刚好并没有像作者想像的那样以空结束符存在,于时在遍历的时候越界了,读取到了一个不存在的内存地址,从而倒致蓝屏。

查看了这个驱动文件发现是NV显卡的一个驱动,我电脑上崩溃的驱动版本如下: 

后记:    

在获取UNICODE_STRING中BUFFER的长度时,不要自作聪明的想当然,按规章办事还是很有必要的,就像NV(英伟达)这种大公司写的驱动也是不可避免存在这些低级错误(该驱动的这个函数中的其地方很多这样的写法!!!!!!!)。 

===================================================================================
今天早上起来更新了下显卡驱动发现这个问题已经修复了。
那段有问题的代码已经删除了。 09/12/2013

[课程]FART 脱壳王!加量不加价!FART作者讲授!

收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 12848
活跃值: (9108)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
2
老黄的驱动不是一向各种随意么  之前还被爆出各种内核溢出漏洞
2017-9-19 21:18
0
雪    币: 615
活跃值: (530)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
3
学习,原来不以NULL结尾是这个意思。学习了。内核编程比应用层麻烦多了。
2017-9-20 08:36
0
雪    币: 3700
活跃值: (3817)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
感谢分享分析dump过程!
2017-9-21 13:13
0
游客
登录 | 注册 方可回帖
返回
//