国内最全IT社区平台 联系我们 | 收藏本站
阿里云优惠2流量王
您当前位置:首页 > php开源 > 综合技术 > <Oday安全 11.6利用加载模块之外的地址绕过SafeSEH>一节注记---jmp [ebp+N] (上)

<Oday安全 11.6利用加载模块之外的地址绕过SafeSEH>一节注记---jmp [ebp+N] (上)

来源:程序员人生   发布时间:2017-03-10 09:33:59 阅读次数:1107次

    看到这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.这个差值很重要,为何这么说?如果你仔细视察作者提到的指令地址就会发现
1个[ebp+N-0x20]=[esp+M],换句话说,两个指令指向的地址是同1个,只是用了不同的寄存器做索引,因而,我们有了这样的对应关系:

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个短跳转语句。
    大概的流程读者可能知道了,但是我知道依然有许多未解的疑惑----那就是[ebp+N]代表啥?哎,这是难得部份。先看[ebp+N]这类表达方式是否是很眼熟?对,这些语句就是用作取局部变量/函数参数。先看看第1个[ebp-0x0c]:

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 ebp
ebp=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篇分析

生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生