用户模拟异常的记录

用户模拟异常的记录

  我们现在来分析一下用户模拟异常

 

1. 测试代码

 1 #include "pch.h"
 2 #include <iostream>
 3 void exceptiontest() {
 4     throw 1;
 5 
 6 }
 7 int main()
 8 {
 9 
10     exceptiontest();
11 }

 

二、分析过程

1. 使用 visual Studio 在 "throw 1"处下断点启动调试

 throw 1;
  00D91808  mov         dword ptr [ebp-0C8h],1  
  00D91812  push        offset __TI1H (0D99018h)  
  00D91817  lea         eax,[ebp-0C8h]  
  00D9181D  push        eax  
  00D9181E  call        __CxxThrowException@8 (0D9139Dh) 

 可以看到其首先进行了两个步骤:

  1)创建了一个局部变量1。

  2)向__CxxThrowException@8函数传入了一个 偏移地址 offset __TI1H (0D99018h) ,和 局部变量1 的地址。

 

2. 分析 _CxxThrowException函数传入参数:

  _CxxThrowException(
        void*           pExceptionObject,   // The object thrown
        _ThrowInfo*     pThrowInfo          // Everything we need to know about it
  )

  通过分析参数我们知道:

  1)第一个传入的一个指针pThrowInfo,我们需要知道该异常的有关信息(即 _EXCEPTION_RECORD类似模板)

  2)第二个是异常地址(1)

 

3. 分析在_CxxThrowException函数创建的一个结构体

static const EHExceptionRecord ExceptionTemplate = { // A generic exception record
            EH_EXCEPTION_NUMBER,            // Exception number
            EXCEPTION_NONCONTINUABLE,       // Exception flags (we don't do resume)
            nullptr,                           // Additional record (none)
            nullptr,                           // Address of exception (OS fills in)
            EH_EXCEPTION_PARAMETERS,        // Number of parameters
            {   EH_MAGIC_NUMBER1,           // Our version control magic number
                nullptr,                       // pExceptionObject
                nullptr,
#if EH_EXCEPTION_PARAMETERS == 4
                nullptr                        // Image base of thrown object
#endif
            }                      // pThrowInfo
        };
        EHExceptionRecord ThisException = ExceptionTemplate;    // This exception
61409708  mov         ecx,8  
6140970D  mov         esi,offset ExceptionTemplate (61401588h)  
61409712  lea         edi,[ThisException]  
61409715  rep movs    dword ptr es:[edi],dword ptr [esi]  

        //
        // Fill in the blanks:
        //
        ThisException.params.pExceptionObject = pExceptionObject;
61409770  mov         eax,dword ptr [pExceptionObject]  
61409773  mov         dword ptr [ebp-1Ch],eax  
        ThisException.params.pThrowInfo = pTI;
61409776  mov         ecx,dword ptr [pTI]  61409779  mov         dword ptr [ebp-18h],ecx  
#if _EH_RELATIVE_TYPEINFO
        PVOID ThrowImageBase = RtlPcToFileHeader((PVOID)pTI, &ThrowImageBase);
        ThisException.params.pThrowImageBase = ThrowImageBase;
#endif

  1)创建结构体的反汇编:如上面反汇编代码,lea edi,[ebp-x],使用 rep movs 一共传入8次(ecx),看上面结构体正好对起来。

  2)之后填写结构体,可以看到有异常号,发生异常时的信息,以及某些异常地址。

 

4. _CxxThrowException函数的作用

1 RaiseException( 
2     ThisException.ExceptionCode,
3     ThisException.ExceptionFlags,
4     ThisException.NumberParameters,
5     (PULONG_PTR)&ThisException.params );
6 }
  1)通过分析其函数最后进入一个 RaiseException 函数中。
   2)因此我们可以分析出该函数的作用是打包上层异常信息,创建 ThisException 结构,将其成员传递给 RaiseException。
 
