第23章 SEH结构化异常处理(2)_编译器对系统SEH机制的封装
23.2 编译器层面对系统SEH机制的封装
23.2.1 扩展的EXCEPTION_REGISTRATION级相关结构:VC_EXCEPTION_REGISTRATION
(1)VC_EXCEPTION_REGISTRATION结构
struct VC_EXCEPTION_REGISTRATION
{
VC_EXCEPTION_REGISTRATION* prev; //前一个结构体的指针
FARPROC handler; //永远指向_exception_handler4回调函数
scopetable_entry* scopetable;//指向scpoetable数组的指针
int _index; //有的书上也叫tryLevel。scopetable项的当前项
DWORD _ebp; //当前ebp值,用于访问各成员
}
(2)scopetable_entry结构体
struct scopetable_entry
{
DWORD prev_entryindex;//指向前一个scopetable_entry在scopetable中的索引
FARPROC lpfnFilter; //对应于__except块后小括号内的过滤函数;__finally时为NULL
FARPROC lpfnHandler;//__exception或__finally后花括号{}内的代码地址。
}
(3)VC异常帧堆栈布局及VC默认的异常处理

23.2.2 数据结构组织

(1)每个函数只注册一个VC_EXCEPTION_REGISTRATION结构(也叫异常帧,如图中的有5个Frame,即有5个函数调用)。可见,该SEH异常链从链表头部到链表尾共有5节点,分对应于5个异常处理帧。但需注意的是,通过VC安装的节点为VC_EXCEPTION_REGISTRATION结构,图中有3个,对应的回调函数为VCSHE!_exception_handler4(0x00E0178),而系统安装的是EXCEPTION_REGISTRATION结构的帧,位于链表尾部最后的两个节点,对应的回调函数分别为ntdll!_exception_handler4(0x77B44FF3)和0x77B50CF5。
(2)VC为每个函数内的所有__try块建立一个scopetable表,其中每个__try块对应于scopetable中的一项。(用scopetable_entry结构体来表示这个__try项,结构里分别用lpfnFilter和lpfnHandler来表示__except/__finally的过滤函数和处理函数,其中_finally没有过滤函数,只有异常处理函数)。
(3)若有__try块嵌套,则在scopetable_entry结构里的prev_entryindex或指明,多层嵌套形成单向链表。
(4)对于VC的异常处理,其每个异常帧的回调处理函数都统一设为_except_handler4。每进入一个try块里,编译器会将VC_EXCEPTION_REGISTRATION中tryLevel赋值为相应的值。一旦该try块异常发生,系统会先从VC_EXCEPTION_REGISTRATION的handler域中找到_exception_handler4函数(C运行时库函数),然后根据当前tryLevel的值找到scopetable表中这个__try块相应的过滤函数和处理函数对异常进行相应的处理。
(5)与_except块不同,_finally块的lpfnFilter为NULL,即没有过滤函数。
23.2.3 _exception_handler4函数的执行流程

(1)异常发生时,根据index找到scopetable项,并调用lfpnFilter。如果过滤函数lpfnFilter返回EXCEPTION_EXECUTE_HANDLER,则执行全局展开之后调用lpfnHandler函数。如果过滤函数lpfnFilter返回EXCEPTION_CONTINUE_EXECUTION,则_except_handler4简单地返回EXCEPTION_CONTINUE_EXECUTION,交由系统恢复线程的执行。
(2)如果lpfnFilter返回EXCEPTION_CONTINUE_SEARCH时,此时_except_handler4查看previndex是否是0xFFFFFFFE,若是则_except_handler4返回ExceptionContinueSearch让系统继续遍历外层SEH链或由系统直接处理。否则_except_handler4根据previndex找到相应的过滤函数,根据其返回值重复上面的动作。直到异常被处理或previndex为0xFFFFFFFE为止。
【VcSEH程序】演示多层嵌套try块的调用

