• 异常篇——异常处理


    写在前面

      此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

    你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

      看此教程之前,问几个问题,基础知识储备好了吗?保护模式篇学会了吗?练习做完了吗?没有的话就不要继续了。


    🔒 华丽的分割线 🔒


    概述

      之前我们介绍了异常的记录。既然记录好的异常,我肯定需要分发来处理它。对于异常有来自用户模式的异常和内核态的异常。在我们之前学习中,它们的区别还是挺大的。无论是内核的异常,还是用户的异常,异常处理的重心我们得放到KiDispatchException这个函数的分析上,先介绍相对简单的内核异常处理流程。

    内核异常处理

      对于内核异常处理,我们先看看KiDispatchException的处理流程:

    1. KeContextFromKframesTrapFrame备份到Context,为返回3环做准备。
    2. 判断先前模式,0是内核调用反之是用户层调用。
    3. 是否是第一次机会。
    4. 是否有内核调试器。
    5. 如果没有或者内核调试器不处理。
    6. 调用RtlDispatchException,查找并调用异常处理函数。
    7. 如果返回FALSE,再次判断是否有内核调试器,有调用,没有直接蓝屏。

      下面我们通过反汇编的形式过一遍这个流程:

    push    390h
    push    offset stru_401FA0
    call    __SEH_prolog
    mov     eax, ___security_cookie
    mov     [ebp+security_cookie], eax
    mov     esi, [ebp+ExceptionRecord]
    mov     [ebp+var_ExceptionRecord], esi
    mov     ecx, [ebp+ExceptionFrame]
    mov     [ebp+var_ExceptionFrame], ecx
    mov     ebx, [ebp+TrapFrame]
    mov     [ebp+var_TrapFrame], ebx
    db      3Eh
    mov     eax, ds:0FFDFF020h
    inc     [eax+_KPRCB.KeExceptionDispatchCount] ; 异常派发数加1
    mov     [ebp+Context.ContextFlags], 10017h
    cmp     [ebp+PreviousMode], 1
    jz      short IsUserMode
    cmp     _KdDebuggerEnabled, 0
    jz      short KernelModeAndNoDBG
    

      函数开始开始赋值一波,然后判断否是为3环,但我们是内核的,故不会跳转,然后cmp _KdDebuggerEnabled, 0判断调试器是否开启,具体将会在调试篇进行讲解,这里就不多说了。
      这里我们没有调试器,就会跳转到下面的汇编代码:

    KernelModeAndNoDBG:                     ; CODE XREF: KiDispatchException(x,x,x,x,x)+55↑j
                                            ; KiDispatchException(x,x,x,x,x)+68↑j
                    lea     eax, [ebp+Context]
                    push    eax             ; ContextRecord
                    push    ecx             ; ExceptionFrame
                    push    ebx             ; TrapFrame
                    call    _KeContextFromKframes@12 ; KeContextFromKframes(x,x,x)
                    mov     eax, [esi]
                    cmp     eax, STATUS_BREAKPOINT
                    jz      short IsBreakPoint
                    cmp     eax, KI_EXCEPTION_ACCESS_VIOLATION
                    jnz     short OtherExceptionProc
                    mov     dword ptr [esi], STATUS_ACCESS_VIOLATION
                    cmp     [ebp+PreviousMode], 1
                    jnz     short OtherExceptionProc
                    lea     eax, [ebp+Context]
                    push    eax             ; Context
                    push    esi             ; ExceptionRecord
                    call    _KiCheckForAtlThunk@8 ; KiCheckForAtlThunk(x,x)
                    test    al, al
                    jnz     loc_425AFD
                    cmp     byte ptr ds:0FFDF0280h, 1
                    jnz     short OtherExceptionProc
                    cmp     dword ptr [esi+14h], 8
                    jnz     short OtherExceptionProc
                    test    _KeNumberProcessors+0Fh, 40h
                    jnz     short loc_425A30
                    mov     eax, large fs:_KPCR.PrcbData.CurrentThread
                    mov     eax, [eax+_KTHREAD.ApcState.Process]
                    test    [eax+_EPROCESS.Pcb.___u27.Flags._bf_0], 2
                    jnz     short loc_425A30
                    test    _KeNumberProcessors+0Fh, 80h
                    jnz     short OtherExceptionProc
                    mov     eax, large fs:_KPCR.PrcbData.CurrentThread
                    mov     eax, [eax+_KTHREAD.ApcState.Process]
                    test    [eax+_EPROCESS.Pcb.___u27.Flags._bf_0], 1
                    jnz     short OtherExceptionProc
    

      这里就开始将TrapFrame备份到Context,首先判断是否是int3断点,如果是的话就会跳转到如下代码:

    IsBreakPoint:                           ; CODE XREF: KiDispatchException(x,x,x,x,x)+89↑j
                    dec     [ebp+Context._Eip]
    
    OtherExceptionProc:                     ; CODE XREF: KiDispatchException(x,x,x,x,x)+90↑j
                                            ; KiDispatchException(x,x,x,x,x)+9C↑j ...
                    xor     edi, edi
    
    DebugRoutine:                           ; CODE XREF: KiDispatchException(x,x,x,x,x)+F7↑j
    

      该代码就会修正EIP,继续下面的流程。如果不是断点,就判断一系列的异常,并进行相应的处理,如果都不是就跳转到所谓的OtherExceptionProc进行处理:

    DebugRoutine:                           ; CODE XREF: KiDispatchException(x,x,x,x,x)+F7↑j
                    cmp     [ebp+PreviousMode], 0
                    jnz     short UserMode
                    cmp     [ebp+FirstChance], 1
                    jnz     short SecondChance
                    mov     eax, _KiDebugRoutine
                    cmp     eax, edi        ; edi = 0
                    jz      short NoDebugRoutine
                    push    edi             ; SecondChance
                    push    edi             ; PreviousMode
                    lea     ecx, [ebp+Context]
                    push    ecx             ; ContextRecord
                    push    esi             ; ExceptionRecord
                    push    [ebp+var_ExceptionFrame] ; ExceptionFrame
                    push    ebx             ; TrapFrame
                    call    eax ; _KiDebugRoutine
                    test    al, al
                    jnz     loc_425AFD
    
    NoDebugRoutine:                         ; CODE XREF: KiDispatchException(x,x,x,x,x)+114↑j
                    lea     eax, [ebp+Context]
                    push    eax             ; Context
                    push    esi             ; ExceptionRecord
                    call    _RtlDispatchException@8 ; RtlDispatchException(x,x)
                    cmp     al, 1
                    jz      short loc_425AFD
    
    

      这里同理,我们没有调试器,就会调用RtlDispatchException这个函数。如果失败,就会继续执行下面的代码:

                     jz      short loc_425AFD
    
     SecondChance:                           ; CODE XREF: KiDispatchException(x,x,x,x,x)+10B↑j
                     mov     eax, _KiDebugRoutine
                     cmp     eax, edi
                     jz      NoDebugRoutine_0
                     push    1               ; SecondChance
                     push    edi             ; PreviousMode
                     lea     ecx, [ebp+Context]
                     push    ecx             ; ContextRecord
                     push    esi             ; ExceptionRecord
                     push    [ebp+var_ExceptionFrame] ; ExceptionFrame
                     push    ebx             ; TrapFrame
                     call    eax ; _KiDebugRoutine
                     test    al, al
                     jnz     short loc_425AFD
                     jmp     NoDebugRoutine_0
    

      就继续判断有没有调试器,但我这里没有,就会跳到下面的汇编代码:

    NoDebugRoutine_0:                       ; CODE XREF: KiDispatchException(x,x,x,x,x)+149↑j
                                            ; KiDispatchException(x,x,x,x,x)+167↑j
                    push    edi             ; BugCheckParameter4
                    push    ebx             ; BugCheckParameter3
                    push    [esi+_EXCEPTION_RECORD32.ExceptionAddress] ; BugCheckParameter2
                    push    [esi+_EXCEPTION_RECORD32.ExceptionCode] ; BugCheckParameter1
                    push    KERNEL_MODE_EXCEPTION_NOT_HANDLED ; BugCheckCode
                    call    _KeBugCheckEx@20 ; KeBugCheckEx(x,x,x,x,x)
    

      上面的代码,就是所谓的蓝屏。有关内核异常处理就介绍到这里,具体的细节将会到总结与提升篇进行介绍。

    用户异常处理

      对于用户异常处理,我们先看看KiDispatchException的处理流程:

    1. KeContextFromKframesTrapFrame备份到Context,为返回3环做准备。
    2. 判断先前模式,0是内核调用反之是用户层调用。
    3. 是否是第一次机会。
    4. 是否有内核调试器。
    5. 发送给3环调试。

      有了内核异常的介绍,我就不赘述了,直接定位到关键代码:

    inc     [eax+_KPRCB.KeExceptionDispatchCount] ; 异常派发数加1
    mov     [ebp+Context.ContextFlags], 10017h
    cmp     [ebp+PreviousMode], 1
    jz      short IsUserMode
    cmp     _KdDebuggerEnabled, 0
    jz      short KernelModeAndNoDBG
    

      这里我们是用户的异常,所以会跳转:

    IsUserMode:                             ; CODE XREF: KiDispatchException(x,x,x,x,x)+4C↑j
                    mov     [ebp+Context.ContextFlags], 1001Fh
                    cmp     _KeI386XMMIPresent, 0
                    jz      short KernelModeAndNoDBG
                    mov     [ebp+Context.ContextFlags], 1003Fh
    
    KernelModeAndNoDBG:                     ; CODE XREF: KiDispatchException(x,x,x,x,x)+55↑j
                                            ; KiDispatchException(x,x,x,x,x)+68↑j
                    lea     eax, [ebp+Context]
    

      这里就是赋值,不是我们关注的重点,我们继续:

    KernelModeAndNoDBG:                     ; CODE XREF: KiDispatchException(x,x,x,x,x)+55↑j
                                            ; KiDispatchException(x,x,x,x,x)+68↑j
                    lea     eax, [ebp+Context]
                    push    eax             ; ContextRecord
                    push    ecx             ; ExceptionFrame
                    push    ebx             ; TrapFrame
                    call    _KeContextFromKframes@12 ; KeContextFromKframes(x,x,x)
                    mov     eax, [esi]
                    cmp     eax, STATUS_BREAKPOINT
                    jz      short IsBreakPoint
                    cmp     eax, KI_EXCEPTION_ACCESS_VIOLATION
                    jnz     short OtherExceptionProc
                    mov     dword ptr [esi], STATUS_ACCESS_VIOLATION
                    cmp     [ebp+PreviousMode], 1
                    jnz     short OtherExceptionProc
                    lea     eax, [ebp+Context]
                    push    eax             ; Context
                    push    esi             ; ExceptionRecord
                    call    _KiCheckForAtlThunk@8 ; KiCheckForAtlThunk(x,x)
                    test    al, al
                    jnz     loc_425AFD
                    cmp     byte ptr ds:0FFDF0280h, 1
                    jnz     short OtherExceptionProc
                    cmp     dword ptr [esi+14h], 8
                    jnz     short OtherExceptionProc
                    test    _KeNumberProcessors+0Fh, 40h
                    jnz     short loc_425A30
                    mov     eax, large fs:_KPCR.PrcbData.CurrentThread
                    mov     eax, [eax+_KTHREAD.ApcState.Process]
                    test    [eax+_EPROCESS.Pcb.___u27.Flags._bf_0], 2
                    jnz     short loc_425A30
                    test    _KeNumberProcessors+0Fh, 80h
                    jnz     short OtherExceptionProc
                    mov     eax, large fs:_KPCR.PrcbData.CurrentThread
                    mov     eax, [eax+_KTHREAD.ApcState.Process]
                    test    [eax+_EPROCESS.Pcb.___u27.Flags._bf_0], 1
                    jnz     short OtherExceptionProc
    

      由于我们是3环的,就会调用到KiCheckForAtlThunk这个函数作用如下:

    This marks the beginning of an x86 ATL thunk. Control will branch here if the ATL thunk was built in memory marked no execute. If that is the case, then the thunk code will be emulated and control returned to the thread.

      看描述并不是我们关注的重点,继续走到了我所标注的DebugRoutine

    DebugRoutine:                           ; CODE XREF: KiDispatchException(x,x,x,x,x)+F7↑j
                    cmp     [ebp+PreviousMode], 0
                    jnz     short UserMode_0
                    cmp     [ebp+FirstChance], 1
                    jnz     short SecondChance
                    mov     eax, _KiDebugRoutine
                    cmp     eax, edi        ; edi = 0
                    jz      short NoDebugRoutine
                    push    edi             ; SecondChance
                    push    edi             ; PreviousMode
                    lea     ecx, [ebp+Context]
                    push    ecx             ; ContextRecord
                    push    esi             ; ExceptionRecord
                    push    [ebp+var_ExceptionFrame] ; ExceptionFrame
                    push    ebx             ; TrapFrame
                    call    eax ; _KiDebugRoutine
                    test    al, al
                    jnz     loc_425AFD
    

      由于我们是用户态,就直接跳转到UserMode_0

    UserMode_0:                             ; CODE XREF: KiDispatchException(x,x,x,x,x)+105↑j
                    cmp     [ebp+FirstChance], 1
                    jnz     SecondChance_0
                    cmp     _KiDebugRoutine, edi
                    jz      short NoDebugRoutine_1
                    mov     eax, large fs:_KPCR.PrcbData.CurrentThread
                    mov     eax, [eax+_KTHREAD.ApcState.Process]
                    cmp     [eax+_EPROCESS.DebugPort], edi
                    jz      short NoDebugPort
                    push    1
                    lea     eax, [ebp+Context]
                    push    eax
                    push    esi
                    call    _KdIsThisAKdTrap@12 ; KdIsThisAKdTrap(x,x,x)
                    test    al, al
                    jz      short NoDebugRoutine_1
    
    NoDebugPort:                            ; CODE XREF: KiDispatchException(x,x,x,x,x)+18D↑j
                    push    edi             ; SecondChance
                    push    dword ptr [ebp+PreviousMode] ; PreviousMode
                    lea     eax, [ebp+Context]
                    push    eax             ; ContextRecord
                    push    esi             ; ExceptionRecord
                    push    [ebp+var_ExceptionFrame] ; ExceptionFrame
                    push    ebx             ; TrapFrame
                    call    _KiDebugRoutine
                    test    al, al
                    jz      short NoDebugRoutine_1
    

      由于我们不是调试模式,会跳到NoDebugRoutine_1

    NoDebugRoutine_1:                       ; CODE XREF: KiDispatchException(x,x,x,x,x)+17C↑j
                                            ; KiDispatchException(x,x,x,x,x)+1A0↑j ...
                    push    edi             ; SecondChance
                    push    1               ; DebugException
                    push    esi             ; ExceptionRecord
                    call    _DbgkForwardException@12 ; DbgkForwardException(x,x,x)
                    test    al, al
                    jnz     short EndProc
                    mov     [ebp+var_SecondChance], edi
    

      DbgkForwardException就是向3环派发处理异常了,如果没有调试器的话,就会继续往下走,最终会到如下代码:

    mov     [ebx+_KTRAP_FRAME.SegFs], eax
    and     [ebx+_KTRAP_FRAME.SegGs], 0
    mov     eax, _KeUserExceptionDispatcher
    mov     [ebx+_KTRAP_FRAME._Eip], eax
    or      [ebp+ms_exc.registration.TryLevel], 0FFFFFFFFh
    

      这里就开始修改KTRAP_FRAME开始回到3环进行处理,剩余的具体细节的讲解同样将会在总结与提升进行。

    下一篇

      异常篇—— VEH 与 SEH

  • 相关阅读:
    Postman 的使用教程(详细)
    腾讯笔试题总结2
    学习SpringSecurity这一篇就够了
    easyexcel字体加粗
    【动力节点】最新rabbitmq笔记-1-6章What is RabbitMQ?
    一文弄懂CNN中的BatchNorm
    语音识别中输入输出的可能形式有哪些
    简单的个人博客网站设计 静态HTML个人博客主页 DW个人网站模板下载 大学生简单个人网页作品代码 个人网页制作 学生个人网页设计作业
    安卓玩机教程---全机型安卓4----安卓12 框架xp edx lsp安装方法
    Rancher(V2.6.3)安装K8s教程
  • 原文地址:https://www.cnblogs.com/wingsummer/p/15941913.html