ACCESS解析结构化至极处理(SEH)(第②部分)

style=”font-size: 14pt; background-color: #ff0000″>参考大拿总括自个儿: style=”background-color: #ff0000″>http://www.cppblog.com/weiym/archive/2015/02/27/209884.html

  • style=”font-size: 14pt”>当二个线程出现谬误时,操作系统给您3个时机被报告这些荒唐。说得更精晓一些便是,当1个线程出现谬误时,操作系统调用用户定义的二个回调函数。那几个回调函数能够做它想做的满贯。例如它可以修复错误,大概它也足以播放一段音乐。无论回调函数做什么,它说到底都要赶回一个值来告诉系统下一步做什么样。(那不是可怜准儿,但就此刻来说分外相近。)

  • style=”font-size: 14pt”>当你的某一有个别代码出错开上下班时间,系统再回调你的别的代码,那么那几个回调函数看起来是什么样子吧?换句话说,你想驾驭有关那四个什么项目标音信吗?实际上那并不重大,因为Win32一度替你做了控制。十分的回调函数的样子如下:

    //TIB的 第③个DWO卡宴D是多个对准线程的EXCEPTION_REGISTA索罗德TION结构的指针。
    //在基于AMD处理器的Win32阳台上,FS寄存器总是 指向当前的TIB。
    //由此在FS:[0]处你可以找到1个指向EXCEPTION_REGISTA奥迪Q5TION结构的指针

    typedef struct _EXCEPTION_REGISTRATION_RECORD
    {
    PEXCEPTION_REGISTRATION_RECORD Next;

    PEXCEPTION_DISPOSITION Handler;

    } EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

enum EXCEPTION_DISPOSITION 
typedef enum _EXCEPTION_DISPOSITION
{
         ExceptionContinueExecution       = 0,
         ExceptionContinueSearch          = 1,
         ExceptionNestedException         = 2,
         ExceptionCollidedUnwind          = 3

} EXCEPTION_DISPOSITION;


