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;
......

發佈了59 篇原創文章 · 獲贊 9 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章