/************************************************************************
Module :ExceptFrameInfo.h
Notices:Copyright(c) Microsoft System Journal,February 1997,Matt Pietrek
MSVC 2005之后的编译器开启/GS选项仍可能会回滚到SEH3。不过,CRT的代码总是使用SEH4。
************************************************************************/
#pragma once #include <windows.h>
#include <stdio.h> //-------------------------------------------------------------------
// 本程序仅适用于Visual C++,它使用的数据结构是特定于Visual C++的
//-------------------------------------------------------------------
#ifndef _MSC_VER
#error Visual C++ Required (Visual C++ specific information is displayed)
#endif /////////////////////////////////结构定义////////////////////////////////
//操作系统定义的基本异常帧 struct EXCEPTION_REGISTRATION
{
EXCEPTION_REGISTRATION* prev;
FARPROC handler;
}; //VC++扩展异常帧指向的数据结构
struct scopetable_entry
{
DWORD previousTryLevel;
FARPROC lpfnFilter; //过滤函数
FARPROC lpfnHandler; //异常处理程序实体的地址
}; //VC++使用的扩展异常帧
struct VC_EXCEPTION_REGISTRATION :EXCEPTION_REGISTRATION
{
scopetable_entry* scopetable;
int trylevel;
int _ebp;
}; //////////////////////////////////////////////////////////////////////////
//原型声明
//__except_handler3是Visual C++运行时库函数,我们想打印出它的地址,但是它的原型
//并没有出现在任何头文件中,所以需要自己声明它。
extern "C" DWORD __security_cookie;
extern "C" int _except_handler4(PEXCEPTION_RECORD,
EXCEPTION_REGISTRATION*,
PCONTEXT,
PEXCEPTION_RECORD); //////////////////////////////////////////////////////////////////////////
//显示一个异常帧及其相应的scopetable的信息
void ShowSEHFrame(VC_EXCEPTION_REGISTRATION* pVCExcReg){
BOOL bVcExceptionHandler4 = pVCExcReg->handler == (FARPROC)_except_handler4; //VC的_except_handler4函数
if (bVcExceptionHandler4){ //VC的_except_handler4函数
printf("Frame:%08X Handler:%08X prev:%08X Scopetable:%08X\n",
pVCExcReg, pVCExcReg->handler, pVCExcReg->prev, (DWORD)pVCExcReg->scopetable^__security_cookie);
} else{
printf("Frame:%08X Handler:%08X prev:%08X\n",
pVCExcReg, pVCExcReg->handler, pVCExcReg->prev);
} DWORD iAddr = (DWORD)pVCExcReg->scopetable ^ __security_cookie;
//iAddr = 0x77090928;//在我的系统中,这个值为offset ntdll!ResCSegmentValidateHeader+0x118e (77090928) //Scopetable前16个字节几SecurityCookie相关的字段,后面才是scopetable_entry项
scopetable_entry* pScopeTableEntry = (scopetable_entry*)(iAddr + ); for (int i = ; i <= pVCExcReg->trylevel; i++){ if (bVcExceptionHandler4){ //VC的_except_handler4函数 printf(" scopetable[%u] PreTryLevel:%08X filter:%08X __except:%08X\n",
i, pScopeTableEntry->previousTryLevel,
pScopeTableEntry->lpfnFilter,
pScopeTableEntry->lpfnHandler);
} pScopeTableEntry++;
}
printf("\n");
} //////////////////////////////////////////////////////////////////////////
//遍历异常帧的链表,按顺序显示它们的信息
void WalkSEHFrames(void){
VC_EXCEPTION_REGISTRATION* pVCExcReg; //打印出_except_handler4函数的位置
printf("_except_handler4 is at address:%08X\n", _except_handler4);
printf("\n"); //从FS:[0]处获取指向链表头的指针
__asm mov eax, FS:[]
__asm mov[pVCExcReg], EAX //遍历异常帧的链表。0xFFFFFFFF标志着链表的结尾
while (0xFFFFFFFF != (unsigned)pVCExcReg){
ShowSEHFrame(pVCExcReg);
pVCExcReg = (VC_EXCEPTION_REGISTRATION*)(pVCExcReg->prev);
}
}
//VcSEH.cpp
#include <windows.h>
#include <stdio.h>
#include "ExceptionFrameInfo.h" void test(void){
int i = ;
//A块
__try{//第1层
__try{ //第2层
__try{ //第3层
__try{ //第4层
i++;
}__finally {//第4层
}
}__except (EXCEPTION_CONTINUE_SEARCH){//第3层
//这里不会被执行
}
}__except (EXCEPTION_CONTINUE_SEARCH){//第2层
//这里不会被执行
}
}
__except (EXCEPTION_EXECUTE_HANDLER){ //第1层
//该try块及内层发生异常时,这里被执行
} //B块
__try{
WalkSEHFrames();
}__except (EXCEPTION_CONTINUE_SEARCH){
}
} int main(){
__try{
test();
}__except (EXCEPTION_EXECUTE_HANDLER){ }
return ;
}
23.2.4 小结:异常处理流程及全局展开

【GlobalUnwind程序】全局展开

