NVidia本地&远程提权漏洞分析

漏洞背景

圣诞节,一个网名“Winter-Smith”(冬日史密斯)的黑客公布了NVIDIA显卡驱动中存在的一个严重安全漏洞,可以轻松获取系统权限。这位老兄还很厚道地在网上公开了漏洞的完整细节,有经验的黑客完全可以拿过来就利用,搞的NVIDIA的某些员工这个圣诞和新年是没法清净。

NVIDIA Driver Helper Service,是一个r3层的服务,程序是nvvsvc.exe(win7)或nvvsvc32.exe(xp),这个程序启动后会创建一个命名管道\pipe\nvsr,比较特别的是这个管道的dacl是空,也就是说everyone都可以访问,xpx32版下0x52命令也不支持,同时老版本的xp版nvsvc32.exe根本没建命名管道。这个漏洞利用只针对win7x64版有效,在win7x32版上只能够做到infoleak。

调试环境

系统环境 Win7sp1 x64 windbg IDA
驱动程序提供商 NVIDIA GT520M
驱动程序版本 8.17.12.9555
服务名称 NVIDIA Display Driver Service
服务进程 nvvsvc.exe
需要说明的是,在windows xp下产生服务的程序是nvsvc32.exe,windows 7下产生服务的是nvvsvc.exe。

漏洞分析

刚开始从网上找到源码,先在我的win7x32下编译运行,程序到第二步直接就挂掉了,提示:!! Unable to create named pipe。然后将程序编译成64位,在虚拟机里win7x64下运行,依然得到相同的结果。

开始看博客原文,大概了解以后知道是测试环境没有对,需要Nvidia的gt540m显卡和win7x64。而且在虚拟机里无法进行测试,因为需要nvvsvc.exe后台服务运行,虚拟机不支持。这就为测试环境提出了非常苛刻的条件。原文博客上记载如果测试成功,会增加一个r00t的管理员,测试窗口显示如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
C:\Users\Peter\Desktop\NVDelMe1>nvvsvc_expl.exe 127.0.0.1
** Nvvsvc.exe Nsvr Pipe Exploit (Local/Domain) **
[@peterwintrsmith]
- Win7 x64 DEP + ASLR + GS Bypass - Christmas 2012 -
Action 1 of 9:  - CONNECT
Action 2 of 9:  - CLIENT => SERVER
Written 16416 (0x4020) characters to pipe
Action 3 of 9:   - SERVER => CLIENT
Read 16504 (0x4078) characters from pipe
Action 4 of 9: Building exploit ...
=> Stack cookie 0xe2e2893340d4:
=> nvvsvc.exe base 0x13fb90000:
Action 5 of 9:  - CLIENT => SERVER
Written 16416 (0x4020) characters to pipe
Action 6 of 9:   - SERVER => CLIENT
Read 16384 (0x4000) characters from pipe
Action 7 of 9:  - CLIENT => SERVER
Written 16416 (0x4020) characters to pipe
Action 8 of 9:   - SERVER => CLIENT
Read 16896 (0x4200) characters from pipe
Action 9 of 9:  - DISCONNECT

找到gt540m + win7x64这个环境以后测试只能走到第八步,错误提示:

!! Error reading from pipe (at least, no data on pipe)

然后我在自己的电脑上gt520m + win7x64测试也是得到相同的结果,挂到第八步,这样的话就需要去分析源码了。

源码的大概流程是先查找目标机是否存在一个名字为nvsr的管道,如果存在与其进行连接,通过往管道里写入数据从得到的返回数据里得到目标机泄漏出来的cookie和nvvsvc.exe基址,然后通过构造ROP链突破DEP执行shellcode,增加管理员r00t也就是shellcode完成的工作。Shellcode的执行是在nvvsvc进程中执行的,而nvvsvc.exe驱动服务具有管理员权限,因此就完成了提权的过程。

通过对源码的分析,回过头看,挂在第二步肯定就是没找到nvsr管道。本机存在管道的查看可以通过pipelist工具进行查看。挂在第八步就需要对源码进行调试分析,看到底是哪里出错了。通过与测试成功窗口进行对比,挂在第八步的时候,我们看到第四步,并没有显示=> nvvsvc.exe base 0x13fb90000:这条提示符,也就是说并没有找对nvvsvc.exe基址。