5. RaiseException 函数分析
 1 .text:7C812A99 ; void __stdcall RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, const ULONG_PTR *lpArguments)
 2 .text:7C812A99                 public _RaiseException@16
 3 .text:7C812A99 _RaiseException@16 proc near            ; CODE XREF: OutputDebugStringA(x)+4F↓p
 4 .text:7C812A99                                         ; DATA XREF: .text:off_7C802654↑o ...
 5 .text:7C812A99
 6 .text:7C812A99 ExceptionRecord = EXCEPTION_RECORD ptr -50h
 7 .text:7C812A99 dwExceptionCode = dword ptr  8
 8 .text:7C812A99 dwExceptionFlags= dword ptr  0Ch
 9 .text:7C812A99 nNumberOfArguments= dword ptr  10h
10 .text:7C812A99 lpArguments     = dword ptr  14h
11 .text:7C812A99 arg_14          = word ptr  1Ch
12 .text:7C812A99 arg_18          = dword ptr  20h
13 .text:7C812A99
14 .text:7C812A99 ; FUNCTION CHUNK AT .text:7C8449F0 SIZE 00000008 BYTES
15 .text:7C812A99 ; FUNCTION CHUNK AT .text:7C84B6A4 SIZE 00000037 BYTES
16 .text:7C812A99
17 .text:7C812A99                 mov     edi, edi
18 .text:7C812A9B                 push    ebp
19 .text:7C812A9C                 mov     ebp, esp
20 .text:7C812A9E                 sub     esp, 50h
21 .text:7C812AA1                 mov     eax, [ebp+dwExceptionCode]
22 .text:7C812AA4                 and     [ebp+ExceptionRecord.ExceptionRecord], 0
23 .text:7C812AA8                 mov     [ebp+ExceptionRecord.ExceptionCode], eax
24 .text:7C812AAB                 mov     eax, [ebp+dwExceptionFlags]
25 .text:7C812AAE                 push    esi
26 .text:7C812AAF                 mov     esi, [ebp+lpArguments]
27 .text:7C812AB2                 and     eax, 1
28 .text:7C812AB5                 test    esi, esi
29 .text:7C812AB7                 mov     [ebp+ExceptionRecord.ExceptionFlags], eax
30 .text:7C812ABA                 mov     [ebp+ExceptionRecord.ExceptionAddress], offset _RaiseException@16 ; RaiseException(x,x,x,x)
31 .text:7C812AC1                 jz      loc_7C812B60
32 .text:7C812AC7                 mov     ecx, [ebp+nNumberOfArguments]
33 .text:7C812ACA                 cmp     ecx, 0Fh
34 .text:7C812ACD                 ja      loc_7C8449F0
35 .text:7C812AD3
36 .text:7C812AD3 loc_7C812AD3:                           ; CODE XREF: RaiseException(x,x,x,x)+31F5A↓j
37 .text:7C812AD3                 test    ecx, ecx
38 .text:7C812AD5                 mov     [ebp+ExceptionRecord.NumberParameters], ecx
39 .text:7C812AD8                 jz      short loc_7C812AE1
40 .text:7C812ADA                 push    edi
41 .text:7C812ADB                 lea     edi, [ebp+ExceptionRecord.ExceptionInformation]
42 .text:7C812ADE                 rep movsd
43 .text:7C812AE0                 pop     edi
44 .text:7C812AE1
45 .text:7C812AE1 loc_7C812AE1:                           ; CODE XREF: RaiseException(x,x,x,x)+3F↑j
46 .text:7C812AE1                                         ; RaiseException(x,x,x,x)+CB↓j
47 .text:7C812AE1                 lea     eax, [ebp+ExceptionRecord]
48 .text:7C812AE4                 push    eax             ; ExceptionRecord
49 .text:7C812AE5                 call    ds:__imp__RtlRaiseException@4 ; RtlRaiseException(x)
50 .text:7C812AEB                 pop     esi
51 .text:7C812AEC                 leave
52 .text:7C812AED                 retn    10h

  其在 kernel32.dll 中,我们采用IDA静态分析该函数

  1)在这里,我们终于遇见 EXCEPTION_RECORD 这个结构体

  kd> dt _EXCEPTION_RECORD
    ntdll!_EXCEPTION_RECORD
       +0x000 ExceptionCode    : Int4B
       +0x004 ExceptionFlags   : Uint4B
       +0x008 ExceptionRecord  : Ptr32 _EXCEPTION_RECORD
       +0x00c ExceptionAddress : Ptr32 Void
       +0x010 NumberParameters : Uint4B
       +0x014 ExceptionInformation : [15] Uint4B

  2)该函数的目的就是 包装好 EXCEPTION_RECORD 结构体,然后调用下一层内核中的函数 RtlRaiseException。

  3)需要注意一点,是用户层触发的异常, ExceptionFlags为1,如果是内核触发的异常, ExceptionFlags为0。

    and eax,

    test esi, esi  

    jz      loc_7C812B60   

    1> 当发现为用户层,将参数个数清零,之后直接调用  RtlRaiseException                                                        2> 当发现为内核层,对参数个数进行分析,之后也跳转到  RtlRaiseException  

  4)RtlRaiseException 在 ntdll 这个库中,我们分析这个。

 

