zoukankan      html  css  js  c++  java
  • IDT hook KiTrap03

    关于idt的基本知识就不进行赘述了,先看一个例子

    0x1000:    mov   eax,0
    0x1006:    Int    3            ;------->进入内核,找到中断处理例程KiTrap03
    0x1007:   Mov   eax,1

    这段代码执行,触发3号中断,然后开始执行KiTrap03例程,要知道,执行完中断以后还是要回到原来的程序处继续执行的,也就是我们这的Mov eax, 1的指令,显然,发生中断时的寄存器环境就要被保存,便于之后的恢复程序运行,这里,就出现了一个结构_KTRAP_FRAME,陷阱帧。

    kd> dt _KTRAP_FRAME
    nt!_KTRAP_FRAME
       +0x000 DbgEbp           : Uint4B
       +0x004 DbgEip           : Uint4B
       +0x008 DbgArgMark       : Uint4B
       +0x00c DbgArgPointer    : Uint4B
       +0x010 TempSegCs        : Uint2B
       +0x012 Logging          : UChar
       +0x013 Reserved         : UChar
       +0x014 TempEsp          : Uint4B
       +0x018 Dr0              : Uint4B
       +0x01c Dr1              : Uint4B
       +0x020 Dr2              : Uint4B
       +0x024 Dr3              : Uint4B
       +0x028 Dr6              : Uint4B
       +0x02c Dr7              : Uint4B
       +0x030 SegGs            : Uint4B
       +0x034 SegEs            : Uint4B
       +0x038 SegDs            : Uint4B
       +0x03c Edx              : Uint4B
       +0x040 Ecx              : Uint4B
       +0x044 Eax              : Uint4B
       +0x048 PreviousPreviousMode : Uint4B
       +0x04c ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD
       +0x050 SegFs            : Uint4B
       +0x054 Edi              : Uint4B
       +0x058 Esi              : Uint4B
       +0x05c Ebx              : Uint4B
       +0x060 Ebp              : Uint4B
       +0x064 ErrCode          : Uint4B
    +0x068 Eip : Uint4B //硬件自动填充 +0x06c SegCs : Uint4B //硬件自动填充 +0x070 EFlags : Uint4B //硬件自动填充 //后面都是可选的,只有当我们运行的程序处于虚拟86模式异常才会有 +0x074 HardwareEsp : Uint4B +0x078 HardwareSegSs : Uint4B +0x07c V86Es : Uint4B +0x080 V86Ds : Uint4B +0x084 V86Fs : Uint4B +0x088 V86Gs : Uint4B

    整个陷阱帧结构记录中断发生时的寄存器环境,其中,结构体最后面的几个成员只有运行于虚拟86模式下才会存在,而Eip,SegCs和EFlags三个成员由硬件自动填充,然后剩下的成员才是由KiTrap03自己构建的陷阱帧,我们可以分析下KiTrap03的代码,由于在wrk中只有asm文件,没有C源代码,用ida进行分析

    .text:00436C50 _KiTrap03 proc near           ; DATA XREF: INIT:0077317Co
    .text:00436C50
    .text:00436C50 var_2= word ptr -2
    .text:00436C50 arg_4= dword ptr  8
    .text:00436C50
    .text:00436C50 push    0                     ; Trap_frame.Errcode
    .text:00436C52 mov     [esp+4+var_2], 0      ; Trap_frame.Errcode = 0
    .text:00436C59 push    ebp                   ; Trap_frame.Ebp
    .text:00436C5A push    ebx                   ; Trap_frame.Ebx
    .text:00436C5B push    esi                   ; Trap_frame.Esi
    .text:00436C5C push    edi                   ; Trap_frame.Edi
    .text:00436C5D push    fs                    ; Trap_frame.SegFs
    .text:00436C5F mov     ebx, 30h
    .text:00436C64 mov     fs, bx
    .text:00436C67 mov     ebx, large fs:0       ; fs对应处理器相关的_KPCR结构,kpcr,那么得到的是
    .text:00436C67                               ; kpcr.NtTib.ExceptionList
    .text:00436C6E push    ebx                   ; Trap_frame.ExceptionList
    .text:00436C6F sub     esp, 4                ; Trap_frame.PreviousPreviousMode,在后面填充
    .text:00436C72 push    eax                   ; Trap_frame.Eax
    .text:00436C73 push    ecx                   ; Trap_frame.Ecx
    .text:00436C74 push    edx                   ; Trap_frame.Edx
    .text:00436C75 push    ds                    ; Trap_frame.SegDs
    .text:00436C76 push    es                    ; Trap_frame.SegEs
    .text:00436C77 push    gs                    ; Trap_frame.SegGs
    .text:00436C79 mov     ax, 23h               ; ???
    .text:00436C7D sub     esp, 30h              ; 继续增大栈空间,刚好是整个Trap_Frame的大小
    .text:00436C80 mov     ds, ax                ; ???
    .text:00436C83 mov     es, ax
    .text:00436C86 mov     ebp, esp              ; ebp指向Trap_frame
    .text:00436C88 test    [esp+68h+arg_4], 20000h ; Trap_frame.EFlags,0x2000代表EFLAGS_V86_MASK,
    .text:00436C88                               ; 标记虚拟86模式
    .text:00436C90 jnz     short V86_kit3_a      ; 如果是虚拟86模式跳转
    .text:00436C92
    .text:00436C92 loc_436C92:                   ; CODE XREF: V86_kit3_a+25j
    .text:00436C92 mov     ecx, large fs:124h    ; ecx = CurrentThread
    .text:00436C92                               ; +0x004 CurrentThread    : Ptr32 _KTHREAD  当前线程 ecx
    .text:00436C99 cld
    .text:00436C9A and     dword ptr [ebp+2Ch], 0 ; Trap_frame.Dr7清零
    .text:00436C9E test    byte ptr [ecx+3], 0DFh
    .text:00436CA2 jnz     Dr_kit3_a
    .text:00436CA8
    .text:00436CA8 loc_436CA8:                   ; CODE XREF: Dr_kit3_a+Dj
    .text:00436CA8                               ; Dr_kit3_a+79j
    .text:00436CA8 mov     ebx, [ebp+60h]
    .text:00436CAB mov     edi, [ebp+68h]
    .text:00436CAE mov     [ebp+0Ch], edx        ; Trap_frame.DbgArgPointer = edx
    .text:00436CB1 mov     dword ptr [ebp+8], 0BADB0D00h ; Trap_frame.DbgArgMark = 0BADB0D00h
    .text:00436CB8 mov     [ebp+0], ebx          ; Trap_frame.DbgEbp = kTrap_frame.Ebp
    .text:00436CBB mov     [ebp+4], edi          ; Trap_frame.DbgEip = kTrap_frame.Eip
    .text:00436CBE cmp     ds:_PoHiberInProgress, 0
    .text:00436CC5 jnz     short loc_436CCE
    .text:00436CC7 lock inc ds:_KiHardwareTrigger
    .text:00436CCE
    .text:00436CCE loc_436CCE:                   ; CODE XREF: _KiTrap03+75j
    .text:00436CCE mov     eax, 0
    .text:00436CD3
    .text:00436CD3 loc_436CD3:                   ; CODE XREF: _KiDebugService+7Aj
    .text:00436CD3 test    byte ptr [ebp+72h], 2
    .text:00436CD7 jnz     short loc_436D08
    .text:00436CD9 test    byte ptr [ebp+6Ch], 1 ; CS 最后一位,判断是否为UserMode
    .text:00436CDD jnz     short loc_436CE7      ; 如果是UserMode,跳转
    .text:00436CDF test    byte ptr [ebp+71h], 2 ; 判断Eflags.IF位是否存在
    .text:00436CE3 jz      short loc_436CEF
    .text:00436CE5 jmp     short loc_436CEE
    .text:00436CE7 ; ---------------------------------------------------------------------------
    .text:00436CE7
    .text:00436CE7 loc_436CE7:                   ; CODE XREF: _KiTrap03+8Dj
    .text:00436CE7 cmp     word ptr [ebp+6Ch], 1Bh
    .text:00436CEC jnz     short loc_436D08
    .text:00436CEE
    .text:00436CEE loc_436CEE:                   ; CODE XREF: _KiTrap03+95j
    .text:00436CEE                               ; _KiTrap03+C9j
    .text:00436CEE sti                           ; IF标志位存在
    .text:00436CEF
    .text:00436CEF loc_436CEF:                   ; CODE XREF: _KiTrap03+93j
    .text:00436CEF                               ; _KiTrap03+D6j
    .text:00436CEF mov     esi, ecx              ; esi=CurrentThread
    .text:00436CF1 mov     edi, edx
    .text:00436CF3 mov     edx, eax
    .text:00436CF5 mov     ebx, [ebp+68h]        ; ebx = kTrap_frame.Eip
    .text:00436CF8 dec     ebx                   ; "eip-1"是因为int 3本身占一个字节
    .text:00436CF9 mov     ecx, 3                ; ???在下层函数中会用到,到时候就知道了
    .text:00436CFE mov     eax, 80000003h        ; 异常类型(STATUS_BREAKPOINT)
    .text:00436D03 call    CommonDispatchException ; 处理异常
    .text:00436D08
    .text:00436D08 loc_436D08:                   ; CODE XREF: _KiTrap03+87j
    .text:00436D08                               ; _KiTrap03+9Cj
    .text:00436D08 mov     ebx, large fs:124h
    .text:00436D0F mov     ebx, [ebx+50h]
    .text:00436D12 cmp     dword ptr [ebx+148h], 0
    .text:00436D19 jz      short loc_436CEE
    .text:00436D1B push    3
    .text:00436D1D call    _Ki386VdmReflectException_A@4 ; Ki386VdmReflectException_A(x)
    .text:00436D22 test    ax, 0FFFFh
    .text:00436D26 jz      short loc_436CEF
    .text:00436D28 jmp     Kei386EoiHelper@0
    .text:00436D28 _KiTrap03 endp

    很明显,在KiTrap03中没有进行太多的处理,只是判断是否是虚拟86模式来构建了一个不同的陷阱帧结构,之后调用了CommonDispatchException 函数来处理异常。

    在虚拟86模式下将会跳转到V86_kit3_a,我们可以看下实现

    .text:00436C28 V86_kit3_a proc near          ; CODE XREF: _KiTrap03+40j
    .text:00436C28 mov     eax, [ebp+84h]
    .text:00436C2E mov     ebx, [ebp+88h]
    .text:00436C34 mov     ecx, [ebp+7Ch]
    .text:00436C37 mov     edx, [ebp+80h]
    .text:00436C3D mov     [ebp+50h], ax         ; Trap_frame.SegFs = Trap_frame.V86Fs
    .text:00436C41 mov     [ebp+30h], bx         ; Trap_frame.SegGs = Trap_frame.V86Gs
    .text:00436C45 mov     [ebp+34h], cx         ; Trap_frame.SegEs = Trap_frame.V86Es
    .text:00436C49 mov     [ebp+38h], dx         ; Trap_frame.SegDs = Trap_frame.V86Ds
    .text:00436C4D jmp     short loc_436C92
    .text:00436C4D V86_kit3_a endp

     可以看到V86_kit3_a中没有进行处理,只是将陷阱帧中保存的寄存器的值对应为虚拟86模式特有的值。

     然后跟入CommonDispatchException 函数

    .text:0043641C CommonDispatchException proc near ; CODE XREF: _KiTrap00-253p
    .text:0043641C                               ; _KiTrap00-247p _KiTrap00-23Bp
    .text:0043641C                               ; _KiTrap03+B3p _KiTrap0E+21Ap
    .text:0043641C                               ; sub_671B78+24p
    .text:0043641C
    .text:0043641C var_50= dword ptr -50h
    .text:0043641C var_4C= dword ptr -4Ch
    .text:0043641C var_48= dword ptr -48h
    .text:0043641C var_44= dword ptr -44h
    .text:0043641C var_40= dword ptr -40h
    .text:0043641C var_3C= byte ptr -3Ch
    .text:0043641C
    .text:0043641C sub     esp, 50h              ; EXCEPTION_RECORD结构空间,ExceptionRecord
    .text:0043641F mov     [esp+50h+var_50], eax ; eax是上层调用设置的错误码,这里是
    .text:0043641F                               ; ExceptionRecord->ExceptionCode = eax
    .text:00436422 xor     eax, eax
    .text:00436424 mov     [esp+50h+var_4C], eax ; ExceptionRecord->ExceptionFlags = 0
    .text:00436428 mov     [esp+50h+var_48], eax ; ExceptionRecord->ExceptionRecord = 0
    .text:0043642C mov     [esp+50h+var_44], ebx ; ebx是上层调用设置的异常发生处地址,
    .text:0043642C                               ; ExceptionRecord->ExceptionAddress = ebx
    .text:00436430 mov     [esp+50h+var_40], ecx ; ExceptionRecord->NumberParameters = ecx
    .text:00436430                               ; 表示ExceptionRecord->ExceptionInformation数组的数量
    .text:00436434 cmp     ecx, 0                ; ExceptionRecord->NumberParameters为零跳转
    .text:00436437 jz      short loc_436445
    .text:00436439 lea     ebx, [esp+50h+var_3C] ; ebx = ExceptionRecord->ExceptionInformation
    .text:0043643D mov     [ebx], edx            ; 开始填充ExceptionInformation的信息
    .text:0043643F mov     [ebx+4], esi
    .text:00436442 mov     [ebx+8], edi
    .text:00436445
    .text:00436445 loc_436445:                   ; CODE XREF: CommonDispatchException+1Bj
    .text:00436445 mov     ecx, esp              ; ecx = ExceptionRecord
    .text:00436447 test    byte ptr [ebp+72h], 2 ; ebp还是指向TrapFrame,TrapFrame->EFlags,
    .text:00436447                               ; 这里是EFlags的高2字节,判断是否为虚拟86模式
    .text:0043644B jz      short loc_436454
    .text:0043644D mov     eax, 0FFFFh           ; ????
    .text:00436452 jmp     short loc_436457
    .text:00436454 ; ---------------------------------------------------------------------------
    .text:00436454
    .text:00436454 loc_436454:                   ; CODE XREF: CommonDispatchException+2Fj
    .text:00436454 mov     eax, [ebp+6Ch]        ; eax = TrapFrame->SegCs
    .text:00436457
    .text:00436457 loc_436457:                   ; CODE XREF: CommonDispatchException+36j
    .text:00436457 and     eax, 1                ; Cs的低两位得到处理器模式
    .text:0043645A push    1                     ; FirstChance
    .text:0043645C push    eax                   ; PreviousMode
    .text:0043645D push    ebp                   ; TrapFrame
    .text:0043645E push    0                     ; ExceptionFrame
    .text:00436460 push    ecx                   ; void *
    .text:00436461 call    _KiDispatchException@20 ; 对异常进行分发处理
    .text:00436466 mov     esp, ebp              ; 修正esp,然后执行退出操作
    .text:00436468 jmp     Kei386EoiHelper@0
    .text:00436468 CommonDispatchException endp

    可以看到CommonDispatchException也没有进行处理,只是构建了一个ExceptionRecord的结构体,就对异常进行了分发处理,进入了熟悉的KiDispatchException函数。

    了解了陷阱帧的构建,再看idt表的基本结构,每个CPU的核心都有自己的idt表,

    typedef struct _IDTR
    {
      USHORT    limit;   //整个表所占内存大小
      ULONG    base;    //IDT表项起始地址
    }IDTR,*PIDTR;

    在我的虚拟机上 limit= 0x7ff (包含0)  0x800  = 2048  Entry每项大小8字节,就2048/8 = 256 成员,idt表有256个例程

    可以使用kd> !idt -a  命令来查看idt表的详细信息,可以发现就是有256个例程

    idt表中的每一项对应一种中断处理例程,结构体如下,我们最关心的是LowOffset和HiOffset这两个成员,他们组成了处理例程地址的高16位和低16位

    typedef struct _IDTENTRY
    {
        unsigned short LowOffset;
        unsigned short selector;
        unsigned char retention:5;
        unsigned char zero1:3;
        unsigned char gate_type:1;
        unsigned char zero2:1;
        unsigned char interrupt_gate_size:1;
        unsigned char zero3:1;
        unsigned char zero4:1;
        unsigned char DPL:2;
        unsigned char P:1;
        unsigned short HiOffset;
    } IDTENTRY,*PIDTENTRY;

    利用MAKELONG 的宏,可以得到处理例程的真正地址

    #define MAKELONG(a, b)      ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))

    idt表的获得可以通过sidt指令或者时_KPCR中的成员获得,在内核态fs段寄存器是指向_KPCR结构。

    kd> dt _kpcr
    ......
     +0x038 IDT              ; Ptr32 _KIDTENTRY
    ......

    下面的代码打印了每次触发INT 3断点的地址,可以用OD下普通的断点进行测试,可以打印出断点的地址。

    ULONG_PTR g_OrigKiTrap03;
    KIRQL  Irql;
    
    
    _declspec(naked) void NewKiTrap03()
    {
    
        __asm
        {
            //测试
            //jmp g_OrigKiTrap03
    
            //构建Trap03的异常帧
            //保存现场环境,和原始Trap03一样
            push    0   ;ErrorCode
            push    ebp
            push    ebx
            push    esi
            push    edi
            push    fs
            mov     ebx,30h
            mov     fs,bx
            mov     ebx,dword ptr fs:[0]
            push    ebx
            sub     esp,4
            push    eax
            push    ecx
            push    edx
            push    ds
            push    es
            push    gs
    
            sub     esp,30h    //esp此时就指向陷阱帧
    
            push    esp         //FilterExceptionInfo自己清理了
    
            call   FilterExceptionInfo   //过滤函数
    
            add     esp , 0x30
            pop        gs
            pop        es
            pop        ds
            pop        edx
            pop        ecx
            pop        eax
            add        esp , 4
            pop        ebx
            pop        fs
            pop        edi
            pop        esi
            pop        ebx
            pop        ebp
            add     esp , 0x4
            jmp     g_OrigKiTrap03
        }
    }
    
    
    
    VOID __stdcall FilterExceptionInfo(PX86_KTRAP_FRAME pTrapFrame)
    {
    
        //eip的值减一过int3,汇编代码分析中dec, 
        DbgPrint("Eip:%x
    ",(pTrapFrame->Eip)-1);
    }
    
    
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryString)
    {
        NTSTATUS    Status = STATUS_SUCCESS;
        IDTR Idtr;
        PIDTENTRY pIdtArray = NULL;
        ULONG_PTR Index = 0;
    
        DriverObject->DriverUnload = UnloadDriver;
            __asm sidt Idtr
        //虚拟机是单核的,只用一个就可以了
        if(KeGetIdt(&pIdtArray))
        {
            DbgPrint("%x---%x
    ",Idtr.base,Idtr.limit);
            for (Index =0;Index<(Idtr.limit+1)/sizeof(IDTENTRY);Index++)         
            {
                DbgPrint("TrapHandle[%d]:%x
    ",Index,MAKELONG(pIdtArray[Index].LowOffset,pIdtArray[Index].HiOffset));
            }
    
            g_OrigKiTrap03 = MAKELONG(pIdtArray[3].LowOffset,pIdtArray[3].HiOffset);
    
            WPOFF();
            pIdtArray[3].LowOffset = (ULONG_PTR)NewKiTrap03 & 0xFFFF;  //低16位
            pIdtArray[3].HiOffset =  (ULONG_PTR)NewKiTrap03 >> 16;     //高16位
            WPON();
    
        }
        
        //limit 0x7ff (包含0)  0x800  = 2048  Entry每项大小8字节,就2048/8 = 256 成员
        //!idt -a   0ff  = 256 
        //MAKELONG
        //#define MAKELONG(a, b)      ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
        return Status;
    }
    
    BOOLEAN KeGetIdt(PIDTENTRY *pIdtArray)
    {
        ULONG Index,Affinity,CurrentAffinity;
        pfnKESETAFFINITYTHREAD fnpKeSetAffinityThread;
    
        UNICODE_STRING usFuncName;
        PIDTENTRY pIdtEntry;
    
        RtlInitUnicodeString(&usFuncName,L"KeSetAffinityThread");
        fnpKeSetAffinityThread = (pfnKESETAFFINITYTHREAD)MmGetSystemRoutineAddress(&usFuncName);
    
        if (fnpKeSetAffinityThread==0)
        {
            return FALSE;
        }
    
        Affinity = KeQueryActiveProcessors();                    
        //KeQueryActiveProcessors获取处理器相关的位图
        //(这里的位图可以理解为个数,比如返回1代表一个处理器,返回3表示两个处理器,返回7表示三个处理器,依此类推。
        //也就是说从有多少个处理器,那么Affinity的值就会从低位到高位依此填充多少位)
    
        CurrentAffinity = 1;
        Index = 0;
        while(Affinity)
        {
            //下面只是个简单的算法,使当前线程运行到不同的处理器上
            Affinity &= ~CurrentAffinity;
            fnpKeSetAffinityThread(PsGetCurrentThread(),(KAFFINITY)CurrentAffinity);
            CurrentAffinity <<= 1;
    
            __asm{
                push        eax
                mov         eax,fs:[0x38]
                mov         pIdtEntry,eax
                pop         eax
            }
            //得到我们要的东西
            pIdtArray[Index] = pIdtEntry;
            Index++;
        }
    
        return TRUE;
    }
    
    
    VOID WPOFF()
    {
        ULONG_PTR cr0 = 0;
        Irql = KeRaiseIrqlToDpcLevel();
        cr0 =__readcr0();
        cr0 &= 0xfffffffffffeffff;
        __writecr0(cr0);
    
    }
    
    VOID WPON()
    {
    
        ULONG_PTR cr0=__readcr0();
        cr0 |= 0x10000;
        __writecr0(cr0);
        KeLowerIrql(Irql);
    }
    
    
    
    VOID UnloadDriver(PDRIVER_OBJECT DriverObject)
    {
        //恢复
        PIDTENTRY pIdtEntry;
        if (g_OrigKiTrap03 && KeGetIdt(&pIdtEntry))
        {
            WPOFF();
            pIdtEntry[3].LowOffset = g_OrigKiTrap03 & 0xFFFF;
            pIdtEntry[3].HiOffset = g_OrigKiTrap03 >> 16;
            WPON();
        }
    }
  • 相关阅读:
    amazon海淘+CUL中美速递转运详细教程(免税)
    单页 Web 应用概述
    JavaScript JQuery SPA Frameworks
    win环境的apache下Virtualhost 设置
    Windows下创建.htaccess文件的N种方法
    ARP欺骗攻击与防护介绍
    静态构造函数(转载)
    SQL从表内容条件满足查询
    SQL数据库开发—精典
    如何学好C++语言(转载)
  • 原文地址:https://www.cnblogs.com/lanrenxinxin/p/4360983.html
Copyright © 2011-2022 走看看