SEH中的prolog和epilog

来源:互联网 发布:android开源项目源码 编辑:程序博客网 时间:2024/05/29 12:15
SEH中的prolog和epilog 2011-9-19 15:56阅读(172)

http://user.qzone.qq.com/31731705/blog/1316419000#!app=2&via=QZ.HashRefresh&pos=1316419000

使用SEH的代码都需要构建栈桢,支持exception处理的栈桢,而重复的代码就应当提炼成函数,微软自然不会例外。为此系统提供了prolog和epilog系列,类似的函数有不少版本,但大同小异。这次不用调试器,分析下__SEH_prolog4_GS和__SEH_epilog4_GS 2个函数,看看它们如何工作支持异常的栈桢结构。

先看看prolog,这是内核函数KiDispatchException调用__SEH_prolog4_GS的代码,
000 push 0F8h
004 push offset off_8488DCE0
008 call __SEH_prolog4_GS

调用函数前传递2个参数,一个是需要保留的栈的大小,一个是指针。
再看看函数代码本身,详细的注释在代码后面,
; two instrucitons before call it.
;
; take KiDispatchException as example,
;
; 000 push 0F8h ; it's used as a stack size
; 004 push offset off_8488DCE0 ; it's pointer to scope table entry.
__SEH_prolog4_GS proc near
arg_4= dword ptr 8
000 push offset __except_handler4
004 push large dword ptr fs:0
008 mov eax, [esp+8+arg_4] ; so now there are 5 values in stack.
;
; f8
; 8488dce0
; return address of call
; _except_handler4
; old fs:0
; ; follwing is additional information
; to be pointer to EXCEPTION_POINTERS
; to be esp value
; to be cookie value
008 mov [esp+8+arg_4], ebp ; push current ebp value into place
; where 0f8 is here.
008 lea ebp, [esp+8+arg_4] ; update ebp to current frame pointer
008 sub esp, eax ; reserve stack
008 push ebx
00C push esi
010 push edi
014 mov eax, ___security_cookie
014 xor [ebp-4], eax ; encrypt pointer to scope table entry
014 xor eax, ebp
014 mov [ebp-1Ch], eax ; store cookie value
014 push eax
018 mov [ebp-18h], esp ; store current esp
; middle part
018 push dword ptr [ebp-8] ; push "return address" of current call
; for next ret instruction
01C mov eax, [ebp-4] ; mov [ebp-4] to [ebp-8]
01C mov dword ptr [ebp-4], 0FFFFFFFEh ; initialize try level
01C mov [ebp-8], eax ; so far, the stack is following:
;
; ebp
; -2
; 8488DCE0 xor ___security_cookie
; _except_handler4
; old fs:[0]
; to be pointer to EXCEPTION_POINTERS
; current esp value
; cookie value (___security_cookie xor ebp)
; ...
; ebx
; esi
; edi
; cookie value
; return address to parent function
01C lea eax, [ebp-10h]
01C mov large fs:0, eax ; update fs:[0] to current frame
01C retn ; return to parent function.

由于注释比较详细,就不多说什么了,补充几张图。
函数开始执行2条push指令后的栈,
图片
执行到中间处的栈如下,注意数字顺序。
图片
执行到最后retn之前的栈,此时(ebp-10)处已经构建好了异常栈桢结构,(ebp-18)(ebp-1c)处也赋了相应的值。

图片

接下来分析epilog,先看看KiDispatchException如何调用它的,
; why is there 118?
11C lea esp, [ebp-118h] ; sizeof exception_frame + F8(includes additional information) + sizeof( ebx + esi + edi + cookie )
; ebp - (10+F8+10)
000 call __SEH_epilog4_GS
000 retn 14h

将栈指针修正到合适的位置,实际上就是__SEH_prolog4_GS函数返回后的栈的位置。然后调用函数__SEH_epilog4_GS,注意call指令会将返回地址压栈。
再看看函数的实现部分,
__SEH_epilog4_GS proc near
000 mov ecx, [ebp-1Ch] ; cookie value (___security_cookie xor ebp)
000 xor ecx, ebp
000 call @__security_check_cookie@4 ;
; cmp ecx, ___security_cookie
; jnz ___report_gsfailure
; retn
000 jmp __SEH_epilog4
__SEH_epilog4_GS endp

检查cookie有没有被破坏,如果没有问题就跳转到__SEH_epilog4。
__SEH_epilog4 proc near
000 mov ecx, [ebp-10h] ; restore old fs:[0]
000 mov large fs:0, ecx
000 pop ecx ; return address of parent function
-04 pop edi ; another cookie value besides [ebp-1c]
-08 pop edi
-0C pop esi
-10 pop ebx
-14 mov esp, ebp ; restore esp and ebp
000 pop ebp
-04 push ecx
000 retn
__SEH_epilog4 endp

这个函数比prolog简单的多了,恢复fs:[0],恢复寄存器,返回到调用__SEH_epilog4_GS的地方。
对这2个函数的分析最终演变成了对栈中数据结构的理解,这也让我想起了<人月神话>中的一句话。"给我看你的流程图,而藏起你的表,我仍然莫名其妙,给我看你的表藏起流程图,它们是如此的明显。 "这里的“表”指的就是数据结构,它最终决定了代码的实现。

原创粉丝点击