• 第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
    ",
                   pVCExcReg, pVCExcReg->handler, pVCExcReg->prev, (DWORD)pVCExcReg->scopetable^__security_cookie);
        } else{
            printf("Frame:%08X Handler:%08X prev:%08X
    ",
                   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
    ",
                       i, pScopeTableEntry->previousTryLevel,
                       pScopeTableEntry->lpfnFilter,
                       pScopeTableEntry->lpfnHandler);
            }
    
            pScopeTableEntry++;
        }
        printf("
    ");
    }
    
    //////////////////////////////////////////////////////////////////////////
    //遍历异常帧的链表,按顺序显示它们的信息
    void WalkSEHFrames(void){
        VC_EXCEPTION_REGISTRATION* pVCExcReg;
    
        //打印出_except_handler4函数的位置
        printf("_except_handler4 is at address:%08X
    ", _except_handler4);
        printf("
    ");
    
        //从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;
    }

    23.2.4 小结:异常处理流程及全局展开

    【GlobalUnwind程序】全局展开

    #include <windows.h>
    #include <stdio.h>
    
    int MyExceptionFilter(int tryLevel,DWORD dwErrorCode){
        printf("第%d层的过滤函数被执行,错误码:%d!
    ", tryLevel, dwErrorCode);
    
        //最外层里返回同意处理;否则,继续查找。
        return (tryLevel == 1) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
    }
    
    void test(void){
        int i = 0;
        //A块
        __try{//第1层
            __try{ //第2层
                __try{ //第3层    
                    __try{ //第4层
                        RaiseException(999, 0, 0, NULL); //抛出一个异常
                    }
                    __finally {//第4层
                        printf("最4层的_finally块被执行!
    ");
                    }
                }
                __except (MyExceptionFilter(3, GetExceptionCode())){//第3层
                    //这里不会被执行
                    printf("最3层的_except块被执行!
    ");
                }
            }
            __except (MyExceptionFilter(2, GetExceptionCode())){//第2层
                //这里不会被执行
                printf("最2层的_except块被执行!
    ");
            }
        }
        __except (MyExceptionFilter(1, GetExceptionCode())){ //第1层
            //该try块及内层发生异常时,这里被执行
            printf("最1层的_except块被执行!
    ");
        }
    
        //B块
        __try{
    
        }
        __except (EXCEPTION_CONTINUE_SEARCH){
            printf("B块中的_except被执行!
    ");
        }
    }
    
    int main(){
        __try{
            test(); //test()函数内部会处理异常,所以main中的_except块后的代码不会被执行!
        }
        __except (EXCEPTION_EXECUTE_HANDLER){
            printf("main中的_except被执行!
    ");
    
        }
        system("pause");
        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

    《软件加密技术内幕》,看雪学院

  • 相关阅读:
    css自定义字体
    nuxt按需引入 element-UI、自定义主题色(终极按需引入)
    Vue 拖拽组件 vuedraggable 和 awe-dnd
    css 0.5px
    react取消监听scroll事件
    vue使用jsonp
    nuxt使用QRCode.js 生成二维码
    阻止冒泡
    nuxt.js配置BASE_URL(基本域名)和NODE_ENV(环境变量)
    Numpy基础学习笔记1
  • 原文地址:https://www.cnblogs.com/5iedu/p/5223846.html
Copyright © 2020-2023  润新知