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个函数调用)。为这个函数内的所有__try块建立一个scopetable表,其中每个__try块对应于scopetable中的一项。(用scopetable_entry结构体来表示这个__try项,结构里分别用lpfnFilter和lpfnHandler来表示__except/__finally的过滤函数和处理函数,其中_finally没有过滤函数,只有异常处理函数)。
(2)若有__try块嵌套,则在scopetable_entry结构里的prev_entryindex或指明,多层嵌套形成单向链表。
(3)对于VC的异常处理,其每个异常帧的回调处理函数都统一设为_except_handler4。每进入一个try块里,编译器会将VC_EXCEPTION_REGISTRATION中tryLevel赋值为相应的值。一旦该try块异常发生,系统会先从VC_EXCEPTION_REGISTRATION的handler域中找到_exception_handler4函数(C运行时库函数),然后根据当前tryLevel的值找到scopetable表中这个__try块相应的过滤函数和处理函数对异常进行相应的处理。
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为止。
23.2.4 小结:异常处理流程及全局展开
【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 + 16); for (int i = 0; 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:[0] __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 = 0; //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 0; }
【参考文献】
深入解析结构化异常处理(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
《软件加密技术内幕》,看雪学院