我第一次知道SEH是在学习缓冲区溢出时,就知道这是windows的一个异常处理机制。SEH是基于线程的,使用SEH可以为每个线程设置不同的异常处理程序,而且可以为每个线程设置多个异常处理程序;SEH是硬件平台相关的,因为使用了与硬件平台相关的数据指针。
win32为每个线程定义了一个线程信息块(TIB),其中保存了线程的一些属性数据,TIB的格式被定义为NT_TIB结构:
NT_TIB STRUCT
ExceptionList dd ? ;SEH链入口
StackBase dd ? ;堆栈基址
StackLimit dd ? ;堆栈大小
SubSystemTib dd ?
FiberData dd ?
ArbitraryUserPointter dd ?
Self dd ? ;本NT_TIB结构的自身的线性地址
NT_TIB ENDS
该结构的第一个字段ExceptionList指向一个EXCEPTION_REGISTRATION结构,SEH异常处理回调函数的入口地址由该结构指定,这个结构的定义如下:
EXCEPTION_REGISTRATION STRUCT
prev dd ? ;前一个EXCEPTION_REGISTRATION结构的地址
handler dd ? ;异常处理回调函数地址
EXCEPTION_REGISTRATION ENDS
当异常发生时,系统从TIB中取出ExceptionList字段,然后从该字段指定的EXCEPTION_REGISTRATION结构中取出hanlder字段,并根据其中的地址去调用回调函数。所以,如果要自定义一个异常处理程序的话,只要构建一个新的含有ExceptionList字段,将其指向这个结构就可以注册一个SEH异常处理回调函数了。而TIB永远都放在fs段选择器指定的数据段的0偏移处,fs:[0]的地方就是TIB结构的Exception字段。可以使用以下的代码在堆栈中构造一个EXCEPTION_REGISTRATION结构,设置新的异常处理回调函数。
assume fs:nothing
push offset _Handler
push fs:[0]
mov fs:[0],esp
第一条语句将回调函数的地址入栈;第二条语句将原先使用的EXCEPTION_REGISTRATION结构入栈,现在esp指向的地方刚好是一个新的EXCEPTION_REGISTRATION结构---[esp]等于原结构地址,就是prev字段,而[esp+4]等于回调函数地址,就是handler字段,第三条指令将esp的值放入fs:[0]中,设置工作完成。由于MASM编译器默认将fs段寄存器定义为error,所有在使用前要使用assume fs:nothing伪指令来启用该寄存器。
SEH的注册回调函数
最新推荐文章于 2025-01-09 21:13:01 发布