吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3899|回复: 6
收起左侧

[调试逆向] int3异常处理流程分析

[复制链接]
Nattevak 发表于 2021-12-7 17:08
本帖最后由 Nattevak 于 2021-12-8 13:53 编辑

一、处理int3异常的函数为KiTrap03
在开始异常处理之初,先构造KTRAP_FRAME陷阱帧结构,陷阱帧是指一个结构体,用来保存系统调用、中断、异常发生时的寄存器现场,方便以后回到用户空间/回到中断处时,恢复那些寄存器的值,继续执行。

1.1

1.1

FS进入内核层就不是TEB了
[Asm] 纯文本查看 复制代码
83e8e5c0 6a00            push    0
83e8e5c2 66c74424020000  mov     word ptr [esp+2],0
83e8e5c9 55              push    ebp
83e8e5ca 53              push    ebx
83e8e5cb 56              push    esi
83e8e5cc 57              push    edi
83e8e5cd 0fa0            push    fs

TRAP_FRAME  用于在处理完异常之后,还能还原这些寄存器
保存寄存器环境  构建了一个陷阱帧
[Asm] 纯文本查看 复制代码
83e8e5cf bb30000000      mov     ebx,30h
83e8e5d4 668ee3          mov     fs,bx
83e8e5d7 648b1d00000000  mov     ebx,dword ptr fs:[0]
83e8e5de 53              push    ebx
83e8e5df 83ec04          sub     esp,4
83e8e5e2 50              push    eax
83e8e5e3 51              push    ecx
83e8e5e4 52              push    edx
83e8e5e5 1e              push    ds
83e8e5e6 06              push    es
83e8e5e7 0fa8            push    gs         
83e8e5e9 66b82300        mov     ax,23h
83e8e5ed 83ec30          sub     esp,30h

EBP 此时就是栈顶,陷阱帧的起始位置
[Asm] 纯文本查看 复制代码
83e8e5f0 668ed8          mov     ds,ax
83e8e5f3 668ec0          mov     es,ax
83e8e5f6 8bec            mov     ebp,esp

线程的内核对象
[Asm] 纯文本查看 复制代码
83e8e5f8 f744247000000200 test    dword ptr [esp+70h],20000h
83e8e600 7596            jne     nt!V86_kit3_a (83e8e598)
83e8e602 648b0d24010000  mov     ecx,dword ptr fs:[124h]

[Asm] 纯文本查看 复制代码
83e8e609 fc              cld
83e8e60a 83652c00        and     dword ptr [ebp+2Ch],0
83e8e60e f64103df        test    byte ptr [ecx+3],0DFh
83e8e612 0f8500ffffff    jne     nt!Dr_kit3_a (83e8e518)

用户层的EBP
[Asm] 纯文本查看 复制代码
83e8e618 8b5d60          mov     ebx,dword ptr [ebp+60h]

产生异常时的EIP
[Asm] 纯文本查看 复制代码
83e8e61b 8b7d68          mov     edi,dword ptr [ebp+68h]

以下4行还是在构建陷阱帧
[Asm] 纯文本查看 复制代码
83e8e61e 89550c          mov     dword ptr [ebp+0Ch],edx
83e8e621 c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h
83e8e628 895d00          mov     dword ptr [ebp],ebx
83e8e62b 897d04          mov     dword ptr [ebp+4],edi

检测一些什么属性,不合适就跳走
[Asm] 纯文本查看 复制代码
83e8e62e 803dd923f98300  cmp     byte ptr [nt!PoHiberInProgress (83f923d9)],0
83e8e635 7507            jne     nt!KiTrap03+0x7e (83e8e63e)
83e8e637 f0ff05fc81f883  lock inc dword ptr [nt!KiHardwareTrigger (83f881fc)]
83e8e63e b800000000      mov     eax,0
83e8e643 f6457202        test    byte ptr [ebp+72h],2
83e8e647 752f            jne     nt!KiTrap03+0xb8 (83e8e678)
83e8e649 f6456c01        test    byte ptr [ebp+6Ch],1
83e8e64d 7508            jne     nt!KiTrap03+0x97 (83e8e657)
83e8e64f f6457102        test    byte ptr [ebp+71h],2
83e8e653 740a            je      nt!KiTrap03+0x9f (83e8e65f)
83e8e655 eb07            jmp     nt!KiTrap03+0x9e (83e8e65e)
83e8e657 66837d6c1b      cmp     word ptr [ebp+6Ch],1Bh
83e8e65c 751a            jne     nt!KiTrap03+0xb8 (83e8e678)

线程内核对象地址给了esi
[Asm] 纯文本查看 复制代码
83e8e65e fb              sti
83e8e65f 8bf1            mov     esi,ecx

被覆盖为EDX
[Asm] 纯文本查看 复制代码
83e8e661 8bfa            mov     edi,edx

EAX是0    cc是一个陷阱
[Asm] 纯文本查看 复制代码
83e8e663 8bd0            mov     edx,eax

产生异常的下一条的EIP给了ebx
[Asm] 纯文本查看 复制代码
83e8e665 8b5d68          mov     ebx,dword ptr [ebp+68h]

0xCC的地址
[Asm] 纯文本查看 复制代码
83e8e668 4b              dec     ebx

异常号   3号
[Asm] 纯文本查看 复制代码
83e8e669 b903000000      mov     ecx,3

