Rootkits——Windows内核的安全防护

| |
02:23,29,Aug,2007 | (10330/1/0) | csdn
4.2  内核钩子
前一节中说明了用户空间钩子是有用的,但它们相对易于检测和预防(第10章“rootkit检测”将详细讨论用户空间钩子的检测问题)。更好的解决方法是安装内核内存钩子。通过使用内核钩子,rootkit将与所有检测软件保持同步。

内核内存是高端虚存地址区域。在Intel x86体系结构中,内核内存通常驻留在内存地址0x80000000及以上范围。若使用了允许进程拥有3GB虚存的/3GB引导配置开关,则内核内存起始于0xC0000000地址处。

进程不能访问内核内存是一条通用规则。该规则的例外情况是进程具有调试权限并且使用了特定的调试API,或者已安装了调用门。本书不涉及这些例外情况。关于调用门的更多信息,可参考Intel体系结构手册。

在本书中,rootkit通过实现一个设备驱动程序来访问内核内存。

出于众多因素考虑,内核提供了安装钩子的理想位置。两个最重要的原因包括:内核钩子是全局的(相对而言);并且它们更难以检测,因为若rootkit和防护/检测软件都处于环0级时,rootkit有一个平等竞赛(even playing)域,可以在其上躲避或禁止保护/检测软件(关于环的更多信息,参见第3章“硬件相关问题”)。

本节介绍3个最常见的钩子位置。但应该理解的是,根据不同的rootkit预期目的,也可以发现其他钩子位置。

4.2.1  钩住系统服务描述符表
Windows可执行程序在内核模式中运行,并且对操作系统的所有子系统(Win32、POSIX和OS/2)都提供本地支持。这些本地系统服务的地址在内核结构中称为系统服务调度表(System Service Dispatch Table,SSDT)中列出。该表可以基于系统调用编号进行索引,以便定位函数的内存地址。还有一个系统服务参数表(System Service Parameter Table,SSPT)指定了每个系统服务的函数参数的字节数。

KeServiceDescriptorTable是由内核导出的表。该表拥有一个指针,指向SSDT中包含由Ntoskrnl.exe实现的核心系统服务的相应部分,它是内核的主要组成部分。KeServiceDescriptorTable表还包含一个指向SSPT的指针。

图4-4描述了KeServiceDescriptorTable表。该图中的数据来自于未安装服务补丁包的Windows 2000 Advanced Server系统。其中SSDT包含了所有内核导出函数的地址。每个地址长度为4个字节。

为了调用特定函数,系统服务调度程序KiSystemService将该函数的ID编号乘以4以获取它在SSDT中的偏移量。注意KeServiceDescriptorTable包含了服务数目,该值用于确定在SSDT或SSPT中的最大偏移量。图4-4中也描述了SSPT。该表中的每个元素为单字节长度,以十六进制指定了它在SSDT中的相应函数采取多少字节作为参数。在这个示例中,地址0x804AB3BF处的函数采用0x18个字节的参数。
Highslide JS
KeServiceDescriptorTableShadow表包含了在内核驱动程序Win32k.sys中实现的USER和GDI服务的地址。Dabak等人在Undocumented Windows NT一书中描述了这些表。

当调用INT 2E或SYSENTER指令时会激活系统服务调度程序。这导致进程通过调用该程序转换到内核模式。应用程序可以直接或通过使用子系统调用系统服务调度程序KiSystemService。若采用子系统(例如Win32)方式,它会调用到Ntdll.dll中。后者向EAX中加载所请求的系统服务标识符编号或系统函数索引,然后向EDX中加载用户模式中函数参数的地址。系统服务调度程序对参数数目进行验证,将它们从用户堆栈中复制到内核堆栈,然后调用在SSDT中存储于EAX中的服务标识符编号的索引地址处的函数(该进程在本章后面4.2.1节中详细讨论)。

一旦将rootkit作为设备驱动程序加载之后,它可以将SSDT改为指向它所提供的函数,而不是指向Ntoskrnl.exe或Win32k.sys。当非核心的应用程序调用到内核中时,该请求由系统服务调度程序处理,并且调用了rootkit的函数。这时,rootkit可以将它想要的任何假信息传回到应用程序,从而有效地隐藏自身以及所用的资源。

4.2.2  修改SSDT内存保护机制
在第2章中讨论过,有些Windows系统版本对某些内存区域启用了写保护功能。这在后期版本,如Windows XP和Windows 2003,中更为普遍。这些后期版本的操作系统要求SSDT是只读的,因为任何合法程序都不可能需要修改这个表。

如果希望通过函数调用钩子来过滤特定系统调用所返回的响应,则写保护机制对rootkit提出了一个重大的难题。若向只读内存区域,例如SSDT,中执行写入操作,则会发生蓝屏死机(Blue Screen of Death,BSoD)。第2章介绍了如何修改CR0寄存器来绕过内存保护机制,从而避免这种BSoD。本节解释另一种微软公司已深入说明的进程来修改内存保护机制的方法。

您可以在内存描述符表(Memory Descriptor List,MDL)中描述一块内存区域。MDL包含了该内存区域的起始地址、拥有者进程、字节数量以及标志:


为了修改内存标志,以下代码首先声明一个结构,该结构用于强制转换由Windows内核导出的KeServiceDescriptorTable变量的类型。调用MmCreateMdl时,需要KeServiceDescriptorTable基地址以及它所包含的项数。它们定义了MDL所描述的内存区域的起始地址和大小。然后rootkit从不分页的内存池中构建MDL。

rootkit将MDL的标志与前面提及的MDL_MAPPED_TO_SYSTEM_VA进行或操作,以便允许写入一块内存区域。然后调用MmMapLockedPages来锁定内存中的MDL页。

现在就可以开始钩住SSDT。在以下代码中,MappedSystemCallTable 代表了与原始SSDT相同的地址,但现在可以向其中执行写入操作。
内文分页: [1] [2] [3] [4] [5] [6] [7] [8]

最后编辑: chen 编辑于2007/08/29 02:41
2007/08/29 08:31 #{louceng}青蛙
sm13好东西~收藏了
分页: 1/1 第一页 1 最后页
发表评论
  • 昵称 [注册]
  • 密码 游客无需密码
  • 网址
  • 电邮
打开HTML 打开UBB 打开表情 设置密码加密 记住我