用户模拟异常的记录
用户模拟异常的记录
我们现在来分析一下用户模拟异常
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 }
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, 1
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 来进行分发处理。
浙公网安备 33010602011771号