1.3 控制数据流
为了完成其隐匿任务,内核Rootkit必须修改控制流或数据流(或修改两者)内核的系统调用,与此同时,操作系统原始的控制流或数据流就会对照揭示恶意软件的任何静止组件(例如文件)或其任何运行任务或工件(例如内核数据结构)的存在。为此,Rootkit通常将它们的代码注入系统调用实现的执行路径上,这些代码挂钩的位置是Rootkit最具启发性的方面之一。
1.3.1 自带链接器
挂钩本质上就是链接。现代Rootkit自带链接器,将自己的代码与系统链接起来,这种设计模式称为“自带链接器”。为了通过隐匿的方式嵌入这些“链接器”,TDL3遵循了一些常见的恶意软件设计原则。
首先,目标必须保持健壮,尽管注入了额外的代码,但因为攻击者从崩溃的目标软件中什么都没有得到,所以反而会失去很多。从软件工程的角度来看,挂接是软件组合的一种形式,需要使用谨慎的方法。攻击者必须确保系统只在可预测的状态下到达新代码,这样代码才能得到正确处理,以避免会引起用户注意的任何崩溃或异常行为。钩子的位置似乎只受Rootkit作者想象的限制,但实际上,作者必须坚持稳定的软件边界和他们真正理解的接口。因此,无论是否有公开记录,挂钩往往将系统原生动态链接功能的相同结构作为目标就不足为奇了。链接抽象层或软件模块的回调、方法和其他函数指针的表是挂钩最安全的地方,挂钩函数前置也很有效。
其次,钩子的位置不能太明显。尽管早期的Rootkit挂接了内核的顶级系统调用表,但这种技术很快就过时了,因为它太显眼了。事实上,在2005年用于索尼Rootkit时[1],这种布局已经被认为是落后的了。随着Rootkit变得越来越复杂,它们的钩子迁移到栈的底层,从主系统调用分派表迁移到为不同实现提供统一API层的操作系统子系统,比如虚拟文件系统(VFS),然后向下迁移到特定驱动程序的方法和回调。TDL3是这种迁移的一个特别好的例子。
1.3.2 TDL3的内核模式钩子是如何工作的
为了保持神秘,TDL3使用了一个之前从未在真实环境中见过的相当复杂的挂接技术,它拦截了读写I/O请求发送到硬盘的存储端口/微型端口驱动程序(硬件存储媒体驱动发现的最底部存储驱动程序栈)。端口驱动程序是为微型端口驱动程序提供编程接口的系统模块,由相应存储设备的供应商提供。图1-2显示了微软Windows中存储设备驱动程序栈的架构。
图1-2 微软Windows中存储设备驱动程序栈的架构
要对位于存储设备上的某个对象的I/O请求包(IRP)结构进行寻址,处理过程是从文件系统驱动程序层开始的。相应的文件系统驱动程序确定对象存储的特定设备(如磁盘分区和磁盘区段,最初为文件系统保留的连续存储区域),并向类驱动程序的设备对象发出另一个IRP。后者依次将I/O请求转换为相应的微型端口设备对象。
根据Windows Driver Kit(WDK)文档,存储端口驱动程序在硬件无关的类驱动程序和特定于HBA(基于主机的架构)的微型端口驱动程序之间提供了接口。一旦接口可用,TDL3就在存储设备驱动程序栈中尽可能低的与硬件无关的级别上设置内核模式挂钩,从而绕过了在文件系统或存储类驱动程序级别上操作的任何监视工具或保护系统。只有那些知道特定设备组或特定机器的已知良好配置正常组成方式的工具才能检测到这样的钩子。
为了实现这种挂接技术,TDL3需要首先获得一个指向对应设备对象的微型端口驱动对象的指针。特别地,钩子代码试图打开\??\PhysicalDriveXX的句柄(其中XX代表硬盘驱动的编码),但该字符串实际上是指向设备对象\Device\HardDisk0\DR0的符号链接,该对象是由存储类驱动程序创建的。从\Device\HardDisk0\DR0向下移动设备栈,我们将在最底部找到微型端口存储设备对象。一旦找到微型端口存储设备对象,通过跟踪记录的DEVICE_OBJECT
结构中的DriverObject
字段,就可以直接获得指向其驱动对象的指针。此时,恶意软件已经拥有了挂接存储驱动程序栈所需的所有信息。
接下来,TDL3创建一个新的恶意驱动对象,并用指向新创建字段的指针覆盖微型端口驱动对象中的DriverObject
字段,如图1-3所示。这允许恶意软件拦截对底层硬盘驱动器的读/写请求,因为所有处理程序的地址都在相关驱动程序对象结构中指定:DRIVER_OBJECT
结构中的MajorFunction
数组。
图1-3 挂载存储微型端口驱动程序对象
如图1-3所示,恶意主处理器拦截了下面的输入/输出控制(IOCTL)代码中的IRP_MJ_INTERNAL_CONTROL
和IRP_MJ_DEVICE_CONTROL
,来监控和修改硬盘的读/写请求,存储受感染的驱动程序和由恶意软件实现的隐藏文件系统的映像:
IOCTL_ATA_PASS_THROUGH_DIRECT
IOCTL_ATA_PASS_THROUGH
TDL3防止包含受保护数据的硬盘驱动器扇区被Windows工具读取或被Windows文件系统意外覆盖,从而保护了Rootkit的隐匿性和完整性。当遇到读操作时,TDL3在I/O操作完成时将返回缓冲区的零输出,在写数据请求时跳过整个读操作。TDL3的挂载技术允许它绕过一些内核补丁检测技术,也就是说,TDL3的修改不涉及任何经常受到保护和监视的区域,包括系统模块、系统服务描述符表(SSDT)、全局描述符表(GDT)或中断描述符表(IDT)。它的继任者TDL4采用了同样的方法来绕过64位Windows操作系统上可用的内核模式PatchGuard保护补丁,因为它从TDL3继承了大量的内核模式功能,包括这些到存储微型端口驱动程序的钩子。
[1]https://blogs.technet.microsoft.com/markrussinovich/2005/10/31/sony-rootkits-and-digital-rights-management-gone-too-far/