在使用C++编写的调试器项目时,引发了对Windows软件异常机制的兴趣。
分析工具:IDA7.5
分析方式:静态分析
分析用到的DLL模块以及内核程序:Kernel32.dll、Ntdll.dll 、ntoskrnl.exe
初学者飘过,如有错误,还请指正,共同学习。
CPU捕获
→查IDT表,执行中断处理函数(中断处理函数不处理异常,给程序员一次机会)
→调用CommonDispatchException(给构建的结构体进行赋值)
→在堆栈上构建了一个结构体(记录相关异常信息)
→KiDispatchExceptin(分发异常:目的是找到异常的处理函数)判断是用户异常还是内核异常
KiDispatchException (
IN PEXCEPTION_RECORD ExceptionRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame, 备份到context里面
IN KPROCESSOR_MODE PreviousMode, 0是内核 1是用户
IN BOOLEAN FirstChance ;是否是第一次执行 1是第一次执行
)
→KeContextFormKframes(将Trap_Frame备份到context 为返回3环做准备)
→RtlDispatchException(如果没有内核调试器,或者调试器没有处理,才会执行)
→RtlpGetRegistrationHead注册登记循环遍历
→返回0,否则未处理异常
→再次判断是否存在内核调试器
→蓝屏
→KiDispatchException 通过IRETD返回3环
→返回CommonDispatchException进行第二次异常触发
CPU捕获
→查IDT表,执行中断处理函数(中断处理函数不处理异常,给程序员一次机会)
→调用CommonDispatchException(给构建的结构体进行赋值)
→在堆栈上构建了一个结构体(记录相关异常信息)
→KiDispatchExceptin(分发异常:目的是找到异常的处理函数)判断是用户异常还是内核异常
→KeContextFormKframes(将Trap_Frame备份到context 为返回3环做准备)
前面的处理流程和CPU异常处理的流程是一致的,用户异常到这就开始有所不同
→KiDispatchException中修改Trap_Frame里面的寄存器的值
→最关键的修改是在0环中的全局变量KeUserExceptionDispatcher会赋一个值是3环中的KiUserExceptionDispatcher函数
修正EIP为KiUserExceptionDispatcher ,通过系统调用返回3环
→返回CommonDispatchException
→当线程再次回到3环时,将执行KiUserExceptionDispatcher 函数
为什么叫模拟异常,因为当用户异常第二次产生的时候,就是走的模拟异常的流程,它是系统或编写程序的作者主动产生的。
throw关键字(依赖编译器)(主动抛异常)
→CxxThrowException不同编译器所调用的模拟异常的函数
→KERNEL32.DLL!RaiseException(DWORD dwExceptionCode,DWORD dwExceptionFlags,
DWORD nNumberOfArguments,const ULONG_PTR* lpArguments)(填充EXceptionRecord结构体)
(KERNEL32.DLL)RaiseException函数分析:
和CPU异常的差异:
ExceptionCode根据编译器的不同,存在不同的异常代码
ExceptionAddress 存储的是RaiseException的地址
相同点:使用相同的_EXCEPTION_RECORD结构体
_EXCEPTION_RECORD结构体:
type struct _EXCEPTION_RECORD
{
DWORD ExceptionCode ; 异常代码
DWORD ExceptionFlags ; 异常状态
struct _EXCEPTION_RECORD* ExcptionRecord ;下一个异常
PVOID ExceptionAddress ; 异常发生地址
DWORD NumberParameters ; 附加参数个数
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];附加参数指针
}
→NTDLL.DLL!RtRaiseException()
→NT!NtRaiseException (进内核啦)
原型:
→NT!KiRaiseException(EXceptionRecord 最高位清零)
原型:
→KiDispatchExceptin(分发异常:目的是找到异常的处理函数)判断是用户异常还是内核异常
→KeContextFormKframes(将Trap_Frame备份到context 为返回3环做准备)
→KiDebugRoutine(内核调试器)(判断是否存在)
→DbgkForwardException(内核调试器不存在或返回FALSE,那么此函数将调用3环调试器)
→如果3环的调试器没有处理这个异常那么就得修改Trap_Frame里面的寄存器的值
→最关键的修改是在0环中的全局变量KeUserExceptionDispatcher会赋一个值是3环中的KiUserExceptionDispatcher函数
修正EIP为KiUserExceptionDispatcher ,通过系统调用返回3环
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-3-20 14:05
被APT_华生编辑
,原因: