admin 管理员组文章数量: 1184232
SEH结构体异常处理
异常的主要结构体信息
一般当程序发生异常时,用户代码停止执行,并将CPU的控制权转交给操作系统,操作系统接到控制权后,将当前线程的寄存器环境保存到结构体CONTEXT中,然后查找针对此异常的处理函数
系统利用结构EXCEPTION_RECORD保存了异常描述信息,它与CONTEXT一同构成了结构体EXCEPTION_POINTERS,一般在异常处理中经常使用这个结构体。
两个结构定义如下
EXCEPTION_POINTERS结构体包含EXCEPTION_RECORD和CONTEXT
typedef struct _EXCEPTION_POINTERS {PEXCEPTION_RECORD ExceptionRecord; PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS异常信息EXCEPTION_RECORD的定义
typedef struct _EXCEPTION_RECORD{ DWORD ExceptionCode; //异常码DWORD ExceptionFlags; //标志异常发生后是否还可以继续执行struct _EXCEPTION_RECORD* ExceptionRecord; //指向下一个异常节点的指针,这是一个链表结构PVOID ExceptionAddress; //异常发生的地址DWORD NumberParameters; //异常附加信息ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //异常的字符串
} EXCEPTION_RECORD, *PEXCEPTION_RECORD;
系统处理异常的顺序
如果按筛选器异常,调试器,SEH(结构化异常处理)这三个排序的话则是
调试器(优先级最高)----SEH(第二)--------筛选器异常(最后)
异常默认处理时SEH的注意点:如果你没有没有处理这个异常,系统会调用默认的系统处理程序,通常显示一个对话框,你可以选择关闭或者最后将其附加到调试器上的调试按钮.如果没有调试器能被附加于其上或者调试器也处理不了,系统就调用ExitProcess终结程序,不过在终结之前,系统仍然对发生异常的线程异常处理句柄来一次展开的信息(也就是在进行通知一次异常处理函数),这是线程异常处理例程最后清理的机会
异常展开
异常展开:假如fun1调fun2,fs[0]指向fun2的异常处理,这时候fun2的异常处理不了就会找到fun1,fun1可以处理,假如这时候fun1又发生异常的话,因为SEH没有把fun2的节点去掉,也就是没有清栈,这时候这时候调用了一个错误的地址, 那么现在我们应该把链表的位置重置为fun1的异常链表,fun2的不在需要了.进行清栈,这就是异常展开
异常展开的时候,我们自己也可以去做,也可以交给操作系统做,而操作系统做的时候也是调用的API RtIUnwind
SEH结构化异常
1FS段存有线程信息结构体TIB
2储异常信息的链表指针为TIB结构体的第一个成员,这样fs:[0]也就获取到了ExceptionList头节点
3通过dt _EXCEPTION_REGISTRATION_RECORD发现链表指针的结构
前4个字节:指向下一个异常处理结构体指针的位置,形成链表
后4个字节:指向一个异常回调函数
4异常回调函数
//异常回调函数声明
int _except_handler3(PEXCEPTION_RECORD exception_record, //异常记录PEXCEPTION_REGISTRATION registration, //异常回调链表指针PCONTEXT context, //寄存器信息PEXCEPTION_REGISTRATION dispatcher
)
{return ExceptionContinueExecution//返回值说明//ExceptionContinueExecution = 00 //表示已经处理程序继续执行 //ExceptionContinueSearch = 01 //根据链表往下搜索找回调处理//ExceptionNestedException = 02 //异常嵌套异常 //ExceptionCollidedUnwind = 03
}//微软官方给EXCEPTION_REGISTRATION结构体的定义
typedef struct _EXCEPTION_REGISTRATION PEXCEPTION_REGISTRATION;_EXCEPTION_REGISTRATION strucprev dd ? //上一个异常处理结构体指针的位置handler dd ? //异常回调函数
_EXCEPTION_REGISTRATION ends
也就说明_EXCEPTION_REGISTRATION就是_EXCEPTION_REGISTRATION_RECORD/*
微软定义的参考,表明结构体可以无限扩展
由使用者定义需要的参数,但是前两个参数必须为是prev和handler,这样扩展才不影响用fs:[0]去遍历异常链表,fs:[0]也就是执行链表的头结点
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;
};
*/
示例代码
使用微软提供的SEH,模拟c中的try和catch,并且手动异常展开进行清栈
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitiveinclude windows.inc
include kernel32.inc
include user32.inc
include msvcrt.incincludelib KERNEL32.LIB
includeLIB user32.lib
includelib msvcrt.lib.constMAIN db "main: ", 0FUN1 db "fun1: ", 0FUN2 db "fun2: ", 0MAIN_EXCE db "main except", 0FORMAT db "%s ExceptionAddress=%p ExceptionCode=%p ExceptionFlags=%p NumberParameters:%d", 0SAFETEXIT db "SafetyExit",0.dataBUFFER db 256 dup(0)MYSTACK db 256 dup(0)FIRST dd 0
.code;SEH结构化注册的宏
ExceptRegist macro Fun_Name,ContinueAddrassume fs :nothinglea eax,[esp-18h]push eax ;拓展参数 4:保存 esp的地址push ebp ;拓展参数 3:保存 ebp的地址push offset ContinueAddr ;拓展参数 2:异常处理完后面应该执行的代码位置 push offset Fun_Name ;拓展参数 1:函数名push offset except_handler ;结构体第二个参数:回调函数指指针push fs:[0] ;结构体第一个参数:指向下一个异常结构的指针存放指向头结点(fs:[0]指向的就是头结点),这样就形成了链表头插入,也就形成了栈结构,后进先出mov fs:[0], esp ;将栈里模拟的 EXCEPTION_REGISTRATION_RECORD结构体指针放在原来的 FS:[0]位置,也就是FS:[0]指向当前函数的异常结构
endm;SEH结构化注销的宏
ExceptUnLoad macromov eax, [esp] ;拿出上一个异常处理结构体指针mov fs:[0], eax ;还原至FS:[0]处add esp, 18h
endmexcept_handler proc c, ExceptionInfo:ptr EXCEPTION_RECORD, registration: ptr EXCEPTION_REGISTRATION, context:ptr CONTEXT, dispatcher:PVOID mov esi, ExceptionInfoassume esi :ptr EXCEPTION_RECORD .if [esi].ExceptionFlags == 2mov eax, ExceptionContinueExecutionret.endif;遍历异常链表,拿到当前函数在第几层mov ebx, registration;字符串拼接显示当前函数的栈结构.while ebx != NULLmov edx, [ebx+8]invoke crt_strcat, offset MYSTACK, edx .if ebx == FIRST.break.endifmov ebx, [ebx].endwinvoke wsprintf, offset BUFFER, offset FORMAT,offset MYSTACK,[esi].ExceptionAddress,[esi].ExceptionCode, [esi].ExceptionFlags, [esi].NumberParametersinvoke MessageBox,NULL, offset BUFFER, NULL, MB_OK;恢复栈,并继续执行下一层的代码mov esi,contextassume esi :ptr CONTEXTmov ebx,registrationmov edx,[ebx+0ch]mov [esi].regEip,edx ;还原执行代码的位置mov edx,[ebx+10h]mov [esi].regEbp,edx ;还原ebpmov edx,[ebx+14h]mov [esi].regEsp,edx ;还原espmov eax, ExceptionContinueExecution ;继续执行代码ret
except_handler endpfun2 proc;注册SEHExceptRegist FUN2,fun2_except_end;异常代码mov eax, 0mov dword ptr [eax], 0fun2_except_end:;注销SEHExceptUnLoadret
fun2 endpfun1 proc;注册SEHExceptRegist FUN1,fun1_except_endinvoke fun2fun1_except_end:;注销SEHExceptUnLoadretfun1 endpSTART:;注册SEHExceptRegist MAIN,main_except_endmov FIRST, espinvoke fun1main_except_end:invoke MessageBox,NULL,offset SAFETEXIT, NULL, MB_OK ;printf("SafetyExit");invoke ExitProcess,0;注销SEHExceptUnLoadretend START
本文标签: SEH结构体异常处理
版权声明:本文标题:SEH结构体异常处理 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.roclinux.cn/b/1693755029a240918.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论