SEH stack 结构探索(2)--- 用户程序构造的 SEH stack 结构

SEH stack 结构探索(2)--- 用户程序构造的 SEH stack 结构


经过上一篇对 prolog 的分析,我们对 SEH 的 stack 结构有大概了解,现在我们回过头来看看在第 2 篇《SEH 机制探讨2 --- 构建 SEH 链》的示例,

但是 __except() 部分有了重大的变化:

int _tmain(int argc, _TCHAR* argv[])
{

        DWORD dwI;
 
        EXCEPTION_RECORD ExceptionRecord;
        CONTEXT Context;

        __try
        {
                dwI = 0;
        }
        __except(ExceptionRecord = *(GetExceptionInformation())->ExceptionRecord,
                 Context = *(GetExceptionInformation())->ContextRecord, 
                EXCEPTION_EXECUTE_HANDLER)
        {
                return 0;
        }

 

        return 0;
}

 

1. SEH stack 的构建

这是用户程序的代码,这个 SEH stack 是在函数头,进入 __try{ } 块之前就已经设好:

int _tmain(int argc, _TCHAR* argv[])
{
011E33D0 55                   push        ebp  
011E33D1 8B EC                mov         ebp,esp  
011E33D3 6A FE                push        0FFFFFFFEh  
011E33D5 68 C8 6B 1E 01       push        offset ___rtc_tzz+1A8h (11E6BC8h)  
011E33DA 68 6E 10 1E 01       push        offset @ILT+105(__except_handler4) (11E106Eh)  
011E33DF 64 A1 00 00 00 00    mov         eax,dword ptr fs:[00000000h]  
011E33E5 50                   push        eax  
011E33E6 81 C4 F0 FB FF FF    add         esp,0FFFFFBF0h  
011E33EC 53                   push        ebx  
011E33ED 56                   push        esi  
011E33EE 57                   push        edi  
011E33EF 8D BD E0 FB FF FF    lea         edi,[ebp-420h]  
011E33F5 B9 02 01 00 00       mov         ecx,102h  
011E33FA B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
011E33FF F3 AB                rep stos    dword ptr es:[edi]  
011E3401 A1 14 70 1E 01       mov         eax,dword ptr [___security_cookie (11E7014h)]  
011E3406 31 45 F8             xor         dword ptr [ebp-8],eax  
011E3409 33 C5                xor         eax,ebp  
011E340B 89 45 E4             mov         dword ptr [ebp-1Ch],eax  
011E340E 50                   push        eax  
011E340F 8D 45 F0             lea         eax,[ebp-10h]  
011E3412 64 A3 00 00 00 00    mov         dword ptr fs:[00000000h],eax  
011E3418 89 65 E8             mov         dword ptr [ebp-18h],esp 

 DWORD dwI;
 
 EXCEPTION_RECORD ExceptionRecord;
 CONTEXT Context;

 __try
011E341B C7 45 FC 00 00 00 00 mov         dword ptr [ebp-4],0 

上面的代码是用户程序里的进入 __try{}前的代码,它和 ntdll32!_SEH_prolog4() 构造的 stack 结构是一致的,如下图所示:

这里同样是用 __security_cookie 值与 [ebp-8] 异或,并且与 ebp 异或后保存起来备用。

这是到目前为止,我们对 VC++ 对 SEH4 实现的了解。当我们逐步深入了解 SEH 机制后,这个图或许有进一步的修改。

 

2. EXCEPTION_POINTERS 指针

到目前为止,我们对 [ebp-14] 里的值还不解。

这里我可以明确地告诉大家:实际上 [ebp-14] 里的值是一个指向 EXCEPTION_POINTERS 结构的指针值。这个指针与 __except() 块是紧密相关的。

现在我不知道该从何说起,是从 __except() 说起呢?,还是先看看 EXCEPTION_POINTERS 结构是什么? 好吧,还是先看看 EXCEPTION_POINTERS 是什么吧。


2.1 EXCEPTION_POINTERS 结构

EXCEPTION_POINTERS 结构只有两个成员,这两个成员是指针:ExceptionRecord 和 ContextRecord

这个结构在 WinNT.h 中定义为:

//
// Typedef for pointer returned by exception_info()
//

typedef struct _EXCEPTION_POINTERS {
    PEXCEPTION_RECORD ExceptionRecord;
    PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

这两个指针指向 EXCEPTION_RECORD 结构和 CONTEXT 结构


2.2 EXCEPTION_RECORD 结构

EXCEPTION_RECORD 是用来记录线程发生异常时的记录信息,在 WinNT.h 定义为:

typedef struct _EXCEPTION_RECORD {
    DWORD    ExceptionCode;
    DWORD ExceptionFlags;
    struct _EXCEPTION_RECORD *ExceptionRecord;
    PVOID ExceptionAddress;
    DWORD NumberParameters;
    ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
    } EXCEPTION_RECORD;

这些信息重要的有:异常码,标志,地址等。异常码在 WinNT.h 中定义了一部分:

#define STATUS_WAIT_0                    ((DWORD   )0x00000000L)    
#define STATUS_ABANDONED_WAIT_0          ((DWORD   )0x00000080L)    
#define STATUS_USER_APC                  ((DWORD   )0x000000C0L)    
#define STATUS_TIMEOUT                   ((DWORD   )0x00000102L)    
#define STATUS_PENDING                   ((DWORD   )0x00000103L)    
#define DBG_EXCEPTION_HANDLED            ((DWORD   )0x00010001L)    
#define DBG_CONTINUE                     ((DWORD   )0x00010002L)    
#define STATUS_SEGMENT_NOTIFICATION      ((DWORD   )0x40000005L)    
#define DBG_TERMINATE_THREAD             ((DWORD   )0x40010003L)    
#define DBG_TERMINATE_PROCESS            ((DWORD   )0x40010004L)    
#define DBG_CONTROL_C                    ((DWORD   )0x40010005L)    
#define DBG_PRINTEXCEPTION_C             ((DWORD   )0x40010006L)    
#define DBG_RIPEXCEPTION                 ((DWORD   )0x40010007L)    
#define DBG_CONTROL_BREAK                ((DWORD   )0x40010008L)    
#define DBG_COMMAND_EXCEPTION            ((DWORD   )0x40010009L)    
#define STATUS_GUARD_PAGE_VIOLATION      ((DWORD   )0x80000001L)    
#define STATUS_DATATYPE_MISALIGNMENT     ((DWORD   )0x80000002L)    
#define STATUS_BREAKPOINT                ((DWORD   )0x80000003L)    
#define STATUS_SINGLE_STEP               ((DWORD   )0x80000004L)    
#define STATUS_LONGJUMP                  ((DWORD   )0x80000026L)    
#define STATUS_UNWIND_CONSOLIDATE        ((DWORD   )0x80000029L)    
#define DBG_EXCEPTION_NOT_HANDLED        ((DWORD   )0x80010001L)    
#define STATUS_ACCESS_VIOLATION          ((DWORD   )0xC0000005L)    
#define STATUS_IN_PAGE_ERROR             ((DWORD   )0xC0000006L)    
#define STATUS_INVALID_HANDLE            ((DWORD   )0xC0000008L)    
#define STATUS_INVALID_PARAMETER         ((DWORD   )0xC000000DL)    
#define STATUS_NO_MEMORY                 ((DWORD   )0xC0000017L)    
#define STATUS_ILLEGAL_INSTRUCTION       ((DWORD   )0xC000001DL)    
#define STATUS_NONCONTINUABLE_EXCEPTION  ((DWORD   )0xC0000025L)    
#define STATUS_INVALID_DISPOSITION       ((DWORD   )0xC0000026L)    
#define STATUS_ARRAY_BOUNDS_EXCEEDED     ((DWORD   )0xC000008CL)    
#define STATUS_FLOAT_DENORMAL_OPERAND    ((DWORD   )0xC000008DL)    
#define STATUS_FLOAT_DIVIDE_BY_ZERO      ((DWORD   )0xC000008EL)    
#define STATUS_FLOAT_INEXACT_RESULT      ((DWORD   )0xC000008FL)    
#define STATUS_FLOAT_INVALID_OPERATION   ((DWORD   )0xC0000090L)    
#define STATUS_FLOAT_OVERFLOW            ((DWORD   )0xC0000091L)    
#define STATUS_FLOAT_STACK_CHECK         ((DWORD   )0xC0000092L)    
#define STATUS_FLOAT_UNDERFLOW           ((DWORD   )0xC0000093L)    
#define STATUS_INTEGER_DIVIDE_BY_ZERO    ((DWORD   )0xC0000094L)    
#define STATUS_INTEGER_OVERFLOW          ((DWORD   )0xC0000095L)    
#define STATUS_PRIVILEGED_INSTRUCTION    ((DWORD   )0xC0000096L)    
#define STATUS_STACK_OVERFLOW            ((DWORD   )0xC00000FDL)    
#define STATUS_DLL_NOT_FOUND             ((DWORD   )0xC0000135L)    
#define STATUS_ORDINAL_NOT_FOUND         ((DWORD   )0xC0000138L)    
#define STATUS_ENTRYPOINT_NOT_FOUND      ((DWORD   )0xC0000139L)    
#define STATUS_CONTROL_C_EXIT            ((DWORD   )0xC000013AL)    
#define STATUS_DLL_INIT_FAILED           ((DWORD   )0xC0000142L)    
#define STATUS_FLOAT_MULTIPLE_FAULTS     ((DWORD   )0xC00002B4L)    
#define STATUS_FLOAT_MULTIPLE_TRAPS      ((DWORD   )0xC00002B5L)    
#define STATUS_REG_NAT_CONSUMPTION       ((DWORD   )0xC00002C9L)    
#define STATUS_STACK_BUFFER_OVERRUN      ((DWORD   )0xC0000409L)    
#define STATUS_INVALID_CRUNTIME_PARAMETER ((DWORD   )0xC0000417L)    
#define STATUS_ASSERTION_FAILURE         ((DWORD   )0xC0000420L)    
#if defined(STATUS_SUCCESS) || (_WIN32_WINNT > 0x0500) || (_WIN32_FUSION >= 0x0100) 
#define STATUS_SXS_EARLY_DEACTIVATION    ((DWORD   )0xC015000FL)    
#define STATUS_SXS_INVALID_DEACTIVATION  ((DWORD   )0xC0150010L)   

上现在非常常见的:ACCESS_VIOLATION 访问违例异常,它的值是 0xC0000005


2.3 CONTEXT 结构

这个结构很简单但较长,我还是打算在这里贴出来,好有个直观的认识,它在 WinNT.h 定义为:

typedef struct _CONTEXT {

    //
    // The flags values within this flag control the contents of
    // a CONTEXT record.
    //
    // If the context record is used as an input parameter, then
    // for each portion of the context record controlled by a flag
    // whose value is set, it is assumed that that portion of the
    // context record contains valid context. If the context record
    // is being used to modify a threads context, then only that
    // portion of the threads context will be modified.
    //
    // If the context record is used as an IN OUT parameter to capture
    // the context of a thread, then only those portions of the thread's
    // context corresponding to set flags will be returned.
    //
    // The context record is never used as an OUT only parameter.
    //

    DWORD ContextFlags;

    //
    // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
    // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT
    // included in CONTEXT_FULL.
    //

    DWORD   Dr0;
    DWORD   Dr1;
    DWORD   Dr2;
    DWORD   Dr3;
    DWORD   Dr6;
    DWORD   Dr7;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
    //

    FLOATING_SAVE_AREA FloatSave;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_SEGMENTS.
    //

    DWORD   SegGs;
    DWORD   SegFs;
    DWORD   SegEs;
    DWORD   SegDs;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_INTEGER.
    //

    DWORD   Edi;
    DWORD   Esi;
    DWORD   Ebx;
    DWORD   Edx;
    DWORD   Ecx;
    DWORD   Eax;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_CONTROL.
    //

    DWORD   Ebp;
    DWORD   Eip;
    DWORD   SegCs;              // MUST BE SANITIZED
    DWORD   EFlags;             // MUST BE SANITIZED
    DWORD   Esp;
    DWORD   SegSs;

    //
    // This section is specified/returned if the ContextFlags word
    // contains the flag CONTEXT_EXTENDED_REGISTERS.
    // The format and contexts are processor specific
    //

    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

} CONTEXT;

typedef CONTEXT *PCONTEXT;

用来保存当线程发生异常时的 CPU 上下文环境。


2.4 GetExceptionInfomation 内置函数

上面介绍的几个结构是为了哄托出这个 GetExceptionInformation() 函数,这个函数是 VC++ 内置的函数,在 MSDN 上说它是一个宏:http://msdn.microsoft.com/en-us/library/ms679357(VS.85).aspx

LPEXCEPTION_POINTERS GetExceptionInformation(void);

使用它时,编译器会嵌入几行代码而不是生成函数调用语句。 它返回一个 EXCEPTION_POINTERS 结构的指针值,这个 EXCEPTION_POINTERS 结构就是前面所介绍的,它的两个成员分别指向 EXCEPTION_RECORD 结构和 CONTEXT 结构

GetExceptionInformation() 只能用在 __except() 块的 括号 内,否则编译器会产生错误。

下面回到前面的示例代码中的 __except() 块:

        __except(ExceptionRecord = *(GetExceptionInformation())->ExceptionRecord,
                 Context = *(GetExceptionInformation())->ContextRecord, 
                EXCEPTION_EXECUTE_HANDLER)
        {
                return 0;
        }

__except() 括号里使用了 GetExceptionInformation(),可以看出由于 GetExceptionInformation() 返回 EXCEPTION_POINTERS 指针,因此:

  • 用这个指针值来获取 ExceptionRecord 和 ContextRecord 这两个指针值
  • 然后通过这两个指针值来获取 EXCEPTION_RECORD 结构和 CONTEXT 结构的内容

 

3. 揭开 [ebp-14] 的面纱

到现在,我们应该要揭开 [ebp-14] 的神秘面纱了,前面说过这个值就是 EXCEPTION_POINTERS 的指针值。

下面看看是不是这样?这种情况下,只有看反汇编结果才能确定:

 __except(ExceptionRecord = *(GetExceptionInformation())->ExceptionRecord,
   Context = *(GetExceptionInformation())->ContextRecord, 
  EXCEPTION_EXECUTE_HANDLER)
011E3432 8B 45 EC             mov         eax,dword ptr [ebp-14h]                      // EXCEPTION_POINTERS 指针
011E3435 8B 30                mov         esi,dword ptr [eax]                          // 获取 ExceptionRecord 值  
011E3437 B9 14 00 00 00       mov         ecx,14h  
011E343C 8D 7D 84             lea         edi,[ebp-7Ch]  
011E343F F3 A5                rep movs    dword ptr es:[edi],dword ptr [esi]  
011E3441 8B 4D EC             mov         ecx,dword ptr [ebp-14h]                      // EXCEPTION_POINTERS 指针
011E3444 8B 71 04             mov         esi,dword ptr [ecx+4]                        // 获取 ContextRecord 值  
011E3447 B9 B3 00 00 00       mov         ecx,0B3h  
011E344C 8D BD B0 FC FF FF    lea         edi,[ebp-350h]  
011E3452 F3 A5                rep movs    dword ptr es:[edi],dword ptr [esi]  
011E3454 B8 01 00 00 00       mov         eax,1  
$LN7:
011E3459 C3                   ret  
$LN6:
011E345A 8B 65 E8             mov         esp,dword ptr [ebp-18h]  
 {
  return 0;

代码中从 [ebp-14] 处得到 EXCEPTION_POINTERS 结构指针值,在第 1 个成员中得到 ExceptionRecord 值,它是指向 EXCEPTION_RECORD 结构的指针。然后从第 2 个成员里得到 ContextRecord 值,它是指向 CONTEXT 结构的指针值。

然后,分别将这些复制到用户的 stack 中,对应 C 语句的赋值语句。对 EXCEPTION_RECORD 结构复制 14h 字节。对 CONTEXT 结构复制 B3h 字节。

于是我们 SEH 的 stack 结构有了进一步的认识,如图

4. GetExceptionCode 内置函数

与 GetExceptionInformation() 一样 GetExceptionCode() 也是内置函数,用来获得线程刚刚发生的异常码。

GetExceptionCode() 生成的代码类似:

mov eax, dword ptr [ebp-14h]          // get EXCEPTION_POINTERS
mov eax, dword ptr [eax]              // get ExceptionRecord
mov ecx, dword ptr [eax]              // get ExceptionCode

GetExceptionCode() 只能用在 __except(){} 块的括号或者异常处理程序的代码({} 块内)

 

上一页  目录  下一页


版权 mik 所有,转载请注明出处!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值