剖析结构化异常处理(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”>当一个线程出现谬误时,操作系统为您一个空子吃告知这荒唐。说得重复明亮有即是,当一个线程出现错误时,操作系统调用用户定义之一个回调函数。这个回调函数可以开她想做的整整。例如它好修复错误,或者它们为足以播放一段音乐。无论回调函数开啊,它最终还使回到一个值来告诉系统下同样步做啊。(这不是很准确,但就这来说非常相近。)

  • style=”font-size: 14pt”>当你的某某同部分代码出错时,系统更回调你的其余代码,那么是回调函数看起是什么样子吗?换句话说,你想了解关于大什么品种的信息也?实际上这并无重要,因为Win32早就同而做了决定。异常的回调函数的规范如下:

    //TIB的 第一只DWORD是一个对准线程的EXCEPTION_REGISTARTION结构的指针。
    //在基于Intel处理器的Win32平台上,FS寄存器总是 指向当前底TIB。
    //因此在FS:[0]处你得找到一个指向EXCEPTION_REGISTARTION结构的指针

    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”>这个原型来自专业的Win32峰文件EXCPT.H,乍看起有些费解。但如果你仔细看,它并无是大麻烦掌握。首先,忽小掉返回值的种类(EXCEPTION_DISPOSITION)。你得的骨干信息就是是她是一个受作_except_handler而且带有四单参数的函数。

  • style=”font-size: 14pt”>这个函数的首先个参数是一个针对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定义,你得得一个雅代码列
    表。例如有人数犹深熟悉的STATUS_ACCESS_VIOLATION的代码是0xC0000005。一个重新完善的良代码列表可以当
    Windows NT
    DDK的NTSTATUS.H倍受找到。此布局的季只分子是坏来的地址。其它成员暂时可以忽略。

  • style=”font-size: 14pt”>_except_handler函数的次只参数是一个指向establisher帧结构的指针。它是SEH中一个生死攸关的参数,但是今若可忽略她。
  • 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回调函数接收及操作系统传递过来的森起价之信,例如异常的类和有的地址。使用这些信息,异常回调函数就会操纵下同样步做啊。
  • 本着
    我来说,现在就描写一个能亮_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_RECORD。唉,没有一个地方能找到
    _EXCEPTION_REGISTRATION_RECORD的定义,所以我不得不使用EXSUP.INC中是汇编语言的结构定义。这是自家前面所说
    SEH未公开的一个证据。(读者可采用本调试器,如KD或SoftICE并加载调试符号来查此结构的定义。

  • 生图是在KD中的结果:

    style=”font-size: 14pt”>图片 1

  • 下图是当SoftICE中之结果: 

    style=”font-size: 14pt”>图片 2

  • style=”font-size: 14pt”> 当好来时,操作系统是怎晓得到乌去调用回调函数的吗?实际
    上,EXCEPTION_REGISTARTION结构由个别单地段组成,第一单你本好忽略。第二独域handler,包含一个指向
    _except_handler回调函数的指针。这叫您去答案再近一点,但现的题材是,操作系统及哪里去寻找
    EXCEPTION_REGISTATRION结构呢?

  • style=”font-size: 14pt”>要回答这个题目,记住结构化异常处理是根据线程的当下一点是深有效之。也就是说,每个线程有它和谐之酷处理回调函数。在1996年五月底Under
    The
    Hood专栏中,我介绍了一个要害的Win32数据结构——线程信息块(Thread
    Information/Environment
    Block,TIB或TEB)
    。这个布局的少数地段于Windows NT、Windows
    95、Win32s和OS/2上是平等的。TIB的
    第一个DWORD是一个针对线程的EXCEPTION_REGISTARTION结构的指针。在冲Intel处理器的Win32阳台上,FS寄存器总是
    指向当前底TIB。因此当FS:[0]处你得找到一个指向EXCEPTION_REGISTARTION结构的指针。

  • 现在为止,我们曾来矣足足的认识。当好出常,系统查找出错线程的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”>图片 3

 图片 4

  •  把
    这些有些片知识拼凑起来,我勾勒了一个多少程序来演示上面这个对操作系统层面的结构化异常处理的简化描述,如图3的MYSEH.CPP所示。它独自生个别个函数。
    main函数使用了三单内联汇编块。第一只内联汇编块通过个别独PUSH指令(“PUSH
    handler”和“PUSH
    FS:[0]”)在库房上创立了一个EXCEPTION_REGISTRATION结构。PUSH
    FS:[0]当下长达指令保存了原先底FS:[0]受到的价当之布局的平片段,但立刻在这时候并无根本。重要的凡现在堆栈上生一个8字节的
    EXCEPTION_REGISTRATION结构。紧接着的产一致久指令(MOV
    FS:[0],ESP)使线程信息块被的第一独DWORD指向了初的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中寻找,然后发现了一个对准
    EXCEPTION_REGISTRATION结构的指针。在MYSEH.CPP中,在这组织中生一个对准_except_handler函数的指针。
    系统然后将所要的季单参数(我于头里已经说过)压入堆栈,接着调用_except_handler函数。

  • 旦进入_except_handler,这段代码首先通过一个printf语句表明“哈!是自我于其改变到此地的!”。接着,_except_handler
    修复了诱惑错误的题目——即EAX寄存器指向了一个非能够写的内存地址(地址0)。修复方法就是改CONTEXT结构被的EAX的价如果她对一个兴写的
    位置。在是大概的次第中,我特别为夫设置了一个DWORD变量(scratch)。_except_handler函数最后的动作是回到
    ExceptionContinueExecution这个价,它于EXCPT.H文件被定义。

  • 操作系统看到返回值为ExceptionContinueExecution时,它用其知道呢而早就修复了问题,而引起错误的那么漫长指令应该于再履行。由
    于自家之_except_handler函数已经让EAX寄存器指向一个合法的内存,MOV
    [EAX],1限令再次实施,这次main函数一切正常。看,这吗并无复杂,不是啊?

相关文章