在第四步代码之前下断点,经过多次调试以后,发现不同的Nvidia驱动版本对应的匹配字节是不相同的。比如在我的gt520m 驱动版本:8.17.12.9555上边测试就应该是0xb41,这样匹配以后才能算出正确的nvvsvc.exe基址。后边在经过测试验证,在相同的显卡,不同的驱动版本下匹配字节也是不相同的,这就是为什么我们找到了gt540m+win7x64的环境依然不能得到正确的结果。再后来又经过测试,在不同的显卡,相同的驱动版本下匹配字节相同,也就是可以做到通用。在整个调试过程中,不能在虚拟机调试,而且只要调试过一次,nvvsvc.exe后台驱动服务就会挂掉,需要重新启动系统才可以恢复,是一个相当麻烦的过程。

问题找到后,接下来就是构造ROP链的问题了,因为基址不同,ROP链也需要自己重新构造。在构造ROP链过程中,遇到了两个问题,首先是virtualprotect函数参数的使用,源码中给了第二个参数dwSize赋值的是0x1,源码中说明是整个一页,但是实际测试中并不能够成功,最后将其改成0x300成功。第二个问题也是想了很久,也是一个很有意思的问题,在构造ROP链的最后一步jmp rsp,这条指令地址为基址+0xb7153,在调试时候始终跳不过去执行shellcode,最后找到的原因是因为这个偏移太大了,已经不在代码段了,因此是不可执行的指令,最后换上call rsp成功实现跳转执行shellcode。

漏洞利用

攻击者先查找是否能够找到命名管道\pipe\nvsr,如果找到就与其建立通信,分三次往管道中写入数据,第一次泄漏出模块基址用于构造ROP链,第二次将带有ROP链和shellcode的数据写入管道,第三次重新布局数据最后执行shellcode.由于nvvsvc服务具有管理员权限,因此就实现了提权过程。

泄漏出模块基址构造ROP链

第一次写入数据BuildMalicious_LeakStack()总共0x4020字节

1
2
3
4
5
6
7
8
9
10
Base + 0x0000          52 00
Base + 0x0002          41 00 41 00 41 00 41 00....
Base + 0x4002          00 00
Base + 0x4004          00 00 00 00
Base + 0x4008          78 40 00 00
Base + 0x400c          41 41 41 41
Base + 0x4010          41 41 41 41
Base + 0x4014          41 41 41 41
Base + 0x4018          41 41 41 41
Base + 0x401c          41 41 41 41

读出数据 0x4200(即nvvsvc服务写入管道数据0x4078)

1
2
3
4
5
6
7
8
9
10
rgReadBuf+0x0000           41 41 41 41
rgReadBuf+0x0004           41 41 41 41
rgReadBuf+0x0008           41 41 41 41
rgReadBuf+0x000c           41 41 41 41
rgReadBuf+0x0010           41 41 41 41
rgReadBuf+0x0010           00 00 00 00
...
rgReadBuf+0x4034         2e 5d 29 39 44 f5 00 00
rgReadBuf+0x403c           41 3b 7f 3f 01 00 00 00
...

ROP链和shellcode的数据写入管道

nvvsvc服务写入管道数据的方法:
首先会先取管道中数据(即攻击者CLIENT写入管道的数据)头两个字节作为操作判断符号,本漏洞利用的就是0x52操作,操作方式为读写数据。然后判断第三个字节是否为0x00,如果不为0x00,循环判断,最多循环0x2000次,判断结束后,将其后边数据偏移四个字节作为写入管道的数据长度,如上边的0x4078.然后将长度后边的数据作为写入管道的数据,如上边的0x41414141。也就是说最后nvvsvc服务先将Base + 0x400c处开始的长度为0x4078个数据复制到Base + 0x4020处,最后再将这些数据写入管道中。在调用复制数据函数的时候没有做任何有关长度的检测,导致将base+0x400c+0x4034的cookie和base+0x400c+0x403c处的模块基址(需要减去一个偏移)泄漏出来并写入管道,最后攻击者CLIENT读出这些数据加以利用。

