首页
社区
课程
招聘
[翻译]System Management Mode Hack
2012-5-14 13:13 7893

[翻译]System Management Mode Hack

2012-5-14 13:13
7893
|=-----------------------------------------------------------------------=|
|=-----------------=[ System Management Mode Hack    ]=------------------=|
|=-----------------=[ Using SMM for "Other Purposes" ]=------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
|=---------------=[ By BSDaemon                          ]=--------------=|
|=---------------=[ <bsdaemon *noSPAM* risesecurity_org> ]=--------------=|
|=---------------=[                                      ]=--------------=|
|=---------------=[    coideloko                         ]=--------------=|
|=---------------=[ <cdlk     *noSPAM* kernelhacking_com>]=--------------=|
|=---------------=[                                      ]=--------------=|
|=---------------=[    D0nAnd0n                          ]=--------------=|
|=---------------=[ <d0nand0n *noSPAM* kernelhacking.com>]=--------------=|
|=-----------------------------------------------------------------------=|
|=--------------------------=[ March 29 2008 ]=--------------------------=|
|=-----------------------------------------------------------------------=|

                    "Very nice! How much?"
                        - Borat Sagdyiev

------[ Index

1 - Introduction
    1.1 - Paper structure

2 - System Management Mode
    2.1 - Pentium modes of operation
    2.2 - SMM Overview
    2.2.1 - SMRAM
    2.2.2 - SMI Handler
    2.2.3 - SMI Triggering
    2.2.4 - Duflot discovery - Exploit
    2.3 - Duflot misses
        2.3.1 - PCI Configuration
    2.3.2 - Why and when the system generates a SMI
    2.4 - SMM Internals - Our first experiences
    2.4.1 - Analysing the SMM registers
    2.4.2 - SMM Details

3 - SMM for evil purposes
    3.1 - Challenges
        3.1.1 - Cache-originated overwrites
        3.1.2 - SMM Locking
        3.1.3 - Portability
    3.1.4 - Address translation
    3.2 - Copying our code in the SMM space
    3.2.1 - Testing
    3.2.2 - Descriptor caches
        3.2.3 - Code relocation

4 - SMM Manipulation Library

5 - Future and other uses

6 - Acknowledgements

7 - References

8 - Sources - Implementation details

------[ 1 - 概述
本文将讲述Intel体系结构的一些细节,以及邪恶用户如何编写硬件保护级的邪恶软件。
当然,因为本文的重点是 System Management Mode 特性,我们将深入Duflot(注二)的学习,
演示如何创建一个稳定的系统运行在SMM模式。
必须注意的是本文的内容是体系结构相关的,本文只关注Intel处理器。
因为运行在SMM里的恶意软件可以控制整个内存,修改内核结构,所以可以成为强大的rootkits。

---[ 1.1 - 文章结构
本文的目的是完整地研究将SMM用在开发恶意软件上。
因此本文在结构上被分为重要的两个部分。章节2讲述基本的奔腾处理器操作模式,它将阐述Duflot
的研究的基础。之后将说明Duflot遗漏的,系统行为如何对我们设置许可;介绍SMM的内部机制和我们
控制SMM的编程库和工具。
章节3讲述了如何以邪恶目的使用SMM,以及使用开发库的例子。

------[ 2 - 系统管理模式
摘自Intel手册:
Intel系统管理模式(System Management Mode,SMM)是专门为电源管理设计的执行规则。
使用SMM后,系统的各个部件可以被关闭或者使用最低的功耗。SMM独立于其他的系统软件,也可以被
用在其他目的。
每当我们看到"and can be used for other purposes" 这样的话时就会去想,到底什么是其它目的。
Internet上关于SMM的信息就是电源管理,说不出别的目的。
2006年,Duflot和其他一些人发布了一篇文章,讲述如何把SMM用在逃避系统保护上。这是第一次另类的使用
SMM的公开展示,并给出了一些方案(像如何把代码放进SMM,如何控制SMM里的系统内存,如何强迫系统进入SMM)。
Duflot遗漏的问题也将在本文回答(如何创建稳定的代码去控制SMM,如何控制SMM寄存器,创建稳定的SMM代码的
困难,以及Duflot仅仅提到的为什么系统行为阻碍了他)。

---[ 2.1 - 奔腾的操作模式
每个人都知道P6家族处理器的操作模式:
实模式是16位地址模式,保持处理器向前兼容,现在仅仅在启动时才会用到。保护模式是32位地址,提供了现代操作
系统使用的保护模式。虚拟8086模式广泛用来运行为老的体系结构编写的程序,如8086、8088。
系统管理模式是另外的一种操作模式,如前所述,是用在电源管理功能上的。

Intel处理器手册卷三已经说明了在这些操作模式之间的转换方法。

   -------------------                    SMI (interrupt)
      |->|Real Address Mode| -------------------------------------------|
      | ------------------- <----------------------------------|       |
      | | PE=1    ^ PE=0 (requires ring0) or                   |rsm or |
      | v         | reset                                      |reset V
      | -------------------                                    ---------
reset | | Protected Mode | -------> SMI (interrupt) ------> | SMM Mode |
      | ------------------- <------- rsm instruction <------   ---------
      | | VM=1    ^ VM=0                                       |       ^
      | v         |                                            |rsm    |
      | ------------------- <----------------------------------|       |
      |- |Virtual 8086 Mode| -------------------------------------------|
         -------------------                    SMI (interrupt)

P.S.:PE 和 VM是CR0(control register 0,控制寄存器0)里的标志位。

我们从这里得出:
         Intel构架下的任何操作模式在一个SMI中断发出时都可以切换到系统管理模式。
            系统管理模式将通过发出一个rsm指令返回到前一个操作模式(处理器会读一个在进入SMM前
            保存的当前的操作模式状态)。

---[ 2.2 - SMM 概述
首先,在系统进入SMM模式时,整个处理器上下文必须被保存以便恢复。这样,处理器将进入特殊的上下文
并开始执行SMI操作。从这个模式(SMM)返回前一个操作模式可以通过RSM指令(只能在SMM模式里使用此指令)
完成,此指令会读取保存的处理器上下文并回到上一个操作模式。

同样的,在SMM里分页机制不可用,你有16位的操作模式,但是可以寻址所有的物理内存。

没有对I/O端口和内存的限制,相当于Ring0的权限(实际上,在SMM有时候只能控制系统内存)。

Duflot演示了一种你自己发出SMI中断,强迫系统进入SMM模式,改变系统内存绕过保护措施
(他的例子是在OpenBsd上的)然后执行自己的代码。

---[ 2.2.1 - SMRAM
System Management Mode有一个特殊的内存区域称为SMRAM。它开始于SMBASE,占0x1FFFF 字节
(如果使用了扩展的SMRAM,这个数值将更大)。

SMBASE默认为0x30000,但是因为现代芯片的组织更新,实际上是0xA0000(BIOS将它通过视频卡的
I/O端口重新加载到基于内存映射的地址)。

正如Duflot指出的,内存控制Hub有一个称为SMRAM控制寄存器(SMRAM Control Register)的控制寄存器。
在上面设置一位后((D_OPEN - bit 6)),使得整个内存的地址空间都可以作为SMBASE重定向到SMRAM。

如果处理器不在SMM模式,且D_OPEN没有被设置,所有对SMRAM的内存地址的读取都是按照视频卡的规则(正如前面所说,
这片地址已经被定位到共享地址),-对SMRAM进行保护,稍后我们也用这个来保护我们的恶意软件。否则,如果
D_OPEN被设置,这个地址就是SMRAM的地址。

他展示的另外一个重要的事情是SMRAM Control Register的第4位 (D_LCK)。如果它被设置,将保护
SMRAM control register 和SMRAM ,如果D_OPEN也没有被置位,控制寄存器将被锁。如果更改
这个位,系统需要重启(这给了我们一个重大的挑战,因为大多数现代BIOS都将锁住SMRAM控制寄存器)。

虽然这些在Intel的手册里说的很详细,但是事实上一个超级用户可以通过视频驱动写它,然后强制触发
一个SMI中断。

当进入SMM,处理器将跳转到SMBASE+0x8000(意味着SMI中断处理程序必须位于SMRAM里0x8000的位置)。
因为当D_OPEN 被置位,我们就可以把一些代码放进SMRAM,只需要强制触发一个SMI中断来执行这些代码。

                 -----------------
SMBASE+0x1FFFF |                 |
                |                 |
                |                 |
                |                 |
SMBASE+0xFFFF    -----------------
                |                 |
                | State save area |
                |                 |
SMBASE+0xFE00    -----------------
                |                 |
                | Code,Heap,Stack |
                |                 |
SMBASE+0x8000    ----------------- ----> First SMI Handler instruction
                |                 |
                |                 |
                |                 |
SMBASE=0xA0000   -----------------

---[ 2.2.2 - SMI 处理程序(SMI Handler)
一旦我们要设置D_OPEN位,我们要想办法避免显卡的使用,因为此时对显卡内存的访问将会被重定向到
SMRAM,而不是显卡。Duflot没有展示如何做到这一点,因为他的例子是在OpenBsd上的,假设没有
使用显卡(他的exploit运行在OpenBsd上,但是关键的,没有使用X)。

在我们的例子里,我们也将演示如何直接操作寄存器,但是我们使用libpci确保不会出现这样的问题
(因为libpci使用系统接口管理PCI子系统,避免出现资源竞争状况)。libpci也更可靠,因为它支持
很多不同的操作系统。

因此,把攻击代码插入SMI Handler需要
    -检查D_LCK 位是否没有被置位
    -设置D_OPEN位
    -有内存空间的存取能力(在这里,0xA0000-0xBFFFF)

为了存取内存,我们使用/dev/mem设备映射内存,因为它提供了物理内存地址的存取
(而不是 /dev/kmem,它提供的是虚拟内存的全镜像)。

---[ 2.2.3 - SMI 触发
因为SMI触发是一个硬件中断,没有软件指令去产生它。一些芯片可以产生它
(Intel 82801 BA-I/O Controler HUB (ICH2) 、
Intel 82845 Memory Controler HUB (MCH) Datasheet)。

Duflot在他的文章中也提到了SMI_EN寄存器,它的最低有效位用来全局标识SMI是否可用。
SMI_EN寄存器的其他位标识那些设备可以产生一个 SMI中断。

SMI_STS 寄存器保持记录上一个产生SMI中断的设备。

这些寄存器可以使用正式的PCI机制来存取(in和out)。他们的位置是可变的,但是相对于PMBASE
的地址是(SMI_EN=PMBASE+0x30 and SMI_STS=PMBASE+0x34).

PMBASE可以通过使用bus 0, device 0x1F, function 0 and offset 0x40访问。

更多的关于PCI机制的细节见2.3.1小节。

---[ 2.2.4 - Duflot 的发现 - Exploit
在Duflot和他的朋友们的文章里展示了一种OpenBSD上的exploit。这是我们首先要分析的代码,
也仅仅需要很少的修改就能移植到Linux上。正如前面说的,使用x server会带来一些问题,因为
需要重定向使用显卡的内存地址去访问SMRAM.

因为Linux操作系统同大多数Unix一样,可以在用户模式提高I/O等级。 exploit可以使用in/out指令。
        if(iopl(3) < 0) {

To get access to the SMRAM, the D_OPEN bit must be set:
    outl(data1, 0xcf8);
        outl(data2, 0xcfc);

这段代码执行的是
      addr32 mov $test, %eax
    mov %eax, %cs:0xfff0

这里的偏移量0xfff0是SMRAM里保存的EIP状态。这样在系统执行rsm指令回到保护模式时,会执行
我们的test()函数(译者注:EIP相当于计算机体系结构里说的PC寄存器,这里修改了EIP的值)。

Duflot发现访问可编程I/O 端口 0xB2设置SMI_EN寄存器的第5位可以产生一个SMI
outl(0x0000000f, 0xb2);

这确实很有趣,但是我们能用它来做什么呢?

---[ 2.3 - Duflot 遗漏的
在Duflot的文章里没有演示PCI机制如何工作,他仅仅只是说如: 把端口0xCF8作为地址,端口0xCFC执行
操作等等。他没有说什么时候,为什么系统会产生一个SMI中断。使用SMM去控制系统内存确实应该展开说,
创造一个恶意软件运行在SMM或绕过启动保护机制。
本章余下的部分和下一张将阐述SMM如何工作和我们能在SMM里做什么。当然,最好还能展示如何分析一个系统,
创建可靠的库去控制SMM相关的寄存器。

---[ 2.3.1 - PCI 机制

通常来说,有两种I/O端口,一种是地址端口(0xCF8-0xCFB),另一种是数据端口(0xCFC-0xCFF)。
为了配置一个设备,你必须首先写要访问的设备和寄存器的地址端口,然后才能从数据端口读写数据。写
地址端口的数据格式如下:
位         描述
0..1    00b (总是 0)
2..7    Which 32-bit space in the config space to access
8..10   Device function
11..15 Device Number

完整的PCI设备清单可以从 http://www.pcidatabase.com获得。
PCI设备的地址被分解为一个PCI总线号,一个设备号(values 0-31),一个功能号(values 0-7)。

为了在bus:device:function 这些PCI空间里存取一个寄存器REG,你需要如下的地址
0x80000000L | ((bus & 0xFF) << 16) |
        ((((unsigned)device) & 0x1F) << 11) |
        ((((unsigned)func) & 0x07) << 8) | (REG & 0xFC);
每个PCI设备的配置通常都有一个或多个基址寄存器,用来设置或寻找物理内存或每一个
资源的I/O空间的地址。

---[ 2.3.2 - When and why 系统产生一个 SMI
所有来自CPU的内存事务(读/写许可)在主总线上被一些设备耗尽。
如果CPU自己译码来自内存的一段数据,如同本地的APIC(Advanced Programmable
Interrupt Controller高级程序中断控制器. ),事务就根本不需要外部总线。

如果CPU没有译码,那么这些内存数据必须被发出去。在一个典型的Intel体系结构,这些将被
MCH (Memory Controller Hub)译码,也被要求作为MCH拥有的地址。如果基于译码发现
这个事务不属于MCH,将被投递到链上的下一个设备。

如果MCH在真实的DRAM里找不到这个地址,它将在自己拥有的其他I/O范围里寻找(ISA, EISA, PCI)。

在旧的系统中,MCH将会精确译码,而不是把它传给I/O 桥。

如果MCH认为这个事务不属于它,这个事务将被转投到系统中可能存在的其他I/O 桥。
这个 确认所有权/响应 或者转投 的译码处理过程如果没有其他情况将循环下去。

最后的出口不是一个事务处理代理,就是返回这个地址上的值。或者这个地址没有事务代理,
返回一个终止错误,通常是0FFFFFFFFh的数据被返回。

在某些情况下,有些地址被两种不同的设备所拥有(例如在0A0000h - 0BFFFFh,
VGA frame buffer and system memory ),这将强迫体系结构发出一个SMI中断
满足这个事务。

如果SMI没有发生,事务会绕过MCH被VGA控制器捕获。
如果当事务把被MCH接受时发生SMI,事务将被定位到DRAM 单元,执行我们的代码。

---[ 2.4 - SMM 内部 - 我们的第一份经验

这里我们将展示SMM一些重要的细节和它如何工作,这对理解我们的附件里的库很有帮助。

---[ 2.4.1 - SMM 寄存器分析
让我们使用libpci来分析SMM,来获得更大的稳定性。
以下代码在ICH5 和 ICH3M 控制器上运行良好。

---   code   ---

#include <stdio.h>
#include <pci/pci.h>
#include <sys/io.h>

/* Defines - bit positions (will be used in more samples) */
#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)

/* Function to print SMRAM registers */
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;

    /* Provided by 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;
}

--- end code ---
编译
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#讯号,告诉芯片cpu处于SMM模式。
SMI中断能在除了SMM模式以外任何时间触发。将导致SMM处理程序执行。一旦SMIACT#讯号
被芯片检测到,远内存地址将被重定位到SMRAM饱和的内存。在那之后,cpu开始保存内部状态
到SMRAM里的内部状态保存映射区。然后,SMM处理程序开始执行。

当前是什么状态?系统处于实模式,所有的段组成了4GB,并且可以读写。
如前所述,为了离开SMM模式,SMM处理程序执行一条rsm指令。处理器再次读 状态保存映射区,
仅仅执行一些检查(很好),把系统恢复到前一个状态。

SMM把信息写入状态保存映射区是一种像栈一样的处理方式,从SMBASE寄存器(允许重定位)的顶部到底部,
记着这个对操作状态保存映射区很有帮助。

如果SMM是由于关机或者I/O指令造成的,SMM处理程序会仅仅根据入状态保存映射区的一个标志来决定
是返回继续执行还是关机。

在SMM的入口中断是禁止的,包括NMI中断和INIT ,IDT保持不变。为了在SMM里响应中断(为了我们的目的),
需要重新建立一个我们自己的中断向量表,用它替换原来的IDT。因为老的IDT里的地址空间在SMM模式下不再有效。

在STI指令后,系统开始接受一些中断,但是NMI仍然不可以用。要使用它,执行IRET/IRETD指令。

在SMM里最重要的可以重新使用的中断是如果一个NMI中断被接受,将被记住。所以如果hook了NMI中断程序就
可以绕过SMM的任何证明(NMI中断程序将在rsm之后立刻执行,在执行保存在状态保存映射区里的EIP之前)。

在我们的测始中,SMM重设置给我们带来了一些问题(在早期机器上(pentium II/III))。当然,我们更喜欢在
老式机器上执行这些测试,因为那些BIOS没有设置SMM锁(通常,BIOS比cpu还要早2年)。

看样子,老cpu有一个固定的CS值指向0x30000(默认的SMM位置,现代BIOS重定位到 0xA0000)。

如果我们在SMM里恢复中断,当一个中断被请求,cpu将保存CS:IP 为了以后的恢复。
但是它将使用固定的CS而不是SMBASE的值,没有反映SMM真实使用的段,因此代码会返回到错误的位置。

当然,Intel的文档还提到了SMBASE在老式CPU上的对齐问题,向前到P4。

------[ 3 - SMM for evil purposes

如前所述,SMM可以用来修改系统内核结构,这里我们就展示SMM里的恶意软件挑战和其他的一些可能的方法。

---[ 3.1 - 挑战

---[ 3.1.1 - 原始Cache 改写
在进入SMM时如果发生一个 #FLUSH,SMRAM可能被cache中的数据改写。
为了避免这些问题,我们可以把SMRAM映射到没有cache的区域,或者断言#FLUSH 和#SMI同时事件,
(#FLUSH将被首先执行)。大部分的BIOS设置SMRAM 位于没有cache的区域,但是同时也锁住了它。

---[ 3.1.2 - SMM 锁
大部分的BIOS制造商如今锁住了SMM,如果只是在保护主机上使用SMM,可以用开源BIOS替换原来的BIOS。
我们讨论的恶意代码不能使用,必须打BIOS补丁。

一种很好的绕过BIOS的保护方式是使用TOP_SWAP [8]位,在执行原BIOS之前执行我们的SMM处理程序并且
锁住它,这样可以防止原来的BIOS覆盖我们的SMM处理程序。

通常,这个位用来决定是从第一个64k还是第二个64k开始加载BIOS,看到这里,设置TOP_SWAP位,
把自己的代码放在第二个64k,在自己的代码里跳回原来的BIOS。这样的代码会在BIOS之前执行。

TOP_SWAP位给BIOS更新提供了一种安全方法。把原始的BIOS拷贝到第二个64k,当BIOS更新
完成,经过正式的检查,如果出现了什么问题使机器重启,可以使用第二个64k的BIOS来进行
没有问题的启动。

---[ 3.1.3 - 可移植性

如前所述,SMM是基于硬件的,更确切地说,是基于ICH的。附带的代码在ICH5 和 ICH3M上
运行良好,在Linux下测试通过,因为采用libpci,也支持FreeBSD,
NetBSD, OpenBSD (也在上面测试过), Solaris, Aix, GNU/Hurd and Windows).

为了获得在其他ICH芯片上的支持,必须修改libSMM.h头文件去描述总线、设备、函数、偏移的
正确值,确保函数get_pmbase()的返回值PMBASE是正确的(参照手册)。

之后,确保SMRAM_OFFSET是正确定义的(参考你的I8xx手册)。如果是,SMRAM控制寄存器里的
值会正确显示(可以使用D_LCK位测试,因为这一位被设置,其他位就不能被更改)。One can also
test it using the dd command showed next in this article and the D_OPEN bit
(use the open_smram function, write to the SMRAM memory mmap'ing it and
then dump it to verify if it's working).

---[ 3.1.4 - Address 转换

地址转换是在SMM里的巨大困难,我们需要CR3的值(可以从状态保存映射区里读取)去手工解析页表,
执行实际的转换。

最好我们能有一个简单的SMI处理程序,给我们的代码最高的权限,这样我们就能少呆在SMM里。
接下来的部分我们讲述如何把代码放进SMM,执行它,然后使用Descriptor caches去提供以
上程序的方法。

---[ 3.2 - Copying our code in the SMM space

---[ 3.2.1 - Testing

首先,把通过设置 D_OPEN位打开SMRAM把一些代码放进SMM。

---   code   ---
pci_write_byte(smram_dev, SMRAM_OFFSET, (current_value | D_OPEN_BIT));
--- end code ---

完成后关闭SMRAM ,代码如下
---   code   ---
pci_write_byte(smram_dev, SMRAM_OFFSET, (current_value & ~D_OPEN_BIT));
--- end code ---

当然,进入我们的代码,需要锁住SMRAM的访问,避免其他人对SMM相关寄存器的访问。

---   code   ---
pci_write_byte(smram_dev, SMRAM_OFFSET, (current_value | D_LCK_BIT));
--- end code ---

为了把我们的代码插入SMRAM存储区,我们需要映射它,像我们在exploit里做的一样
---   code   ---
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);

/* Here we are copying our code to the SMRAM memory */
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;
}
--- end code ---

有必要验证一下它是否工作得很好,在那之前先对SMRAM内存内容做一个副本。
使用如下命令
dd if=/dev/mem of=my_smram bs=1 skip=`expr 655360 - 1` count=64K
P.S.:655360 是 0xa0000 的10进制。(正如Duflot指出的,SMM重定向于0xa0000,
而不是默认的0x30000)

---[ 3.2.2 - Descriptor caches

这个方法只能在一些系统上运行,因为Intel的文档关于这一部分并不是很详细。
摘自Intel的手册:
每一个段寄存器有一个可见部分和一个不可见部分(隐藏部分作为descriptor
cache 或者 shadow register)。当一个段选择器加载段可见部分时,处理器同时也用
base address, segment limit,和来自段选择器指出的段描述符的"Access control information"
加载隐藏部分。

"Access control information"用 xPL:很好理解
      - RPL -> Request privilege level
    - CPL -> Current privilege level
    - DPL -> Descriptor privilege level

在SMRAM里的saved-state map,基于Intel的手册 ,被descriptor caches 和
the 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

saved-state map被保存在 SMBASE + 0xFE00 to SMBASE + 0xFFFF.

修改SS descriptor cache的DPL域从3到0会给我们的程序Ring0权限(一个新处理器的
保护漏洞)。

---[ 3.2.3 - Code relocation
SMM有能力重定位他的保护内存空间。保存在state save map里的SMBASE可能被修改,
这个值在rsmzhi指令时被读取。当再次进入SMM,SMRAM 将被定位到新位置。

在我们的SMM处理程序里,我们能修改saved-state map的值,(at
offset 0xFEF8 from SMBASE)。为了这么做,我们必须代码里CS的调整。

这用来把SMRAM重定位到我们指定的内存空间,一旦我们的恶意软件锁住了SMM清空了D_OPEN,
我们就不需要这些技术了。

------[ 4 - SMM Manipulation Library

SMM Manipulation Library提供了简单可靠的办法控制SMRAM控制寄存器。
它提供一下方法:
u8 show_smram (struct pci_dev* smram_dev, u8 bits_to_show)
        It's used to test if specific bits are set or not
        The pci_dev structure are optional, NULL can be passed.

    u16 get_pmbase (void)
        Internally used by the library to manipulate the SMI-enablement.
        Exported by the function to turn easy to an external program
        verify the correct offsets for the SMI_EN and SMI_STS.

    u16 get_smi_en_iop (void)
        Return the location of the SMI_EN

    u16 get_smi_sts_iop (void)
        Return the location of the SMI_STS

    int enable_smi_gbl (u16 smi_en_iop)
        Enable SMI globally

    int disable_smi_gbl (u16 smi_en_iop)
        Disable SMI globally

    int enable_smi_on_apm (u16 smi_en_iop)
        Enable SMI on APM events

    int disable_smi_on_apm (u16 smi_en_iop)
        Disable SMI on APM events

    int open_smram(void)   
        Open SMRAM for access (set D_OPEN bit)

    int close_smram(void)
        Close SMRAM for access (unset D_OPEN bit)

    int lock_smram(void)
        Lock the SMRAM (set D_LCK bit)

    void write_to_apm_cnt(void)
        Write to the APM CNT (generate a SMI)

当然,包含文件libSMM.h必须必须根据本地的SMM 手册设置正确的值,如device, function bus 和
offsets.这个库也包含对关键寄存器值的设置,如D_OPEN 和the D_LCK.

libSMM_test.c 也依赖于文档,显示如何使用SMM Manipulation Library.这个程序会全部置位
或清空所有的控制寄存器,将会影响到SMM操作。能够用来测试这个库在你的机器上是否运行的很好。它
会测试 D_LCK 位。运行后需要重启系统。

evil.c将用SMM Manipulation Library来插入SMM处理程序,冻结处理器。

[培训]《安卓高级研修班(网课)》月薪三万计划,掌 握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞3
打赏
分享
最新回复 (3)
雪    币: 601
活跃值: (256)
能力值: ( LV11,RANK:190 )
在线值:
发帖
回帖
粉丝
RootSuLe 4 2012-5-14 15:08
2
0
暂时看不懂,但必须收藏,谢谢
雪    币: 4581
活跃值: (942)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
b23526 2012-5-14 15:09
3
0
啊擦,哥竟然抢了个沙发啊
雪    币: 108
活跃值: (888)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yangya 2012-5-19 20:37
4
0
这个过时了!
游客
登录 | 注册 方可回帖
返回