1、用户层异常与内核异常
异常可以发生在用户空间也可以发生在内核空间。
无论是CPU异常还是模拟异常,是用户层异常还是内核层异常,都要通过KiDispatchException函数进行分发。理解这个函数是弄懂异常的关键。
2、VOID KiDispatchException (
IN PEXCEPTION_RECORD ExceptionRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame,
IN KPROCESSOR_MODE PreviousMode,
IN BOOLEAN FirstChance
)
<1>_KeContextFromKframes将Trap_frames备份到context为返回3环做准备
<2>判断先前模式 , 0是内核调用,1是用户调用
<3>是否是第一次机会
<4>是否有内核调试器
<5>如果没有或者内核调试器不处理
<6>调用RtlDispatchException
<7>如果返回FALSE 也就是0
<8>再次判断是否有内核调试器,有就调用,没有直接蓝屏
代码片段
call _KeContextFromKframes@12 ; 将Trap_frames备份到context为返回3环做准备
.text:004259F0 mov eax, [esi]
.text:004259F2 cmp eax, 80000003h
.text:004259F7 jz short loc_425A67
.text:004259F9 cmp eax, 10000004h
.text:004259FE jnz short loc_425A6D
.text:00425A00 mov dword ptr [esi], 0C0000005h
.text:00425A06 cmp byte ptr [ebp+arg_C], 1
.text:00425A0A jnz short loc_425A6D
.text:00425A0C lea eax, [ebp+Context]
.text:00425A12 push eax
.text:00425A13 push esi
.text:00425A14 call _KiCheckForAtlThunk@8 ; KiCheckForAtlThunk(x,x)
.text:00425A19 test al, al
.text:00425A1B jnz loc_425B2D
.text:00425A21 cmp byte ptr ds:0FFDF0280h, 1
.text:00425A28 jnz short loc_425A6D
.text:00425A2A cmp dword ptr [esi+14h], 8
.text:00425A2E jnz short loc_425A6D
.text:00425A30 test _KeNumberProcessors+0Fh, 40h
.text:00425A37 jnz short loc_425A60
.text:00425A39 mov eax, large fs:124h
.text:00425A3F mov eax, [eax+44h]
.text:00425A42 test byte ptr [eax+6Bh], 2
.text:00425A46 jnz short loc_425A60
.text:00425A48 test _KeNumberProcessors+0Fh, 80h
.text:00425A4F jnz short loc_425A6D
.text:00425A51 mov eax, large fs:124h
.text:00425A57 mov eax, [eax+44h]
.text:00425A5A test byte ptr [eax+6Bh], 1
.text:00425A5E jnz short loc_425A6D
.text:00425A60
.text:00425A60 loc_425A60: ; CODE XREF: KiDispatchException(x,x,x,x,x)+C9↑j
.text:00425A60 ; KiDispatchException(x,x,x,x,x)+D8↑j
.text:00425A60 xor edi, edi
.text:00425A62 mov [esi+14h], edi
.text:00425A65 jmp short loc_425A6F
.text:00425A67 ; ---------------------------------------------------------------------------
.text:00425A67
.text:00425A67 loc_425A67: ; CODE XREF: KiDispatchException(x,x,x,x,x)+89↑j
.text:00425A67 dec [ebp+Context._Eip]
.text:00425A6D
.text:00425A6D loc_425A6D: ; CODE XREF: KiDispatchException(x,x,x,x,x)+90↑j
.text:00425A6D ; KiDispatchException(x,x,x,x,x)+9C↑j ...
.text:00425A6D xor edi, edi
.text:00425A6F
.text:00425A6F loc_425A6F: ; CODE XREF: KiDispatchException(x,x,x,x,x)+F7↑j
.text:00425A6F cmp byte ptr [ebp+arg_C], 0 ; 判断先前模式 , 0是内核调用,1是用户调用
.text:00425A73 jnz short loc_425ADA ; 用户态调用跳转
.text:00425A75 cmp [ebp+arg_10], 1 ; 是否第一次调用
.text:00425A79 jnz short loc_425AB0
.text:00425A7B mov eax, _KiDebugRoutine ; 是否有内核调试器
.text:00425A80 cmp eax, edi
.text:00425A82 jz short loc_425A9F ; 如果没有内核调试器
.text:00425A84 push edi
.text:00425A85 push edi
.text:00425A86 lea ecx, [ebp+Context]
.text:00425A8C push ecx
.text:00425A8D push esi
.text:00425A8E push [ebp+var_2F0]
.text:00425A94 push ebx
.text:00425A95 call eax ; _KiDebugRoutine ; 有内核调试器,会调用这个内核调试器
.text:00425A95 ; 如果调用返回成功,说明内核调试器处理了,将context恢复到trap_frame
.text:00425A95 ; 如果内核调试器没有处理,就让3环的程序处理
.text:00425A97 test al, al
.text:00425A99 jnz loc_425B2D ; 如果有内核调试器处理,直接跳转到要处理处
.text:00425A9F
.text:00425A9F loc_425A9F: ; CODE XREF: KiDispatchException(x,x,x,x,x)+114↑j
.text:00425A9F lea eax, [ebp+Context] ; 如果没有内核调试器,或者内核调试器没有处理
.text:00425AA5 push eax ; Context
.text:00425AA6 push esi ; ExceptionRecord
.text:00425AA7 call _RtlDispatchException@8 ; 这个函数负责调用处理函数
3、RtlDispatchException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context)函数执行流程
typedef struct _EXCEPTION_REGISTRATION_RECORD{
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
}EXCEPTION_REGISTRATION_RECORD;
RtlDispatchException的作用就是:
遍历异常链表,调用异常处理函数,如果异常被正常处理了,该函数返回1。如果当前异常处理函数不能处理该异常,那么调用下一个异常处理函数,一次类推,如果到最后也没有能处理这个异常,则返回0
4、RtlDispatchException函数式执行流程