base+0x400c+0x403c处的模块基址需要做一些处理才能使用,比如上边读到的是0x013f7f3b41,先将这个值&0xfff得到后三位0xb41,这个值对应着每个NviDia的驱动版本,不同版本的驱动程序释放出来的nvvsvc.exe模块代码稍有不同,因此构造ROP链也会有差异。最后将0x013f7f3b41减去后四位0x3b41就得到了nvvsvc.exe模块基址。

第二次 写入数据BuildMalicious_FillBuf() 总共0x4020

1
2
3
4
5
6
7
8
9
10
11
Base + 0x0000          52 00
Base + 0x0002          00 00
Base + 0x0004          00 00 00 00
Base + 0x0008          00 40 00 00
Base + 0x000c          43 43 43 43...
Base + 0x0018          2e 5d 29 39 44 f5 00 00 cookie
Base + 0x0020          43 43 43 43....
Base + 0x0040          ROP链...
Base + 0x0158          shellcode...
...
其他0x43覆盖

读出数据 0x4000(即nvvsvc服务写入管道数据0x4000)

1
2
3
4
5
6
rgReadBuf+0x0000           43 43 43 43...
...
rgReadBuf+0x0044           ROP链...
rgReadBuf+0x014c           shellcode...
...
其他0x43覆盖

第三次 写入数据BuildMalicious_OverwriteStack() 总共0x4020

1
2
3
4
5
6
Base + 0x0000          52 00
Base + 0x0002          00 00
Base + 0x0004          00 00 00 00
Base + 0x0008          40 43 00 00
Base + 0x000c          42 42 42 42...
...

经过管道处理复制(0x4340个字节)以后,在nvvsvc模块缓冲区中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Base + 0x0000          52 00
Base + 0x0002          00 00
Base + 0x0004          00 00 00 00
Base + 0x0008          40 43 00 00
Base + 0x000c          42 42 42 42...
...
Base+0x401c            42 42 42 42
Base+0x4020            42 42 42 42
...
Base+0x8034            43 43 43 43
...
Base+0x8064            rop链...
Base+0x8078            shellcode...
...

通过控制复制的字节长度,最后一步恢复堆栈时候 lea r11,[rsp+82B0h]将跳到我们控制的地方。
读出数据 0x4200(nvvsvc服务写入管道数据0x4340)

1
2
3
4
5
6
7
rgReadBuf+0x0000           42 42 42 42...
...
rgReadBuf+0x4014           43 43 43 43
...
rgReadBuf+0x4044           rop链...
rgReadBuf+0x414c           shellcode...
...

