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\r\n",(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\r\n",Idtr.base,Idtr.limit);
        for (Index =0;Index<(Idtr.limit+1)/sizeof(IDTENTRY);Index++)         
        {
            DbgPrint("TrapHandle[%d]:%x\r\n",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();
    }
}

posted on 2015-03-23 20:57  _懒人  阅读(1863)  评论(0编辑  收藏  举报

导航