测试函数:
------------------------
使用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异常抛出与处理
最新推荐文章于 2023-04-26 11:36:21 发布