IDA关键代码:

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
.text:0000000000003B18                 mov     rax, rsp
.text:0000000000003B1B                 mov     [rax+10h], rbx
.text:0000000000003B1F                 mov     [rax+18h], rsi
.text:0000000000003B23                 mov     [rax+20h], rdi
.text:0000000000003B27                 push    rbp
.text:0000000000003B28                 push    r12
.text:0000000000003B2A                 push    r13
.text:0000000000003B2C                 push    r14
.text:0000000000003B2E                 push    r15
.text:0000000000003B30                 lea     rbp, [rax-81D8h]
.text:0000000000003B37                 mov     eax, 82B0h
.text:0000000000003B3C                 call    sub_49880
.text:0000000000003B41                 sub     rsp, rax
.text:0000000000003B44                 mov     rax, cs:qword_C5148
.text:0000000000003B4B                 xor     rax, rsp
.text:0000000000003B4E                 mov     [rbp+81A0h], rax
.text:0000000000003B55                 xor     r14d, r14d
.text:0000000000003B58                 lea     r9, [rsp+28h+NumberOfBytesRead] ; lpNumberOfBytesRead
.text:0000000000003B5D                 lea     rdx, [rbp+160h] ; lpBuffer
.text:0000000000003B64                 mov     r8d, 4020h      ; nNumberOfBytesToRead
.text:0000000000003B6A                 mov     rsi, rcx
.text:0000000000003B6D                 mov     [rsp+28h+NumberOfBytesRead], r14d
.text:0000000000003B72                 mov     [rsp+28h+NumberOfBytesWritten], r14d
.text:0000000000003B77                 mov     [rsp+28h+nNumberOfBytesToWrite], r14d
.text:0000000000003B7C                 mov     [rsp+28h+var_8], r14
.text:0000000000003B81                 call    cs:ReadFile
; nvvsvc服务从管道固定读入4020个字节数据到缓冲区
.text:0000000000003B87                 test    eax, eax
.text:0000000000003B89                 jz      loc_406F
.text:0000000000003B8F                 mov     ebx, 2000h
.text:0000000000003B94                 lea     edi, [r14+52h]  ; edi=52h
.text:0000000000003B98                 lea     r15d, [r14+12h] ; r15=12h
.text:0000000000003B9C
.text:0000000000003B9C loc_3B9C:                               ; CODE XREF: sub_3B18+551j
.text:0000000000003B9C                 cmp     [rsp+28h+NumberOfBytesRead], r14d ; [rsp+28h+NumberOfBytesRead]=4020h,r14d=0
.text:0000000000003BA1                 jz      loc_406F
.text:0000000000003BA7                 movzx   ecx, word ptr [rbp+160h]
;将读入的数据头两个字节放入ecx中,ecx=0052,根据头两字节来判断操作,根据后边调试分析,0x52为管道读写操作,读写方法上边已经介绍。
.text:0000000000003BAE                 sub     ecx, 23h        ; ecx=2f
.text:0000000000003BB1                 jz      loc_3F70
.text:0000000000003BB7                 sub     ecx, 20h        ; ecx=f
.text:0000000000003BBA                 jz      loc_3E9C
.text:0000000000003BC0                 dec     ecx
.text:0000000000003BC2                 jz      loc_3E41
.text:0000000000003BC8                 sub     ecx, 3
.text:0000000000003BCB                 jz      loc_3DBB
.text:0000000000003BD1                 sub     ecx, 5
.text:0000000000003BD4                 jz      loc_3D35
.text:0000000000003BDA                 sub     ecx, 6
.text:0000000000003BDD                 jz      loc_3CB2        ; 跳转
.text:0000000000003BE3                 cmp     ecx, 5
.text:0000000000003BE6                 jz      short loc_3BFA
.text:0000000000003BE8                 mov     r8d, 1Ch
.text:0000000000003BEE                 lea     rdx, aUnknownToken ; "Unknown token"
.text:0000000000003BF5                 jmp     loc_3F5F
......
text:0000000000003CB2 loc_3CB2:                               ; CODE XREF: sub_3B18+C5j
.text:0000000000003CB2                 lea     rcx, [rbp+162h]
.text:0000000000003CB9                 mov     rdx, rbx
.text:0000000000003CBC                 mov     [rsp+28h+arg_4], 3
.text:0000000000003CC4                 call    sub_462E0
; 该函数的功能是循环判断rax次(rax最大2000h),以0作为判断终止符,然后将rax返回,下边将根据rax来复制将要写入管道的数据。
.text:0000000000003CC9                 cmp     cs:dword_D0990, r14d
.text:0000000000003CD0                 lea     r9, [rsp+28h+arg_8]
.text:0000000000003CD5                 mov     ecx, [rbp+rax*2+164h]
; ecx=0  [rbp+rax*2+164h]=000000007840000041414141...
.text:0000000000003CDC                 lea     rdi, [rbp+rax*2+16Ch]
; [rbp+rax*2+16Ch]=4141414141...
.text:0000000000003CE4                 lea     rdx, [rsp+28h+arg_4]
.text:0000000000003CE9                 mov     [rsp+28h+arg_4], ecx
.text:0000000000003CED                 mov     ecx, [rbp+rax*2+168h] ; rcx=4078
.text:0000000000003CF4                 mov     r8, rdi         ; [r8]=41414141...
.text:0000000000003CF7                 mov     [rsp+28h+arg_8], ecx
.text:0000000000003CFB                 lea     rcx, [rbp+162h]
.text:0000000000003D02                 jle     short loc_3D0B
.text:0000000000003D04                 call    sub_2C84
.text:0000000000003D09                 jmp     short loc_3D10
.text:0000000000003D0B ; ---------------------------------------------------------------------------

