内核级Rootkit技术基础知识 下载本文

内容发布更新时间 : 2024/6/28 14:36:42星期一 下面是文章的全部内容请认真阅读。

本帖最后由 占地挤着 于 2011-1-29 20:23 编辑

Rootkit是一种奇特的程序,它具有隐身功能:无论静止时(作为文件存在),还是活动时,(作为进程存在),都不会被察觉。换句话说,这种程序可能一直存在于我们的计算机中,但我们却浑然不知,这一功能正是许多人梦寐以求的——不论是计算机黑客,还是计算机取证人员。黑客可以在入侵后置入Rootkit,秘密地窥探敏感信息,或等待时机,伺机而动;取证人员也可以利用Rootkit实时监控嫌疑人员的不法行为,它不仅能搜集证据,还有利于及时采取行动。而本文的目的,就是同读者一起踏上Windows内核级Rootkit之旅!

一、背景知识

我们通常所说的智能机器,大至超级计算机,中到个人PC,小至智能手机,通常都有两部分组成:硬件和软件。并且,设备的智能是通过软件来实现的。所有软件中,有一种是必不可少的,那就是操作系统。操作系统可以简单理解为一组高度复用的核心程序,一方面,它要管理低层的硬件设备,另一方面,为上层其它程序提供一个良好的运行环境。真是同人不同命,同为软件,操作系统却享有至高无上的特权:它不仅管理硬件,而且其他所有软件也都受制于它。

因为在应用程序和硬件之间隔着操作系统,所以应用程序不能直接访问硬件,而是通过调用操作系统提供的接口来使用硬件。也就是说,对应用程序而言,硬件是不可见的。当然,凡事是没有绝对的,应用程序绕过操作系统来直接访问硬件也不是不可能的,但这样做会付出高昂的代价。设想一个软件开发商在开发一功能丰富的软件,功能本身就够他头痛得了,现在他还得操心某个数据在某个磁道的某个簇上,某个字符在某品牌显示器上的颜色的二进制代码等等繁琐的事情,不用说财力和物力,单说开发周期就是无法容忍的。所以,现在的应用程序都是使用操作系统提供的简单明了的服务来访问系统的,因为毕竟没有谁愿意自讨苦吃。

二、内核的主要功能

从上文中我们已经了解,内核在系统中处于核心枢纽的地位,下面我们具体介绍内核中与Rootkit紧密相关的几个主要功能,更重要的是这些功能对Rootkit的意义所在:

进程管理。进程可以简单理解为运行中的程序,它需要占用内存、CPU时间等系统资源。现在的操作系统大多支持多用户多任务,也就是说系统要并行运行多个程序。为此,内核不仅要有专门代码来负责为进程或线程分配CPU时间,另一方面还要开辟一段内存区域存放用来记录这些进程详细情况的数据结构。内核是怎么知道系统中有多少进程、各进程的状态等信息的?就是通过这些数据结构,换句话说它们就是内核感知进程存在的依据。因此,只要修改这些数据结构,就能达到隐藏进程的目的。

文件访问。文件系统是操作系统提供的最为重要的功能之一。内核中的驱动程序把设备的柱面、扇区等原始结构抽象成为更加易用的文件系统,并提供一个一致的接口供上层程序调用。也就是说,这部分代码完全控制着对硬盘的访问,通过修改内核的这部分代码,攻击者能够隐藏文件和目录。

安全控制。对大部分操作系统来说,因为系统中同时存在多个进程,为了避免各进程之间发生冲突,内核必须对各进程实施有效的隔离措施。比如,在MS-Windows系统中,每个进程都被强制规定了具体的权限和单独的内存范围。因此,对攻击者而言,只要对内核中负责安全事务的代码稍事修改,整个安全机制就会全线崩溃。

内存管理。现在的硬件平台(比如英特尔的奔腾系列处理器)的内存管理机制已经复杂到可以将一个内存地址转换成多个物理地址的地步。举例来说,进程A按照地址0x0030030读取内存,它得到值的是“飞机”;然而,进程B也是按照同样的地址0x0030030来读取内存,但它取得的值却是“大炮”。像上面这样,同一个地址指向截然不同的两个物理内存位置,并且每个位置存放不同的数据这种现象并不足以为怪——只不过是两个进程对虚拟地址到物理地址进行了不同的映射而已。如果这一点利用好了,我们可以让Rootkit躲避调试程序和取证软件的追踪。