6. RtlRaiseException  分析                                                                             

 1 .text:7C92E508 ; __stdcall RtlRaiseException(x)
 2 .text:7C92E508                 public _RtlRaiseException@4
 3 .text:7C92E508 _RtlRaiseException@4 proc near          ; CODE XREF: KiUserExceptionDispatcher(x,x)+44↑p
 4 .text:7C92E508                                         ; KiRaiseUserExceptionDispatcher()+32↑p ...
 5 .text:7C92E508                 push    ebp
 6 .text:7C92E509                 mov     ebp, esp
 7 .text:7C92E50B                 pushf
 8 .text:7C92E50C                 sub     esp, 2D0h
 9 .text:7C92E512                 mov     [ebp-224h], eax ; eax 存储着 _EXCEPTION_RECORD
10 .text:7C92E518                 mov     [ebp-228h], ecx
11 .text:7C92E51E                 mov     eax, [ebp+CONTEXT.Dr1]
12 .text:7C92E521                 mov     ecx, [ebp+CONTEXT.Dr0]
13 .text:7C92E524                 mov     [eax+CONTEXT.Dr2], ecx
14 .text:7C92E527                 lea     eax, [ebp-2D4h]
15 .text:7C92E52D                 mov     [eax+CONTEXT._Eip], ecx
16 .text:7C92E533                 mov     [eax+CONTEXT._Ebx], ebx
17 .text:7C92E539                 mov     [eax+CONTEXT._Edx], edx
18 .text:7C92E53F                 mov     [eax+CONTEXT._Esi], esi
19 .text:7C92E545                 mov     [eax+CONTEXT._Edi], edi
20 .text:7C92E54B                 lea     ecx, [ebp+CONTEXT.Dr2]
21 .text:7C92E54E                 mov     [eax+CONTEXT._Esp], ecx
22 .text:7C92E554                 mov     ecx, [ebp+CONTEXT.ContextFlags]
23 .text:7C92E557                 mov     [eax+CONTEXT._Ebp], ecx
24 .text:7C92E55D                 mov     ecx, [ebp-4]
25 .text:7C92E560                 mov     [eax+CONTEXT.EFlags], ecx
26 .text:7C92E566                 mov     word ptr [eax+CONTEXT.SegCs], cs
27 .text:7C92E56C                 mov     word ptr [eax+CONTEXT.SegDs], ds
28 .text:7C92E572                 mov     word ptr [eax+CONTEXT.SegEs], es
29 .text:7C92E578                 mov     word ptr [eax+CONTEXT.SegFs], fs
30 .text:7C92E57E                 mov     word ptr [eax+CONTEXT.SegGs], gs
31 .text:7C92E584                 mov     word ptr [eax+CONTEXT.SegSs], ss
32 .text:7C92E58A                 mov     [eax+CONTEXT.ContextFlags], 10007h
33 .text:7C92E590                 push    1
34 .text:7C92E592                 push    eax
35 .text:7C92E593                 push    [ebp+CONTEXT.Dr1]
36 .text:7C92E596                 call    _ZwRaiseException@12 ; ZwRaiseException(x,x,x)
37 .text:7C92E59B                 sub     esp, 20h
38 .text:7C92E59E                 mov     [esp], eax
39 .text:7C92E5A1                 mov     dword ptr [esp+4], 1
40 .text:7C92E5A9                 mov     dword ptr [esp+10h], 0
41 .text:7C92E5B1                 mov     eax, [ebp+8]
42 .text:7C92E5B4                 mov     [esp+8], eax
43 .text:7C92E5B8                 mov     eax, esp
44 .text:7C92E5BA                 push    eax
45 .text:7C92E5BB                 call    _RtlRaiseException@4 ; RtlRaiseException(x)
46 .text:7C92E5BB _RtlRaiseException@4 endp

 