.text:0000000000003D0B
.text:0000000000003D0B loc_3D0B:                               ; CODE XREF: sub_3B18+1EAj
.text:0000000000003D0B                 call    sub_2A00
.text:0000000000003D10 ; ---------------------------------------------------------------------------

.text:0000000000003D10
.text:0000000000003D10 loc_3D10:                               ; CODE XREF: sub_3B18+1F1j
.text:0000000000003D10                 movsxd  rbx, [rsp+28h+arg_8]
.text:0000000000003D15                 lea     rcx, [rbp+4180h]
.text:0000000000003D1C                 mov     rdx, rdi
.text:0000000000003D1F                 mov     r8, rbx
.text:0000000000003D22                 call    sub_45980
; bug!!!复制数据函数 该函数的功能就是复制将要写入管道的数据,没有做任何检测,最后返回存放该数据缓冲区指针rax。该数据将发送回攻击者,长度为4078导致内存泄漏
.text:0000000000003D27                 mov     [rsp+28h+nNumberOfBytesToWrite], ebx
.text:0000000000003D2B
.text:0000000000003D2B loc_3D2B:                               ; CODE XREF: sub_3B18+156j
.text:0000000000003D2B                                         ; sub_3B18+198j
.text:0000000000003D2B                 mov     edi, 52h
.text:0000000000003D30                 jmp     loc_4017
......

.text:0000000000004017 loc_4017:                              ; CODE XREF: sub_3B18+218j
.text:0000000000004017                                         ; sub_3B18+435j ...
.text:0000000000004017                 mov     ebx, [rsp+28h+nNumberOfBytesToWrite] ; ebx=4078
.text:000000000000401B                 lea     r9, [rsp+28h+NumberOfBytesWritten] ; lpNumberOfBytesWritten  [lp]=4078
.text:0000000000004020                 lea     rdx, [rbp+4180h] ; lpBuffer
.text:0000000000004027                 mov     rcx, rsi        ; hFile
.text:000000000000402A                 mov     r8d, ebx        ; nNumberOfBytesToWrite  4078个字节 每次写入没有限制也没有任何检测
.text:000000000000402D                 mov     [rsp+28h+var_8], r14
.text:0000000000004032                 call    cs:WriteFile    ; 将4078个字节的数据写入管道中,导致内存泄漏
.text:0000000000004038                 test    eax, eax        ; 判断是否调用成功
.text:000000000000403A                 jz      short loc_406F
.text:000000000000403C                 cmp     ebx, [rsp+28h+NumberOfBytesWritten] ; 判断是否写入的是4078个字节
.text:0000000000004040                 jnz     short loc_406F
.text:0000000000004042                 lea     r9, [rsp+28h+NumberOfBytesRead] ; lpnNumberOfBytesToRead ---step 5  [lp]=4020h
.text:0000000000004047                 lea     rdx, [rbp+160h] ; lpBuffer
.text:000000000000404E                 mov     r8d, 4020h      ; nNumberOfBytesToRead  4020h 每次固定读入4020h个字节
.text:0000000000004054                 mov     rcx, rsi        ; hFile
.text:0000000000004057                 mov     [rsp+28h+var_8], r14
.text:000000000000405C                 call    cs:ReadFile     ;将攻击者CLIENT发送的数据从管道读入缓冲区
.text:0000000000004062                 mov     ebx, 2000h
.text:0000000000004067                 test    eax, eax
.text:0000000000004069                 jnz     loc_3B9C        ; 如果读到数据则跳回前边做循环处理

攻击者发送完三次数据,nvvsvc服务最后一次从管道中读取数据失败,然后去执行关闭管道句柄过程,如下:

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
.text:000000000000406F
.text:000000000000406F loc_406F:                               ; CODE XREF: sub_3B18+71j
.text:000000000000406F                                         ; sub_3B18+89j ...
.text:000000000000406F                 mov     rcx, rsi        ; hFile
.text:0000000000004072                 call    cs:FlushFileBuffers
.text:0000000000004078                 mov     rcx, rsi        ; hNamedPipe
.text:000000000000407B                 call    cs:DisconnectNamedPipe
.text:0000000000004081                 mov     rcx, rsi        ; hObject
.text:0000000000004084                 call    cs:CloseHandle
.text:000000000000408A                 lock dec cs:dword_D091C
.text:0000000000004091                 mov     eax, 1
.text:0000000000004096                 mov     rcx, [rbp+81A0h]
.text:000000000000409D                 xor     rcx, rsp
.text:00000000000040A0                 call    sub_455A0
.text:00000000000040A5                 lea     r11, [rsp+28h+arg_8280]
;恢复堆栈指针rsp, lea r11,[rsp+82b0],通过最后一步写入数据的精心布局,rsp最终将指向攻击者精心构造的数据。
.text:00000000000040AD                 mov     rbx, [r11+38h]
.text:00000000000040B1                 mov     rsi, [r11+40h]
.text:00000000000040B5                 mov     rdi, [r11+48h]
.text:00000000000040B9                 mov     rsp, r11        ; 得到rsp的控制权,此时堆栈数据如下:

