看到这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脸迷茫。迷茫很久我决定还调试1遍视察进程栈布局并把调试的结果记录于此。示例代码就用作者书本上的那个,这里就不重复贴代码了。
进入__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个异常块的内容 0012fe80 0012ffa8 00411078在这个基础上,当异常产生并且OS接收异常后,经过1途经关斩将终究会进入函数ntdll!ExecuteHandler4
0:000> x *!__except_handler4 ;查找异常处理函数ExecuteHandler4所在模块 00411840 offset!_except_handler4 = <no type information> 102d3280 MSVCR90D!_except_handler4 = <no type information> 0:000> bp offset!_except_handler4 ;为了方便视察异常产生时,OS调用异常处理函数ExecuteHandler4的栈回溯,需要在这个函数上下断点 0:000> g ;触发除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调用异常处理函数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]根据栈回溯信息,我们可以肯定offset!_except_handler4是由ntdll!ExecuteHandler2调用的,而异常处理函数的接口情势是固定的,形如:
EXCEPTION_DISPOSITION ExcuteHandler(PEXCEPTION_RECORD,PEXCEPTION_REGISTRATION,struct _CONTEXT*,void*);既然ntdll!ExecuteHandler2负责调用offset!_except_handler4,那末它也必须负责为offset!_except_handler4传递参数。代码片断摘取了部份ntdll!ExecuteHandler2实现,虽然是汇编代码,但还是请读者耐心的往下看下去:
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箭头指向处出现了作者提到的第1个跳板指令的跳转目标:[ebp+0x0c]。固然,看了这段代码,你其实不能肯定最后1行call ecx就是调用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在数值上差0x20.这个差值很重要,为何这么说?如果你仔细视察作者提到的指令地址就会发现
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类指令上,比如,后面的章节我将集中分析[ebp+N]系列的指令。
目前为止,溢出后能利用SEH的缘由是由于异常处理结构EXCEPTION_REGISTRATION_RECORD寄存在retAddr前面(地址比retAddr更低)的栈地址。buf溢出后,shellcode散布在从变量地址到retAddr附近的栈空间上,定位这段空间相对茫茫4G进程空间而言几近是海底捞针。不过这其实不代表没有办法定位,办法就是利用异常处理进程中传递的
EXCEPTION_REGISTRATION_RECORD变量。如前所述,EXCEPTION_REGISTRATION_RECORD结构中包括了触发异常时,当前栈上的异常处理结构:
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函数地址时,毛病的将[ebp+N]的地址当作offset!_except_handler4的地址。然后跳转到EXCEPTION_REGISTRATION_RECORD!Next继续取指令履行,固然,取到的是1个短跳转语句。
0:000> kb ;查看堆栈,程序当前进入函数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 ;查看当前履行到函数中哪条指令和函数反汇编 eip=00411840 0:000> uf . offset!_except_handler4: 00411840 8bff mov edi,edi 00411842 55 push ebp ;上面的结果告知我们,虽然进入函数offset!_except_handler4,但ebp没有变动依然保持它在ntdll!ExecuteHandler2中的值, ;这和产生溢出后异常处理调用被溢出的异常处理函数而进入到单指令函数call [ebp+N]时的情形是1致的 0:000> r ebpebp=0012f9d8 0:000> ?? @ebp-0xc ;[ebp-0x0c]指向的地址 unsigned int 0x12f9cc 0:000> dd 0x12f9cc L2 ;这个地址包括的内容是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后构成的内嵌异常处理器 (参见<软件调试>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个异常处理节点(前面屡次提到该节点寄存在栈上,已被溢出覆盖)。经过这次push操作,这个节点寄存于地址[ebp-c]。jmp [ebp-c]就是跳到栈上异常处理节点EXCEPTION_REGISTRATION_RECORD!prev地址处。
剩下的指令地址将在下1篇分析
上一篇 Java动态代理