转载自:https://bbs.pediy.com/thread-227076.htm
环境
被调试机:7600.16385.x86fre.win7_rtm.090713-1255
调试机:win10,
调试工具:windbg proview
导致蓝屏的软件:pchunter
视频:https://www.youtube.com/watch?v=8tBRtlvapWU
描述
运行pchunter,点击“网络”卡片页时,系统就会蓝屏。
对第一次蓝屏捕捉到的信息进行分析。这里只列出了一些重点信息及描述。
BUG的概述
1 2 3 4 5 6 7 8 9 |
|
- BugCheckCode:PAGE_FAULT_IN_NONPAGED_AREA 是微软定义的Bug编码,其值为0x00000050.下面英文说的是,系统使用了无效的系统内存导致触发异常,且该异常没能被处理。可能是内存地址被破坏了或被释放了。
- Arguments,为参数错误检查。这里检测到的信息是,参数1:指出当前系统访问当地址是0xfffffff5;参数2:是说当前的操作是读;参数3:是说当前指令的地址是0x840bf2ee;参数4:保留。
BUG的详情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
- BUILD_VERSION_STRING:指出了运行的系统版本信息。
- BUGCHECK_P1~4:和BUG概述中的基本一致。
- READ_ADDRESS:读地址为0xfffffff5的内存。
- FAULTING_IP:导致Bug的地址。即该bug发生在nt!ObpQueryNameString+2b处,该地址的需要执行的指令是
movzx eax,byte ptr [esi+0Ch] - DEFAULT_BUCKET_ID:故障类型。这里指出是win7驱动发生故障。
- PROCESS_NAME:与该BUG相关的进程。
陷阱帧
1 2 3 4 5 6 7 8 9 10 11 12 |
|
- TRAP_FRAME :陷阱帧,是一个nt!_KTRAP_FRAME结构体。 KTRAP_FRAME 用于在系统处理异常或中断期间,保存CPU的寄存器的内容。 KTRAP_FRAME结构通常分配在线程的内核模式堆栈中。陷阱帧的一小部分由CPU填充,作为其自身中断和异常处理的一部分,陷阱帧的其余部分由Windows提供的软件异常和中断处理程序 创建 (例如KiTrap0E()、KiPageFault KiInterruptDispatch 等函数)。
- LAST_CONTROL_TRANSFER:最后的控制权转让,也就是调用栈中的最后一个CALL。这里,在地址0x83f1ee71调用了0x83ead394
调用堆栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
- 前面5个函数是系统用来保存现场(寄存器)以及检测崩溃信息。
- 从BUG详情中已经知道,崩溃地址是nt!ObpQueryNameString+0x2b。是由于[esi+0Ch]=0xfffffff5导致的。
追踪崩溃源头
查看 nt!ObpQueryNameString ~ nt!ObpQueryNameString+0x2b的 反汇编:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
上述代码有关esi的整理如下:
1 2 3 4 5 |
|
这说明,导致esi+0ch崩溃的是因为arg1=1 。通过查看调用栈可知:arg1 是 nt!ObQueryNameString 传递给 nt!ObpQueryNameString 的第一个参数。
1
2
98926a2c 840bfa7a 00000001 98926a6c 00000050 nt!ObpQueryNameString+0x2b
98926a48 8bc76887 00000001 98926a6c 00000050 nt!ObQueryNameString+0x18
查看 nt!ObQueryNameString~ nt!ObQueryNameString +0x18的反汇编:
1
2
3
4
5
6
7
8
9
10
11
12
13
uf nt!ObQueryNameString
nt!ObQueryNameString:
840bfa62 8bff mov edi,edi
840bfa64 55 push ebp
840bfa65 8bec mov ebp,esp
840bfa67 6a00 push 0
840bfa69 ff7514 push dword ptr [ebp+14h] ;
840bfa6c ff7510 push dword ptr [ebp+10h] ;
840bfa6f ff750c push dword ptr [ebp+0Ch] ;
840bfa72 ff7508 push dword ptr [ebp+8] ;arg1:
840bfa75 e849f8ffff call nt!ObpQueryNameString (840bf2c3)
840bfa7a 5d pop ebp
840bfa7b c21000 ret 10h
分析 nt!ObQueryNameString 传给 nt!ObpQueryNameString的第一个参数是从哪里来的?都做了什么操作?
从汇编代码中很容易看出,传递给 nt!ObpQueryNameString的第一个参数也是 nt!ObQueryNameString的第一个参数,而且 nt!ObQueryNameString 未修改参数1.
补充:
ObQueryNameString 函数:返回指定内核对象的名称。
1
2
3
4
5
6
7
NTKERNELAPI NTSTATUS ObQueryNameString(
PVOID Object,
POBJECT_NAME_INFORMATION ObjectNameInfo,
ULONG Length,
PULONG ReturnLength
);<span style="color:rgb(0, 0, 0); font-family:none; font-size:15px;">
</span>
参数
Object:内核对象的指针,该值不能为NULL.
ObjectNameInfo:由用户提供的存放返回值得缓冲区,若不知大小则可以为NULL,由ReturnLength返回需要的缓冲区大小。
Length: 缓冲区的字节数.该值必须包括OBJECT_NAME_INFORMATION结构和对象名称的长度。根据DDK上推荐该值为1024
ReturnLength: 返回的数据的大小。此值包括OBJECT_NAME_INFORMATION结构和对象名称的长度
接下来分nt!ObQueryNameString 的参数1的来历。
1
2
3
98926a48 8bc76887 00000001 98926a6c 00000050 nt!ObQueryNameString+0x18
98926af4 8bc77245 03fc016c 001ffeb4 00000000 PCHunter32aq+0x52887
98926b2c 8bc772d3 00000010 0000013c 98926bfc PCHunter32aq+0x53245
使用 ub 8bc77245 找到 nt!ObQueryNameString 的父函数的入口地址:
1
2
3
4
5
6
7
8
9
10
1: kd> ub 8bc77245
PCHunter32aq+0x53233:
8bc77233 7230 jb PCHunter32aq+0x53265 (8bc77265)
8bc77235 03f0 add esi,eax
8bc77237 2bf8 sub edi,eax
8bc77239 83ff0c cmp edi,0Ch
8bc7723c 7227 jb PCHunter32aq+0x53265 (8bc77265)
8bc7723e 57 push edi
8bc7723f 56 push esi
8bc77240 e84bf5ffff call PCHunter32aq+0x52790 (8bc76790)//8bc76790为nt!ObQueryNameString的父函数入口
把 PCHunter32aq+0x52790 (8bc76790)记作 function_1。用uf PCHunter32aq+0x52790命令 查看function_1的汇编代码,这里主要关注 PCHunter32aq+0x52790 ~PCHunter32aq+0x52887之间的代码,分析 function_1 在调用nt!ObQueryNameString函数前对 nt!ObQueryNameString函数 的参数1做了哪些操作?
由于 PCHunter32aq+0x52790~PCHunter32aq+0x5282c之间的代码没有对 nt!ObQueryNameString函数的参数1做操作,所以下文的代码省略了该部分内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
PCHunter32aq+0x5282c:
8bc7682c 57 push edi
8bc7682d 8d3c00 lea edi,[eax+eax]
8bc76830 e8fb780200 call PCHunter32aq+0x7a130 (a287e130) ;Pchunter获取一些内核信息
8bc76835 8945ec mov dword ptr [ebp-14h],eax ;ebp-14h=PCHunter32aq+0x7a130返回的缓冲区地址,是一个数组
8bc76838 85c0 test eax,eax
8bc7683a 0f84c8000000 je PCHunter32aq+0x52908 (8bc76908) Branch
PCHunter32aq+0x52840:
8bc76840 8b08 mov ecx,dword ptr [eax]
8bc76842 894df0 mov dword ptr [ebp-10h],ecx
8bc76845 c745f400000000 mov dword ptr [ebp-0Ch],0
8bc7684c 85c9 test ecx,ecx
8bc7684e 0f849f000000 je PCHunter32aq+0x528f3 (8bc768f3) Branch
PCHunter32aq+0x52854:
8bc76854 56 push esi
8bc76855 8d700c lea esi,[eax+0Ch]
8bc76858 eb06 jmp PCHunter32aq+0x52860 (8bc76860) Branch
;------------------------------
;分析1
;8bc76840 8b08 mov ecx,dword ptr [eax]
;8bc76855 8d700c lea esi,[eax+0Ch]
;通过这两处推测 eax 为一个结构体
;------------------------------
PCHunter32aq+0x52860:
8bc76860 0fb656fc movzx edx,byte ptr [esi-4]
8bc76864 3b55f8 cmp edx,dword ptr [ebp-8]
8bc76867 7576 jne PCHunter32aq+0x528df (8bc768df) Branch
PCHunter32aq+0x52869:
8bc76869 8b06 mov eax,dword ptr [esi]
8bc7686b 85c0 test eax,eax
8bc7686d 7470 je PCHunter32aq+0x528df (8bc768df) Branch
PCHunter32aq+0x5286f:
8bc7686f 8b4004 mov eax,dword ptr [eax+4]
8bc76872 85c0 test eax,eax ;只判断了eax=0的情况,而当前情况是eax=1,所以导致崩溃
8bc76874 7469 je PCHunter32aq+0x528df (8bc768df) Branch
;------------------------------
;分析2
;EAX=[EBP-14H]
;ESI=&[EAX+0CH] ESI存放一个指针,这个指针指向一个结构体,这个结构体就是数组单个元素的结构体
;@$t4:arg1 LPVOID object
;r @$t0=EBP
;r @$t1=ESI
;r @$t2=EBP-8=00000007 //通过内存查看到该处的值为7
;.if( by(@$t1-4)==7 )
;{
; r @$t3=poi(@$t1);
; .if(@$t3!=0)
; {
; r @$t4=poi(@$t3+4);
; dd @$t3 L4;
; r @$t4;
; }
;}
;------------------------------
PCHunter32aq+0x52876:
8bc76876 8d4dc8 lea ecx,[ebp-38h]
8bc76879 51 push ecx ; arg4:ReturnLength
8bc7687a 6a50 push 50h ; arg3:Length
8bc7687c 8d9578ffffff lea edx,[ebp-88h]
8bc76882 52 push edx ; arg2:ObjectNameInfo
8bc76883 50 push eax ; arg1:object
8bc76884 ff55e8 call dword ptr [ebp-18h] ; call nt!ObpQueryNameString
8bc76887 85c0 test eax,eax
8bc76889 7554 jne PCHunter32aq+0x528df (8bc768df) Branch
------------------------------------------------------------------------------------------
....该处代码与nt!ObQueryNameString的参数1无关
------------------------------------------------------------------------------------------
PCHunter32aq+0x528df:
8bc768df 8b45f4 mov eax,dword ptr [ebp-0Ch]
8bc768e2 40 inc eax
8bc768e3 83c610 add esi,10h
8bc768e6 8945f4 mov dword ptr [ebp-0Ch],eax
8bc768e9 3b45f0 cmp eax,dword ptr [ebp-10h]
8bc768ec 0f826effffff jb PCHunter32aq+0x52860 (8bc76860) Branch
------------------------------
;分析3
;8bc76842 894DF0 MOV DWORD PTR [EBP-10H],ECX
;8bc768E9 3B45F0 CMP EAX,DWORD PTR [EBP-10H]
;通过这两处判断 EBP-10H 为一个 DWORD 值,
;8bc76860 与 8bc768E2 构成了一个循环
;而 EBP-0CH 为循环计数器
;而 EBP-10H 就是这个循环的最大次数 => 推测 EBP-14H 为一个数组类型 而 EBP-10H 为该数组的元素个数
;8bc768E3 83C610 ADD ESI,10H => 推测 数组中的单个元素的大小为 10H
;@$t5=EAX=[ebp-0ch]=0 初始值为0; 此时为 61H
;查看数组中第61H个元素的内容
;数组的起始地址为 ESI= [EAX+0CH]
分析到这里function_1在调用 call dword ptr [ebp-18h]; call nt!ObpQueryNameString时只检验了参数1是否为0,并没有校验是否为有效地址。
但我认为导致 nt!ObpQueryNameString的参数1位“1”的原因应该是在调用PCHunter32aq+0x7a130函数时造成的。有时间再研究吧,暂时到这里吧。
1
2
8bc76830 e8fb780200 call PCHunter32aq+0x7a130 (a287e130) ;获取一些内核信息
8bc76835 8945ec mov dword ptr [ebp-14h],eax ;ebp-14h=获取的内核信息地址
dump文件下载: 050718-19328-01.dmp (148.47kb,18次下载)