00000000`01b7fb00 43 43 43 43 43 43 43 43  CCCCCCCC
00000000`01b7fb08 43 43 43 43 43 43 43 43  CCCCCCCC
00000000`01b7fb10 43 43 43 43 43 43 43 43  CCCCCCCC
00000000`01b7fb18 43 43 43 43 43 43 43 43  CCCCCCCC
00000000`01b7fb20 43 43 43 43 43 43 43 43  CCCCCCCC
00000000`01b7fb28 6e 5f 31 3f 01 00 00 00  n_1?....   //rop链
00000000`01b7fb30 9c 4f 35 3f 01 00 00 00  .O5?....
00000000`01b7fb38 00 00 00 00 00 00 00 00  ........

.text:00000000000040BC                 pop     r15
.text:00000000000040BE                 pop     r14
.text:00000000000040C0                 pop     r13
.text:00000000000040C2                 pop     r12
.text:00000000000040C4                 pop     rbp
.text:00000000000040C5                 retn
; 经过五次pop过后rsp指向ROP链第一条语句,retn之后开始执行ROP链,最后成功跳进shellcode.
.text:00000000000040C5 sub_3B18        endp
.text:00000000000040C5

远程利用条件

  • 本机(作为攻击的计算机)必须有账户名和密码,并且密码不能为空,目标机器密码可为空。
  • 本机与被攻击计算机必须在同一网段
  • 关闭防火墙
  • 被攻击的目标计算机必须关闭密码保护共享(本机可不关闭),如下图所示:
  • 如果攻击目标机器为Win2008 R2 x64系统,则本机(作为攻击计算机)的用户名中不能与目标机器具有相同的用户名。比如本机用户为test,而目标机器(Win2008中)的账户中也有个名为test的账户,则攻击不成功。对于攻击系统为Win7 x64的计算机则没有要求。

Win7 x64和Win2008 R2 x64均测试成功!

总结

经过多次试验,关键的地方就是c:\windows\system32\nvvsvc.exe这个程序,可以将这个程序拷贝到其他计算机中相同的目录下,不论是否装有N卡驱动,然后重启查看进程,确定该驱动服务后台加载了,就可以得到相同的测试结果。也就是说nvvsvc.exe这个驱动服务在启动的时候创建了一个名字为nvsr的管道,这个管道的dacl是空,也就是说everyone都可以访问。这就是该漏洞利用是否成功的关键。

这个漏洞要做到通用,还需要收集各种显卡的驱动程序,其实也仅仅需要收集c:\windows\system32\nvvsvc.exe这个程序,而且实际构造测试发现,有些相近版本的驱动(比如gt520m的8.17.12.8590和gt630m的8.17.12.8580)也是可以通用的。目前已经收集到10个,相应的ROP链也已经构造完成,并测试能够正确执行。

最后关于这个漏洞的分析,刚开始一直对这个nvvsvc.exe下断点断下来,后来原因也找到了,在进程中显示所有用户进程会发现两个nvvsvc.exe服务进程,一个可以断到,一个断不到,所以两个都需要下断点才可以。调试发现给最先加载的那个服务进程下断点就可以了。对于这个漏洞还有需要研究的地方,比如除了0x52操作,其他几个操作完成了什么样的功能,是否有可利用之处,需要有时间再跟踪调试一下。

参考

NVIDIA Driver Helper Service栈溢出简单说明
Nvvsvc32.exe 溢出简要分析