简介
KiUserExceptionDispatcher 是SEH分发器的用户模式的负责函数。当一个异常发生的时候,该异常将生成一个异常事件,内核检查该异常是否是由于执行用户模式代码导致的。如果是这样的话,内核修改栈上的trap frame,因此当内核从中断或者异常返回的时候,线程将从KiUserExceptionDispatcher 函数执行而不是导致异常的指令。内核将另外安排几个参数(一个 PCONTEXT 和一个 PEXCEPTION_RECORD),它们描述了异常发生时机器的状态,而且在线程返回到用户模式之前被传递给KiUserExceptionDispatcher 函数。
一旦内核模式栈展开,而且指令转移到用户模式的KiUserExceptionDispatcher
函数,该函数通过调用一个本地的函数RtlDispatchException来处理异常,RtlDispatchException是用户模式异常处理逻辑中的核心函数。如果异常被成功分发的话(也就是SHE
链表中有一个函数宣称可以处理该异常), RtlDispatchException调用RtlRestoreContext
函数实现最终的用户模式上下文的设置,该函数只是加载给定的上下文中的寄存器到到处理器的体系结构执行状态中。 否则,通过调用 NtRaiseException
函数,异常重新被提交到内核模式,这是最后一次机会了。在内核停止该进程之前,这给了用户模式调试器(如果有的话)一个处理该异常的最后机会。
(内核内部在安排KiUserExceptionDispatcher执行之前给了用户模式调试器和内核模式调试器第一次处理该异常的机会)
原型和伪码
改函数位于模块ntdll.dll,声明如下:
VOID KiUserExceptionDispatcher(__in PEXCEPTION_RECORD ExceptionRecord,__in PCONTEXT ContextRecord)
下面是汇编代码:
.text:7C958550 ; __stdcall KiUserExceptionDispatcher(x, x) .text:7C958550 public _KiUserExceptionDispatcher@8 .text:7C958550 _KiUserExceptionDispatcher@8 proc near ; DATA XREF: .text:off_7C94C618o .text:7C958550 .text:7C958550 var_C = dword ptr -0Ch .text:7C958550 var_8 = dword ptr -8 .text:7C958550 var_4 = dword ptr -4 .text:7C958550 arg_0 = dword ptr 4 .text:7C958550 .text:7C958550 mov ecx, [esp+arg_0] ; CONTEXT .text:7C958554 mov ebx, [esp+0] ; EXCEPTION_RECORD .text:7C958557 push ecx .text:7C958558 push ebx .text:7C958559 call _RtlDispatchException@8 ; RtlDispatchException(x,x) .text:7C95855E or al, al .text:7C958560 jz short loc_7C95856E ;如果返回FALSE .text:7C958562 pop ebx ; ebx = EXCEPTION_RECORD .text:7C958563 pop ecx ; ecx = CONTEXT .text:7C958564 push 0 .text:7C958566 push ecx ; ecx = CONTEXT .text:7C958567 call _ZwContinue@8 ;已经处理好了,按照CONTEXT 中设置的值继续执行就好了,此函数不返回 .text:7C95856C jmp short loc_7C958579 .text:7C95856E ; --------------------------------------------------------------------------- .text:7C95856E .text:7C95856E loc_7C95856E: ; 没有找到处理函数,提交一个异常->FirstChance = FALSE .text:7C95856E pop ebx ; ebx = EXCEPTION_RECORD .text:7C95856F pop ecx ; ecx = CONTEXT .text:7C958570 push 0 .text:7C958572 push ecx .text:7C958573 push ebx .text:7C958574 call _ZwRaiseException@12 ; ZwRaiseException(x,x,x) .text:7C958574 _KiUserExceptionDispatcher@8 endp ; sp-analysis failed .text:7C958574 .text:7C958579 ; --------------------------------------------------------------------------- .text:7C958579 retn 8 .text:0000000078EA124A public KiUserExceptionDispatcher .text:0000000078EA124A KiUserExceptionDispatcher proc near ; DATA XREF: .rdata:0000000078F54BB0o .text:0000000078EA124A ; .rdata:off_78F56298o .text:0000000078EA124A cld .text:0000000078EA124B mov rax, cs:Wow64PrepareForException .text:0000000078EA1252 test rax, rax .text:0000000078EA1255 jz short loc_78EA1266 .text:0000000078EA1257 mov rcx, rsp .text:0000000078EA125A add rcx, 4F0h ; rcx 为第一个参数ExceptionRecord 0x4F0 为其CONTEXT 的大小 .text:0000000078EA1261 mov rdx, rsp ; rdx 为第二个参数,指向CONTEXT 结构 .text:0000000078EA1264 call rax ; Wow64PrepareForException .text:0000000078EA1266 .text:0000000078EA1266 loc_78EA1266: .text:0000000078EA1266 mov rcx, rsp .text:0000000078EA1269 add rcx, 4F0h ;ExceptionRecord .text:0000000078EA1270 mov rdx, rsp ;ContextRecord .text:0000000078EA1273 call RtlDispatchException ;分发该异常RtlDispatchException(ExceptionRecord,ContextRecord); .text:0000000078EA1278 test al, al .text:0000000078EA127A jz short loc_78EA1288 .text:0000000078EA127C mov rcx, rsp ; ContextRecord .text:0000000078EA127F xor edx, edx ; ExceptionRecord-->0 .text:0000000078EA1281 call RtlRestoreContext .text:0000000078EA1286 jmp short loc_78EA129D .text:0000000078EA1288 ; --------------------------------------------------------------------------- .text:0000000078EA1288 .text:0000000078EA1288 loc_78EA1288: .text:0000000078EA1288 mov rcx, rsp .text:0000000078EA128B add rcx, 4F0h .text:0000000078EA1292 mov rdx, rsp .text:0000000078EA1295 xor r8b, r8b .text:0000000078EA1298 call ZwRaiseException ;ZwRaiseException(ExceptionRecord,ContextRecord,FALSE); .text:0000000078EA129D .text:0000000078EA129D loc_78EA129D: .text:0000000078EA129D mov ecx, eax .text:0000000078EA129F call RtlRaiseStatus ; RtlRaiseStatus(上面的函数的返回值); .text:0000000078EA12A4 nop .text:0000000078EA12A5 jmp short $+2 .text:0000000078EA12A7 ; --------------------------------------------------------------------------- .text:0000000078EA12A7 .text:0000000078EA12A7 loc_78EA12A7: .text:0000000078EA12A7 nop .text:0000000078EA12A7 KiUserExceptionDispatcher endp ; sp-analysis failed
下面是c++伪码:
VOID KiUserExceptionDispatcher(__in PEXCEPTION_RECORD ExceptionRecord,__in PCONTEXT ContextRecord) { NTSTATUS Status; // // (A custom calling convention is used that does not pass the parameter // values in a C-compatible fashion.) // #if defined(_WIN64) // // 如果Wow64.dll 注册它的帮助函数来处理异常事件,调用这个函数 if (Wow64PrepareForException) Wow64PrepareForException( ExceptionRecord, ContextRecord ); #endif if (RtlDispatchException( ExceptionRecord, ContextRecord)) { #if defined(_WIN64) RtlRestoreContext( ContextRecord ); #else NtContinue( ContextRecord, FALSE ); #endif Status = (NTSTATUS)ContextRecord->Rax; RtlRaiseStatus( Status ); // // No return from RtlRaiseStatus. // } Status = NtRaiseException( ContextRecord, ExceptionRecord, FALSE ); RtlRaiseStatus( Status ); // // No return from RtlRaiseStatus. // }
参数
- PCONTEXT ContextRecord
这个结构包含有关最近发生的异常的详细信息,这些信息独立于C P U,具体参考《Windows异常相关数据结构》 - PEXCEPTION_RECORD ExceptionRecord
包含处理器特定的寄存器数据。系统使用上下文结构执行各种内部操作,具体参考《Windows异常相关数据结构》
这两个参数对我们进行异常调试和dmp分析很有用,可以得到异常信息和还原调用栈