Windows异常学习笔记(二)—— 内核异常处理流程&用户异常分发
用户层与内核层异常
描述:
- 异常可以发生在用户空间,也可以发生在内核空间
- 无论是CPU异常还是模拟异常,是用户层异常还是内核异常,都要通过 KiDispatchException 函数进行分发,理解这个函数是学好异常的关键
内核异常
分析 KiDispatchException
声明:
VOID KiDispatchException(
ExceptionRecord,
ExceptionFrame,
TrapFrame,
PreviousMode,
FirstChance);
处理逻辑:
- 调用 _KeContextFromKframes,由于操作系统不知道分发的异常到底是3环的异常还是0环的异常,如果是3环的异常,进0环前的环境会被备份到Trap_Frame中,如果要在中途回到3环的话,就需要将 Trap_frame 备份到 context,为返回3环做准备
- 判断先前模式,0是内核调用,1是用户层调用,此时能够知道要分发的异常来自哪里。
- 是否是第一次调用
- 是否有内核调试器
- 如果没有或者内核调试器不处理,调用RtlDispatchException,寻找异常处理函数
- 如果RtlDispatchException返回FALSE,也就是0
- 再次判断是否有内核调试器,有就调用,没有会直接蓝屏
分析 RtlDispatchException
作用:
- 遍历异常链表,调用异常处理函数,如果异常被正确处理了,该函数返回
- 如果当前异常处理函数不能处理该异常,那么调用下一个,以此类推
- 如果到最好也没有人处理这个异常,返回0
反汇编:
可以发现,RtlDispatchException 调用了 RtlpGetRegistrationHead,RtlpGetRegistrationHead的唯一作用就是获取fs寄存器的值给eax
当程序位于0环是,FS寄存器指向KPCR,而KPCR的第一个成员又指向_EXCEPTION_REGISTRATION_RECORD这样一个结构体
_EXCEPTION_REGISTRATION_RECORD
声明:
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
描述:
- _EXCEPTION_REGISTRATION_RECORD位于当前线程的堆栈中
- Next指向下一个结构体,Handler指向异常处理函数,是一个链表结构
- 当调用RtlDispatchException时,按顺序执行异常处理函数,若其中一个异常处理函数返回结果为真,就不再继续向下执行
- 若执行完所有异常处理函数后,异常仍然没有被处理,那么就返回FALSE
用户异常的分发
描述:
-
异常如果发生在内核层,处理起来比较简单,因为异常处理函数也在0环,不用切换堆栈,但是如果异常发生在3环,就意味着必须要切换堆栈,回到3环执行处理函数
-
切换堆栈的处理方式与用户APC的执行过程几乎是一样的,惟一的区别就是执行用户APC时返回3环后执行的函数是KiUserApcDispatcher,而异常处理时返回3环后执行的函数是KiUserExceptionDispatcher
-
理解用户APC的执行过程是理解3环异常处理的关键
用户异常
描述:当异常发生时,无论是CPU异常还是模拟异常,最终在0环都要通过一个函数进行分发处理,这个函数就是KiDispatchException
VOID KiDispatchException(
ExceptionRecord,
ExceptionFrame,
TrapFrame,
PreviousMode,
FirstChance);
注意:需先掌握用户APC执行过程
分析 KiDispatchException
- 将Trap_frame被分到context为返回3环做准备,因为目前不确定是从0环进来还是从3环进来
- 判断先前模式,0是内核调用,1是用户层调用
3. 从loc_42590B向下分析,是在为TRAP_FRAME结构体赋值,准备结束
总结:用户异常处理流程
- _KeContextFromKframes将Trap_frame被分到context为返回3环做准备
- 判断席安全模式,0是内核调用,1是用户层调用
- 是否都是第一次机会
- 是否有内核调试器
- 发送给3环调试
- 如果3环调试器没有处理这个异常,修正EIP为KiUserExceptionDispatcher
- KiUserExceptionDispatcher函数执行结束:CPU异常与模拟异常返回地点不同
CPU异常: CPU检测到异常 →查IDT执行处理函数 →CommonDispatchException →KiDispatchException通过IRETD返回3环 模拟异常: CxxThrowException →RaiseException →RtlRaiseException →NT!NtRaiseException →NT!KiRaiseException →KiDispatchException通过系统调用返回3环