zoukankan      html  css  js  c++  java
  • Windows调试2.异常产生详细流程

    程序设置了int3断点函数

    CPU获取后,根据IDT表中的int3的处理函数

    主要工作大概是

    填充 _KTRAP_FRAME 结构体,

    保存的是异常发生时的寄存器环境,

    因为在异常处理完毕之后,需要返回产生异常的地方继续执行

    然后把异常给CommonDispatchException

     

    //总结如下几个顺序
    __asm int 3;
    ​
        -> IDT[3]: _KiTrap03
            (填充一个陷阱帧,用于继续执行代码)
    ​
            -> CommonDisapathException(异常地址, 异常的类型, 异常的参数个数)
                填充了一个 ExceptionRecord 结构
    ​
                -> KiDispatchException(先前模式,是否是第一次分发,陷阱桢, 异常结构, 0)

    IDT 表中 int 3 的处理函数

    ; IDT 表中 int 3 的处理函数
    _KiTrap03       proc
        ; 填充 _KTRAP_FRAME 结构体,保存的是异常发生时的寄存器环境,
        ; 因为在异常处理完毕之后,需要返回产生异常的地方继续执行
        push    0                       
        ENTER_TRAP      kit3_a, kit3_t
    ​
        ;--------------------------------------------
        ; 硬件相关的一些操作
        cmp     ds:_PoHiberInProgress, 0
        jnz     short kit03_01
        lock    inc ds:_KiHardwareTrigger
    kit03_01:
        mov     eax, BREAKPOINT_BREAK
    ​
        ; 判断当前是否属于虚拟 86 模式
        test    byte ptr [ebp+72h], 2
        jne     kit03_30
        ;---------------------------------------------
    ​
        ; 判断 CS 段寄存器的最低为是否为 1,如果最
        ; 低为为 1 表示异常产生于 R3 用户空间。
        ; 0  R0   00
        ; 3  R3   11
        test    byte ptr [ebp+SegCs], 1
        jne     kit03_04
    ​
        ; 判断 EFlags 中的段 IF 标志位是否为 0
        test    byte ptr [ebp+71h], 2
        je      kit03_10
        jmp     kit03_05
    ​
    kit03_04:
        ; 如果异常产生于系统代码,则进行其它操作
        cmp     word ptr [ebp+6Ch], 1Bh
        jne     kit03_30
    ​
    kit03_05:
        ; 如果 IF 为 0 ,则设置 IF 
        sti
    ​
    kit03_10:
        ; 设置附加参数
        mov     esi, ecx                ; ExceptionInfo 2
        mov     edi, edx                ; ExceptionInfo 3
        mov     edx, eax                ; ExceptionInfo 1
    ​
        ; 设置异常信息
        mov     ebx, [ebp]+TsEip
        dec     ebx                     ; ebx = 异常地址(int 3)
        mov     ecx, 3                  ; ecx = 参数个数
        mov     eax, STATUS_BREAKPOINT  ; eax = 异常类型
    ​
        ; 调用函数继续执行异常的分发,函数不会返回
        call    CommonDispatchException
    ​
    kit03_30:
        ; 如果当前属于 V86 模式或内核模式下
        mov     ebx,PCR[PcPrcbData+PbCurrentThread]
        mov     ebx,[ebx]+ThApcState+AsProcess
        cmp     dword ptr [ebx] + PrVdmObjects,0 
        je      kit03_05
        stdCall _Ki386VdmReflectException_A, <03h>
        test    ax,0FFFFh
        jz      Kit03_10
    ​
        ; 继续执行发生异常的代码
        jmp     _KiExceptionExit
    ​
    _KiTrap03       endp
    ​
     
    ​
    ; 一个宏,用于填充 _KTRAP_FRAME 结构,该结构用于保存寄存器环境
    ENTER_TRAP  macro    AssistLabel, TargetLabel
        ; 清除异常代码的低二字节
        mov     word ptr [esp+2], 0 
    ​
        ; 当产生异常的时候,CPU会自动保存下列的寄存器
        ;+0x068 Eip              : Uint4B
        ;+0x06c SegCs            : Uint4B
        ;+0x070 EFlags           : Uint4B
    ​
    ​
        ; 保存非易失性寄存器
        push    ebp                 
        push    ebx
        push    esi
        push    edi
    ​
        ; 替换 fs 寄存器和异常链表
        push    fs
        mov     ebx, KGDT_R0_PCR(KPCR)
        mov     fs, bx
        mov     ebx, fs:[PcExceptionList] 
    ​
        ; 开辟空间存储先前模式(R0R3)
        sub     esp, 4             
    ​
        ; 保存易失性寄存器
        push    eax                 
        push    ecx
        push    edx
    ​
        ; 保存段寄存器
        push    ds                  
        push    es
        push    gs
    ​
        ; 扩充栈的大小,构建一个完整的 _KTRAP_FRAME 结构
        mov     ax, KGDT_R3_DATA OR RPL_MASK ; 不做考虑
        sub     esp, TsSegGs
        mov     ds, ax  ; 不做考虑
        mov     es, ax  ; 不做考虑
    ​
        ; **  此时 [esp] 以及 [ebp] 都指向了 _KTRAP_FRAME 结构体 
        mov     ebp, esp
    ​
        ; 判断当前是否是虚拟 8086 模式,如果是的话就重新进行填充
        test    dword ptr [esp].TsEflags,EFLAGS_V86_MASK
        jnz     V86$AssistLabel(V86_kit3_a)
    ​
        ; 从 KPCR 结构体中获取当前线程地址 ecx = 当前线程
        mov     ecx, PCR[PcPrcbData+PbCurrentThread]
    ​
        ; 清除方向标志位
        cld
    ​
        ; 清除 Dr7 寄存器
        and     dword ptr [ebp].TsDr7, 0
    ​
        ; 检查是否需要保存调试寄存器,如果是则保存
        test    byte ptr [ecx].ThDebugActive, 0Dfh
        jnz     nt!Dr_kit3_a        ; 保存寄存器
    ​
    Dr_$TargetLabel(Dr_kitb_t):
        ; 调用宏用于填充 _KTRAP_FRAME 结构中的调试部分
        SET_DEBUG_DATA
    endm
    ​
    ​
    ​
    ; 如果是虚拟 8086 模式则跳转
    V86_kit3_a  proc near
        mov     eax,dword ptr [ebp + 84h]   ;  V86Fs
        mov     ebx,dword ptr [ebp + 88h]   ;  V86GS
        mov     ecx,dword ptr [ebp + 7Ch]   ;  V86Es
        mov     edx,dword ptr [ebp + 80h]   ;  V86Ds
        mov     word ptr [ebp + 50h], ax    ;  Trap_frame.SegFs = Trap_frame.V86Fs
        mov     word ptr [ebp + 30h], bx    ;  Trap_frame.SegGs = Trap_frame.V86Gs
        mov     word ptr [ebp + 34h], cx    ;  Trap_frame.SegEs = Trap_frame.V86Es
        mov     word ptr [ebp + 38h], dx    ;  Trap_frame.SegDs = Trap_frame.V86Ds
        jmp     nt!KiTrap03+0x42 (83e9278a)
    V86_kit3_a  endp
    ​
    ​
    ​
    ; 设置 _KTRAP_FRAME 中的调试相关部分
    SET_DEBUG_DATA  macro
            mov     ebx,[ebp] + TsEbp
            mov     edi,[ebp] + TsEip
            mov     [ebp] + TsDbgArgPointer, edx
            mov     [ebp] + TsDbgArgMark, 0BADB0D00h
            mov     [ebp] + TsDbgEbp,ebx
            mov     [ebp] + TsDbgEip,edi
    endm
    ​
    ​
    ​
    ; 用于保存调试寄存器
    Dr_kit3_a   proc near:
        test    dword ptr [ebp+70h],20000h
        jne     nt!Dr_kit3_a+0x13
        test    byte ptr [ebp+6Ch],1
        je      nt!KiTrap03+0x58 (83e92618)
        mov     ebx, dr0
        mov     ecx, dr1
        mov     edi, dr2
        mov     dword ptr [ebp+18h], ebx
        mov     dword ptr [ebp+1Ch], ecx
        mov     dword ptr [ebp+20h], edi
        mov     ebx, dr3
        mov     ecx, dr6
        mov     edi, dr7
        mov     dword ptr [ebp+24h], ebx
        mov     dword ptr [ebp+28h], ecx
        xor     ebx, ebx
        mov     dword ptr [ebp+2Ch], edi
        mov     dr7, ebx
        mov     edi, dword ptr fs:[20h]
        mov     ebx, dword ptr [edi+2F4h]
        mov     ecx, dword ptr [edi+2F8h]
        mov     dr0, ebx
        mov     dr1, ecx
        mov     ebx, dword ptr [edi+2FCh]
        mov     ecx, dword ptr [edi+300h]
        mov     dr2, ebx
        mov     dr3, ecx
        mov     ebx, dword ptr [edi+304h]
        mov     ecx, dword ptr [edi+308h]
        mov     dr6, ebx
        mov     dr7, ecx
        jmp     nt!KiTrap03+0x58 (83e92618)
    Dr_kit3_a   endp

    CommonDispatchException用于填充异常结构,

    并调用 _KiDispatchException 函数

    //流程总结
    KiDispatchException
        R0:
            1: 检查是否有内核调试器,并尝试调用
                
                -> 失败或没有内核调试器:[0]RtlDispatchException(SEH) 
    ​
                    -> 第二次异常分发
    ​
            2: 检查是否有内核调试器,并尝试调用
    ​
                -> 失败或没有内核调试器: KeBugCheckEx 蓝屏
    ​
        R3:
    ​
            1:是否有内核调试器并且不处于调试状态
    ​
                -> [0]通过指针 KiDebugRoutine 调用内核调试器
    ​
                -> [0]DbgkForwardException(DbgkpSendApiMessage) 发送给用户调试器,等待返回是否处理
    ​
                -> [0]构建了一个 ExceptionPointers 结构体并且将 esp 指向了R3的KiUserExceptionDispatcher
    ​
                -> [3] KiUserExceptionDispatcher
    ​
                    -> [3] RtlDispatchException: veh vch seh
    ​
                    -> 成功: NtContinue 返回 R0 设置 eip 并且重新回到 R3
    ​
                    -> 失败: 调用 RtlRaiseException 进行第二次的异常分发
    ​
            2: DbgkForwardException(ExceptionRecord, TRUE, TRUE) 发送给调试端口
    ​
                DbgkForwardException(ExceptionRecord, FALSE, TRUE) 发送给异常端口
    ​
                ZwTerminateProcess()  结束进程
    ​
                KeBugCheckEx

     

    ; 用于填充异常结构,并调用 _KiDispatchException 函数
    CommonDispatchException proc
        ; esi = 附加参数2            edi = 附加参数3
        ; edx = 附加参数1            ebx = 异常地址
        ; ecx = 参数个数             eax = 异常类型
        ; esp = EXCEPTION_RECORD    ebp = _KTRAP_FRAME
    ​
        ; 在栈中开辟记录异常所需的栈空间 EXCEPTION_RECORD
        sub     esp, ExceptionRecordLength
    ​
        ; 使用 _KiTrap03 传入的参数构建异常结构
        mov     dword ptr [esp].ErExceptionCode,eax     ;*
        xor     eax, eax
        mov     dword ptr [esp].ExceptionFlags,eax  
        mov     dword ptr [esp].ExceptionRecord,eax
        mov     dword ptr [esp].ExceptionAddress,ebx    ;*
        mov     dword ptr [esp].NumberParameters,ecx    ;*
       
        ; 参数个数为0跳转
        cmp     ecx,0
        je      _FLAG01
    ​
        ; 保存异常的附加参数到结构体中
        lea     ebx,[esp].ExceptionInformation
        mov     dword ptr [ebx],edx
        mov     dword ptr [ebx + 4],esi
        mov     dword ptr [ebx + 8],edi
    ​
    _FLAG01:
        mov     ecx,esp
        
        ; 判断是否为虚拟 86 模式,如果是则跳转
        test    byte ptr [ebp].TsEFlags,2
        je      _FLAG02
        
        ; 如果为虚拟 86 模式
        mov     eax,0FFFFh
        jmp     _FLAG03
    ​
    _FLAG02:
        ; 如果为保护模式则获取 CS 段寄存器
        mov     eax,dword ptr [ebp].SegCs
    ​
    _FLAG03:
        ; 获取 CS 最低位
        and     eax, MODE_MASK
    ​
        ; 传入参数
        push    1       ; first chance: 表示是否是第一次处理异常
        push    eax     ; PreviousMode: 先前模式,当前异常由 R0 产生还是 R3 产生
        push    ebp     ; TrapFrame: 陷阱帧,保存的是异常产生的寄存器环境
        push    0       ; ExceptionFrame: 空的异常栈帧
        push    ecx     ; EXCEPTION_RECORD: 填充的 EXCEPTION_RECORD
        call    nt!KiDispatchException (83f07ee0)
        mov     esp,ebp
        
        ; 结束异常处理
        jmp     nt!KiExceptionExit (83e91bf8)
    CommonDispatchException endp

     

    如果异常是第一次分发并且进程具有调试端口, 则发送一个消息 到调试端口,并等待回复. 如果调试器处理了这个异常, 则结束异常的分发. 否则, 将异常信息拷贝到用户态的栈中, 并转到用户模式 , 在用户模式下尝试将异常派发 给异常处理程序. 如果异常处理程序处理了异常, 则结束异常分发. 如果用户层的 异常处理程序处理不了, 则调用NtRaiseException函数 主动触发异常, 并将 FirstChance设置为TRUE. 这个函数(KiDispatchException) 将会被第二次调用以 继续处理异常. 如果这次处理是第二次异常处理,并且进程有一个调试端口, 则发送 一个消息到调试端口,并等待调试器回复.如果调试器回复已经处理了异常, 则结束异 常分发. 否则发送给进程的异常端口, 并等其回复. 若异常被处理, 则异常分发结束 否则直接结束掉当前进程.

    VOID KiDispatchException (
        IN PEXCEPTION_RECORD ExceptionRecord,   // 指向异常信息结构体
        IN PKEXCEPTION_FRAME ExceptionFrame,    // 指向异常帧,0
        IN PKTRAP_FRAME TrapFrame,              // 指向陷阱帧,寄存器环境 
        IN KPROCESSOR_MODE PreviousMode,        // 异常产生的位置 R0/R3
        IN BOOLEAN FirstChance                  // 是否是第一次分发异常
        )
    {
        // 递增异常分发计数器
        KeGetCurrentPrcb()->KeExceptionDispatchCount++;
     
        // 创建保存线程环境的结构体,其中的参数 1 表示要操作哪些寄存器
        CONTEXT Context = { CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS };
     
        // --------------------------------------------------------------
        // 如果当前处于用户模式产生的异常,或内核调试器处于开启状态
        if (PreviousMode == UserMode) || KdDebuggerEnabled)
        {
            // 尝试获取硬件相关的一些寄存器
            ContextFrame.ContextFlags |= CONTEXT_FLOATING_POINT;
            if (KeI386XMMIPresent) {
                ContextFrame.ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
            }
        }
        // --------------------------------------------------------------
    ​
    ​
        // 通过传入的陷阱帧和异常帧填充 Context 结构体中的线程环境
        KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
     
    ​
        // 对不同的异常执行不同的处理操作
        switch (ExceptionRecord->ExceptionCode)
        {
            // 如果是断点异常 [int 3]
        case STATUS_BREAKPOINT:            
            // 因为 int 3 是陷阱类异常,实际产生异常的指令是上一条
            Context.Pc -= sizeof(ULONG);
            break;
     
        // ------------------------------------------------------------------------
        // 如果是设备访问异常
        case KI_EXCEPTION_ACCESS_VIOLATION:
        // 设置异常的类型
        ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION;
    ​
        // 用户模式下对 Thunk 进行的一些检查
        if (PreviousMode == UserMode)
        {
            if (KiCheckForAtlThunk(ExceptionRecord,&ContextFrame) != FALSE) {
                goto Handled1;
            }
    ​
            if ((SharedUserData->ProcessorFeatures[PF_NX_ENABLED] == TRUE) &&
                (ExceptionRecord->ExceptionInformation [0] == EXCEPTION_EXECUTE_FAULT)) {
                
                if (((KeFeatureBits & KF_GLOBAL_32BIT_EXECUTE) != 0) ||
                    (PsGetCurrentProcess()->Pcb.Flags.ExecuteEnable != 0) ||
                    (((KeFeatureBits & KF_GLOBAL_32BIT_NOEXECUTE) == 0) &&
                        (PsGetCurrentProcess()->Pcb.Flags.ExecuteDisable == 0))) {
                    ExceptionRecord->ExceptionInformation [0] = 0;
                }
            }
        }
        break;
        }
        // ------------------------------------------------------------------------
    // 如果异常产生于内核态且处理器位 V86 模式
        ASSERT ((
                 !((PreviousMode == KernelMode) &&
                 (ContextFrame.EFlags & EFLAGS_V86_MASK))
               ));
    ​
    ​
        // 如果异常产生于内核模式下
        if (PreviousMode == KernelMode(0))
        {
             // 查看当前是否是第一次产生的异常分发
             if (FirstChance == TRUE)
             {
                // 调用 KiDebugRoutine 函数指针(实际就是内核调试器,如果没有内核调试器,
                // 函数指针保存的是KdpTrap函数的地址,如果有,则保存KdpStub函数地址) 
                // - 1. 指向内核调试器
                // - 2. 指向一个没有意义的函数 KdpStub
                if ((KiDebugRoutine != NULL) &&
                   (((KiDebugRoutine) (TrapFrame,       // 线程环境
                                       ExceptionFrame,  // 异常栈帧
                                       ExceptionRecord, // 异常信息记录
                                       &ContextFrame, 
                                       PreviousMode, 
                                       FALSE)) != FALSE)) {
                    // 如果调试处理了异常, 则跳转到函数末尾.
                    goto Handled;
                }
                // 没有调试器, 或者有调试器但没有处理得了异常
                // 则将异常交给内核的 SEH 来处理
                if (RtlDispatchException(ExceptionRecord, &ContextFrame) == TRUE) {
                    // 如果SEH处理成功, 则跳转到函数末尾
                    goto Handled;
                }
            }
    ​
            // 如果调试器和SEH异常处理都没有处理得了异常, 则进行第二次异常分发:
            // 判断有无内核调试器,并调用(再给内核调试器一次处理异常得机会)
            if ((KiDebugRoutine != NULL) &&
                (((KiDebugRoutine) (TrapFrame,
                                    ExceptionFrame,
                                    ExceptionRecord,
                                    &ContextFrame,
                                    PreviousMode,
                                    TRUE)) != FALSE)) {
                goto Handled;
            }
     
            // 没有内核调试器, 或内核调试器不处理, 则调用函数KeBugCheckEx记录异常,之后蓝屏死机
            KeBugCheckEx(
                KERNEL_MODE_EXCEPTION_NOT_HANDLED,
                ExceptionRecord->ExceptionCode,
                (ULONG)ExceptionRecord->ExceptionAddress,
                (ULONG)TrapFrame,
                0);
        }
    ​
    ​
    ​
    ​
        else // 异常在在用户模式下触发 
        {   
            // 如果异常是第一次分发并且进程[具有调试端口](处于被调试状态), 则发送一个消息
            // 到调试端口,并等待回复. 如果调试器处理了这个异常, 则结束异常的分发. 否则,
            // 将异常信息拷贝到用户态的栈中, 并转到用户模式 , 在用户模式下尝试将异常派发
            // 给异常处理程序. 如果异常处理程序处理了异常, 则结束异常分发. 如果用户层的
            // 异常处理程序处理不了, 则调用NtRaiseException函数 主动触发异常, 并将
            // FirstChance设置为TRUE. 这个函数(KiDispatchException)  将会被第二次调用以
            // 继续处理异常.  如果这次处理是第二次异常处理,并且进程有一个调试端口, 则发送
            // 一个消息到调试端口,并等待调试器回复.如果调试器回复已经处理了异常, 则结束异
            // 常分发. 否则发送给进程的异常端口, 并等其回复. 若异常被处理, 则异常分发结束
            // 否则直接结束掉当前进程.
    ​
    ​
            if (FirstChance == TRUE) {
                // 检查是进程是否被调试,如果当前有内核调试器, 并且进程没有被调试,则将异常交给
                // 内核调试器去处理.
                if ((KiDebugRoutine != NULL)  &&
                // DebugPort 调试端口,当程序属于被调试状态时,为非空
                    ((PsGetCurrentProcess()->DebugPort == NULL &&
                      !KdIgnoreUmExceptions) ||
                     (KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode)))) {
    ​
                    // 将异常信息交给内核调试器处理
                    if ((((KiDebugRoutine) (TrapFrame,
                                            ExceptionFrame,
                                            ExceptionRecord,
                                            &ContextFrame,
                                            PreviousMode,
                                            FALSE)) != FALSE)) {
                        // 处理成功则异常分发结束
                        goto Handled1;
                    }
                }
                
    // 将异常交给调试子系统去处理. DbgkForwardException函数会将
    // 异常记录发送给3环的调试器进程, 并等待3环的调试器回复.
    // 如果调试器回复了异常被处理, 则异常分发到此结束.
                // *DbgkpSendApiMessage()*   用于发送调试信息,阻塞函数,
                // 会等待调试器返回处理结果
                if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) {
                    goto Handled2;
                }
    // 如果没有用户调试器,或用户调试器没有处理异常则接着往下走.
    // 函数会试图将异常记录, 线程环境进行保存
                ExceptionRecord1.ExceptionCode = 0; // satisfy no_opt compilation
    ​
            repeat:
                try {
                    // ------------------------------------------------------
                    // 如果SS不位于32位保护模式下
                    if (TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK) ||
                        TrapFrame->EFlags & EFLAGS_V86_MASK ) 
                    {
                        // 触发一个内存访问异常
                        ExceptionRecord2.ExceptionCode = STATUS_ACCESS_VIOLATION;
                        ExceptionRecord2.ExceptionFlags = 0;
                        ExceptionRecord2.NumberParameters = 0;
                        ExRaiseException(&ExceptionRecord2);
                    }
                    // ------------------------------------------------------
    // 为将线程上下文块整个结构体拷贝到用户的栈空间, 需要找到栈空间上一个空闲
    // 的地址, 此处是计算esp(栈顶位置) - 一个线程上下文块的大小, 也就是相当于
    // sub esp , sizeof(CONTEXT) 
                    UserStack1 = (ContextFrame.Esp & ~CONTEXT_ROUND) - CONTEXT_ALIGNED_SIZE;
                    // ---userstack1(esp)
                    // context  <- 线程环境
                    // -------
    // 将指定地址设置为可写入
                    ProbeForWrite((PCHAR)UserStack1, CONTEXT_ALIGNED_SIZE, CONTEXT_ALIGN);
    // 将线程上下文拷贝到用户的栈空间中.
                    RtlCopyMemory((PULONG)UserStack1, &ContextFrame, sizeof(CONTEXT));
    ​
    // 计算处异常信息结构体的在用户栈空间中的位置, 也是为了将异常信息写入
    // 到用户栈空间中.   EXCEPTION_RECORD 的大小
                    Length = (sizeof(EXCEPTION_RECORD) - (EXCEPTION_MAXIMUM_PARAMETERS -
                             ExceptionRecord->NumberParameters) * sizeof(ULONG) + 3) &
                             (~3);
                    UserStack2 = UserStack1 - Length;
                    // ---userstack2(esp)
                    // EXCEPTION_RECORD
                    // ---userstack1
                    // context  <- 线程环境
                    // -------
                    //   在用户空间合成了一个 ExceptionPointers 结构
    // 将地址设置为可写
                    ProbeForWrite((PCHAR)(UserStack2 - 8), Length + 8, sizeof(ULONG));
    // 将异常信息拷贝用户的栈空间中.
                    RtlCopyMemory((PULONG)UserStack2, ExceptionRecord, Length);
    ​
    // 构造处一个EXCEPTION_POINTERS的结构体, 并保存线程上下文, 异常信息两个结构体
    // 变量的首地址.
                    *(PULONG)(UserStack2 - sizeof(ULONG)) = UserStack1;
                    *(PULONG)(UserStack2 - 2*sizeof(ULONG)) = UserStack2;
    ​
                    // 设置新的SS寄存器和ESP寄存器
                    KiSegSsToTrapFrame(TrapFrame, KGDT_R3_DATA);
                    KiEspToTrapFrame(TrapFrame, (UserStack2 - sizeof(ULONG)*2));
    ​
    // 填充三环的段寄存器
                    TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, PreviousMode);
                    TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
                    TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
                    TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, PreviousMode);
                    TrapFrame->SegGs = 0;
    ​
    // 将发生异常的线程的eip的地址设置为KeUserExceptionDispatcher函数的地址
    // 这个函数是ntdll中的导出函数,这个导出函数就是负责用户层的异常分发的,
    // 在这个函数中,它会把异常发给进程的异常处理机制(VEH,SEH)去处理.
    // 这样一来, 当执行流从0环回到3环的时候, eip指向何处, 那个地方的代码就开始
    // 被执行.
                    TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;
                    // KeUserExceptionDispatcher 是指针指向了R3的 KiUserExceptionDispatcher
                    return;
    ​
                } except (KiCopyInformation(&ExceptionRecord1,
                            (GetExceptionInformation())->ExceptionRecord)) {
    ​
                    // 如果这些代码产生了栈溢出错误,就构建一个栈溢出异常进行分发
    if (ExceptionRecord1.ExceptionCode == STATUS_STACK_OVERFLOW) {
                        ExceptionRecord1.ExceptionAddress = ExceptionRecord->ExceptionAddress;
                        RtlCopyMemory((PVOID)ExceptionRecord,
                                      &ExceptionRecord1, sizeof(EXCEPTION_RECORD));
                        goto repeat;
                    }
                }
            }
    ​
            // 如果是第二次异常分发
            // 将异常信息发送到调试器的 DebugPort
            // 第三个参数表示当前是不是第二次调用
            if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) {
                goto Handled2;
    ​
            // 将异常信息发送到调试器的 ExceptionPort
            } else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) {
                goto Handled2;
            // 如果还不能处理就结束进程并发送错误报告
            } else {
                ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);
                
                /////////////// 保留 ////////////////
                KeBugCheckEx(
                    KERNEL_MODE_EXCEPTION_NOT_HANDLED,
                    ExceptionRecord->ExceptionCode,
                    (ULONG)ExceptionRecord->ExceptionAddress,
                    (ULONG)TrapFrame,
                    0);
            }
        }
    ​
    Handled:
    // 将异常栈帧, 线程环境设置到线程中,并继续执行程序
        // 处理异常可能会修改CONTEXT的寄存器,但是返回异常产生的位置
        // 使用的是 TrapFrame,所以需要将修改更新到 TrapFrame
        KeContextToKframes(TrapFrame, ExceptionFrame, &ContextFrame,
                           ContextFrame.ContextFlags, PreviousMode);
    ​
    Handled2:
        // 如果异常已经被处理,则直接进行返回
        return;
    }

    RtlDispatchException[R0].c

    // 通过遍历 SEH 链调用其中的函数来处理异常并返回处理结果
    BOOLEAN RtlDispatchException (
        IN PEXCEPTION_RECORD ExceptionRecord,
        IN PCONTEXT ContextRecord
        )
    {
        // 是否处理成功
        BOOLEAN Completion = FALSE;
        DISPATCHER_CONTEXT DispatcherContext;
        EXCEPTION_DISPOSITION Disposition;
        PEXCEPTION_REGISTRATION_RECORD RegistrationPointer;
        PEXCEPTION_REGISTRATION_RECORD NestedRegistration;
        ULONG HighAddress;
        ULONG HighLimit;
        ULONG LowLimit;
        EXCEPTION_RECORD ExceptionRecord1;
        ULONG Index;
    ​
        // 获取当前堆栈的栈顶和栈底
        RtlpGetStackLimits(&LowLimit, &HighLimit);
    ​
        // 获取 SEH 链的头节点
        RegistrationPointer = RtlpGetRegistrationHead();
        // mov eax, fs:[0]
        // mov RegistrationPointer, eax
    ​
        NestedRegistration = 0;
    ​
        // 依次遍历调用 SEH 链中的函数
        while (RegistrationPointer != EXCEPTION_CHAIN_END(-1)) {
    ​
            // 获取当前异常处理结构的起始地址和末尾地址
            HighAddress = (ULONG)RegistrationPointer +
                sizeof(EXCEPTION_REGISTRATION_RECORD);
    ​
            // 如果起始地址小于栈底,结束位置大于栈顶,或者结构体地址没有对齐
            if (((ULONG)RegistrationPointer < LowLimit) ||
                 (HighAddress > HighLimit) ||
                 // test xxx, 3   0x11
                 (((ULONG)RegistrationPointer & 0x3) != 0) 
               ) { 
    // -------------------------------------------------
                // 尝试对结构体的地址进行修复
                ULONG TestAddress = (ULONG)RegistrationPointer;
    ​
                if (((TestAddress & 0x3) == 0) &&
                    KeGetCurrentIrql() >= DISPATCH_LEVEL) {
    ​
                    PKPRCB Prcb = KeGetCurrentPrcb();
                    ULONG DpcStack = (ULONG)Prcb->DpcStack;
    ​
                    if ((Prcb->DpcRoutineActive) &&
                        (HighAddress <= DpcStack) &&
                        (TestAddress >= DpcStack - KERNEL_STACK_SIZE)) {
                        
                        HighLimit = DpcStack;
                        LowLimit = DpcStack - KERNEL_STACK_SIZE;
                        continue;
                    }
                }
    ​
                // 如果无法修复就退出函数并设置异常标志为栈无效
                ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
                goto DispatchExit;
            }
    ​
            // 查看当前处理程序是否是有效的
            if (!RtlIsValidHandler(RegistrationPointer->Handler)) {
                // 如果是无效的就结束遍历
                ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
                goto DispatchExit;
            }
    ​
            
            // 如果启用了调试异常的记录,则记录异常
            if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
                Index = RtlpLogExceptionHandler(
                                ExceptionRecord,
                                ContextRecord,
                                0,
                                (PULONG)RegistrationPointer,
                                4 * sizeof(ULONG));
            }
    // -------------------------------------------------
    // 执行当前遍历到的 SEH 函数   _except_handler4
            Disposition = RtlpExecuteHandlerForException(
                ExceptionRecord,
                (PVOID)RegistrationPointer,
                ContextRecord,
                (PVOID)&DispatcherContext,
                (PEXCEPTION_ROUTINE)RegistrationPointer->Handler);
    ​
            // 记录 SEH 函数的处理结果
            if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
                RtlpLogLastExceptionDisposition(Index, Disposition);
            }
    ​
            // 当位于嵌套异常且处于上下文末尾,则清除嵌套标志
            if (NestedRegistration == RegistrationPointer) {
                ExceptionRecord->ExceptionFlags &= (~EXCEPTION_NESTED_CALL);
                NestedRegistration = 0;
            }
    ​
            // 根据 SEH 函数的处理结果进行性对应的操作
            switch (Disposition) {
            
            // 继续执行
            case ExceptionContinueExecution :
                // 如果这是一个不可持续的异常,就继续使用 
                // RtlRaiseException进行第二次的异常分发
                if ((ExceptionRecord->ExceptionFlags &
                    EXCEPTION_NONCONTINUABLE) != 0) {
                    ExceptionRecord1.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
                    ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
                    ExceptionRecord1.ExceptionRecord = ExceptionRecord;
                    ExceptionRecord1.NumberParameters = 0;
                    RtlRaiseException(&ExceptionRecord1);
                } else {
                    // 否则返回成功
                    Completion = TRUE;
                    goto DispatchExit;
                }
    ​
            // 向下搜索
            case ExceptionContinueSearch :
                // 如果栈没有出现损坏,则继续遍历
                if (ExceptionRecord->ExceptionFlags & EXCEPTION_STACK_INVALID)
                    goto DispatchExit;
                break;
    ​
            // 嵌套的异常
            case ExceptionNestedException :
                // 如果在异常处理程序中产生了异常,就从指定位置开始重新遍历
                ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
                if (DispatcherContext.RegistrationPointer > NestedRegistration) {
                    NestedRegistration = DispatcherContext.RegistrationPointer;
                }
                break;
    ​
    ​
            default :
                // 如果以上都不是,就生成一个新的异常
                ExceptionRecord1.ExceptionCode = STATUS_INVALID_DISPOSITION;
                ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
                ExceptionRecord1.ExceptionRecord = ExceptionRecord;
                ExceptionRecord1.NumberParameters = 0;
                RtlRaiseException(&ExceptionRecord1);
                break;
            }
    ​
            // 循环遍历下一个处理程序
            RegistrationPointer = RegistrationPointer->Next;
        }
    ​
    DispatchExit:
    ​
        return Completion;
    }

    RtlDispatchException[R3].c

    {
         PEXCEPTION_REGISTRATION_RECORD RegistrationFrame;
         PEXCEPTION_REGISTRATION_RECORD NestedFrame = NULL;
         DISPATCHER_CONTEXT DispatcherContext;
         EXCEPTION_RECORD ExceptionRecord2;
         EXCEPTION_DISPOSITION Disposition;
         ULONG_PTR StackLow, StackHigh;
         ULONG_PTR RegistrationFrameEnd;
     
        // 调用用户模式下的向量化异常处理程序(VEH)
        if (RtlCallVectoredExceptionHandlers(ExceptionRecord, Context))
        {
            // 异常被处理了就调用向量化异常处理程序(VCH)
            RtlCallVectoredContinueHandlers(ExceptionRecord, Context);
     
            // 返回已处理
            return TRUE;
        }
     
        // 获取当前栈顶以及栈限长
         RtlpGetStackLimits(&StackLow, &StackHigh);
         RegistrationFrame = RtlpGetExceptionList();
     
         // 不断循环遍历所有的 SEH 结构
        while (RegistrationFrame != EXCEPTION_CHAIN_END)
        {
            // 检查是否为空,一个被注册的异常结构永远不为空
            ASSERT(RegistrationFrame != NULL);
    ​
            // 获取异常结构的结束位置
            RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
                                    sizeof(EXCEPTION_REGISTRATION_RECORD);
    ​
            // 检查异常结构是否在栈内,并且是否对齐
            if ((RegistrationFrameEnd > StackHigh) ||
                ((ULONG_PTR)RegistrationFrame < StackLow) ||
                ((ULONG_PTR)RegistrationFrame & 0x3) ||
                !RtlIsValidHandler(SehBase->Handler))
            {
                // 如果不满足条件
                ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
    ​
                // 异常被处理了就调用向量化异常处理程序(VCH)
                RtlCallVectoredContinueHandlers(ExceptionRecord, Context);
     
                // 返回已处理
                return FALSE;
            }
    ​
            // 如果启用了调试异常的记录,则记录异常
            RtlpCheckLogException(ExceptionRecord,
                                Context,
                                RegistrationFrame,
                                sizeof(*RegistrationFrame));
    ​
            // 调用异常处理函数, exception_handler4
            Disposition = RtlpExecuteHandlerForException(ExceptionRecord,
                                                        RegistrationFrame,
                                                        Context,
                                                        &DispatcherContext,
                                                        RegistrationFrame->Handler);
    ​
            // 如果是嵌套异常处理结构体
            if (RegistrationFrame == NestedFrame)
            {
                // 屏蔽嵌套标志位
                ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL;
                NestedFrame = NULL;
            }
    ​
            // 根据 SEH 函数的处理结果进行性对应的操作
            switch (Disposition)
            {
                // 继续执行
                case ExceptionContinueExecution:
    ​
                    // 如果是一个不可继续的异常
                    if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
                    {
                        // 设置异常信息
                        ExceptionRecord2.ExceptionRecord = ExceptionRecord;
                        ExceptionRecord2.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
                        ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
                        ExceptionRecord2.NumberParameters = 0;
                        RtlRaiseException(&ExceptionRecord2);
                    }
                    else
                    {
                        // 调用向量化异常处理程序(VCH)
                        RtlCallVectoredContinueHandlers(ExceptionRecord,
                                                        Context);
                        return TRUE;
                    }
    ​
                // 搜索下一层异常处理函数
                case ExceptionContinueSearch:
                    break;
    ​
                // 如果是嵌套异常
                case ExceptionNestedException:
                    ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
                    if (DispatcherContext.RegistrationPointer > NestedFrame)
                    {
                        NestedFrame = DispatcherContext.RegistrationPointer;
                    }
                    break;
    ​
                // 其它操作
                default:
                    // 设置异常信息结构
                    ExceptionRecord2.ExceptionRecord = ExceptionRecord;
                    ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
                    ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
                    ExceptionRecord2.NumberParameters = 0;
                    RtlRaiseException(&ExceptionRecord2);
                    break;
            }
    ​
            // 获取下一个异常处理函数
            RegistrationFrame = RegistrationFrame->Next;
        }
    ​
        // 调用向量化异常处理程序(VCH)
        RtlCallVectoredContinueHandlers(ExceptionRecord,
                                        Context);
        // 如果没有处理就返回 FALSE
        return FALSE;
    }

    KiUserExceptionDispatcher.c

    {
        EXCEPTION_RECORD NestedExceptionRecord;
        NTSTATUS Status;
    ​
        // 处理异常并检查返回值
        if (RtlDispatchException(ExceptionRecord, Context))
        {
            // 如果成功了就直接通过 NtContinue 进入 R0 并返回执行代码的位置
            Status = NtContinue(Context, FALSE);
        }
        else
        {
            // 如果没有处理成功就继续抛出异常(Ki)
            // 最后一个参数表示是否是第一次产生的异常 FALSE
            Status = NtRaiseException(ExceptionRecord, Context, FALSE);
        }
    ​
        // 设置异常函数
        NestedExceptionRecord.ExceptionCode = Status;
        NestedExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
        NestedExceptionRecord.ExceptionRecord = ExceptionRecord;
        NestedExceptionRecord.NumberParameters = Status;
    ​
        // 抛出异常(Zw)
        RtlRaiseException(&NestedExceptionRecord);
    }

    遍历当前线程中的所有 SEH 函数及添加自定义的 SEH 函数

    #include <iostream>
    #include <windows.h>// SEH 保存在 TEB 结构体偏移为 0 的地方,是一个链表
    // SEH 链表可以通过 FS:[0] 这样一个地址找到
    // TEB 可以通过 FS:[0x18] 这样一个地址找到
    // try except 的原理就是在 SEH 链表中添加一个新的节点
    // 没有设置 SEH 的函数
    void func1()
    {
    ​
    }
    ​
    // 设置了 SEH 的函数
    void func2()
    {
        // 如果一个函数中设置了 SEH 处理程序,那么必然会操作 FS:[0]
        // 在 VS 下,无论设置了多少个 SEH,最终只会添加  _except_handler4 函数
        // 系统调用 _except_handler4,_except_handler4 内部区分当前的 try 位于第几层
    ​
        __try {
            __try {
                __try {
                    __try {
                        __try {
                        }
                        __except (1) {
                        }
                    }
                    __except (1) {
                    }
                }
                __except (1) {
                }
            }
            __except (1) {
            }
        }
        __except (1) {
        }
    }
    ​
    // 自定义的 SEH 函数
    EXCEPTION_DISPOSITION NTAPI ExceptionHandler(
        struct _EXCEPTION_RECORD* ExceptionRecord,
        PVOID EstablisherFrame,
        struct _CONTEXT* ContextRecord,
        PVOID DispatcherContext)
    {
        return ExceptionContinueSearch;
    }
    ​
    // 遍历当前线程中的所有 SEH 函数
    void GetThreadList()
    {
        // 1. 获取当前 SEH 链表的头节点
        PEXCEPTION_REGISTRATION_RECORD ExceptionList = nullptr;
        __asm push FS : [0];
        __asm pop ExceptionList;
    ​
        // 2. 遍历 SEH 中的所有函数
        //(PEXCEPTION_REGISTRATION_RECORD)-1指Next节点。
        //因为PEXCEPTION_REGISTRATION_RECORD-1是指针减1
        //即减少一个自己大小的结构体
        while (ExceptionList != (PEXCEPTION_REGISTRATION_RECORD)-1)
        {
            // 3. 输出当前层,对应的处理函数
            printf("0x%08X
    ", ExceptionList->Handler);
    ​
            // 4. 将指针指向下一个节点
            ExceptionList = ExceptionList->Next;
        }
    ​
        printf("
    ");
    }
    ​
    int main()
    {
        // 0. 保存 SEH 头节点,主要用于恢复
        PEXCEPTION_REGISTRATION_RECORD ExceptionList = nullptr;
        __asm push FS : [0];
        __asm pop ExceptionList;
    ​
        // 1. 添加自定义 SEH 函数之前的 SEH 链
        GetThreadList();
    ​
        // 2. 添加自定义的 SEH 函数
        __asm push ExceptionHandler
        __asm push fs : [0]
            __asm mov fs : [0], esp
    ​
        // 3. 添加自定义 SEH 函数之后的 SEH 链
        GetThreadList();
    ​
        // 4. 恢复旧的 SEH 头节点
        __asm add esp, 0x08
        __asm mov eax, ExceptionList
        __asm mov fs : [0], eax
    ​
        // 5. 应该和以前的节点是相同的
        GetThreadList();
    ​
        return 0;
    }

     

  • 相关阅读:
    雅米新一代企业管理平台
    Telerik.Web.UI应用到sharepoint2010注意事项
    SharePoint Server 2010 容量管理:软件边界和限制
    【读】构建可枚举类型(IEnumerator和IEnumerable)
    【读】C# 窗口间传递数据
    分类(转)
    “盗”亦有道
    English学习指南
    爱情是什么?
    客户需要的26个待遇 (转)
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11161236.html
Copyright © 2011-2022 走看看