异常链
一个进程中可以有多个线程,进程Id可作为线程表的外键。为了快速查询一个进程中的线程信息,可以在进程信息中加入线程表的地址。
线程环境块TEB:Thread Environment Block
线程信息块TIB:Thread Information Block
TEB结构起始处保存了TIB,TIB的第一个字段ExceptionList是SEH链表的表头地址。X86系统中,FS总是指向TEB/TIB,所以FS:[0]总是指向SEH链表的表头。
ExceptionList指向EXCEPTION_REGISTRATION_RECORD结构:
SEH处理函数的原型:
EXCEPTION_DISPOSITION __cdecl _except_handler(
_In_ struct _EXCEPTION_RECORD* _ExceptionRecord,//要处理的异常
_In_ void* _EstablisherFrame, //栈帧中的异常登记结构
_Inout_ struct _CONTEXT* _ContextRecord, //异常时线程上下文
_Inout_ void* _DispatcherContext//额外信息
);
登记异常处理:
Push sehHandler ;处理函数的地址
Push FS:[0] ;前一个EXCEPTION_REGISTRATION_RECORD地址
Mov FS:[0], esp ;上两句push形成了EXCEPTION_REGISTRATION_RECORD结构,将结构地址赋值给FS:[0],即ExceptionList,以后发生异常时去FS:[0]获取sehHandler。
此时栈结构:
FS:[0] |
前一个结构地址 |
sehHandler |
当前handler |
注销异常处理:
Pop FS:[0] ;前一个结构地址给FS:[0],相当于还原了
Add esp, 4
异常展开:
从头节点(fs:[0])开始,fs:[0] -> fun1_seh -> main_seh -> startup_seh -> -1,当发生异常时,fun1_seh如果不能处理,先发送展开通知,便于异常处理执行清理工作,再卸载fun1_seh(看需要是否也卸载main_seh),最后更新下一个异常处理,例如startup_seh到fs:[0]。
try{//进入try时增加fs:[0]节点
}//离开try时删除fs:[0]节点
异常处理顺序:SEH -> Filter -> OS,这3步中每次都优先问调试器是否要处理,OS处理一般是直接关闭程序,关闭程序前还会问一次试器是否要处理。
Debugger -> SEH -> Debugger -> Filter -> Debugger -> OS -> Debugger