1、Image Patches(映像补丁)
修改内核模块比如ntoskrnl.exe,ndis.sys,ntfs.sys等的代码段
1.1、函数前导Hook
Intel架构下,控制转移指令有三种,分别是2字节的寄存器操作,5字节的相对偏移操作和6字节的内存操作。当然,我们有两种方式来实现这种inline hook:
1)用Microsoft的detour库
2)用一个长度反汇编引擎,字节构建
我自己实现过一个动态HOOK引擎,在我的即将推出的书<<网游外挂艺术大揭秘>>的HOOK一节中有详细介绍。
现在的xp sp2和vista为动态HOOK提供了更方便的方式,函数的前导是两个字节的no-op操作指令,比如mov edi,edi
这样我们可以用一个两个字节的短相对jmp指令(0xEBXX)。
1.2、禁用SeAccessCheck
Greg Hoglund首次提出禁用nt!SecAccessCheck,这样就能绕过对象的访问检测。虽然,不能执行特权指令,但是,能够使非特权用户访问和修改系统进程。
2、Descriptor 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)转换为线性地址的时候,会用包含了BaseAddress、limit、privilege information etc的描述符。
段寄存器CS,DS,ES存放了段选择子,段选择子通过offset定位到GDT/LDT中对应得段描述符。
在GDI/LDT中添加描述符,可以使ring3代码执行和访问ring0空间。
2.3 SSDT
SSDT由全局变量nt!KeServiceDescriptoTable导出,windows支持动态注册新的系统调用nt!KeAddSystemServiceTable
SSDT(Native and GDT),SSDT HOOK比较简单,就不多说了。我也曾用HOOK虚表的方式,替换过整个ssdt,没问题,这样的话,可以全部监控SYSTEM CALL,这个技术我觉得在网络安全行为分析模型中应该用的到。
2.4 Model -specific Registers
跟处理器模型相关的特定寄存器,未来的处理器分支不一定支持。
读写用rdmsr和wrmsr
2.4.1 IA32_SYSENTER_EIP
Pentium II引入了支持用户态向内核态转换的强化机制,通过sysenter和sysexit两条指令实现。ring3要进ring0,执行sysenter;ring0要退回到ring3,执行sysexit。
IA32_SYSENTER_CS(0x174)处理器设置内核CS
IA32_SYSENTER_EIP(0x176)切换之后,设置内核态要执行代码的入口
IA32_SYSENTER_ESP(0x175)指向内核态的栈
替换IA32_SYSENTER_EIP可以监控所有通过sysenter进入ring0的调用。
缺点:不是所有处理器支持MSR
kd> rdmsr 176
msr[176] = 00000000‘804de6f0
kd> u 00000000‘804de6f0
nt!KiFastCallEntry: //KiFastCallEntry这个符号内核未导出
804de6f0 b923000000 mov ecx,23h
2.4 Page Table Entries
保护模式下,线性地址转物理地址。PDE/PTE中的user/supervisor位为0,ring0才能访问;否则,ring0/ring3都能访问。修改这个标志位以访问内核代码
2.5 函数指针
IRQL提升时候,HOOK函数不能放在分页内存中。
SharedUserData物理页同时映射到user mode(只读0x7ffe0000)和kernel mode(读写0xffdf0000)。这里面可以保存少量的代码。
native dll,ntdll.dll,映射到所有进程,包括system进程,通过nt!PspMapSystemDll
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_TYPE中OBJECT 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
还有好多,暂不译了