C++异常处理机制__2.SEH异常抛出与处理

测试函数:


------------------------
使用VS2005, Debug模式,在__except(EXCEPTION_EXECUTE_HANDLER)下的第一行语句打上断点

void SEH_test2()
{......
    int *p1 = NULL;
004396D8  mov         dword ptr [ebp-20h],0
    __try
004396DF  mov         dword ptr [ebp-4],0
    {
        *p1 = 10;
004396E6  mov         eax,dword ptr [ebp-20h]
004396E9  mov         dword ptr [eax],0Ah
    }
004396EF  mov         dword ptr [ebp-4],0FFFFFFFEh
004396F6  jmp         $LN6+35h (439733h)
    __except(EXCEPTION_EXECUTE_HANDLER)
004396F8  mov         eax,1                         <== 断点,跑到这里
$LN7:
004396FD  ret             
$LN6:
004396FE  mov         esp,dword ptr [ebp-18h]
    {
        cout << "SHE_test2::__except" << endl;
......


当跑到这里时,函数的调用栈如下:

>    Cpp2Assember.exe!SEH_test2()  Line 63    C++
     msvcr80d.dll!@_EH4_CallFilterFunc@8()  + 0x12 bytes    Asm
     msvcr80d.dll!_except_handler4_common(unsigned int * CookiePointer=0x004595a0, void (unsigned int)* CookieCheckFunction=0x00418258, _EXCEPTION_RECORD * ExceptionRecord=0x0012f8d0, _EXCEPTION_REGISTRATION_RECORD * EstablisherFrame=0x0012fc9c, _CONTEXT * ContextRecord=0x0012f8ec, void * DispatcherContext=0x0012f8a4)  + 0xba bytes    C
     Cpp2Assember.exe!_except_handler4(_EXCEPTION_RECORD * ExceptionRecord=0x0012f8d0, _EXCEPTION_REGISTRATION_RECORD * EstablisherFrame=0x0012fc9c, _CONTEXT * ContextRecord=0x0012f8ec, void * DispatcherContext=0x0012f8a4)  + 0x22 bytes    C
     ntdll.dll!7c9032a8()    
     [Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]   
     ntdll.dll!7c90327a()    
     ntdll.dll!7c92aa0f()    
     oleaut32.dll!77121fd6()    
     oleaut32.dll!77121ee6()    
     oleaut32.dll!77121e97()    
     oleaut32.dll!77121631()    
     ntdll.dll!7c90e48a()    
     ntdll.dll!7c912d04()    
     ntdll.dll!7c912d71()    
     ntdll.dll!7c9168d6()    
     ntdll.dll!7c912d04()    
     ntdll.dll!7c912d71()    
     ntdll.dll!7c912d78()    
     ntdll.dll!7c9168d6()    
     ntdll.dll!7c90fcb7()    
     ntdll.dll!7c90fdc2()    
     ntdll.dll!7c90fddd()    
     ntdll.dll!7c90daea()    
     ntdll.dll!7c912de8()    
     kernel32.dll!7c810644()    
     ntdll.dll!7c90db4a()    
     kernel32.dll!7c810687()    
     kernel32.dll!7c8106a3()    
     kernel32.dll!7c80e544()    
     kernel32.dll!7c80e64b()    
     Cpp2Assember.exe!SEH_test1()  Line 46    C++
     Cpp2Assember.exe!SEH_test()  Line 28    C++
     Cpp2Assember.exe!main(int argc=1, char * * argv=0x003e87f0)  Line 38    C++
     Cpp2Assember.exe!__tmainCRTStartup()  Line 586 + 0x19 bytes    C
     Cpp2Assember.exe!mainCRTStartup()  Line 403    C
     kernel32.dll!7c817077()    

在运行到 *p1 = 10; 时,由于p1 指向 NULL,这是一个不可写地址,所以CPU会抛出一个异常,然后经过一系列的系统调用,最终调到这个函数:
Cpp2Assember.exe!_except_handler4(......)  + 0x22 bytes    C
这个_except_handler4 就是在函数栈头所存入的异常处理函数
0040201A  push        offset _except_handler4 (401BC5h)

------------------------
察看_except_handler4, 发现它调用一个叫 __except_handler4_common 的函数

_except_handler4:
0043F720  push        ebp 
0043F721  mov         ebp,esp
0043F723  mov         eax,dword ptr [DispatcherContext]
0043F726  push        eax 
0043F727  mov         ecx,dword ptr [ContextRecord]
0043F72A  push        ecx 
0043F72B  mov         edx,dword ptr [EstablisherFrame]
0043F72E  push        edx 
0043F72F  mov         eax,dword ptr [ExceptionRecord]
0043F732  push        eax 
0043F733  push        offset @ILT+595(@__security_check_cookie@4) (418258h)
0043F738  push        offset ___security_cookie (4595A0h)
0043F73D  call        @ILT+4920(__except_handler4_common) (41933Dh)    <== 这里调到 __except_handler4_common

------------------------
在 _except_handler4_common 里,调了一系列的函数,其中有一个叫 @_EH4_CallFilterFunc@8 的

_except_handler4_common:
......
10219E0D  call        ValidateLocalCookies (10219F90h)
10219E85  call        @_EH4_CallFilterFunc@8 (1021DA32h)                <== 这里最终调到__except(EXCEPTION_EXECUTE_HANDLER) 处
10219ECB  call        _IsNonwritableInCurrentImage (1021DB80h)
10219EDD  call        dword ptr [__pDestructExceptionObject (102D8EECh)]
10219EEC  call        @_EH4_GlobalUnwind@4 (1021DA62h)
10219F0D  call        @_EH4_LocalUnwind@16 (1021DA7Ch)
10219F27  call        ValidateLocalCookies (10219F90h)
10219F38  call        @_EH4_TransferToHandler@8 (1021DA49h)
10219F60  call        @_EH4_LocalUnwind@16 (1021DA7Ch)
10219F7D  call        ValidateLocalCookies (10219F90h)
10219F8B  ret              

------------------------
在调用 @_EH4_CallFilterFunc@8 前,编译器将[FilterFunc]的地址插入到ECX中
10219E82  mov         ecx,dword ptr [FilterFunc ]
10219E85  call        @_EH4_CallFilterFunc@8 (1021DA32h)

然后 @_EH4_CallFilterFunc@8 调用ECX中指向的地址
@_EH4_CallFilterFunc@8:
1021DA32  push        ebp 
1021DA33  push        esi 
1021DA34  push        edi 
1021DA35  push        ebx 
1021DA36  mov         ebp,edx
1021DA38  xor         eax,eax
1021DA3A  xor         ebx,ebx
1021DA3C  xor         edx,edx
1021DA3E  xor         esi,esi
1021DA40  xor         edi,edi
1021DA42  call        ecx          <== ecx=ptr [FilterFunc]; 调到__except(EXCEPTION_EXECUTE_HANDLER) 处
1021DA44  pop         ebx 
1021DA45  pop         edi 
1021DA46  pop         esi 
1021DA47  pop         ebp 
1021DA48  ret

上面就是异常过滤(__except)的过程。


------------------------
在 __except 中,根据内部参数的不同,给EAX存入不同的值。(一般函数在返回时将返回值填入EAX中)
当为 EXCEPTION_EXECUTE_HANDLER 时,返回值是1

    __except(EXCEPTION_EXECUTE_HANDLER)
004396F8  mov         eax,1
$LN7:
004396FD  ret 

前面说了,在 _except_handler4_common 里,调了一系列的函数,其中还有一个叫 @_EH4_TransferToHandler@8 的
在调他前,会把 SEH_test2 的 ScopeTableRecord 存入 EAX 中,而其中的 HandlerAddress/FinallyFunc 地址[eax+8]存入 ECX
(这里就是 SEH_test2 内的 __except(EXCEPTION_EXECUTE_HANDLER){...} 处 004396FE)

10219F2C  add         esp,0Ch
10219F2F  mov         edx,dword ptr [FramePointer]
10219F32  mov         eax,dword ptr [ScopeTableRecord]        <==
10219F35  mov         ecx,dword ptr [eax+8]                   <== __except(EXCEPTION_EXECUTE_HANDLER){...} 处
10219F38  call        @_EH4_TransferToHandler@8 (1021DA49h)

在看看 @_EH4_TransferToHandler@8 的实现
@_EH4_TransferToHandler@8:
1021DA49  mov         ebp,edx
1021DA4B  mov         esi,ecx                  <== ESI = ECX = __except(EXCEPTION_EXECUTE_HANDLER){...}
1021DA4D  mov         eax,ecx
1021DA4F  push        1   
1021DA51  call        _NLG_Notify (1021DDA5h)
1021DA56  xor         eax,eax
1021DA58  xor         ebx,ebx
1021DA5A  xor         ecx,ecx
1021DA5C  xor         edx,edx
1021DA5E  xor         edi,edi
1021DA60  jmp         esi                     <== 跳到 __except(EXCEPTION_EXECUTE_HANDLER){...}

可见指令从这里跳到了 __except 的处理处

    __except(EXCEPTION_EXECUTE_HANDLER)
004396F8  mov         eax,1
$LN7:
004396FD  ret             
$LN6:
004396FE  mov         esp,dword ptr [ebp-18h]  <== 跳到这里了
    {
        cout << "SHE_test2::__except" << endl;
......

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值