错误编码
[Asm] 纯文本查看 复制代码
83e8e66e b803000080      mov     eax,80000003h

[Asm] 纯文本查看 复制代码
[b]83e8e673 e814f7ffff      call    nt!CommonDispatchException (83e8dd8c)[/b]

1.2

1.2

注意到KiTrap03 实际上调用了 CommonDispatchException

二、分析CommdispatchException
刚刚构建的结构体的地址
[Asm] 纯文本查看 复制代码
nt!CommonDispatchException:
83e8dd8c 83ec50          sub     esp,50h
83e8dd8f 890424          mov     dword ptr [esp],eax
83e8dd92 33c0            xor     eax,eax
83e8dd94 89442404        mov     dword ptr [esp+4],eax
83e8dd98 89442408        mov     dword ptr [esp+8],eax
83e8dd9c 895c240c        mov     dword ptr [esp+0Ch],ebx
83e8dda0 894c2410        mov     dword ptr [esp+10h],ecx
83e8dda4 83f900          cmp     ecx,0
83e8dda7 740c            je      nt!CommonDispatchException+0x29 (83e8ddb5)
83e8dda9 8d5c2414        lea     ebx,[esp+14h]
83e8ddad 8913            mov     dword ptr [ebx],edx
83e8ddaf 897304          mov     dword ptr [ebx+4],esi
83e8ddb2 897b08          mov     dword ptr [ebx+8],edi
83e8ddb5 8bcc            mov     ecx,esp

[Asm] 纯文本查看 复制代码
83e8ddb7 f6457202        test    byte ptr [ebp+72h],2
83e8ddbb 7407            je      nt!CommonDispatchException+0x38 (83e8ddc4)
83e8ddbd b8ffff0000      mov     eax,0FFFFh
83e8ddc2 eb03            jmp     nt!CommonDispatchException+0x3b (83e8ddc7)
83e8ddc4 8b456c          mov     eax,dword ptr [ebp+6Ch]
83e8ddc7 83e001          and     eax,1
83e8ddca 6a01            push    1
83e8ddcc 50              push    eax
83e8ddcd 55              push    ebp
83e8ddce 6a00            push    0
83e8ddd0 51              push    ecx
83e8ddd1 e80a610700      call    nt!KiDispatchException (83f03ee0)

通过ECX的后两位能够判断异常产生是在0环(高权限的代码,内核层代码)  还是3环(低权限的代码,用户层代码)。
判断是用户异常还是内核异常使用的是CS段寄存器的最后两位

2.2

2.2

3环 1B
0环段寄存器 08  后两位为00       操作系统只使用0、3环,1、2环没有用
3环权限低,0环权限高
构建了一个异常记录的结构之后实际上调用了 KiDispatchException 来处理异常

2.1

2.1


三、分析KIDispathException
内核层异常处理      
        先让调试器处理,处理了就继续执行,处理不了就继续向下传        
        让 RtlDispatchException去处理,处理了就结束,没处理就继续向下传        
        又让内核调试器处理,处理了就继续执行,处理不了就会导致崩溃(直接蓝屏)
用户层异常处理        
        如果是第一次分发               
                注意:通过进程内核对象中的 DebugPort字段去判断有没有用户调试器的
                先检测有没有用户调试器,如果没有用户调试器,就给内核调试器。处理的就继续,没处理就继续向下传
                发给用户层调试器,如果处理了就继续执行,没处理就继续向下传
                设置EIP为 KeUserExceptionDispatcher  (在用户层叫做KiUserExceptionDispatcher)
                然后返回,返回到用户层就会执行KiUserExceptionDispatcher
                KiUserExceptionDispatcher 会调用RtlDispathchException
                依次调用  VEH  ---SEH  ----VCH
                如果都没有处理,就会调用RaiseException发起第二次异常。
                如果处理了,就调用ZwContinue恢复程序继续执行。
        如果是第二次分发(用户层自己处理不了,调用RaiseException 产生的)
                交给用户调试器处理,如果处理不了,程序就直接被结束

四、分析KiUserExceptionDispatcher
可以在微软的服务器下载符号,分析ntdll以及别的windowsdll中的函数
使用IDA配合符号分析相应的DLL

4.1

4.1

RltDispatcheException函数功能:
先进行  VEH的处理,处理了调用  VCH  然后return true、VEH处理不了就让SEH处理,  循环挨个SEH节点处理。如果能够处理调用VCH return true。SEH也处理不了,就出来,然后产生二次异常。

4.2

4.2

RltDispatcheException函数源码:
[C] 纯文本查看 复制代码
BOOLEAN RtlDispatchException (
    IN PEXCEPTION_RECORD ExceptionRecord,
    IN PCONTEXT ContextRecord
    )
{
     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;
 
            // 返回处理失败
            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;
    }
    
    // 如果没有处理就返回 FALSE
    return FALSE;
}

免费评分

参与人数 3威望 +1 吾爱币 +23 热心值 +3 收起 理由
sam喵喵 + 1 谢谢@Thanks!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lyl610abc + 3 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

°゛゛°___囧 发表于 2021-12-7 19:38
牛 膜拜大神
Aza12138 发表于 2021-12-7 22:33
ldw471427015 发表于 2021-12-8 02:20
Stant 发表于 2021-12-8 04:02
不错试试看到底怎么样
Wobe 发表于 2021-12-8 06:52
jifL88 发表于 2021-12-8 08:38
谢谢分享~
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-23 15:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表