1)该函数主要就是保存寄存器信息放进 _CONTEXT 结构体中。

2)之后调用 _ZwRaiseException 函数

 

7. _ZwRaiseException 分析

1 .text:7C92D990 ; __stdcall ZwRaiseException(x, x, x)
2 .text:7C92D990                 public _ZwRaiseException@12
3 .text:7C92D990 _ZwRaiseException@12 proc near          ; CODE XREF: KiUserExceptionDispatcher(x,x)+24↓p
4 .text:7C92D990                                         ; RtlRaiseException(x)+8E↓p ...
5 .text:7C92D990                 mov     eax, 0B5h       ; NtRaiseException
6 .text:7C92D995                 mov     edx, 7FFE0300h
7 .text:7C92D99A                 call    dword ptr [edx]
8 .text:7C92D99C                 retn    0Ch
9 .text:7C92D99C _ZwRaiseException@12 endp

1)该函数即系统调用,以0B5h作为中断服务号 

2)我们利用我们之前学习过的 SSDT 表的知识来解析一下(Windows系统调用中的系统服务表

  1> 搜索IDT表全局变量

     kd> dd KeServiceDescriptorTableShadow

    8055b1e0  804e36a8 00000000 0000011c 80511088

  2> 找到 0B5h号索引的函数地址

    kd> dd 804e36a8+0B9h*4
    804e398c  8058a4c9 8057f2ce 8058fd8c 8056747b
  3> 解析函数

    kd> u 8058a4c9
    nt!NtReadRequestData:
    8058a4c9 8bff            mov     edi,edi
  4> 总之,之后就是一顿操作进入零环的KiRaiseException。

 

8. KiRaiseException 函数分析

因为进内核了,我们可以直接查看WRK源码

 

  1 NTSTATUS
  2 KiRaiseException (
  3     IN PEXCEPTION_RECORD ExceptionRecord,
  4     IN PCONTEXT ContextRecord,
  5     IN PKEXCEPTION_FRAME ExceptionFrame,
  6     IN PKTRAP_FRAME TrapFrame,
  7     IN BOOLEAN FirstChance
  8     )
  9 
 10 /*++
 11 
 12 Routine Description:
 13 
 14     This function is called to raise an exception. The exception can be
 15     raised as a first or second chance exception.
 16 
 17 Arguments:
 18 
 19     ExceptionRecord - Supplies a pointer to an exception record.
 20 
 21     ContextRecord - Supplies a pointer to a context record.
 22 
 23     ExceptionFrame - Supplies a pointer to an exception frame.
 24 
 25     TrapFrame - Supplies a pointer to a trap frame.
 26 
 27     FirstChance - Supplies a boolean value that specifies whether this is
 28         the first (TRUE) or second (FALSE) chance for the exception.
 29 
 30 Return Value:
 31 
 32     STATUS_ACCESS_VIOLATION is returned if either the exception or the context
 33         record is not readable from user mode.
 34 
 35     STATUS_DATATYPE_MISALIGNMENT is returned if the exception record or the
 36         context record are not properly aligned.
 37 
 38     STATUS_INVALID_PARAMETER is returned if the number of exception parameters
 39         is greater than the maximum allowable number of exception parameters.
 40 
 41     STATUS_SUCCESS is returned if the exception is dispatched and handled.
 42 
 43 --*/
 44 
 45 {
 46 
 47     CONTEXT ContextRecord2;
 48     EXCEPTION_RECORD ExceptionRecord2;
 49     ULONG Length;
 50     ULONG Parameters;
 51     KPROCESSOR_MODE PreviousMode;
 52 
 53     //
 54     // Get the previous processor mode and probe the specified exception and
 55     // context records if necessary.
 56     //
 57 
 58     PreviousMode = KeGetPreviousMode();
 59     if (PreviousMode != KernelMode) {
 60         try {
 61             ProbeForReadSmallStructure(ContextRecord,
 62                                        sizeof(CONTEXT),
 63                                        sizeof(UCHAR));
 64     
 65             Parameters = ProbeAndReadUlong(&ExceptionRecord->NumberParameters);
 66             if (Parameters > EXCEPTION_MAXIMUM_PARAMETERS) {
 67                 return STATUS_INVALID_PARAMETER;
 68             }
 69     
 70             //
 71             // The exception record structure is defined unlike others with
 72             // trailing information as being its maximum size rather than
 73             // just a single trailing element.
 74             //
 75     
 76             Length = FIELD_OFFSET(EXCEPTION_RECORD, ExceptionInformation[Parameters]);
 77 
 78             __assume(Length != 0);
 79 
 80             ProbeForRead(ExceptionRecord, Length, sizeof(UCHAR));
 81     
 82             //
 83             // Copy the exception and context record to local storage so an
 84             // access violation cannot occur during exception dispatching.
 85             //
 86     
 87             RtlCopyMemory(&ContextRecord2, ContextRecord, sizeof(CONTEXT));
 88             RtlCopyMemory(&ExceptionRecord2, ExceptionRecord, Length);
 89             ContextRecord = &ContextRecord2;
 90             ExceptionRecord = &ExceptionRecord2;
 91     
 92             //
 93             // Make sure the number of parameter is correct in the copied
 94             // exception record.
 95             //
 96     
 97             ExceptionRecord->NumberParameters = Parameters;
 98     
 99         } except(EXCEPTION_EXECUTE_HANDLER) {
100             return GetExceptionCode();
101         }
102     }
103 
104     //
105     // Move information from the context record to the exception and trap
106     // frames.
107     //
108 
109     KeContextToKframes(TrapFrame,
110                        ExceptionFrame,
111                        ContextRecord,
112                        ContextRecord->ContextFlags,
113                        PreviousMode);
114 
115     //
116     // Make sure the reserved bit is clear in the exception code and
117     // perform exception dispatching.
118     //
119     // N.B. The reserved bit is used to differentiate internally generated
120     //      codes from codes generated by application programs.
121     //
122 
123     ExceptionRecord->ExceptionCode &= ~KI_EXCEPTION_INTERNAL;
124     KiDispatchException(ExceptionRecord,
125                         ExceptionFrame,
126                         TrapFrame,
127                         PreviousMode,
128                         FirstChance);
129 
130     return STATUS_SUCCESS;
131 }

 

其就做了两件事:

  1)先前模式进行判断。

  2)将上面的CONTEXT结构体转换为KTRAP_FRAME。

  3)之后将信息进行打包,通过 KiDispatchException 来进行分发处理。

                                                                                      

 

posted @ 2019-11-01 15:29  OneTrainee  阅读(563)  评论(0)    收藏  举报