Windows异常分发函数---KiUserExceptionDispatcher

简介

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分析很有用,可以得到异常信息和还原调用栈

posted on 2019-09-11 14:13  活着的虫子  阅读(5869)  评论(0编辑  收藏  举报

导航