測試函數:
------------------------
使用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;
......
C++異常處理機制__2.SEH異常拋出與處理
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.