用户模拟异常的记录
我们现在来分析一下用户模拟异常
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>