SEH分析

//__except_handler3函数原型
 int __except_handler3(
     struct _EXCEPTION_RECORD * pExceptionRecord,    //ebp+8
     struct EXCEPTION_REGISTRATION * pRegistrationFrame,//ebp+C   
     struct _CONTEXT *pContextRecord,            //ebp+10
     void * pDispatcherContext )            //ebp+14

__except_handler3:
00403EC4   push        ebp
00403EC5   mov         ebp,esp
00403EC7   sub         esp,8                ;构建堆栈
00403ECA   push        ebx
00403ECB   push        esi
00403ECC   push        edi
00403ECD   push        ebp
00403ECE   cld                        ;edi,esi自增
00403ECF   mov         ebx,dword ptr [ebp+0Ch]        ;pRegistrationFrame=>ebx
00403ED2   mov         eax,dword ptr [ebp+8]        ;pExceptionRecord=>eax
00403ED5   test        dword ptr [eax+4],6        ;比较pExceptionRecord->ExceptionFlags 和 EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND
00403EDC   jne         __except_handler3+0A0h (00403f64);若是unwind则跳
00403EE2   mov         dword ptr [ebp-8],eax        ;ebp-8是EXCEPTION_POINTERS的数据结构,pExceptionRecord=>EXCEPTION_POINTERS.ExceptionRecord
00403EE5   mov         eax,dword ptr [ebp+10h]        ;pContextRecord
00403EE8   mov         dword ptr [ebp-4],eax        ;pContextRecord=>EXCEPTION_POINTERS.ContextRecord
00403EEB   lea         eax,[ebp-8]            ;得到EXCEPTION_POINTERS的指针
00403EEE   mov         dword ptr [ebx-4],eax        ;当时发生异常那个函数的ebp-14 = &EXCEPTION_POINTERS
00403EF1   mov         esi,dword ptr [ebx+0Ch]        ;trylevel
00403EF4   mov         edi,dword ptr [ebx+8]        ;scopetable pointer
00403EF7   cmp         esi,0FFh                ;判断trylevel是否为-1
00403EFA   je          __except_handler3+99h (00403f5d)    ;if trylevel==-1 则跳
00403EFC   lea         ecx,[esi+esi*2]            ;ecx = trylevel*trylevel
00403EFF   cmp         dword ptr [edi+ecx*4+4],0    ;判断lpfnFilter是否为0
00403F04   je          __except_handler3+87h (00403f4b)    ;没有lpfnHandler
00403F06   push        esi                ;
00403F07   push        ebp                ;
00403F08   lea         ebp,[ebx+10h]            ;恢复ebp在发生异常时候的装样子,即_ebp
00403F0B   call        dword ptr [edi+ecx*4+4]        ;调用lpfnFilter
00403F0F   pop         ebp
00403F10   pop         esi
00403F11   mov         ebx,dword ptr [ebp+0Ch]
00403F14   or          eax,eax                ;
00403F16   je          __except_handler3+87h (00403f4b)    ;=0(EXCEPTION_CONTINUE_SEARCH),则跳
00403F18   js          __except_handler3+92h (00403f56)    ;<0(EXCEPTION_CONTINUE_EXECUTION),则跳
00403F1A   mov         edi,dword ptr [ebx+8]        ;>0(EXCEPTION_EXECUTE_HANDLER),应该清理以前的所有EXCEPTION_REGISTRATION
00403F1D   push        ebx
00403F1E   call        __global_unwind2 (00403dcc)
00403F23   add         esp,4
00403F26   lea         ebp,[ebx+10h]
00403F29   push        esi
00403F2A   push        ebx
00403F2B   call        __local_unwind2 (00403e0e)
00403F30   add         esp,8
00403F33   lea         ecx,[esi+esi*2]
00403F36   push        1
00403F38   mov         eax,dword ptr [edi+ecx*4+8]
00403F3C   call        __NLG_Notify (00403ea2)
00403F41   mov         eax,dword ptr [edi+ecx*4]
00403F44   mov         dword ptr [ebx+0Ch],eax
00403F47   call        dword ptr [edi+ecx*4+8]        ;调用lpfnHandler
00403F4B   mov         edi,dword ptr [ebx+8]        ;
00403F4E   lea         ecx,[esi+esi*2]
00403F51   mov         esi,dword ptr [edi+ecx*4]    ;esi = 数组某项的previousTryLevel
00403F54   jmp         __except_handler3+33h (00403ef7)
00403F56   mov         eax,0                ;返回ExceptionContinueExecution
00403F5B   jmp         __except_handler3+0B5h (00403f79)
00403F5D   mov         eax,1                ;返回ExceptionContinueSearch
00403F62   jmp         __except_handler3+0B5h (00403f79)
00403F64   push        ebp
00403F65   lea         ebp,[ebx+10h]
00403F68   push        0FFh
00403F6A   push        ebx
00403F6B   call        __local_unwind2 (00403e0e)
00403F70   add         esp,8
00403F73   pop         ebp
00403F74   mov         eax,1
00403F79   pop         ebp
00403F7A   pop         edi
00403F7B   pop         esi
00403F7C   pop         ebx
00403F7D   mov         esp,ebp
00403F7F   pop         ebp
00403F80   ret

