簡介
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分析很有用,可以得到異常信息和還原調用棧