龙源期刊网 http://www.qikan.com.cn
Windows内核态钩子在软件安全技术中的应用
作者:张立人 魏正曦 万 新
来源:《数字技术与应用》2010年第10期
摘 要:阐述了Windows内核的特点、通过驱动程序进入内核态的方法,剖析了Windows内核中几种数据结构,在此基础上讨论了在内核不同位置安装钩子从而实现安全保护的技术,指出了内核态钩子在软件安全技术中的必要性和发展趋势。 关键词:Windows内核钩子软件安全
中图分类号:O245 文献标识码:A 文章编号:1007-9416(2010)10-0134-02 1 引言
随着计算机应用的普及,各种安全威胁也越来越多,对安全软件产品的要求也空前高涨。当前的病毒、木马和各种rootkit等恶意程序已经深入到系统核心中,运行在内核态,具有系统的最高权限,可以任意绕过或拒绝传统的处于应用层的安全软件的防范和监控,软件安全技术的升级已经是大势所趋,并且只有一种途径,那就是像恶意程序一样向系统核心进军,力求运行在尽可能底层的级别上,从而抢在恶意程序控制系统核心之前进行拦截。在这个方案中,必然要利用早在应用层时代就大量使用的钩子技术,只不过现在这些钩子必须安装在系统内核的某些位置了。
2 如何进入内核
Windows将代码的运行级别分为两种:用户态和内核态。相应的权限也有两种,用户级权限(ring3)和特权级权限(ring0)。ring3下,可执行除特权指令外的所有指令,例如普通的应用程序包含的指令;ring0下,可执行包括特权指令在内的所有指令。对于安全软件来说,追求的是获得ring0权限。而Windows规定,只有运行在内核态的代码才有ring0权限,所以安全软件要做的第一步必须是进入内核。通常,我们可以通过Windows设备驱动程序的形式进入内核。
设备驱动程序被视为操作系统的扩展,运行在内核态,具有ring0权限。我们只要将安全软件按照Windows驱动程序模型(WDM)进行编写就可最终编译得到驱动程序文件,并通过安装驱动
龙源期刊网 http://www.qikan.com.cn
程序或动态加载的方式使其在ring0下运行起来。与应用程序不同,驱动程序的入口函数是DriverEntry():
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
值得一提的是,不需要实现一个设备驱动程序的全部因素,因为安全软件的驱动程序并不去真正驱动什么设备,它只是进入内核的一种手段而已。像电源管理、即插即用、IO操作等内容都可以省掉了。
3 内核数据结构 3.1 系统服务描述符表
系统服务描述符表对应着用户态的Win32 API,它表示当Win32 API被调用时内核中有哪个函数被相应调用。有两个系统服务描述符表:SSDT和SSDT Shadow。前者对应kernel32.dll中的Win32 API,后者对应user32.dll和gdi32.dll中的Win32 API。系统服务描述符表是个一维数组,其中按照固定的顺序存放着内核函数的入口地址。例如,Windows XP中内核函数NtUserSendInput()的地址放在SSDT Shadow的第502项。内核中用一个结构体来封装系统服务描述符表:
typedef struct _SERVICE _DESCRIPTOR _TABLE {
SYSTEM_SERVICE_TABLE ntoskrnl; SYSTEM_SERVICE_TABLE win32k; SYSTEM_SERVICE_TABLE Table3; SYSTEM_SERVICE_TABLE Table4; }SERVICE_DESCRIPTOR_TABLE;
其中SYSTEM_SERVICE_TABLE的定义如下: typedef struct _SYSTEM_SERVICE_TABLE
龙源期刊网 http://www.qikan.com.cn
{
PNTPROC ServiceTable; PDWORD CounterTable; DWORD ServiceLimit; PBYTE ArgumentTable; }SYSTEM_SERVICE_TABLE;
其中的ServiceTable就指向前面所说的一维数组。
两个系统服务描述符表本身的地址可由以下方法求出:SSDT的地址由内核变量KeServiceDescriptorTable指出,SSDT Shadow的地址等于前者减去64(仅限于Windows XP)。
有了这些信息,我们就可以得到任意一个内核函数的入口地址了。例如,SSDT中序号为N的内核函数的入口地址为:
(ULONG)KeServiceDescriptorTable->ntoskrnl. ServiceTable + N * 4。 SSDT Shadow中序号为N的内核函数的入口地址为:
(ULONG)(KeServiceDescriptorTable-64)->win32k.ServiceTable + N * 4。 3.2 中断描述符表(IDT)
IDT也是一个一维数组,可用sidt指令得到这个一维数组的起始地址和长度: sidt idtr
其中idtr是一个IDTR结构体: typedef struct tagIDTR {
unsigned short IDTLimit; unsigned int IDTBase; }IDTR;