• 用户异常与模拟异常的派发


    Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html

    用户异常与模拟异常的派发

    一、KiDispatchException函数处理流程图

      无论用户模拟异常还是CPU异常,经过前面分析,在经过记录之后,最终都会经过KiDispatchException这个派发函数中。

      在KiDispatchException中会对CPU异常和用户异常分别进行处理。

      CPU异常:首先调用内核调试器,如果调用失败则调用RtlDispatchException(该函数后面会有介绍)分发,

          RtlDispatchException函数会检查SEH链中是否有该程序的处理函数,如果有则返回成功,

          若RtlDispatchException函数处理异常失败,其会尝试第二次调用内核调试器进行调试处理,如果最终处理不了直接蓝屏。

      用户异常:先尝试内核调试器,再尝试用户调试器,如果还不行,直接返回用户代码尝试使用try_catch_语法来进行处理,之后还不行再尝试两次用户调试器。

          如果最终还是处理不了异常,该进程会关闭并报出错误。(用户异常不会导致蓝屏出现)

     二、RtlDispatchException的函数解析代码

      1 VOID
      2 KiDispatchException (
      3     IN PEXCEPTION_RECORD ExceptionRecord,
      4     IN PKEXCEPTION_FRAME ExceptionFrame,
      5     IN PKTRAP_FRAME TrapFrame,
      6     IN KPROCESSOR_MODE PreviousMode,
      7     IN BOOLEAN FirstChance
      8     )
      9 
     10 /*++
     11 
     12 Routine Description:
     13 
     14     This function is called to dispatch an exception to the proper mode and
     15     to cause the exception dispatcher to be called. If the previous mode is
     16     kernel, then the exception dispatcher is called directly to process the
     17     exception. Otherwise the exception record, exception frame, and trap
     18     frame contents are copied to the user mode stack. The contents of the
     19     exception frame and trap are then modified such that when control is
     20     returned, execution will commense in user mode in a routine which will
     21     call the exception dispatcher.
     22 
     23 Arguments:
     24 
     25     ExceptionRecord - Supplies a pointer to an exception record.
     26 
     27     ExceptionFrame - Supplies a pointer to an exception frame. For NT386,
     28         this should be NULL.
     29 
     30     TrapFrame - Supplies a pointer to a trap frame.
     31 
     32     PreviousMode - Supplies the previous processor mode.
     33 
     34     FirstChance - Supplies a boolean value that specifies whether this is
     35         the first (TRUE) or second (FALSE) chance for the exception.
     36 
     37 Return Value:
     38 
     39     None.
     40 
     41 --*/
     42 
     43 {
     44     CONTEXT ContextFrame;
     45     EXCEPTION_RECORD ExceptionRecord1, ExceptionRecord2;
     46     LONG Length;
     47     ULONG UserStack1;
     48     ULONG UserStack2;
     49 
     50     //
     51     // Move machine state from trap and exception frames to a context frame,
     52     // and increment the number of exceptions dispatched.
     53     //
     54 
     55     //-------------------------------------//
     56     // 将当前异常分发次数增加1             //
     57     // 修改ContextFrame.ContextFlags标志位 //
     58     //-------------------------------------//
     59     KeGetCurrentPrcb()->KeExceptionDispatchCount += 1;
     60     ContextFrame.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
     61 
     62     //------------------------------------------//
     63     // 对于用户模式异常的处理 or 调试器可以执行 //
     64     // 标记好Context内容                        //
     65     //------------------------------------------//
     66     if ((PreviousMode == UserMode) || KdDebuggerEnabled) {
     67         //
     68         // For usermode exceptions always try to dispatch the floating
     69         // point state.  This allows exception handlers & debuggers to
     70         // examine/edit the npx context if required.  Plus it allows
     71         // exception handlers to use fp instructions without destroying
     72         // the npx state at the time of the exception.
     73         //
     74         // Note: If there's no 80387, ContextTo/FromKFrames will use the
     75         // emulator's current state.  If the emulator can not give the
     76         // current state, then the context_floating_point bit will be
     77         // turned off by ContextFromKFrames.
     78         //
     79 
     80         ContextFrame.ContextFlags |= CONTEXT_FLOATING_POINT;
     81         if (KeI386XMMIPresent) {
     82             ContextFrame.ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
     83         }
     84     }
     85 
     86     //----------------------------------//
     87     // 将TrapFrame转换为ContextFrame    //
     88     // 接下来都是处理ContextFrame的内容 //
     89     // 这部分零环无意义,三环有意义     //
     90     // 否则修改返回地址不行了。         //
     91     //----------------------------------//
     92     KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame);
     93 
     94     //
     95     // if it is BREAK_POINT exception, we subtract 1 from EIP and report
     96     // the updated EIP to user.  This is because Cruiser requires EIP
     97     // points to the int 3 instruction (not the instruction following int 3).
     98     // In this case, BreakPoint exception is fatal. Otherwise we will step
     99     // on the int 3 over and over again, if user does not handle it
    100     //
    101     // if the BREAK_POINT occured in V86 mode, the debugger running in the
    102     // VDM will expect CS:EIP to point after the exception (the way the
    103     // processor left it.  this is also true for protected mode dos
    104     // app debuggers.  We will need a way to detect this.
    105     //
    106     //
    107 
    108     switch (ExceptionRecord->ExceptionCode) {
    109         //-------------------------//
    110         // 如果为int3断点异常      //
    111         // 则修改eip执行原来的位置 //
    112         //-------------------------//
    113         case STATUS_BREAKPOINT:
    114             ContextFrame.Eip--;
    115             break;
    116 
    117         //------------------------------//
    118         // 执行权限问题而发生的访问违例 //
    119         //------------------------------//
    120         case KI_EXCEPTION_ACCESS_VIOLATION:
    121             ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION;
    122             if (PreviousMode == UserMode) {
    123                 if (KiCheckForAtlThunk(ExceptionRecord,&ContextFrame) != FALSE) {
    124                     goto Handled1;
    125                 }
    126 
    127                 if ((SharedUserData->ProcessorFeatures[PF_NX_ENABLED] == TRUE) &&
    128                     (ExceptionRecord->ExceptionInformation [0] == EXCEPTION_EXECUTE_FAULT)) {
    129                     
    130                     if (((KeFeatureBits & KF_GLOBAL_32BIT_EXECUTE) != 0) ||
    131                         (PsGetCurrentProcess()->Pcb.Flags.ExecuteEnable != 0) ||
    132                         (((KeFeatureBits & KF_GLOBAL_32BIT_NOEXECUTE) == 0) &&
    133                          (PsGetCurrentProcess()->Pcb.Flags.ExecuteDisable == 0))) {
    134                         ExceptionRecord->ExceptionInformation [0] = 0;
    135                     }
    136                 }
    137             }
    138             break;
    139     }
    140 
    141     //
    142     // Select the method of handling the exception based on the previous mode.
    143     //
    144 
    145     ASSERT ((
    146              !((PreviousMode == KernelMode) &&
    147              (ContextFrame.EFlags & EFLAGS_V86_MASK))
    148            ));
    149 
    150     //--------------------------//
    151     // 如果是内核模式触发的异常 //
    152     //--------------------------//
    153     if (PreviousMode == KernelMode) {
    154 
    155         //
    156         // Previous mode was kernel.
    157         //
    158         // If the kernel debugger is active, then give the kernel debugger the
    159         // first chance to handle the exception. If the kernel debugger handles
    160         // the exception, then continue execution. Else attempt to dispatch the
    161         // exception to a frame based handler. If a frame based handler handles
    162         // the exception, then continue execution.
    163         //
    164         // If a frame based handler does not handle the exception,
    165         // give the kernel debugger a second chance, if it's present.
    166         //
    167         // If the exception is still unhandled, call KeBugCheck().
    168         //
    169 
    170         //----------------------------------//
    171         // 给内核调试器第一次机会来处理异常 //
    172         //----------------------------------//
    173         if (FirstChance == TRUE) {
    174 
    175             if ((KiDebugRoutine != NULL) &&
    176                (((KiDebugRoutine) (TrapFrame,
    177                                    ExceptionFrame,
    178                                    ExceptionRecord,
    179                                    &ContextFrame,
    180                                    PreviousMode,
    181                                    FALSE)) != FALSE)) {
    182                                    
    183                 //------------------------------//
    184                 // 如果处理成功,不进行下面处理 //
    185                 //------------------------------//
    186                 goto Handled1;
    187             }
    188 
    189             // Kernel debugger didn't handle exception.
    190 
    191             //--------------------------------------//
    192             // 如果第一次调试器不成功,则派发异常   //
    193             // 当派发成功之后,也不会处理第二次异常 //
    194             //--------------------------------------//
    195             if (RtlDispatchException(ExceptionRecord, &ContextFrame) == TRUE) {
    196                 goto Handled1;
    197             }
    198         }
    199 
    200         //
    201         // This is the second chance to handle the exception.
    202         //
    203 
    204         //--------------------------------//
    205         // 第二次机会来调用调试器处理异常 //
    206         //--------------------------------//
    207         if ((KiDebugRoutine != NULL) &&
    208             (((KiDebugRoutine) (TrapFrame,
    209                                 ExceptionFrame,
    210                                 ExceptionRecord,
    211                                 &ContextFrame,
    212                                 PreviousMode,
    213                                 TRUE)) != FALSE)) {
    214 
    215             //------------------//
    216             // 第二次也处理成功 //
    217             //------------------//
    218             goto Handled1;
    219         }
    220 
    221         
    222         //------------------------------------------------//
    223         // 如果两次调试器处理和派发都不成功,系统直接蓝屏 //
    224         //------------------------------------------------//
    225         KeBugCheckEx(
    226             KERNEL_MODE_EXCEPTION_NOT_HANDLED,
    227             ExceptionRecord->ExceptionCode,
    228             (ULONG)ExceptionRecord->ExceptionAddress,
    229             (ULONG)TrapFrame,
    230             0);
    231 
    232     } else {
    233         //--------------//
    234         // 用户模式异常 //
    235         //--------------//
    236 
    237         //
    238         // Previous mode was user.
    239         //
    240         // If this is the first chance and the current process has a debugger
    241         // port, then send a message to the debugger port and wait for a reply.
    242         // If the debugger handles the exception, then continue execution. Else
    243         // transfer the exception information to the user stack, transition to
    244         // user mode, and attempt to dispatch the exception to a frame based
    245         // handler. If a frame based handler handles the exception, then continue
    246         // execution with the continue system service. Else execute the
    247         // NtRaiseException system service with FirstChance == FALSE, which
    248         // will call this routine a second time to process the exception.
    249         //
    250         // If this is the second chance and the current process has a debugger
    251         // port, then send a message to the debugger port and wait for a reply.
    252         // If the debugger handles the exception, then continue execution. Else
    253         // if the current process has a subsystem port, then send a message to
    254         // the subsystem port and wait for a reply. If the subsystem handles the
    255         // exception, then continue execution. Else terminate the process.
    256         //
    257 
    258 
    259         if (FirstChance == TRUE) {
    260 
    261             //
    262             // This is the first chance to handle the exception.
    263             //
    264 
    265             //-------------------------------------------------------------------------------//
    266             // 调用内核调试器有两个条件:                                                     //
    267             // 1. 当前存在一个调试器                                                         //
    268             // 2. (当前进程的调试器端口为NULL && 不忽略异常) or 可以得到当前 ContextFrame )  //
    269             //-------------------------------------------------------------------------------//
    270             if ((KiDebugRoutine != NULL)  &&
    271 
    272                 //---------------------------------------------------------------//
    273                 // 三环调试器最终会建立一个调试对象,挂在被调试进程的DebugPort处 //
    274                 //---------------------------------------------------------------//
    275                 ((PsGetCurrentProcess()->DebugPort == NULL &&
    276                   !KdIgnoreUmExceptions) ||
    277                 //-------------------------------------//
    278                 //  判断R3层的INT3是否能进入内核调试器 //
    279                 //-------------------------------------//
    280                  (KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode)))) {
    281                 //
    282                 // Now dispatch the fault to the kernel debugger.
    283                 //
    284 
    285                 //------------------------//
    286                 // 先尝试第一次调试器处理 //
    287                 //------------------------//
    288                 if ((((KiDebugRoutine) (TrapFrame,
    289                                         ExceptionFrame,
    290                                         ExceptionRecord,
    291                                         &ContextFrame,
    292                                         PreviousMode,
    293                                         FALSE)) != FALSE)) {
    294 
    295                     goto Handled1;
    296                 }
    297             }
    298 
    299             
    300             //---------------------------------------------------//
    301             // 如果零环调试器无法处理 会调用三环调试器来进行处理 //
    302             //---------------------------------------------------//
    303             if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) {
    304                 goto Handled2;
    305             }
    306 
    307             //
    308             // Transfer exception information to the user stack, transition
    309             // to user mode, and attempt to dispatch the exception to a frame
    310             // based handler.
    311 
    312             ExceptionRecord1.ExceptionCode = 0; // satisfy no_opt compilation
    313 
    314         //---------------------------------------------------------//
    315         // 当内核调试器/用户调试器都处理不了该异常时               //
    316         // 返回R3层的用户代码,尝试交给自定的try.catch. 来进行处理 //
    317         // 对栈的存储跟APC操作类似                                 //
    318         //---------------------------------------------------------//
    319         repeat:
    320             try {
    321 
    322                 //
    323                 // If the SS segment is not 32 bit flat, there is no point
    324                 // to dispatch exception to frame based exception handler.
    325                 //
    326 
    327                 
    328                 if (TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK) ||
    329                     TrapFrame->EFlags & EFLAGS_V86_MASK ) {
    330                     //-----------------------------------//
    331                     // 如果TrapFrame保存的不是用户层地址 //
    332                     // 直接出触发二次异常                //
    333                     //-----------------------------------//
    334                     ExceptionRecord2.ExceptionCode = STATUS_ACCESS_VIOLATION;
    335                     ExceptionRecord2.ExceptionFlags = 0;
    336                     ExceptionRecord2.NumberParameters = 0;
    337                     ExRaiseException(&ExceptionRecord2);
    338                 }
    339 
    340                 //
    341                 // Compute length of context record and new aligned user stack
    342                 // pointer.
    343                 //
    344 
    345                 //--------------------------------------//
    346                 // 计算"对齐并提高用户栈的大小"后的指针 //
    347                 //--------------------------------------//
    348                 UserStack1 = (ContextFrame.Esp & ~CONTEXT_ROUND) - CONTEXT_ALIGNED_SIZE;
    349 
    350                 //
    351                 // Probe user stack area for writability and then transfer the
    352                 // context record to the user stack.
    353                 //
    354 
    355                 //--------------------------------------------------------------------//
    356                 // ProbeForWrite 检测是否可写入,如果可以写入则写入内存,否则抛出异常 //
    357                 // 将Context结构保存到三环栈帧中,为进入零环时恢复三环的环境做准备    //                             //
    358                 //--------------------------------------------------------------------//
    359                 ProbeForWrite((PCHAR)UserStack1, CONTEXT_ALIGNED_SIZE, CONTEXT_ALIGN);
    360                 RtlCopyMemory((PULONG)UserStack1, &ContextFrame, sizeof(CONTEXT));
    361 
    362                 //
    363                 // Compute length of exception record and new aligned stack
    364                 // address.
    365                 //
    366 
    367                 //----------------------------//
    368                 // 计算放入异常参数后栈的大小 //
    369                 //----------------------------//
    370                 Length = (sizeof(EXCEPTION_RECORD) - (EXCEPTION_MAXIMUM_PARAMETERS -
    371                          ExceptionRecord->NumberParameters) * sizeof(ULONG) +3) &
    372                          (~3);
    373                 UserStack2 = UserStack1 - Length;
    374 
    375                 //
    376                 // Probe user stack area for writeability and then transfer the
    377                 // context record to the user stack area.
    378                 // N.B. The probing length is Length+8 because there are two
    379                 //      arguments need to be pushed to user stack later.
    380                 //
    381 
    382                 //----------------------------------------------//
    383                 // 将参数放入内核中 这里提升8因为后面还需要参数 //
    384                 //----------------------------------------------//
    385                 ProbeForWrite((PCHAR)(UserStack2 - 8), Length + 8, sizeof(ULONG));
    386                 RtlCopyMemory((PULONG)UserStack2, ExceptionRecord, Length);
    387 
    388                 //
    389                 // Push address of exception record, context record to the
    390                 // user stack.  They are the two parameters required by
    391                 // _KiUserExceptionDispatch.
    392                 //
    393 
    394                 //----------------------------------------------------------------------//
    395                 // 将上述两个栈地址压入栈中,将该参数是 _KiUserExceptionDispatch 需要的 //
    396                 //----------------------------------------------------------------------//
    397                 *(PULONG)(UserStack2 - sizeof(ULONG)) = UserStack1;
    398                 *(PULONG)(UserStack2 - 2*sizeof(ULONG)) = UserStack2;
    399 
    400                 //
    401                 // Set new stack pointer to the trap frame.
    402                 //
    403 
    404                 //-------------------------------------------//
    405                 // 修改_KTRAP_FRAME中寄存器ss与esp寄存器的值 //
    406                 // 这样返回用户层时栈帧就是修改栈帧的数据了  //
    407                 //-------------------------------------------//
    408                 KiSegSsToTrapFrame(TrapFrame, KGDT_R3_DATA);
    409                 KiEspToTrapFrame(TrapFrame, (UserStack2 - sizeof(ULONG)*2));
    410 
    411                 //
    412                 // Force correct R3 selectors into TrapFrame.
    413                 //
    414 
    415                 //-------------------------//
    416                 // 修改 TrapFrame后的数据  //
    417                 //-------------------------//
    418                 TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, PreviousMode);
    419                 TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
    420                 TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
    421                 TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, PreviousMode);
    422                 TrapFrame->SegGs = 0;
    423 
    424                 //
    425                 // Set the address of the exception routine that will call the
    426                 // exception dispatcher and then return to the trap handler.
    427                 // The trap handler will restore the exception and trap frame
    428                 // context and continue execution in the routine that will
    429                 // call the exception dispatcher.
    430                 //
    431 
    432                 //--------------------------------------//
    433                 // 修改到返回三环的地址                 //
    434                 // KeUserExceptionDispathcer 这个函数中 //
    435                 //--------------------------------------//
    436                 TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;
    437                 return;
    438 
    439             } except (KiCopyInformation(&ExceptionRecord1,
    440                         (GetExceptionInformation())->ExceptionRecord)) {
    441 
    442                 //
    443                 // If the exception is a stack overflow, then attempt
    444                 // to raise the stack overflow exception. Otherwise,
    445                 // the user's stack is not accessible, or is misaligned,
    446                 // and second chance processing is performed.
    447                 //
    448 
    449                 if (ExceptionRecord1.ExceptionCode == STATUS_STACK_OVERFLOW) {
    450                     ExceptionRecord1.ExceptionAddress = ExceptionRecord->ExceptionAddress;
    451                     RtlCopyMemory((PVOID)ExceptionRecord,
    452                                   &ExceptionRecord1, sizeof(EXCEPTION_RECORD));
    453                     goto repeat;
    454                 }
    455             }
    456         }
    457 
    458         //
    459         // This is the second chance to handle the exception.
    460         //
    461 
    462         if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) {
    463             goto Handled2;
    464         } else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) {
    465             goto Handled2;
    466         } else {
    467             ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);
    468             KeBugCheckEx(
    469                 KERNEL_MODE_EXCEPTION_NOT_HANDLED,
    470                 ExceptionRecord->ExceptionCode,
    471                 (ULONG)ExceptionRecord->ExceptionAddress,
    472                 (ULONG)TrapFrame,
    473                 0);
    474         }
    475     }
    476 
    477     //
    478     // Move machine state from context frame to trap and exception frames and
    479     // then return to continue execution with the restored state.
    480     //
    481 
    482 Handled1:
    483 
    484     KeContextToKframes(TrapFrame, ExceptionFrame, &ContextFrame,
    485                        ContextFrame.ContextFlags, PreviousMode);
    486 
    487     //
    488     // Exception was handled by the debugger or the associated subsystem
    489     // and state was modified, if necessary, using the get state and set
    490     // state capabilities. Therefore the context frame does not need to
    491     // be transferred to the trap and exception frames.
    492     //
    493 
    494 Handled2:
    495     return;
    496 }
  • 相关阅读:
    Delphi实现在数据库中存取图像
    c#后台修改前台DOM的css属性示例代码
    jQuery编程中的一些核心方法简介
    jquery用ajax方式从后台获取json数据后如何将内容填充到下拉列表
    jQuery实现淡入淡出二级下拉导航菜单的方法
    jQuery实现瀑布流布局详解(PC和移动端)
    jQuery实用技巧必备
    jQuery链式操作实例分析
    谈谈Jquery ajax中success和complete有哪些不同点
    jquery密码强度校验
  • 原文地址:https://www.cnblogs.com/onetrainee/p/11797640.html
Copyright © 2020-2023  润新知