//异常的回调函数的样子如下:
EXCEPTION_DISPOSITION __cdecl _except_handler( 
                                                struct _EXCEPTION_RECORD *ExceptionRecord,

                                                void * EstablisherFrame,

                                                struct _CONTEXT *ContextRecord,

                                                void * DispatcherContext
                                              );
  • style=”font-size: 14pt”>这些原型来自专业的Win叁12头文件EXCPT.H,乍看起来有点费解。但要是您精心看,它并不是很难了然。首先,忽略掉再次回到值的品类(EXCEPTION_DISPOSITION)。你取得的基本新闻就是它是二个叫作_except_handler同时包涵八个参数的函数。

  • style=”font-size: 14pt”>这么些函数的首先个参数是2个针对EXCEPTION_RECORD布局的指针。那几个结构在WINNT.H中定义,如下所示:

    typedef struct _EXCEPTION_RECORD {

                                  DWORD ExceptionCode;
    
                                  DWORD ExceptionFlags;
    
                                  struct _EXCEPTION_RECORD *ExceptionRecord;
    
                                  PVOID ExceptionAddress;
    
                                  DWORD NumberParameters;
    
                                  DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
    
                            } EXCEPTION_RECORD;
    
  • 本条结构中的
    ExcepitonCode成员是给予非常的代码。通过在WINNT.H中检索以“STATUS_”起先的#define定义,你能够取得3个百般代码列
    表。例如全数人都非常熟知的STATUS_ACCESS_VIOLATION的代码是0xC0000005。二个更健全的13分代码列表能够在
    Windows NT
    DDK的NTSTATUS.H中找到。此布局的第多个分子是至极发生的地址。其余成员暂且能够忽略。

  • style=”font-size: 14pt”>_except_handler函数的第二个参数是二个指向establisher帧结构的指针。它是SEH中3个至关心器重要的参数,可是以往您能够忽略它。
  • style=”font-size: 14pt”>except_handler回调函数的第七个参数是三个对准CONTEXT
    构的指针。此布局在WINNT.H中定义,它代表有些特定线程的寄存器值。图1展现了CONTEXT布局的积极分子。当用于SEH时,CONTEXT布局意味着
    极度发生时寄存器的值。顺便说一下,这些CONTEXT结构就是GetThreadContext和SetThreadContext那八个API中动用
    的万分CONTEXT结构。

    typedef struct _CONTEXT
    {
    DWORD ContextFlags;
    DWORD Dr0;
    DWORD Dr1;
    DWORD Dr2;
    DWORD Dr3;
    DWORD Dr6;
    DWORD Dr7;
    FLOATING_SAVE_AREA FloatSave;
    DWORD SegGs;
    DWORD SegFs;
    DWORD SegEs;
    DWORD SegDs;
    DWORD Edi;
    DWORD Esi;
    DWORD Ebx;
    DWORD Edx;
    DWORD Ecx;
    DWORD Eax;
    DWORD Ebp;
    DWORD Eip;
    DWORD SegCs;
    DWORD EFlags;
    DWORD Esp;
    DWORD SegSs;
    } CONTEXT;

  • style=”font-size: 14pt”>_except_handler回调函数的第几个参数被叫作DispatcherContext。它一时半刻也足以被忽视。

  • style=”font-size: 14pt”>到近期完毕,你脑子中早已有了一个当非常爆发时会被操作系统调用的回调函数的模子了。那一个回调函数带八个参数,当中四个针对任何组织。在这个组织中,一些域相比较重要,其余的就不那么首要。那里的最主假诺_exept_handler回调函数接收到操作系统传递过来的众多有价值的音讯,例如卓殊的品类和爆发的地点。使用这几个音讯,很是回调函数就能操纵下一步做哪些。

  • 作者的话,未来就写1个力所能及显得_except_handler功用的样例程序是再摄人心魄可是的了。不过我们还缺乏一些要害消息。特别是,当错误爆发时操作系
    统是怎么通晓到何地去调用那些回调函数的吗?答案是还有三个称为EXCEPTION_REGISTRATION的结构。通篇你都会看出这几个组织,所以不要
    跳过这一有的。笔者唯一能找到的EXCEPTION_REGISTRATION布局的正统定义是在Visual
    C++运维时库源代码中的EXSUP.INC文件中:

    typedef struct _EXCEPTION_REGISTRATION_RECORD
    {

     PEXCEPTION_REGISTRATION_RECORD Next;
     
     PEXCEPTION_DISPOSITION Handler;
     
    

    } EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

  • 那几个布局在WINNT.H的NT_TIB结构的定义中被称为_EXCEPITON_REGISTARTION_RECO大切诺基D。唉,没有三个地点能够找到
    _EXCEPTION_REGISTRATION_RECO逍客D的概念,所以笔者只能使用EXSUP.INC中那个汇编语言的组织定义。那是本身前面所说
    SEH未公开的一个凭证。(读者能够使用基本调节和测试器,如KD或SoftICE并加载调节和测试符号来查看这些结构的概念。

  • 下图是在KD中的结果:

    style=”font-size: 14pt”>ACCESS 1

  • 下图是在SoftICE中的结果: 

    style=”font-size: 14pt”>ACCESS 2

  • style=”font-size: 14pt”> 当至极产生时,操作系统是何等精晓到哪里去调用回调函数的啊?实际
    上,EXCEPTION_REGISTA奥迪Q7TION结构由八个域组成,第2个你将来得以忽略。第3个域handler,包括三个指向
    _except_handler回调函数的指针。那让您离答案更近一点,但今后的标题是,操作系统到何地去找
    EXCEPTION_REGISTATRION结构呢?

  • style=”font-size: 14pt”>要应对这么些难题,记住结构化卓殊处理是依据线程的那或多或少是可怜实用的。也正是说,各类线程有它和谐的相当处理回调函数。在一九九七年二月的Under
    The
    Hood专栏中,小编介绍了二个重点的Win32数据结构——线程新闻块(Thread
    Information/Environment
    Block,TIB或TEB)
    。那个组织的一些域在Windows NT、Windows
    9伍 、Win32s和OS/2上是均等的。TIB的
    第二个DWO酷路泽D是3个针对性线程的EXCEPTION_REGISTALX570TION结构的指针。在根据英特尔处理器的Win32平台上,FS寄存器总是
    指向当前的TIB。由此在FS:[0]处你能够找到1个指向EXCEPTION_REGISTALacrosseTION结构的指针。

  • 今后告竣,大家早已有了足足的认识。当分外产生时,系统查找出错线程的TIB,获取二个指向EXCEPTION_REGISTRATION结构的指针。在
    这几个协会中有三个对准_except_handler回调函数的指针。今后操作系统已经精晓了十足的音讯去调用_except_handler函数,如图
    2所示

    typedef struct _TEB
    {

    NT_TIB         Tib;                        /* 00h */
        typedef struct _NT_TIB 
        {
            struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
                typedef struct _EXCEPTION_REGISTRATION_RECORD
                {
                     PEXCEPTION_REGISTRATION_RECORD Next;
                     
                     PEXCEPTION_DISPOSITION Handler;
                            enum EXCEPTION_DISPOSITION 
                            typedef enum _EXCEPTION_DISPOSITION
                            {
                                     ExceptionContinueExecution     = 0,
                                     ExceptionContinueSearch         = 1,
                                     ExceptionNestedException         = 2,
                                     ExceptionCollidedUnwind         = 3
    
                            } EXCEPTION_DISPOSITION;
    
                            //异常的回调函数的样子如下:
                            EXCEPTION_DISPOSITION __cdecl _except_handler( 
                                                                            struct _EXCEPTION_RECORD *ExceptionRecord,
                                                                                    typedef struct _EXCEPTION_RECORD 
                                                                                    {
                                                                                           DWORD ExceptionCode;

                                                                                           DWORD ExceptionFlags;

                                                                                           struct _EXCEPTION_RECORD *ExceptionRecord;

                                                                                           PVOID ExceptionAddress;

                                                                                           DWORD NumberParameters;

                                                                                           DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

                                                                                     } EXCEPTION_RECORD;

                                                                            void * EstablisherFrame,

                                                                            struct _CONTEXT *ContextRecord,
                                                                                    typedef struct _CONTEXT
                                                                                    {
                                                                                        DWORD ContextFlags;
                                                                                        DWORD Dr0;
                                                                                        DWORD Dr1;
                                                                                        DWORD Dr2;
                                                                                        DWORD Dr3;
                                                                                        DWORD Dr6;
                                                                                        DWORD Dr7;
                                                                                        FLOATING_SAVE_AREA FloatSave;
                                                                                        DWORD SegGs;
                                                                                        DWORD SegFs;
                                                                                        DWORD SegEs;
                                                                                        DWORD SegDs;
                                                                                        DWORD Edi;
                                                                                        DWORD Esi;
                                                                                        DWORD Ebx;
                                                                                        DWORD Edx;
                                                                                        DWORD Ecx;
                                                                                        DWORD Eax;
                                                                                        DWORD Ebp;
                                                                                        DWORD Eip;
                                                                                        DWORD SegCs;
                                                                                        DWORD EFlags;
                                                                                        DWORD Esp;
                                                                                        DWORD SegSs;
                                                                                    } CONTEXT;

                                                                            void * DispatcherContext
                                                                          );
                     
                } EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
            
            PVOID StackBase;
            PVOID StackLimit;
            PVOID SubSystemTib;
            
            _ANONYMOUS_UNION union 
            {
                PVOID FiberData;
                DWORD Version;
            } DUMMYUNIONNAME;
            
            PVOID ArbitraryUserPointer;
            
            struct _NT_TIB *Self;
            
        } NT_TIB,*PNT_TIB;
        
    PVOID EnvironmentPointer;               /* 1Ch */
    CLIENT_ID Cid;                          /* 20h */         //进程ID
    PVOID ActiveRpcHandle;                  /* 28h */
    PVOID ThreadLocalStoragePointer;        /* 2Ch */
    struct _PEB *ProcessEnvironmentBlock;   /* 30h */         //指向PEB
    ULONG LastErrorValue;                   /* 34h */
    ULONG CountOfOwnedCriticalSections;     /* 38h */
    PVOID CsrClientThread;                  /* 3Ch */
    struct _W32THREAD* Win32ThreadInfo;     /* 40h */
    ULONG User32Reserved[0x1A];             /* 44h */
    ULONG UserReserved[5];                  /* ACh */
    PVOID WOW32Reserved;                    /* C0h */
    LCID CurrentLocale;                     /* C4h */
    ULONG FpSoftwareStatusRegister;         /* C8h */
    PVOID SystemReserved1[0x36];            /* CCh */
    LONG ExceptionCode;                     /* 1A4h */
    struct _ACTIVATION_CONTEXT_STACK *ActivationContextStackPointer; /* 1A8h */
    UCHAR SpareBytes1[0x28];                /* 1ACh */
    GDI_TEB_BATCH GdiTebBatch;              /* 1D4h */
    CLIENT_ID RealClientId;                 /* 6B4h */
    PVOID GdiCachedProcessHandle;           /* 6BCh */
    ULONG GdiClientPID;                     /* 6C0h */
    ULONG GdiClientTID;                     /* 6C4h */
    PVOID GdiThreadLocalInfo;               /* 6C8h */
    ULONG Win32ClientInfo[62];              /* 6CCh */
    PVOID glDispatchTable[0xE9];            /* 7C4h */
    ULONG glReserved1[0x1D];                /* B68h */
    PVOID glReserved2;                      /* BDCh */
    PVOID glSectionInfo;                    /* BE0h */
    PVOID glSection;                        /* BE4h */
    PVOID glTable;                          /* BE8h */
    PVOID glCurrentRC;                      /* BECh */
    PVOID glContext;                        /* BF0h */
    NTSTATUS LastStatusValue;               /* BF4h */
    UNICODE_STRING StaticUnicodeString;     /* BF8h */
    WCHAR StaticUnicodeBuffer[0x105];       /* C00h */
    PVOID DeallocationStack;                /* E0Ch */
    PVOID TlsSlots[0x40];                   /* E10h */
    LIST_ENTRY TlsLinks;                    /* F10h */
    PVOID Vdm;                              /* F18h */
    PVOID ReservedForNtRpc;                 /* F1Ch */
    PVOID DbgSsReserved[0x2];               /* F20h */
    ULONG HardErrorDisabled;                /* F28h */
    PVOID Instrumentation[14];              /* F2Ch */
    PVOID SubProcessTag;                    /* F64h */
    PVOID EtwTraceData;                     /* F68h */
    PVOID WinSockData;                      /* F6Ch */
    ULONG GdiBatchCount;                    /* F70h */
    BOOLEAN InDbgPrint;                     /* F74h */
    BOOLEAN FreeStackOnTermination;         /* F75h */
    BOOLEAN HasFiberData;                   /* F76h */
    UCHAR IdealProcessor;                   /* F77h */
    ULONG GuaranteedStackBytes;             /* F78h */
    PVOID ReservedForPerf;                  /* F7Ch */
    PVOID ReservedForOle;                   /* F80h */
    ULONG WaitingOnLoaderLock;              /* F84h */
    ULONG SparePointer1;                    /* F88h */
    ULONG SoftPatchPtr1;                    /* F8Ch */
    ULONG SoftPatchPtr2;                    /* F90h */
    PVOID *TlsExpansionSlots;               /* F94h */
    ULONG ImpersionationLocale;             /* F98h */
    ULONG IsImpersonating;                  /* F9Ch */
    PVOID NlsCache;                         /* FA0h */
    PVOID pShimData;                        /* FA4h */
    ULONG HeapVirualAffinity;               /* FA8h */
    PVOID CurrentTransactionHandle;         /* FACh */
    PTEB_ACTIVE_FRAME ActiveFrame;          /* FB0h */
    PVOID FlsData;                          /* FB4h */
    UCHAR SafeThunkCall;                    /* FB8h */
    UCHAR BooleanSpare[3];                  /* FB9h */
} TEB, *PTEB; 

style=”font-size: 14pt”>ACCESS 3

 ACCESS 4

  •  把
    这几个小块知识拼凑起来,作者写了二个小程序来演示上边这几个对操作系统层面包车型地铁结构化相当处理的简化描述,如图3的MYSEH.CPP所示。它唯有多个函数。
    main函数使用了多个内联系汇率编块。第①个内联系汇率编块通过多少个PUSH指令(“PUSH
    handler”和“PUSH
    FS:[0]”)在库房上创造了2个EXCEPTION_REGISTRATION结构。PUSH
    FS:[0]那条指令保存了此前的FS:[0]中的值作为那么些布局的一有个别,但那在此时并不重大。首要的是前天堆栈上有3个8字节的
    EXCEPTION_REGISTRATION结构。紧接着的下一条指令(MOV
    FS:[0],ESP)使线程新闻块中的第1个DWORAV4D指向了新的EXCEPTION_REGISTRATION结构。(注意堆栈操作)。

    #include “stdafx.h”
    #define WIN32_LEAN_AND_MEAN
    #include
    #include

    DWORD scratch;

    EXCEPTION_DISPOSITION __cdecl _except_handler(

                                                struct _EXCEPTION_RECORD *ExceptionRecord,
                                                void * EstablisherFrame,
                                                struct _CONTEXT *ContextRecord,
                                                void * DispatcherContext 
                                              )
    

    {

    unsigned i;
    // 指明是我们让流程转到我们的异常处理程序的
    printf( "Hello from an exception handler\n" );
    // 改变CONTEXT结构中EAX的值,以便它指向可以成功进写操作的位置
    ContextRecord-> = (DWORD)&scratch;
    // 告诉操作系统重新执行出错的指令
    return ExceptionContinueExecution;
    

    }

    int main()
    {

    DWORD handler = (DWORD)_except_handler;
    __asm
    { 
        // 创建EXCEPTION_REGISTRATION结构:
        push handler        // handler函数的地址
        push FS:[0]            // 前一个handler函数的地址
        mov FS:[0],ESP        // 安装新的EXECEPTION_REGISTRATION结构
    }
    __asm
    {
        mov eax,0            // 将EAX清零
        mov [eax], 1        // 写EAX指向的内存从而故意引发一个错误
    }
    printf( "After writing!\n" );
    __asm
    { 
        // 移去我们的EXECEPTION_REGISTRATION结构
        mov eax,[ESP]        // 获取前一个结构
        mov FS:[0], EAX        // 安装前一个结构
        add esp, 8            // 将我们的EXECEPTION_REGISTRATION弹出堆栈
    }
    return 0;
    

    }

  • 若是你想精晓本身怎么把EXCEPTION_REGISTRATION结构成立在仓库上而不是利用全局变量,作者有二个很好的理由能够表达它。实际上,当您使
    用编写翻译器的__try/__except语法结构时,编写翻译器本人也把EXCEPTION_REGISTRATION结构创设在库房上。我只是简短地向你体现了要是使用__try/__except时编写翻译器做法的简化版。


  • 到main函数,第二个__asm块通过先把EAX寄存器清零(MOV
    EAX,0)然后把此寄存器的值作为内部存款和储蓄器地址让下一条指令(MOV
    [EAX],1)向此地方写入数据而故意引发一个漏洞非常多。最终的__asm块移除那么些简单的要命处理程序:它首先复苏了FS:[0]中先前的始末,然后把
    EXCEPTION_REGISTRATION结构弹出堆栈(ADD ESP,8)。
  • 未来假使你运营MYSEH.EXE,就会看到任何经过。当MOV
    [EAX],1那条指令执行时,它吸引二个造访违法。系统在FS:[0]处的TIB中搜寻,然后发现了3个针对
    EXCEPTION_REGISTRATION结构的指针。在MYSEH.CPP中,在这一个结构中有一个针对_except_handler函数的指针。
    系统然后把所需的多个参数(作者在近来早已说过)压入堆栈,接着调用_except_handler函数。

  • 旦进入_except_handler,那段代码首先通过二个printf语句证明“哈!是自笔者让它转到那里的!”。接着,_except_handler
    修复了吸引错误的题材——即EAX寄存器指向了3个不能够写的内部存款和储蓄器地址(地址0)。修复方法正是改变CONTEXT结构中的EAX的值使它指向2个同意写的
    地点。在那么些不难的先后中,笔者尤其为此设置了八个DWOOdysseyD变量(scratch)。_except_handler函数最后的动作是回到
    Exception孔蒂nueExecution这些值,它在EXCPT.H文件中定义。

  • 操作系统看到重返值为ExceptionContinueExecution时,它将其理解为你已经修复了难题,而滋生错误的那条指令应该被另行履行。由
    于自个儿的_except_handler函数已经让EAX寄存器指向一个法定的内部存款和储蓄器,MOV
    [EAX],1下令再次实施,这一次main函数一切通常。看,那也并不复杂,不是啊?

 

相关文章