异常的主要结构体信息

一般当程序发生异常时,用户代码停止执行,并将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结构体异常处理相关推荐

  1. 异常处理第三讲,SEH(结构化异常处理),异常展开问题

    异常处理第三讲,SEH(结构化异常处理),异常展开问题 作者:IBinary 出处:http://www.cnblogs.com/iBinary/ 版权所有,欢迎保留原文链接进行转载:) 不知道昨天有 ...

  2. 5.SEH(结构化异常处理)

    当用户异常产生后,内核函数KiDispatchException并不是像处理内核异常那样在0环直接进行处理,而是修正3环EIP为KiUserExceptionDispatcher函数后就结束了. 这样 ...

  3. SEH(结构化异常处理)

    文章目录 内容回顾: 总结: 程序代码 实现截图 具体流程 内容回顾: 当用户异常产生后,内核函数KiDispatchException并不是像处理内核异常那样在0环直接处理,而是修正3环EIP为Ki ...

  4. Win32 结构化异常处理(SEH)探秘【下篇】

    图十三 UnHandledExceptionFilter 函数的伪代码 UnhandledExceptionFilter( STRUCT _EXCEPTION_POINTERS *pException ...

  5. 深入解析结构化异常处理(SEH) - by Matt Pietrek

    目录 1.浅析SEH 2.移向更深处 3.编译器层面的SEH 4.扩展的异常处理帧 5.ShowSEHFrames程序 6.展开 7.未处理异常 8.进入地狱 9.结论 ​尽管以前写过一篇SEH相关的 ...

  6. Windows系统程序设计之结构化异常处理

    标 题: [原创]Windows系统程序设计之结构化异常处理 作 者: 北极星2003 时 间: 2006-09-20,20:21:28 链 接: http://bbs.pediy.com/showt ...

  7. go语言初体验(流程控制、range遍历、函数、结构体、面向对象)

    一.流程控制 // main package mainimport ("fmt" )func main() {x := 2switch x {case 1:fmt.Print(&q ...

  8. fread读结构体返回值是0无错误_嵌入式C编程之错误处理(附代码例子)!

    原标题:嵌入式C编程之错误处理(附代码例子)! 作者: clover-toeic 前言 本文主要总结嵌入式系统C语言编程中,主要的错误处理方式.文中涉及的代码运行环境如下: 一.错误概念 错误分类 从 ...

  9. windows核心编程学习笔记(八)结构化异常处理(Structured Exception Handling)

    首先要要知道,结构化异常处理(SEH)和C++提供的异常处理不相同. 一.Termination HandlersTermination Handlers使用很简单.在想使用SEH处理的地方使用 __ ...

最新文章

  1. python【蓝桥杯vip练习题库】BASIC-9特殊回文数
  2. python 数组基本用法
  3. OpenSTF 平台搭建
  4. MiniGUI开发环境搭建全记录(嵌入式计X86)
  5. 改善深层神经网络:超参数调整、正则化以及优化——2.5 指数加权平均的偏差修正
  6. linux 字符串截取_第13篇:Linux防火墙的日志基本审计
  7. [原创]在ObjectDataSource中使用自定义Web控件提供查询参数
  8. 《普林斯顿微积分读本》个人读书笔记
  9. 【自定义WPS插件xlam】
  10. 网上图片的几种保存方法
  11. ABAP 计算时间差
  12. 【JAVASE】IO系列 BufferedReader(超详细解析)
  13. TCSVT2021:一种结合全局和局部细粒度特征的行人再识别方法
  14. jrebel java.lang.ClassCastException: org.springframework.boot.actuate.endpoint.annotation
  15. 批量导入数据将word文档转换成HTML文档
  16. 淘宝开店历程-致广大新开淘宝店主
  17. 自己动手写服务器-1
  18. 彻底搞懂行内元素,块级元素,行内块元素~
  19. Door to Door 日剧改编 励志
  20. 在线OFFICE文档编辑器

热门文章

  1. 容器进程Core Dump处理
  2. Flink SQL Gateway REST Endpoint 使用教程
  3. SQL单行注释,块注释
  4. 2014 Mudanjiang Online Contest Problem D We Need Medicine
  5. python 找不到socket_swift 和 python解决Python找不到ssl模块问题 No module named _ssl
  6. Delphi判断文件大小
  7. 关于JVM类加载机制
  8. 北京奥运会-欢迎焰火
  9. linux显示总文件大小命令,linux查看文件大小命令
  10. c语言中较常见的由内存分配引起的错误_内存越界_内存未初始化_内存太小_结构体隐含指针