编译器扩展SEH(2)
文章目录
- 问题
- 自动挂入的异常处理函数
- __try __except嵌套,重复
- 原始的`_EXCEPTION_REGISTRATION_RECORD`结构体
- 编译器拓展的_EXCEPTION_REGISTRATION_RECORD结构体
- scopetable_entry结构体
- scopetable_entry举例子:
- 第一个:
- 第二个:
- 第三个:
- 第四个:
- _except_handler4执行过程
- 解释:
- trylevel
问题
我们在制作SEH链的时候,每增加一个,就得进行一次如下代码
__asm{mov eax,FS:[0]
mov temp,eax
lea ecx,myException
mov FS:[0],ecx -------------->修改FS:[0]挂入链表
}
那么当编译进行自行生成时,会不会也是每增加一个try,except,然后就压入一个_EXCEPTION_REGISTRATION_RECORD
结构体呢?接下来我们来看看如下代码会压入多少_EXCEPTION_REGISTRATION_RECORD
结构体
#include<Windows.h>
#include<iostream>int ExceptFilter(LPEXCEPTION_POINTERS pExceptionInfo) {pExceptionInfo->ContextRecord->Ecx = 1;return EXCEPTION_CONTINUE_EXECUTION;
}void TestException() {__try { __try {}__except (EXCEPTION_EXECUTE_HANDLER) {printf("异常处理函数Y");}}__except (EXCEPTION_EXECUTE_HANDLER) {printf("异常处理函数X");}__try {__try { }__except (GetExceptionCode() == 0xC0000094 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {printf("异常处理函数Y");}}__except (ExceptFilter(GetExceptionInformation())) {printf("异常处理函数Z");}}int main() {TestException();
}
反汇编代码如下:
看了一下,就只有一个_EXCEPTION_REGISTRATION_RECORD
结构体,只不过这个结构体跟我们手动挂的原始_EXCEPTION_REGISTRATION_RECORD
结构体不太一样,接下来我们来解释一下。
自动挂入的异常处理函数
_except_handler4
(编译器固定,那么异常处理函数名也就固定;随着编译器改变而改变)
__try __except嵌套,重复
每个使用__try __except的函数,不管其内部嵌套或反复使用多少__try, __except
,都只注册一遍,即只将一个_EXCEPTION_REGISTRATION_RECORD
挂入当前线程的异常链表中(对于递归函数,每一次调用都会创建一个_EXCEPTION_REGISTRATION_RECORD
,并挂入线程的异常链表中)。
原始的_EXCEPTION_REGISTRATION_RECORD
结构体
typedef struct _EXCEPTION_REGISTRATION_RECORD{struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEOTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
然而如果编译器的_EXCEPTION_REGISTRATION_RECORD
结构体是这样的话,那么压入一个_EXCEPTION_REGISTRATION_RECORD
结构体就能办到__try __except重复嵌套的话,那是根本不可能的,所以需要自行拓展
编译器拓展的_EXCEPTION_REGISTRATION_RECORD结构体
typedef struct _EXCEPTION_REGISTRATION_RECORD{struct _EXCEPTION_REGISTRATION_RECORD *Next;
void (*void)(PEXCEPTION_RECORD,PEXCEPTION_REGISTRATION,
PCONTEXT,PEXCEPTION_RECORD);
struct scopetable_entry *scopetable;
int trylevel;
int _ebp;
}
这里trylevel暂时等于-2,scopetable地址等于 0x1990F0h,接下来看看这两值有何作用
scopetable_entry结构体
scopetable_entry成员分析struct scopetable_entry{DWORD previousTryLevel //上一个try{}结构编号
PDWRD IpfnFilter //过滤函数的起始地址(小括号里面的常量或者表达式或者函数)
PDWRD IpfnHandler //异常处理程序的地址
}
scopetable[0].previousTryLevel=-2
scopetable[0]. IpfnFilter=过滤函数1
scopetable[0]. IpfnHandler=异常函数1
scopetable[1].previousTryLevel=0
scopetable[1]. IpfnFilter=过滤函数2
scopetable[1]. IpfnHandler=异常函数2
scopetable[2].previousTryLevel=-1
scopetable[2]. IpfnFilter=过滤函数3
scopetable[2]. IpfnHandler=异常函数3
scopetable[3].previousTryLevel=2
scopetable[3]. IpfnFilter=过滤函数4
scopetable[3]. IpfnHandler=异常函数4
scopetable_entry举例子:
随便找出一个来举个例子:
第一个:
上一个try结构编号是-2,过滤函数的起始地址是0x19532F,异常处理程序的地址:0x195335
它把过滤函数当成一个代码来执行,然后执行完后直接返回相应的返回结果。
紧接着,进入了异常处理程序代码
第二个:
上一个try结构编号是0,过滤函数的起始地址是0x195309,异常处理程序的地址:0x19530f
它把过滤函数当成一个代码来执行,然后执行完后直接返回相应的返回结果。
紧接着,进入了异常处理程序代码
第三个:
上一个try结构编号是-1,过滤函数的起始地址是0x1953b9,异常处理程序的地址:0x1953c6
过滤函数中有一个函数,进行跳转之后,再次进行返回,然后执行完后直接返回相应的返回结果。
紧接着,进入了异常处理程序代码
第四个:
上一个try结构编号是2(编号为2,即上一个try位于第三个try),过滤函数的起始地址是0x195363,异常处理程序的地址:0x195399
还是把过滤函数中的当成几段代码,执行完之后再返回,最后进行返回值判断,根据返回值判断下一步如何进行
紧接着,进入了异常处理程序代码
_except_handler4执行过程
1.CPU检测异常 查中断表执行处理函数 CommonDispatchException----->
KiDispatchException---->KiUserExceptionDispatcher------->RtlDispatchException—
—>VEH---->SEH
解释:
CPU发现异常,根据异常类型来查找中断表,查中断表找到具体的异常处理函数,执行处理函数之后,处理函数会调用CommonDispatchException,CommonDispatchException把出异常的那些相关信息存储起来(哪里出异常,什么类型的异常等),然后调用KiDispatchException(异常分发处理函数),这个函数会判断这个异常是0环异常还是3环异常,如果是3环异常,那么会修正EIP,把Trap_Frame指向KiUserExceptionDispatcher,然后当线程回到三环时,会从KiUserExceptionDispatcher开始执行,这个函数第一件事就是查RtlDispatchException,来查找异常处理函数在哪里?先找VEH,然后再找SEH(fs:[0]))
当如果我们自己抛出的异常,执行流程除了前三步(CPU检测异常 查中断表执行处理函数 CommonDispatchException),后面的一模一样
2.执行_except_handler4函数
- 根据trylevel选择scopetable数组
- 调用scopetable数组中对应的IpfnFilter函数
EXCEPTION_EXECUTE_HANDLER(1)执行except代码
EXCEPTION_CONTINUE_SEARCH(0)寻找下一个
EXCEPTION_CONTINUE_EXECUTION(-1)重新执行 - 如果IpfnFilter函数返回0,向上遍历,直到previousTryLevel=-1
trylevel
trylevel表示代码正在执行在第几个try中(它是时刻正在变化的,进入一个try或者走出一个try, trylevel都会被改变)
trylevel初始化为-1,此时的话也就代表未处于try中
当后面开始进入try时
进入第一个try,trylevel 值被赋为0;进入第二个try,trylevel 值被赋为1;退出第二个try,trylevel 值被赋为0
进入第三个try,trylevel 值被赋为2;进入第四个try,trylevel 值被赋为3;退出第四个try,trylevel 值被赋为2
当退出所有的try时,trylevel值变为-1
这样的话trylevel
结合scopetable数组就可以准确找到出错位置。
注意:
如果出异常后,就不会往下执行了,然后异常就被CPU捕获了,通过各种判断,最终转到_except_handler4,然后
- 根据trylevel选择scopetable数组
- 调用scopetable数组中对应的IpfnFilter函数
1.EXCEPTION_EXECUTE_HANDLER(1)执行except代码
2.EXCEPTION_CONTINUE_SEARCH(0)寻找下一个
3.EXCEPTION_CONTINUE_EXECUTION(-1)重新执行
编译器扩展SEH(2)相关推荐
- Windows异常学习笔记(四)—— 编译器扩展SEH
Windows异常学习笔记(四)-- 编译器扩展SEH 要点回顾 编译器支持的SEH 过滤表达式 实验一:理解_try_except 实验二:_try_except 嵌套 拓展SEH结构体 scope ...
- 编译器扩展SEH(1)
文章目录 编译器支持的SEH 过滤表达式规则: 过滤表达式的3种情况 直接写常量值 表达式 调用函数 编译器支持的SEH __try{ -------------------------------- ...
- PL/0语言编译器扩展 编译原理课程实践(1)
转眼大学生活就要结束,编译原理课程学的东西很多都忘记了.当时我们编译原理课程实践是PL/0语言编译器扩展,在原有PL/0语言文法进行扩展.我写这次博文一是为了回忆以前学的知识,加深记忆:二是和大家分享 ...
- 编译器扩展-ScriptableWizard
可以创建一个对话框,然后统一修改某些组件的值 public class Change :ScriptableWizard{public int 字段 = 100;/// <summary> ...
- 6.编译器拓展SEH
//1.挂入链表相当于这部分 //fs[0]-> Exception_asm{mov eax, fs:[0]mov temp,eaxlea ecx,Exceptionmov fs:[0],ecx ...
- 编译器扩展-MenuItem
MenuItem MenuItem属性允许你添加菜单项到主菜单和检视面板上下文菜单. 用法1:层级划分以及划线 [MenuItem("龙之介工具箱/起飞")] static voi ...
- c语言程序设计 滴水视频,编程达人滴水中级班视频教程
Java视频教程详情描述: <编程达人滴水中级班视频教程>编程达人来了,N部视频教程让你成为真正的编程达人,融会贯通将是本套视频最大的目的. Java视频教程目录: ├─APC机制 │ ...
- 深入解析结构化异常处理(SEH) - by Matt Pietrek
目录 1.浅析SEH 2.移向更深处 3.编译器层面的SEH 4.扩展的异常处理帧 5.ShowSEHFrames程序 6.展开 7.未处理异常 8.进入地狱 9.结论 尽管以前写过一篇SEH相关的 ...
- 嵌入式C语言自我修养:从芯片、编译器到操作系统(附送书籍)
关注+星标公众号,不错过精彩内容 来源 | 宅学部落 最近,阅读了王工(王利涛)赠送的一本由他编著的书籍<嵌入式C语言自我修养>,感觉写的挺不错.今天分享一下这本书籍<嵌入式C语言自 ...
最新文章
- 二十二、redis持久化之AOF
- 基于Matlab的标记分水岭分割算法(imreconstruct)
- linux用不用装固态硬盘驱动,固态硬盘要不要安装驱动?总算弄明白了
- html标签anchor,浏览器端-W3School-HTML:HTML DOM Anchor 对象
- mysql自动添加多条数据_用一条mysql语句插入多条数据
- AccessibilityService的具体应用场景
- launchpad不用图标_Launchpad Manager,一款非常方便的启动台图标管理工具
- 源码解析Servlet和HttpServlet
- Effective C# Item18:实现标准Dispose模式
- crossover卸载 linux,Ubuntu中卸载CrossOver困难?那是你没学会这个命令
- Vue 自定义弹出框组件(类似淘宝选择规格)
- 字体arial不支持样式regular的解决方法
- Excel排序、筛选
- laravel php的if判断,Thinkphp框架和Laravel框架的区别
- 【科研绘图】Adobe illustrator加Origin组合绘图
- IP网络摄像机安装注意事项
- 若依框架登录去除验证码
- Photon与Unity核心技术之角色更换武器
- linux运行直播软件,在Linux下可用Wine安装和运行虎牙直播、斗鱼直播
- 理财入门-读《小狗钱钱》有感
热门文章
- Python之pandas:对pandas中dataframe数据中的索引输出、修改、重命名等详细攻略
- Dataset之COCO数据集:COCO数据集的简介、下载、使用方法之详细攻略
- ML之Clustering之K-means:K-means算法简介、应用、经典案例之详细攻略
- DL之ANN/DNN: 人工神经网络ANN/DNN深度神经网络算法的简介、应用、经典案例之详细攻略
- 20 道 Spring Boot 面试题
- 机器学习-MNIST数据集-神经网络
- jQuery $.ajax传递数组的traditional参数传递必须true 对象的序列化
- 单调有界数列一定有极限
- Asp.Net 2.0中的客户端脚本
- STM32学习笔记(三) STM32的GPIO的深入学习