在ring3时候,fs段指向的是TEB。
以前一直有个误解,fs:[0]和fs:[1]记录着EXCEPTION_REGISTRATION
其实不然,而是fs:[0]记录着EXCEPTION_REGISTRATION的指针。
2.数据结构,它可以形成一个链表,这个链表的首地址记录在fs:[0],其中handle是异常处理函数。
_EXCEPTION_REGISTRATION struc
prev dd ?
handler dd ?
_EXCEPTION_REGISTRATION ends
3.异常处理函数会执行两遍。
第一次用来系统通过上述链表,找到确切这个异常的处理函数。
第二次是unwind,用来做一些回收相关的操作。例如,C++的destructor,执行_finally块。
执行完后,系统将恢复到当初出现错误的状态,删除没用的EXCEPTION_REGISTRATION链。
4._try _except嵌套块只使用一个EXCEPTION_REGISTRATION,而没有使用多个
5.vc的编译器将EXCEPTION_REGISTRATION数据结构扩展,使得可以支持_try _excetp
;struct _EXCEPTION_REGISTRATION{
; struct _EXCEPTION_REGISTRATION *prev;
; void (*handler)(PEXCEPTION_RECORD,
; PEXCEPTION_REGISTRATION,
; PCONTEXT,
; PEXCEPTION_RECORD);
; struct scopetable_entry *scopetable;
; int trylevel;
; int _ebp;
; PEXCEPTION_POINTERS xpointers;
;};
6.
有_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
这里解释一下Standard ESP in frame的作用。
保存旧的EBP后,然后把ESP=>EBP,这样就可以用EBP访问堆栈中的局部变量。
函数一般用ADD ESP ,(负数)来开辟堆栈中的局部变量。
这时候如果再push 或者pop则是在stack的上方。这个时候的ESP需要被记住,如果出现异常的时候需要恢复这个值。它被记在EBP-18的位置。
例如:
#include <stdio.h>
#include <windows.h>
int main(int argc, char *argv[])
{
_try
{
*((PUCHAR)NULL)=0;
}_except(1)//这里需要恢复esp的数值
{
}
return 0;
}