6.编译器拓展SEH
//1.挂入链表相当于这部分
//fs[0]-> Exception_asm{mov eax, fs:[0]mov temp,eaxlea ecx,Exceptionmov fs:[0],ecx}//为SEH成员赋值Exception.Next = (_EXCEPTION*)temp;Exception.Handler = (DWORD)&MyEexception_handler;//下面是2,3
EXCEPTION_DISPOSITION _cdecl MyEexception_handler
(struct _EXCEPTION_RECORD *ExceptionRecord, //异常结构体PVOID EstablisherFrame, //SEH结构体地址struct _CONTEXT *ContextRecord, //存储异常发生时的各种寄存器的值 栈位置等PVOID DispatcherContext
)
{if (ExceptionRecord->ExceptionCode == 0xC0000094) //2.异常过滤{ContextRecord->Eip = ContextRecord->Eip + 2; //3.异常处理ContextRecord->Ecx = 100;return ExceptionContinueExecution;}return ExceptionContinueSearch;
}
编译器支持的SEH,SEH是windows平台下特有的换在别的平台下它是不支持的。
//这里的代码底层实现就类似上面的代码。
_try //1.挂入链表{}_except(过滤表达式) //2.异常过滤{异常处理程序 //3.异常处理程序}异常过滤表达式常量值
1) EXCEPTION_EXECUTE_HANDLER (1) 执行except代码
2) EXCEPTION_CONTINUE_SEARCH (0) 寻找下一个异常处理函数
3) EXCEPTION_CONTINUE_EXECUTION (-1) 返回出错位置重新执行
过滤表达式3种方式
- 直接写常量值
- 表达式
- 调用函数
表达式写法
_try{_asm{xor edx,edxxor ecx,ecxmov eax,10idiv ecx //EDX = EAX /ECX}}//如果异常码为0xC0000094返回1否则返回0_except(GetExceptionCode() == 0xC0000094 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH){printf("如果出现异常 此处处理\n");}
调用函数写法
//参数根据需要来写,可以不要参数
int ExceptFilter(LPEXCEPTION_POINTERS pExceptionInfo)
{pExceptionInfo->ContextRecord->Ecx = 1;//异常处理return EXCEPTION_CONTINUE_EXECUTION;//返回出错位置重新执行
}int main()
{_try{_asm{xor edx,edxxor ecx,ecxmov eax,10idiv ecx //EDX = EAX /ECX}}//GetExceptionInformation获取异常结构指针_except(ExceptFilter(GetExceptionInformation())){printf("如果出现异常 此处处理\n");}getchar();
}
这是没用异常的函数汇编代码的生成
这是用了异常的函数汇编代码生成
异常处理函数:_except_handler3(不同的编译器这个也是不一样的)
可以看到用了异常函数的多了很多代码,新增的代码其实就是让 fs[0] 指向新增的异常处理链。
如果我们自己写SEH,用一个异常处理函数就要手动挂入一个SEH链表,两个就挂入两次,以此类推…,但用编译器的就不需要。
_try_except嵌套重复
每个使用_try _except的函数,不管其内部嵌套或反复使用多少try_except,都只注册一遍,即只将一个_EXCEPTION_REGISTRATION_RECORD挂入当前线程的异常链表中(对于递归函数,每一次调用都会创建一个 _EXCEPTION_REGISTRATION_RECORD,并挂入线程的异常链表中)。
我这用了3个_try _except
_try{_try{}_except(1){}}_except(1){}_try{}_except(1){}
只要挂一次SEH链就会修改一次 FS[0]
push ebpmov ebp,esppush 0FFhpush offset string "i386\\chkesp.c"+0FFFFFFD4h (00422020)push offset __except_handler3 (00401224)mov eax,fs:[00000000]push eaxmov dword ptr fs:[0],espadd esp,0B8hpush ebxpush esipush edimov dword ptr [ebp-18h],esplea edi,[ebp-58h]mov ecx,10hmov eax,0CCCCCCCChrep stos dword ptr [edi]_trymov dword ptr [ebp-4],0{_trymov dword ptr [ebp-4],1{}mov dword ptr [ebp-4],0jmp $L16979+0Ah (0040107c)_except(1)mov eax,1retmov esp,dword ptr [ebp-18h]{}mov dword ptr [ebp-4],0}mov dword ptr [ebp-4],0FFFFFFFFhjmp $L16975+0Ah (00401095)_except(1)mov eax,1retmov esp,dword ptr [ebp-18h]{}mov dword ptr [ebp-4],0FFFFFFFFh_trymov dword ptr [ebp-4],2{}mov dword ptr [ebp-4],0FFFFFFFFhjmp $L16983+0Ah (004010b5)_except(1)mov eax,1retmov esp,dword ptr [ebp-18h]{}mov dword ptr [ebp-4],0FFFFFFFFhmov ecx,dword ptr [ebp-10h]mov dword ptr fs:[0],ecxpop edipop esipop ebxmov esp,ebppop ebpret
不管其内部嵌套或反复使用多少try_except,都只挂一次,指向的异常处理函数也是确定的。
3个异常处理函数就应该有3个链,它只挂了一个,这时怎么实现的呢?
拓展SEH链结构
struct _EXCEPTION_REGISTRATION
{ struct _EXCEPTION_REGISTRATION* prev;//下一个异常链void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_ REGISTRATION, PCONTEXT, PEXCEPTION_RECORD);//指向异常函数struct scopetable_entry scopetable;int trylevel; //当前代码执行在那个try里int _ebp;
};
名字有变不过叫什么名字无所谓了,第一和第二个成员没变,后面加了3个成员后面再讲…
无论怎么拓展,有两个成员必须要有,一个是链表节点一个是异常处理函数。
。
_EXCEPTION_REGISTRATION .scopetable成员分析
struct scopetable_entry
{DWORD previousTryLevel; //上一个try{}结构编号PDWORD lpfnFilter; //指向过滤函数的起始地址,_except()括号内那个PDWORD lphnHandle; //异常处理程序的地址
};
int ExceptionFilter()
{return EXCEPTION_CONTINUE_EXECUTION;
}void a()
{_try{//异常点A}_except(EXCEPTION_EXECUTE_HANDLER){printf("异常处理函数1\n");}_try{//异常点B_try{//异常点C }_except(GetExceptionCode() == 0xC0000094 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH){printf("异常处理函数C \n");}}_except(ExceptionFilter()){printf("异常处理函数2\n");}}int main()
{a();
}
我这写了3个异常就会有3块,scopetable_entry结构的第2第3我们知道了,第一个成员是什么?
previousTryLevel第一和第二的值都是 -1 ,第三个是 1
第一个它没有上一个try它是-1,第二个它是最外层它也是-1,第三个它的上一个是 1 。(结合下面的下标理解)
scopetable[0].previousTryLevel =-1;
scopetable[0].IpfnFilter=过滤函数1;
scopetable[0].lfnHandler =异常处理函数1;scopetable[1].previousTryLevel =-1;
scopetable[1].IpfnFilter=过滤函数2;
scopetable[1].IpfnHandler=异常处理函数2;scopetable[2].previousTryLevel = 1;
scopetable[2].IpfnFilter=过滤函数3;
scopetable[2].lpfnHandler=异常处理函数3;
struct scopetable;这个结构已经将所有你所有的过滤函数指针,异常处理函数指针,全部放进数组里了, 所以无论它使用多少try_except,都只需要注册一次。
_EXCEPTION_REGISTRATION .trylevel
int ExceptionFilter()
{return 1;
}void a()
{_try{_try{}_except(EXCEPTION_EXECUTE_HANDLER){printf("异常处理函数\n");}}_except(EXCEPTION_EXECUTE_HANDLER){printf("异常处理函数\n");}_try{_try{}_except(GetExceptionCode() == 0xC0000094 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH){printf("异常处理函数\n");}}_except(ExceptionFilter()){printf("异常处理函数\n");}}
这个 -1 就是当前trylevel的值
struct scopetable_entry虽然记录了异常信息但异常触发了,它不知道查谁所以需要trylevel来记录当前的代码执行在哪个try里。
当它要进入try时第一件事就是将trylevel修改为对应的scopetable[ ]索引值。
退出时又将 trylevel 改为 - 1。
_except_handler3执行过程
- CPU检测到异常 查中断表执行处理函数CommonDispatchException -> KiDispatchException ->KiUserExceptionDispatcher -> RtIDispatchException -> VEH ->SEH
- 执行_except_handler3函数
<1>根据 trylevel 选择 scopetable 数组
<2>调用 scopetable 数组中对应的 IpfnFilter 函数
- EXCEPTION_EXECUTE_HANDLER (1) 执行except代码
- EXCEPTION_CONTINUE_SEARCH (0) 寻找下一个
- EXCEPTION_CONTINUE_EXECUTION (-1) 重新执行触发异常的代码
<3>如果IpfnFilter 函数返回1,调用lpfnHandler指向的异常处理函数
<3>如果IpfnFilter 函数返回0,寻址下一个异常处理函数
<3>如果IpfnFilter 函数返回-1,重新执行触发异常的代码
_try_finally
_try
{//可能出现错误的代码
}
_finally
{//一定会执行的代码
}
_finally里的代码就算你直接将他return或者触发异常,它一样会执行,下面看看它是然后实现的。
scopetable[0].previousTryLevel =-1;
scopetable[0].IpfnFilter=0;
scopetable[0].lfnHandler =0x401179;
通过IpfnFilter来判断如果它的值为0,就是 _try_finally{};
它进入try时马上就掉用了 _local_unwind2 这个函数,_local_unwind2 这个函数就叫局部展开。
调用了lfnHandler 指向的函数。
全局展开_global_unwind2
void a()
{_try{_try{_try{*(int*)0 = 10;}_finally{printf("一定会执行的代码A\n");}}_finally{printf("一定会执行的代码B\n");}} _except(1){printf("异常处理函数\n");}}
记得在_except(1)那下断,往下跟就能看到
_global_unwind2 全局展开
调用局部展开
- 触发异常,_except_handler3接管
- _except_handler3就会找异常处理函数
- 找自己和它的下一个发现scopetable.IpfnFilter=0,处理不了
- 最后那一个_except(1)后可以处理了,但它会先执行_global_unwind2
- _global_unwind2 会从触发异常的那个try开始,调用局部展开
6.编译器拓展SEH相关推荐
- 编译器扩展SEH(2)
文章目录 问题 自动挂入的异常处理函数 __try __except嵌套,重复 原始的`_EXCEPTION_REGISTRATION_RECORD`结构体 编译器拓展的_EXCEPTION_REGI ...
- Windows异常学习笔记(四)—— 编译器扩展SEH
Windows异常学习笔记(四)-- 编译器扩展SEH 要点回顾 编译器支持的SEH 过滤表达式 实验一:理解_try_except 实验二:_try_except 嵌套 拓展SEH结构体 scope ...
- Unity编译器拓展学习总结(二):各个平台菜单栏拓展
Unity编译器拓展学习总结(二):各个平台菜单栏拓展 前情参考:Unity编译器拓展学习总结(一):常用工具API 文章目录 Unity编译器拓展学习总结(二):各个平台菜单栏拓展 前言 菜单栏拓展 ...
- Unity编译器拓展学习总结(一):常用工具API
Unity编译器拓展学习总结(一):常用工具API 文章目录 Unity编译器拓展学习总结(一):常用工具API 前言 一.常用工具API 1.PrefabUtility 2.AssetDatabas ...
- 编译器扩展SEH(1)
文章目录 编译器支持的SEH 过滤表达式规则: 过滤表达式的3种情况 直接写常量值 表达式 调用函数 编译器支持的SEH __try{ -------------------------------- ...
- 深入解析结构化异常处理(SEH) - by Matt Pietrek
目录 1.浅析SEH 2.移向更深处 3.编译器层面的SEH 4.扩展的异常处理帧 5.ShowSEHFrames程序 6.展开 7.未处理异常 8.进入地狱 9.结论 尽管以前写过一篇SEH相关的 ...
- c语言程序设计 滴水视频,编程达人滴水中级班视频教程
Java视频教程详情描述: <编程达人滴水中级班视频教程>编程达人来了,N部视频教程让你成为真正的编程达人,融会贯通将是本套视频最大的目的. Java视频教程目录: ├─APC机制 │ ...
- Windows系统程序设计之结构化异常处理
标 题: [原创]Windows系统程序设计之结构化异常处理 作 者: 北极星2003 时 间: 2006-09-20,20:21:28 链 接: http://bbs.pediy.com/showt ...
- Structured Exception Handling
http://msdn.microsoft.com/zh-cn/magazine/cc301172.aspx Matt Pietrek 著 董岩 译 原文:http://www.microsoft ...
最新文章
- android 图片作为背景图片,Android设置网络图片为手机背景图片的方法
- JavaScript基础04【逻辑、复制、关系、相等运算符、Unicode编码表】
- 零基础学Python(第十七章 模块import)
- dubbo启动时检查服务
- VB快速查找大型文件中包含的字符串
- 计算机中那些事儿(七):近期拆计算机小感
- 「Mac新手必备」自定义 Mac 上的控制中心和菜单栏
- Java程序员最厉害的是什么,「解密」谁是世界上最好的java程序员?
- java方面的文献综述怎么写_java论文参考文献
- 计算机人工智能领域英文文献,人工智能英文参考文献精选(3)
- Python遥感开发之GDAL读写遥感影像
- 计算机网络 如何算 子网号,计算机网络的划分以及主机号子网号的计算方法
- CentOS7系统安装参考
- 人工智能Java SDK:图片分类(支持imagenet数据集分类)
- 计算机摄影比赛,计算机系摄影大赛圆满结束
- 使用ST-LINK下载STM32程序
- 计算机美化标题教案,计算机基础教案标题.doc
- 阿里,昨天被主管告知3.25了,感觉自己好失败...
- Storm Event Imagery Dataset 数据简介
- 如何成为一名合格的运筹优化算法工程师?
热门文章
- 成功解决OpenCV Error: Assertion failed (ssize.width 0 ssize.height 0) in cv::resize, file C:\proj
- Java:Java编程实现导出二维码
- ML之LoRSGD:基于LoR(逻辑回归)、SGD梯度下降算法对乳腺癌肿瘤(10+1)进行二分类预测(良/恶性)
- 3D数学 ---- 矩阵和线性变换
- C语言实现常用数据结构——堆
- android 中的常用组件
- 【转载】OI生涯结束……在逸夫楼那些的日子里
- bzoj1791: [Ioi2008]Island 岛屿 单调队列优化dp
- 【NOI2013】向量内积
- Linux 下如何安装软件?