在_try块外面那层trylevel为-1
在最外层的trylevel为0
在次外层的trylevel为1
另外,如果一个函数有两个并列_try块,如
Foo()
{
  _try{...}_except(){...}//这两个trylevel的也不同,不一定是里外层的trylevel不同
  _try{...}_except(){...}//
}

有_try块的函数的堆栈情况

 EBP-00 _ebp
 EBP-04 trylevel
 EBP-08 scopetable pointer
 EBP-0C handler function address
 EBP-10 previous EXCEPTION_REGISTRATION
 EBP-14 GetExceptionPointers
 EBP-18 Standard ESP in frame


scopetable pointer数据结构
 typedef struct _SCOPETABLE
 {
     DWORD       previousTryLevel;
     DWORD       lpfnFilter
     DWORD       lpfnHandler
 } SCOPETABLE, *PSCOPETABLE;

 异常处理函数可能返回的值
 /*
 * Exception disposition return values.
 */
typedef enum _EXCEPTION_DISPOSITION {
    ExceptionContinueExecution,
    ExceptionContinueSearch,
    ExceptionNestedException,
    ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;




#include <stdio.h>
#include <windows.h>
int main(int argc, char *argv[])
{
    _try
    {
        _try
        {
            *((PUCHAR)NULL)=0;

        }_except(0)
        {

        }


    }_except(1)
    {

    }
    return 0;
}







void foo()
{
00401080  push        ebp 
00401081  mov         ebp,esp
00401083  push        0FFFFFFFFh
00401085  push        40C110h
0040108A  push        offset __except_handler3 (4012F0h)
0040108F  mov         eax,dword ptr fs:[00000000h]
00401095  push        eax 
00401096  mov         dword ptr fs:[0],esp
0040109D  add         esp,0FFFFFFB8h
004010A0  push        ebx 
004010A1  push        esi 
004010A2  push        edi 
004010A3  mov         dword ptr [ebp-18h],esp            ;backup the origin esp
004010A6  lea         edi,[ebp-58h]
004010A9  mov         ecx,10h
004010AE  mov         eax,0CCCCCCCCh
004010B3  rep stos    dword ptr es:[edi]
    _try
004010B5  mov         dword ptr [ebp-4],0            ;trylevel=0
    {
        exception_fun();
004010BC  call        exception_fun (401000h)
    }_except(1)
004010C1  mov         dword ptr [ebp-4],0FFFFFFFFh        ;trylevel=-1
004010C8  jmp         $L579+17h (4010E7h)
$L578:
004010CA  mov         eax,1                    ;handle exception
$L580:
004010CF  ret             
$L579:
004010D0  mov         esp,dword ptr [ebp-18h]            ;restore origin esp   
    {
        printf("In _except!\n");
004010D3  push        offset string "In _except!\n" (40C0ECh)
004010D8  call        printf (401140h)
004010DD  add         esp,4
    }
004010E0  mov         dword ptr [ebp-4],0FFFFFFFFh
}
004010E7  mov         ecx,dword ptr [ebp-10h]            ;restore orgin exception registration
004010EA  mov         dword ptr fs:[0],ecx
004010F1  pop         edi 
004010F2  pop         esi 
004010F3  pop         ebx 
004010F4  add         esp,58h
004010F7  cmp         ebp,esp
004010F9  call        __chkesp (4011C0h)
004010FE  mov         esp,ebp
00401100  pop         ebp 
00401101  ret             


__except_handler3:
004012F0  push        ebp 
004012F1  mov         ebp,esp
004012F3  sub         esp,8                    ;构建堆栈
004012F6  push        ebx 
004012F7  push        esi 
004012F8  push        edi 
004012F9  push        ebp 
004012FA  cld                            ;edi,esi自增
004012FB  mov         ebx,dword ptr [ebp+0Ch]            ;ebx=pRegistrationFrame,ebx相当于发生异常的函数的ebp-10
004012FE  mov         eax,dword ptr [ebp+8]            ;eax=pExceptionRecord
00401301  test        dword ptr [eax+4],6            ;比较pExceptionRecord->ExceptionFlags 和 EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND
00401308  jne         __except_handler3+0A0h (401390h)        ;若是unwind则跳
0040130E  mov         dword ptr [ebp-8],eax            ;ebp-8是EXCEPTION_POINTERS的数据结
00401311  mov         eax,dword ptr [ebp+10h]            ;pContextRecord
00401314  mov         dword ptr [ebp-4],eax            ;pContextRecord=>EXCEPTION_POINTERS.ContextRecord
00401317  lea         eax,[ebp-8]                ;得到EXCEPTION_POINTERS的指针
0040131A  mov         dword ptr [ebx-4],eax            ;相当于发生异常的函数的ebp-14,即Exception pointers
0040131D  mov         esi,dword ptr [ebx+0Ch]            ;相当于发生异常的函数的ebp-4,即trylevel
00401320  mov         edi,dword ptr [ebx+8]            ;相当于发生异常的函数的ebp-4,即scopetable pointer
00401323  cmp         esi,0FFFFFFFFh                ;判断trylevel是否为-1
00401326  je          __except_handler3+99h (401389h)        ;if trylevel==-1 则跳
00401328  lea         ecx,[esi+esi*2]                ;ecx = trylevel*trylevel
0040132B  cmp         dword ptr [edi+ecx*4+4],0            ;判断lpfnFilter是否为0
00401330  je          __except_handler3+87h (401377h)        ;没有lpfnHandler
00401332  push        esi 
00401333  push        ebp 
00401334  lea         ebp,[ebx+10h]                ;相当于发生异常的函数的ebp-0,即_ebp(注意:这里用的是lea,而不是mov。原因_ebp的值没用,而是想使用_ebp的地址。_ebp的地址是一个基准,即ebp)
00401337  call        dword ptr [edi+ecx*4+4]            ;调用lpfnFilter
0040133B  pop         ebp 
0040133C  pop         esi 
0040133D  mov         ebx,dword ptr [ebp+0Ch]
00401340  or          eax,eax
00401342  je          __except_handler3+87h (401377h)        ;=0(EXCEPTION_CONTINUE_SEARCH),则跳
00401344  js          __except_handler3+92h (401382h)        ;<0(EXCEPTION_CONTINUE_EXECUTION),则跳
00401346  mov         edi,dword ptr [ebx+8]            ;>0(EXCEPTION_EXECUTE_HANDLER),应该清理以前的所有EXCEPTION_REGISTRATION
00401349  push        ebx 
0040134A  call        __global_unwind2 (4011F8h)
0040134F  add         esp,4
00401352  lea         ebp,[ebx+10h]
00401355  push        esi 
00401356  push        ebx 
00401357  call        __local_unwind2 (40123Ah)
0040135C  add         esp,8
0040135F  lea         ecx,[esi+esi*2]
00401362  push        1   
00401364  mov         eax,dword ptr [edi+ecx*4+8]
00401368  call        __NLG_Notify (4012CEh)
0040136D  mov         eax,dword ptr [edi+ecx*4]
00401370  mov         dword ptr [ebx+0Ch],eax
00401373  call        dword ptr [edi+ecx*4+8]            ;调用lpfnHandler
00401377  mov         edi,dword ptr [ebx+8]
0040137A  lea         ecx,[esi+esi*2]
0040137D  mov         esi,dword ptr [edi+ecx*4]            ;esi = 数组某项的previousTryLevel
00401380  jmp         __except_handler3+33h (401323h)
00401382  mov         eax,0                    ;返回ExceptionContinueExecution
00401387  jmp         __except_handler3+0B5h (4013A5h)
00401389  mov         eax,1                    ;返回ExceptionContinueSearch
0040138E  jmp         __except_handler3+0B5h (4013A5h)
00401390  push        ebp 
00401391  lea         ebp,[ebx+10h]
00401394  push        0FFFFFFFFh
00401396  push        ebx 
00401397  call        __local_unwind2 (40123Ah)
0040139C  add         esp,8
0040139F  pop         ebp 
004013A0  mov         eax,1
004013A5  pop         ebp 
004013A6  pop         edi 
004013A7  pop         esi 
004013A8  pop         ebx 
004013A9  mov         esp,ebp
004013AB  pop         ebp 
004013AC  ret             

SEH小结
1.SEH全程是(Structured Exception Handling),它是操作系统提供的错误处理机制。
操作系统定义数据结构EXCEPTION_REGISTRATION,对于每个线程都有一个TEB(存在于FS段基地址开始的一段空间),而EXCEPTION_REGISTRATION的指针存放在FS:[0]
 struct EXCEPTION_REGISTRATION
 {
     EXCEPTION_REGISTRATION* prev;
     FARPROC                 handler;
 };
EXCEPTION_REGISTRATION会形成一个链表,当发生错误时,操作系统枚举链表,依次执行EXCEPTION_REGISTRATION中的handler,如果handler能处理这个错误则处理,否则寻找下一个handler.
如果找到相应的handler,操作系统还要负责unwind。unwind会将链表中前面的handler都执行一遍,unwind的作用是执行一些扫尾工作。

2._try块机制,是VC的编译器加入的机制。
VC扩展了EXCEPTION_REGISTRATION数据结构,即VC_EXCEPTION_REGISTRATION
 struct VC_EXCEPTION_REGISTRATION : EXCEPTION_REGISTRATION
 {
     scopetable_entry *  scopetable;
     int                 trylevel;//trylevel是scopetable数组的index
     int                 _ebp;
 };
 struct scopetable_entry
 {
     DWORD       previousTryLevel;
     FARPROC     lpfnFilter;//_excetp小括号中的代码部分
     FARPROC     lpfnHandler;//_excetp大括号中的代码部分
 };

编译器在希望处理错误的函数中,在链表中插入VC_EXCEPTION_REGISTRATION。handler指向__except_handler3.
VC将操作系统负责unwind工作,自己实现。这是通过调用__local_unwind2和 __global_unwind2实现的。

3.为什么要unwind
考虑下面程序
#include <stdio.h>
#include <excpt.h>
void exception_fun()
{
    _try
    {
        _asm int 1;
    }_except(0)
    {
    }
}
void foo()
{
    _try
    {
        exception_fun();
    }_except(1)
    {
        printf("In _except!\n");
    }
}
int main(int argc, char *argv[])
{
    foo();
    return 0;
}

exception_fun函数并没有处理异常,它的异常在foo中处理。但是在exception_fun中的异常处理相关结构,还存在链表里。这时候需要将exception_fun中的异常处理部分从链表中摘除。
__global_unwind2负责模拟操作系统的unwind,再次调用exception_fun函数里的异常处理函数。

pixy.gif?x-id=846e7d41-6f8e-45a0-bdea-20483efcef8e

转载于:https://www.cnblogs.com/fanzi2009/archive/2009/03/19/1416360.html

猜你喜欢

转载自blog.csdn.net/weixin_34205076/article/details/94192501