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函数里的异常处理函数。