上面介绍了内核的主要功能,以及它们对Rootkit的重大意义。说到这里,我们就要切入正题了,即:只要我们颠覆(即修改)了操作系统的核心服务(即内核),那么整个系统包括各种应用就完全处于我们的掌控之下了。要想颠覆内核,前提条件是能把我们的代码导入内核。具体请见下文。

三、将代码导入内核的方法

所有应用程序都是运行在用户模式下的,而操作系统是运行在内核模式下的,所以应用程序根本没有机会来修改内核程序。要想颠覆内核,前提是设法让我们的代码也运行在内核模式下,即将我们的代码导入内核。

设想一下当我们的代码通过驱动程序进入内核后的情形:我们的代码一旦在内核运行,就能访问内核的所有特权内存空间和系统进程——有了这样的内核级访问特权,我们就能任意修改系统中的任何代码和数据了!

将代码导入内核的方法很多,但我们要介绍的是利用设备驱动程序将代码导入内核的方式。内核驱动程序有多种叫法,有时候又称为内核驱动程序,或者可加载模块。我们之所以利用设备驱动程序将代码导入到内核,主要考虑到两点:

1.简单性。能够让我们的代码运行在内核模式之的方法有多种,例如把我们的代码做成设备驱动程序、设法修改正在内存运行的内核、覆盖硬盘中的内核映像等等。其中,利用设备驱动程序是最为简单的一种。

2.适用性。现在,绝大多数的操作系统都允许第三方的硬件厂商加载为其硬件产品制作的内核扩展,从而使得操作系统能够支持它们的硬件产品。这的确是一个双赢之举,只有这样,操作系统开发商才能不断巩固和扩大其用户群。因此,各个操作系统开发商不仅提供该系统下的开发设备驱动程序的详尽帮助文档,而且还提供了相应的开发工具包。

虽然我们已经找到将我们的代码导入到内核模式的路径,但还有一个问题需要回答:除了与硬件打交道的代码外,其它代码是否也能写进设备驱动程序?答案是肯定的。尽管这里的“设

备”一词容易误导我们,实际上任何代码都可以写成设备驱动程序。

迄今为止,我们已经回答了利用设备驱动程序来打造内核级别的Rootkit可行性问题,也就是说,内核级Rootkit大门已经打开了一扇,为了打开另一扇,我们要解决的是“how to”的问题。并且,我们第一个必须解决的“how to”问题就是如何编写Windows设备驱动程序。

四、Windows设备驱动程序

下面我们介绍如何在Windows XP平台上开发一个简单的设备驱动程序。在此过程中,我们要用到设备驱动程序开发包DDK。需要注意的是,微软为不同的Windows版本提供了不同的DDK。就目前来说,我们可以使用Windows 2003 DDK,因为该版本能够支持Windows 2000、Windows XP和Windows 2003等三个平台下的设备驱动程序的开发。

1. 驱动程序源文件

首先在C盘根目录下新建一个目录,本例为C:myrootkit,此后向相应文件都存放在该目录之下。然后可以在VC++下建立一个C源文件,让其内容如下所示:

1. //一个简单的驱动程序实例 2.

3. #include \4.

5. NTSTATUS DriverEntry ( IN   PDRIVER_OBJECT theDriverObject, 6.

7. IN   PUNICODE_STRING theRegistryPath ) 8. 9. { 10.

11. DbgPrint(\12.

13. return STATUS_SUCCESS; 14. 15. }

复制代码

之后,将其保存在我们的C:myrootkit目录下,并且该文件命名为mydriver.c。下面对上面的代码作简要的解释:

DriverEntry是该驱动程序的入口点,即当该驱动程序启动时,操作系统会首先调用该函数。它相当于一般C/C++程序中的main或Winmain函数。另外,我们看到DriverEntry的两个参数中都含有一个关键词IN,说明这两个参数只能用于输入。第一个参数指向应被初始化的当前驱动程序的驱动对象;第二个参数是设备服务键的键名。

该驱动程序实在太简单了,它只是在内核运行时,利用调试语句给出一则消息:“I am a driver!”。不过,和一般程序不同,该消息不会显示在标准输出设备上,也就是说,我们在