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机制的封装的更多相关文章

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

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

  2. 第23章 SEH结构化异常处理(1)_系统SEH机制

    23.1 基础知识 23.1.1 Windows下的软件异常 (1)中断和异常 ①中断是由外部硬件设备或异步事件产生的 ②异常是由内部事件产生的,可分为故障.陷阱和终止三类. (2)两种异常处理机制: ...

  3. 第25章 SEH结构化异常处理_未处理异常及向量化异常

    25.1 UnhandledExceptionFilter函数详解 25.1.1 BaseProcessStart伪代码(Kernel32内部) void BaseProcessStart(PVOID ...

  4. 第23章 SEH结构化异常处理(3)_终止处理程序

    23.3 终止处理程序 23.3.1 程序的结构 (1)框架 __try{ //被保护的代码块 …… } __finally{ //终止处理 } (2)__try/__finally的特点 ①fina ...

  5. 第24章 SEH结构化异常处理_异常处理及软件异常

    24.1  程序的结构 (1)try/except框架 __try{ //被保护的代码块 …… } __except(except fileter/*异常过滤程序*/){ //异常处理程序 } (2) ...

  6. Windows内核读书笔记——SEH结构化异常处理

    SEH是对windows系统中的异常分发和处理机制的总称,其实现分布在很多不同的模块中. SEH提供了终结处理和异常处理两种功能. 终结处理保证终结处理块中的程序一定会被执行 __try { //要保 ...

  7. 深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)

    摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...

  8. [C++]深入解析结构化异常处理(SEH)

    http://www.cppblog.com/weiym/archive/2015/02/27/209884.html 尽管以前写过一篇SEH相关的文章<关于SEH的简单总结>, 但那真的 ...

  9. 深入研究 Win32 结构化异常处理(好多相关文章)

    摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...

随机推荐

  1. ahjesus linux连接阿里云ubuntu服务器并更改默认账号和密码,以及创建子账户

    先确保本地Linux服务器SSH服务开启,如果没有开启直接执行指令:service sshd start 然后我们使用ssh指令进行远程登陆 ssh username@ip-address 输入pas ...

  2. PHP 操作socket 实现简易聊天室

    <?php $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP ); socket_bind($socket ,'127.0.0.1', ...

  3. js事件绑定

    事件绑定,常见的是odiv.onclick=function(){..........};  这种方式绑定事件太单一,如果绑定多个,那么最后一个事件会覆盖掉之前的,也就是说只执行最后一次绑定的事件,这 ...

  4. windows 8 设置hyper-v网络设置

    1 windwos 8 设置hyperv 比较简单,和装操作系统都不多做解释.我只多说说网络的设置问题,因为可能装提windows 2008虚拟机,根据网上设置网络的方式都是要不然只能虚拟 机上网 , ...

  5. c++类的定义《一》

    最近好忙,一来要在店里看店,二来朋友办结婚酒,搞的我这几天好疲惫啊···博客又有好几天没提笔了. 下午简单看了下书,看到了类的部分,自己动手练习了一下 笔记:1.类是数据类型 / 它的变童就是对象  ...

  6. 2015年第2本(英文第1本):《The Practice of Programming》

    2015年计划透析10本英文原著,最开始选定的第一本英文书是<Who Moved my Cheese>,可是这本书实在是太短.太简单了,总体的意思就是要顺应变化,要跳出自己的舒适区,全文不 ...

  7. Sharepoint学习笔记—习题系列--70-573习题解析 --索引目录

                  Sharepoint学习笔记—习题系列--70-573习题解析 为便于查阅,这里整理并列出了我前面播客中的关于70-573习题解析系列的所有问题,有些内容可能会在以后更新, ...

  8. SqlSever大数据分页

    在sql sever中大数据的分页一直是难以处理的一块,利用id自增列分页也存在不足之处.从一个相对全面的分页看,sql sever2005中新增的row_number()函数解决了这个问题.还是从一 ...

  9. UIScrollView常见属性

    什么是UIScrollView •设备的屏幕大小是极其有限的,因此直接展示在用户眼前的内容也相当有限 • •当展示的内容较多,超出一个屏幕时,用户可通过滚动手势来查看屏幕以外的内容 • •普通的UIV ...

  10. JDBC编程 之 增删改查

    JDBC编程之数据增加,更改,查询,删除 package com.good.jdbc; import java.sql.Connection; import java.sql.DriverManage ...