看到這1章我又抓狂了:作者提及從進程空間類型為Map的映照文件中尋覓以下指令地址:
call/jmp dword ptr[esp+0x8] call/jmp dword ptr[esp+0x14] call/jmp dword ptr[esp+0x1c] call/jmp dword ptr[esp+0x2c] call/jmp dword ptr[esp+0x44] call/jmp dword ptr[esp+0x50] call/jmp dword ptr[ebp+0xc] call/jmp dword ptr[ebp+0x24] call/jmp dword ptr[ebp+0x30] call/jmp dword ptr[ebp-0x4] call/jmp dword ptr[ebp-0xc] call/jmp dword ptr[ebp-0x18]作者輕拂衣袖不留任何解釋,空留我1臉迷茫。迷茫很久我決定還調(diào)試1遍視察進程棧布局并把調(diào)試的結果記錄于此。示例代碼就用作者書本上的那個,這里就不重復貼代碼了。
進入__try/__except塊以后,程序當前的異常處理鏈以下:
0:000> !exchain 0012fe80: offset!ILT+115(__except_handler4)+0 (00411078) 0012ffa8: offset!ILT+115(__except_handler4)+0 (00411078) 0012ffe0: kernel32!_except_handler3+0 (7c839ac0) CRT scope 0, filter: kernel32!BaseProcessStart+29 (7c843882) func: kernel32!BaseProcessStart+3a (7c843898) Invalid exception stack at ffffffff 0:000> dd fs:[0] L1 ;進程異常處理鏈表頭 003b:00000000 0012fe80 ;當前第1個異常塊的地址為:0x12fe80 0:000> dd 0012fe80 L2 ;第1個異常塊的內(nèi)容 0012fe80 0012ffa8 00411078在這個基礎上,當異常產(chǎn)生并且OS接收異常后,經(jīng)過1途經(jīng)關斬將終究會進入函數(shù)ntdll!ExecuteHandler4
0:000> x *!__except_handler4 ;查找異常處理函數(shù)ExecuteHandler4所在模塊 00411840 offset!_except_handler4 = <no type information> 102d3280 MSVCR90D!_except_handler4 = <no type information> 0:000> bp offset!_except_handler4 ;為了方便視察異常產(chǎn)生時,OS調(diào)用異常處理函數(shù)ExecuteHandler4的棧回溯,需要在這個函數(shù)上下斷點 0:000> g ;觸發(fā)除0異常 (a78.e6c): Integer divide-by-zero - code c0000094 (first chance) First chance exceptions are reported before any exception handling.我們來視察1下異常處理的棧回溯信息:
0:000> kb ChildEBP RetAddr Args to Child 0012f9b4 7c9232a8 0012faa0 0012fe80 0012fab4 offset!_except_handler4 0012f9d8 7c92327a 0012faa0 0012fe80 0012fab4 ntdll!ExecuteHandler2+0x26 ;由ntdll!ExecuteHandler2調(diào)用異常處理函數(shù)offset!_except_handler4 0012fa88 7c92e46a 00000000 0012fab4 0012faa0 ntdll!ExecuteHandler+0x24 0012fa88 00411493 00000000 0012fab4 0012faa0 ntdll!KiUserExceptionDispatcher+0xe 0012fe90 00411588 0041573c 00edf6ee 00edf7b4 offset!test+0x73 [c:\documents and settings\administrator\桌面\studio\offset\offset\offset.cpp @ 22] 0012ff68 00411b88 00000001 00394c70 00393320 offset!main+0x28 [c:\documents and settings\administrator\桌面\studio\offset\offset\offset.cpp @ 30] 0012ffb8 004119cf 0012fff0 7c817067 00edf6ee offset!__tmainCRTStartup+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 586] 0012ffc0 7c817067 00edf6ee 00edf7b4 7ffdf000 offset!mainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]根據(jù)棧回溯信息,我們可以肯定offset!_except_handler4是由ntdll!ExecuteHandler2調(diào)用的,而異常處理函數(shù)的接口情勢是固定的,形如:
EXCEPTION_DISPOSITION ExcuteHandler(PEXCEPTION_RECORD,PEXCEPTION_REGISTRATION,struct _CONTEXT*,void*);既然ntdll!ExecuteHandler2負責調(diào)用offset!_except_handler4,那末它也必須負責為offset!_except_handler4傳遞參數(shù)。代碼片斷摘取了部份ntdll!ExecuteHandler2實現(xiàn),雖然是匯編代碼,但還是請讀者耐心的往下看下去:
0:000> uf ntdll!ExecuteHandler2 ntdll!ExecuteHandler2: 7c923282 55 push ebp 7c923283 8bec mov ebp,esp 7c923285 ff750c push dword ptr [ebp+0Ch] 7c923288 52 push edx 7c923289 64ff3500000000 push dword ptr fs:[0] 7c923290 64892500000000 mov dword ptr fs:[0],esp 7c923297 ff7514 push dword ptr [ebp+14h] 7c92329a ff7510 push dword ptr [ebp+10h] 7c92329d ff750c push dword ptr [ebp+0Ch] <------------------- 7c9232a0 ff7508 push dword ptr [ebp+8] 7c9232a3 8b4d18 mov ecx,dword ptr [ebp+18h] 7c9232a6 ffd1 call ecx箭頭指向處出現(xiàn)了作者提到的第1個跳板指令的跳轉目標:[ebp+0x0c]。固然,看了這段代碼,你其實不能肯定最后1行call ecx就是調(diào)用offset!_except_handler4,這可以通過查看鄰近符號:
0:000> r esp,ebp esp=0012f9b8 ebp=0012f9d8 0:000> ln ecx (00411078) offset!ILT+115(__except_handler4) | (0041107d) offset!ILT+120(__lock) Exact matches:除取得這個信息,還可以看到剛進入offset!_except_handler4時,esp和ebp在數(shù)值上差0x20.這個差值很重要,為何這么說?如果你仔細視察作者提到的指令地址就會發(fā)現(xiàn)
call/jmp dword ptr[esp+0x8] = call/jmp dword ptr[ebp-0x18] call/jmp dword ptr[esp+0x14] = call/jmp dword ptr[ebp-0xc] call/jmp dword ptr[esp+0x1c] = call/jmp dword ptr[ebp-0x4] call/jmp dword ptr[esp+0x2c] = call/jmp dword ptr[ebp+0xc] call/jmp dword ptr[esp+0x44] = call/jmp dword ptr[ebp+0x24] call/jmp dword ptr[esp+0x50] = call/jmp dword ptr[ebp+0x30]
有了這個關系,我們分析的時候可以把注意力放在1類指令上,比如,后面的章節(jié)我將集中分析[ebp+N]系列的指令。
目前為止,溢出后能利用SEH的緣由是由于異常處理結構EXCEPTION_REGISTRATION_RECORD寄存在retAddr前面(地址比retAddr更低)的棧地址。buf溢出后,shellcode散布在從變量地址到retAddr附近的棧空間上,定位這段空間相對茫茫4G進程空間而言幾近是海底撈針。不過這其實不代表沒有辦法定位,辦法就是利用異常處理進程中傳遞的
EXCEPTION_REGISTRATION_RECORD變量。如前所述,EXCEPTION_REGISTRATION_RECORD結構中包括了觸發(fā)異常時,當前棧上的異常處理結構:
0:000> dt EXCEPTION_REGISTRATION_RECORD offset!EXCEPTION_REGISTRATION_RECORD +0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD +0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION由于人為制造溢出的原因,書中提到Next所在的位置被篡改成短跳轉語句,Handler被篡改成call [ebp+N]指令所在地址。這樣安排的目的是讓call offset!_except_handler4函數(shù)地址時,毛病的將[ebp+N]的地址當作offset!_except_handler4的地址。然后跳轉到EXCEPTION_REGISTRATION_RECORD!Next繼續(xù)取指令履行,固然,取到的是1個短跳轉語句。
0:000> kb ;查看堆棧,程序當前進入函數(shù)offset!_except_handler4 ChildEBP RetAddr Args to Child 0012f9b4 7c9232a8 0012faa0 0012fe80 0012fab4 offset!_except_handler4 0012f9d8 7c92327a 0012faa0 0012fe80 0012fab4 ntdll!ExecuteHandler2+0x26 0:000> r eip ;查看當前履行到函數(shù)中哪條指令和函數(shù)反匯編 eip=00411840 0:000> uf . offset!_except_handler4: 00411840 8bff mov edi,edi 00411842 55 push ebp ;上面的結果告知我們,雖然進入函數(shù)offset!_except_handler4,但ebp沒有變動依然保持它在ntdll!ExecuteHandler2中的值, ;這和產(chǎn)生溢出后異常處理調(diào)用被溢出的異常處理函數(shù)而進入到單指令函數(shù)call [ebp+N]時的情形是1致的 0:000> r ebpebp=0012f9d8 0:000> ?? @ebp-0xc ;[ebp-0x0c]指向的地址 unsigned int 0x12f9cc 0:000> dd 0x12f9cc L2 ;這個地址包括的內(nèi)容是1個異常處理結構,這也能夠從下面!exchain的輸出得到印證 0012f9cc 0012fe80 7c9232bc 0:000> ln 7c9232bc (7c923282) ntdll!ExecuteHandler2+0x3a | (7c92330a) ntdll!RtlpUnlinkHandler 0:000> !exchain 0012f9cc: ntdll!ExecuteHandler2+3a (7c9232bc) 0012fe80: offset!ILT+115(__except_handler4)+0 (00411078) 0012ffa8: offset!ILT+115(__except_handler4)+0 (00411078) 0012ffe0: kernel32!_except_handler3+0 (7c839ac0) CRT scope 0, filter: kernel32!BaseProcessStart+29 (7c843882) func: kernel32!BaseProcessStart+3a (7c843898) Invalid exception stack at ffffffff[ebp-0x0c]這個異常處理結構是進入ntdll!ExecuteHandler2后構成的內(nèi)嵌異常處理器 (參見<軟件調(diào)試>P723) 反匯編代碼以下:ntdll!ExecuteHandler2: 7c923282 55 push ebp ;[ebp+0] 7c923283 8bec mov ebp,esp 7c923285 ff750c push dword ptr [ebp+0Ch] ;[ebp⑷] 7c923288 52 push edx ;[ebp⑻] 7c923289 64ff3500000000 push dword ptr fs:[0] ;[ebp-c] 7c923290 64892500000000 mov dword ptr fs:[0],esp上面第4條push指令,將fs:[0]的值重新壓入堆棧,這個值本來指向程序第1個異常處理節(jié)點(前面屢次提到該節(jié)點寄存在棧上,已被溢出覆蓋)。經(jīng)過這次push操作,這個節(jié)點寄存于地址[ebp-c]。jmp [ebp-c]就是跳到棧上異常處理節(jié)點EXCEPTION_REGISTRATION_RECORD!prev地址處。
剩下的指令地址將在下1篇分析