和DriverStudio"过不去"之加强版
(<<抢先DriverStudio夺取机器控制权>>系列2)
上篇仅仅说到如何抢先DriverStudio,并在结尾留给大家一个遐想。
现在我进一步拓展这个遐想,从而给大家更多的遐想。: )
那么现在我要给这个驱动增加新的功能,不但抢先DriverStudio
启动,而且给用户显示几行提示,并等待用户的输入,如果用户输入
B 键则发生蓝屏,如果用户按任意其他键则继续往下执行,So
DriverStudio可以正常执行下去。
要完成这个功能必须解决两个问题:
0 如何在系统引导时显示字符串;
1 如何捕获用户输入并延时等待。
那么从哪入手呢?我首先想到直接写Video Buffer和直接捕获键盘
寄存器,但是这个方法可行吗?我们不妨看一下DriverStudio的
bootcfg.sys 是如何做的。
照例用IDA载入bootcfg.sys,按照DriverStudio初始化显示的字符
串来到驱动的DriverEntry,如图0
图0
通过查找字符串的引用很快就找到对应的指令点,全文只有一个
引用,如图1所示:
图1
为了确认这一点,我将地址0x107dd - 0x107e6中的指令全部nop掉,
图2
这时的确原先应该显示"Press ESC ..."这段话的地方没有显示,而是
直接跳到Config 对话框那里去了。既然找对了地方下一步就好办了,
通过进一步反汇编bootcfg.sys的代码片断,印证了我先前的想法!
下面简单说说DriverStudio是如何做的:
a. 使用 MmMapIoSpace 完成物理地址到虚拟地址的映射,因为
内核已经进入保护模式,并且开了分页。要想读写物理地址必须做
这样的映射(我的
《Windows 核心编程研究系列之二:读取指定物理内存地址中的内容 》
一篇中有更为详细的说明)。
该函数原形如下:
PVOID
MmMapIoSpace(
IN PHYSICAL_ADDRESS PhysicalAddress,
IN ULONG NumberOfBytes,
IN MEMORY_CACHING_TYPE CacheType
参数含义都比较明显,其中CacheType选择NoCache类型。
b. 使用类似于bios中断字符显示的规则,一个字符占一个word,
低位为字符的ascii码,高位为显示属性。我这里显示闪烁的
红色字体所以属性为 84h。与bios中断不同的是,我们是直接
写视频缓冲,其物理地址为 0b8000h 。我写了一个显示
函数,包含两个参数:第一个是字符串地址,第二个是显示的
位置。为了美观,我将2行字符串显示在屏幕的倒数最后两行
上:
Screen_W equ 50h
Screen_H equ 19h ;1ch
Show_Pos_Line0 equ (Screen_W * (Screen_H - 2) + 5) * 2
Show_Pos_Line1 equ (Screen_W * (Screen_H - 1) + 5) * 2
函数内容如下:
;*************************************************************************
_DisplayString proc _lpstr,_pos
local pa:qword
local lpvmem:dword
;mov dword ptr [pa+1],Video_Addr
;mov dword ptr [pa+5],0
mov dword ptr [pa],Video_Addr
mov dword ptr [pa+4],0
push 0 ;MmNonCached
push 8000h ;NumberOfBytes
;push dword ptr [pa+5]
;push dword ptr [pa+1]
push dword ptr [pa+4]
push dword ptr [pa]
call MmMapIoSpace
mov lpvmem,eax
mov esi,_lpstr
mov edi,lpvmem
add edi,_pos
mov bh,84h ;char show_attribute
.while TRUE
.if byte ptr [esi] != 0
mov bl,byte ptr [esi]
mov word ptr [edi],bx
inc esi
inc edi
inc edi
.else
.break
.endif
.endw
invoke MmUnmapIoSpace,lpvmem,8000h
ret
_DisplayString endp
最后不要忘了用MmUnmapIoSpace取消映射。
c. 为了达到延时的效果调用ntoskrnl.exe中的api
KeDelayExecutionThread,其原形如下:
KeDelayExecutionThread(
IN KPROCESSOR_MODE WaitMode,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Interval
);
其中 WaitMode选择KernelMode,将可报警置为FALSE.值得注意的
是第3个参数Interval,这个参数说明如下:
Interval
Specifies the absolute or relative time, in units of 100 nanoseconds, for which the wait is to occur. A negative value indicates relative time. Absolute expiration times track any changes in system time; relative expiration times are not affected by system time changes.
我们最好是用相对时间的延时方式,这就需要写成负数的形式将前导位
全部置1。为了达到捕获键盘输入,需要直接访问IO端口64h和60h,
这在ring0种都不成问题 。我同样写了一个子函数方便使用,代码如下:
;*************************************************************************
_WaitForInput proc
local al_tmp:byte
local interval:LARGE_INTEGER
local turnsNow:dword
mov dword ptr [interval],0ffffe000h
mov dword ptr [interval+4],0ffffffffh
mov turnsNow,0
.while TRUE
.if turnsNow == Turns
.break
.else
inc turnsNow
in al,64h
test al,1
jz Delay
in al,60h
mov al_tmp,al
movzx eax,al_tmp
cmp eax,1
jz ExitWhile
cmp eax,1eh
jz ExitWhile
cmp eax,0b0h
jz ExitWhile
cmp eax,1ch
jnz Delay
ExitWhile:
.break
Delay:
invoke KeDelayExecutionThread,0,0,addr interval
.endif
.endw
xor eax,eax
mov al,al_tmp
ret
_WaitForInput endp
;*************************************************************************
d. 剩下来做的事就是在Main中判断用户输入的键码:
invoke _DisplayString,addr szhopysay,Show_Pos_Line0
invoke _DisplayString,addr szchoose,Show_Pos_Line1
invoke _WaitForInput
.if al == 01h
;do nothing
.elseif al == 0b0h
invoke _TryBS
.else
;do nothing
.endif
运行的效果如图3所示:
图3
蓝屏的代码就不给出了,因为相信每个人都能写出不同的花样来。
写续篇的原因是因为正好看到驱网(www.driverdevelop.com)的
一篇逆向的文章,想现学现卖一番,呵呵。暂时到这里吧(以前预告的
第3篇文章因为要结合VB的界面而且又有了新的想法,所以还要
等一段时间,呼呼),准备去看女足啦,哇咔咔.......(无语了,这不是我...
不是我....不是我 ... ...)
侯佩|hopy
写于2007.09.18
文章