本文将向读者介绍一种通过处理器的调试机制来实现Rootkit的隐形的技术。这种技术的特点是,无需利用系统的缺陷,直接处理器的正常功能就能达到隐形的目的。本文将以Linux系统为例来介绍如何实现基于调试寄存器的隐形方法。
一、概述
几年来,用于在攻陷的机器上实现隐形技术和方法越来越多,让人目不暇接。其中,有一些直接篡改系统调用表,有一些修改中断处理程序,凡此种种,在此不再一一列举。不过,这些方法的共同之处在于,都以可见的形式修改了底层的操作系统,这使得它们很容易被发现。
本文中,笔者利用调试机制这一x86平台的共同特性,用内核级Rootkit实现了终极的隐形。虽然该方法通用于所有IA-32兼容平台,但我们这里以Linux操作系统为例,讲解如何在不碰钩子技术的各种“传统”目标(如系统调用表、中断处理程序等)的情况下拦截正常执行流程的技术。 实际上,这种隐形技术是如此高超,以至于还没有人发现我们的存在。
在本文中,当我们说“调试器”时,实际上指的是只能从ring 0访问的IA-32的调试机制,对于用户空间的调试程序来说,是无法利用这个机制的,只有某些内核调试工具才能使用这个机制。
二、处理器的调试机制
为了让开发人员过得更轻松些,Intel公司引入了一个机制来处理调试过程。按照Intel公司的用户手册的说法,“IA-32架构提供了大量的调试设施,供调试代码、监视代码执行和处理器性能之用。这些设施对调试应用程序软件、系统软件和多任务操作系统都是非常重要的 ”。开发人员可以通过一组专用寄存器(称为调试寄存器,从DR0至DR7)来使用该机制,这样他们就可以根据内存地址来设置硬件断点了。一旦执行流到达带有断点标记的地址,控制权就会交给调试中断处理程序(INT 1),该处理程序继而调用do_debug()函数(定义在/i386/kernel/traps.c文件中)来处理引起异常的实际情况。
可以通过调试寄存器(DB0~DB7)和两个与模式特定的寄存器(MSR)来访问这种调试支持。对本文而言,我们只需关心调试寄存器就行了。这些寄存器存放的是内存地址和I/O位置,我们将其称之为断点。断点既可以是用户在程序中选定的位置(指令断点),也可以是内存中的数据存储区(数据断点),还可以是指定的I/O端口(I/O断点),程序员或者系统设计人员通常希望程序在这些指定的地方停下来,以便调用调试软件来检查处理器的状态。
当内存或I/O设备访问某个断点地址时,就会引起一个调试异常(#DB)。我们可以进一步为断点规定引起中断的具体的内存访问方式或者I/O访问方式,例如一个内存读和/或写操作或者I/O读和/或写操作(即以规定之外的方式访问该断点时不会引起中断)。调试寄存器同时支持指令断点和数据断点。MSR是从P6系列处理器开始引入IA-32架构的,它用于监视分支、中断和异常,并记录下最后分支、中断或者异常所使用的源地址和目的地址,以及最后中断或者异常之前发生的分支的源地址和目的地址。
三、调试寄存器
Intel 处理器提供了8个调试寄存器,来控制处理器的调试活动。我们不仅可以使用MOV 指令从这些寄存器中读取数据,还可以向这些寄存器中写入数据。调试寄存器既可以作为这些指令的源操作数,也可以用于这些指令的目的操作数,不过,因为调试寄存器是些特权资源,所以只有当MOV 指令在实地址模式、SMM或者CPL为0的保护模式中运行时才能访问这些寄存器;当从任何其他特权级中试图读或者写调试寄存器时,都将引起一个通用保护异常。
调试寄存器的主要作用是设置并监视1到4个断点,编号为从0到3。该调试机制使我们可以通过两个专用寄存器DR6和DR7来管理各断点,对于这两个寄存器将在后面详细介绍。对于每个断点,可以使用这两个调试寄存器来指定和/或检测下列信息:
断点所在的线性地址(将在哪个线性地址上发生断点,即用DRn指出要监视的内存起始地址);
断点位置的长度(1、2、3或4字节)(即用LENn字段指出要监视的内存区域长度);
必须对该地址引起的调试异常所执行的操作;
该断点是否启用;
调试异常发生时,是否提供断点条件;
下面我们对各个寄存器安装功能分别加以详细介绍。
调试地址寄存器
每个调试地址寄存器(DR0-DR3)保存一个断点的32位线性地址。断点的比较是在物理地址转换之前进行的。
调试寄存器DR4和DR5
启用调试扩展时(控制寄存器的DE标志位被置为1,即CR4.DE=1),调试寄存器DR4和DR5被预留,因此,如果试图引用这两个寄存器,将引起无效操作码异常。DE标志位为0时,这些寄存器作为DR6和DR7的别名。
调试状态寄存器(DR6)
这个专用寄存器用来报告最近一次调试异常发生时的调试条件(当断点发生时,向调试器报告该断点的具体情况,以便调试器区分发生的是哪个断点。),在这个寄存器中的标志可以给出下列信息:
B0~B3(位0至位3)指示检测到的断点条件;
BD(位13)指示指令流中的下一个指令将访问一个调试寄存器(DR0~DR7);
BS(位14)指示(当设置时)调试异常是由单步执行模式(EFLAGS.TF=1)触发的;
BT (位15)指示(当设置时)这是由于任务切换时目标任务的TSS中的调试陷阱标志位被置1所导致的调试异常;
看官请注意,处理器从不清空DR6寄存器的内容,为什么?呵呵,这是软件的工作。
调试控制寄存器(DR7)
调试控制寄存器(DR7)启用或者禁用断点,并设置断点条件。它的标志位和字段控制下列事项:
L0~L3(位0、2、4、6)启用(当设置时)与当前任务相关断点的断点条件;
G0~G3 (位1、3、5、7启用(当设置时)所有任务相关断点的断点条件;
LE 和GE (位8和9)导致处理器去精确地检测那个引起数据断点条件的指令。在P6系列处理器中,这两个标志不被支持;
GD (位13)启用(当设置时)调试寄存器的保护,会在任何MOV 指令访问调试寄存器之前产生一个调试异常;
R/W0~R/W3 (位16、17、20、21、24、25、28和29)为相应的断点规定断点条件。要了解更多信息请阅读英特尔手册;
LEN0~LEN3(位18、19、22、23、26、27、30和31)分别对应DR0~DR3四个调试地址寄存器,用来指定要监控的访问长度;