#include <windows.h>
#include <stdio.h> int MyExceptionFilter(int tryLevel,DWORD dwErrorCode){
printf("第%d层的过滤函数被执行,错误码:%d!\n", tryLevel, dwErrorCode); //最外层里返回同意处理;否则,继续查找。
return (tryLevel == ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
} void test(void){
int i = ;
//A块
__try{//第1层
__try{ //第2层
__try{ //第3层
__try{ //第4层
RaiseException(, , , NULL); //抛出一个异常
}
__finally {//第4层
printf("最4层的_finally块被执行!\n");
}
}
__except (MyExceptionFilter(, GetExceptionCode())){//第3层
//这里不会被执行
printf("最3层的_except块被执行!\n");
}
}
__except (MyExceptionFilter(, GetExceptionCode())){//第2层
//这里不会被执行
printf("最2层的_except块被执行!\n");
}
}
__except (MyExceptionFilter(, GetExceptionCode())){ //第1层
//该try块及内层发生异常时,这里被执行
printf("最1层的_except块被执行!\n");
} //B块
__try{ }
__except (EXCEPTION_CONTINUE_SEARCH){
printf("B块中的_except被执行!\n");
}
} int main(){
__try{
test(); //test()函数内部会处理异常,所以main中的_except块后的代码不会被执行!
}
__except (EXCEPTION_EXECUTE_HANDLER){
printf("main中的_except被执行!\n"); }
system("pause");
return ;
}
【参考文献】
深入解析结构化异常处理(SEH)
http://www.cppblog.com/weiym/archive/2015/02/27/209884.html
http://blog.csdn.net/bad_sheep/article/details/5803649
http://blog.csdn.net/yuzl32/article/details/5383542
http://www.mouseos.com/windows/index.html
Windows系统程序设计之结构化异常处理
http://bbs.pediy.com/showthread.php?threadid=32222
《软件加密技术内幕》,看雪学院
第23章 SEH结构化异常处理(2)_编译器对系统SEH机制的封装的更多相关文章
- 异常处理第三讲,SEH(结构化异常处理),异常展开问题
异常处理第三讲,SEH(结构化异常处理),异常展开问题 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 不知道昨天有木有 ...
- 第23章 SEH结构化异常处理(1)_系统SEH机制
23.1 基础知识 23.1.1 Windows下的软件异常 (1)中断和异常 ①中断是由外部硬件设备或异步事件产生的 ②异常是由内部事件产生的,可分为故障.陷阱和终止三类. (2)两种异常处理机制: ...
- 第25章 SEH结构化异常处理_未处理异常及向量化异常
25.1 UnhandledExceptionFilter函数详解 25.1.1 BaseProcessStart伪代码(Kernel32内部) void BaseProcessStart(PVOID ...
- 第23章 SEH结构化异常处理(3)_终止处理程序
23.3 终止处理程序 23.3.1 程序的结构 (1)框架 __try{ //被保护的代码块 …… } __finally{ //终止处理 } (2)__try/__finally的特点 ①fina ...
- 第24章 SEH结构化异常处理_异常处理及软件异常
24.1 程序的结构 (1)try/except框架 __try{ //被保护的代码块 …… } __except(except fileter/*异常过滤程序*/){ //异常处理程序 } (2) ...
- Windows内核读书笔记——SEH结构化异常处理
SEH是对windows系统中的异常分发和处理机制的总称,其实现分布在很多不同的模块中. SEH提供了终结处理和异常处理两种功能. 终结处理保证终结处理块中的程序一定会被执行 __try { //要保 ...
- 深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)
摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...
- [C++]深入解析结构化异常处理(SEH)
http://www.cppblog.com/weiym/archive/2015/02/27/209884.html 尽管以前写过一篇SEH相关的文章<关于SEH的简单总结>, 但那真的 ...
- 深入研究 Win32 结构化异常处理(好多相关文章)
摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...
随机推荐
- ABAP SY-SUBRC 使用过程中返回值的几种含义
当进行Debug的时候,经常会遇到"SY-SUBRC"的返回值.具体如何使用.在各种语句下返回值. ================= FUNCTION MODULE (或RFC中 ...
- Mybatis学习记录(七)----Mybatis查询缓存
1. 什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存. 一级缓存是SqlSession级别的缓存.在操作数据库时需要构造 sql ...
- 如何通过PowerShell在Visual Studio的Post-build中预热SharePoint站点
问题现象 Visual Studio在开发SharePoint的时候,发布部署包后,首次打开及调试站点页面的时候会非常的慢 解决方案 使用PowerShell脚本,加载SharePoint插件后遍历所 ...
- Oracle LPAD/RPAD函数在处理中文时的注意事项
首先看下Oracle官方对函数的定义: The RPAD function returns an expression, right-padded to a specified length with ...
- JAVA EE(简述)
一.平台概述 JavaEE的全称是Java Enterprise Edition,它是一个开发分布式企业级应用的规范和标准 Java 平台三个版本: Java ME(Java Micro Edit ...
- IOS 网络浅析-(十 NSURLSession下载简介)
之前本来打算在写两个篇幅,但是在这片开写的时候觉得还是写一个比较好,有利于理解.NSURLSession下载是通过NSURLSession下载代理实现的,上一片也介绍了代理,之所以没有介绍下载是因为, ...
- HTML列表元素
HTML定义了3类列表: 1.有序列表(通常用数字编号) 2.无序列表(通常前面加原点) 3.自定义列表(列表项目,带有集成的定义) 有序列表和无序列表均为列表中的每一项使用列表项元素(<li& ...
- 【转】Python开发指南:最佳实践精选
总体原则 价值 “为别人开发你也想要使用的工具.” ——Kenneth Reitz "简洁总是胜过可用." ——Pieter Hintjens "满足90%的使用场景.忽 ...
- Visual Studio发布Web项目报错:Unable to add 'xxx' to the Web site. Unable to add file 'xxx'. The specified file could not be encrypted.
背景 Visual Studio下的Web项目 现象 发布时遇到Unable to add 'xxx' to the Web site. Unable to add file 'xxx'. The ...
- win10中文简体繁体切换快捷键
win10中文简体繁体切换快捷键Ctrl+Shift+F