1、模拟异常产生
首先,我们用下面的代码来演示用软件抛出模拟异常时的情况
#include <stdio.h>
void Test()
{
throw 1;
}
int main(int arg, char* args[])
{
Test();
getchar();
return 0;
}
我们断点调试该程序,进入反汇编查看汇编代码我们发现有如下片段:
5: throw 1;
00401038 mov dword ptr [ebp-4],1
0040103F push offset __TI1H (00421558)
00401044 lea eax,[ebp-4]
00401047 push eax
00401048 call __CxxThrowException@8 (00401290)
由上可见其调用了CxxThrowException函数,不同语言不同环境调用的函数可能不同。
接下来我们跟进这个函数里面去看一下是什么情况,发现如下代码片段:
004012B3 lea edx,[ebp-0Ch]
004012B6 push edx
004012B7 mov eax,dword ptr [ebp-10h]
004012BA push eax
004012BB mov ecx,dword ptr [ebp-1Ch]
004012BE push ecx
004012BF mov edx,dword ptr [ebp-20h]
004012C2 push edx
004012C3 call dword ptr [__imp__RaiseException@16 (0042614c)]
发现其调用了一个kernel32里的函数RaiseException
所以我们通过软件模拟产生异常路线如下:
CxxThrowException—>(KERNEL32.DLL)void WINAPI RaiseException(
__in DWORD dwExceptionCode,
__in DWORD dwExceptionFlags,
__in DWORD nNumberOfArguments,
__in const ULONG_PTR *lpArguments );---->NTDLL.DLL!RtlRaiseException()
—>NT!NtRaiseException()—>NT!KiRaiseException()
2、(KERNEL32.DLL)RaiseException函数分析
<1>填充ExceptionRecord结构体
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 //附加参数指针
调用Ntdll.dll!RtlRaiseException()函数
我们跟进CxxThrowException后查看模拟异常时ExceptionCode填的什么,CPU异常对应的一个确定的值,软件模拟异常跟编译环境有关
我们用IDA查看RaiseException函数,发现ExceptionAddress存的是一个函数地址值
.text:7C812AC7 mov [ebp+ExceptionRecord.ExceptionFlags], eax ; 为ExceptionRecord.ExceptionFlags赋值,CPU异常是0,模拟异常是1
.text:7C812ACA mov [ebp+ExceptionRecord.ExceptionAddress], offset _RaiseException@16 ; 为ExceptionRecord.ExceptionAddress赋值,CPU异常是异常发送位置
3、KiRaiseException()函数分析
<1>EXCEPTION_RECORD.ExceptionCode最高位清0,用于区分CPU异常
<2>调用KiDispatchException开始分发异常
4、总结:
异常分发后无法区分是CPU异常还是模拟异常