原文链接:抢先DriverStudio夺取机器控制权(下篇)
上篇仅仅说到如何抢先DriverStudio,并在结尾留给大家一个遐想。现在我进一步拓展这个遐想,从而给大家更多的遐想。: ) 那么现在我要给这个驱动增加新的功能,不但抢先DriverStudio启动,而且给用户显示几行提示,并等待用户的输入,如果用户输入B 键则发生蓝屏,如果用户按任意其他键则继续往下执行,SoDriverStudio可以正常执行下去。
要完成这个功能必须解决两个问题:
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 核心编程研究系列之二:读取指定物理内存地址中的内容 》一篇中有更为详细的说明)。该函数原形如下:
1 PVOID
2 MmMapIoSpace(
3 IN PHYSICAL_ADDRESS PhysicalAddress,
4 IN ULONG NumberOfBytes,
5 IN MEMORY_CACHING_TYPE CacheType
参数含义都比较明显,其中CacheType选择NoCache类型。
b. 使用类似于bios中断字符显示的规则,一个字符占一个word,低位为字符的ascii码,高位为显示属性。我这里显示闪烁的红色字体所以属性为 84h。与bios中断不同的是,我们是直接写视频缓冲,其物理地址为 0b8000h 。我写了一个显示函数,包含两个参数:第一个是字符串地址,第二个是显示的位置。为了美观,我将2行字符串显示在屏幕的倒数最后两行上:
1 Screen_W equ 50h
2 Screen_H equ 19h ;1ch
3 Show_Pos_Line0 equ (Screen_W * (Screen_H - 2) + 5) * 2
4 Show_Pos_Line1 equ (Screen_W * (Screen_H - 1) + 5) * 2
5
6 函数内容如下:
7
8 ;*************************************************************************
9 _DisplayString proc _lpstr,_pos
10 local pa:qword
11 local lpvmem:dword
12
13 ;mov dword ptr [pa+1],Video_Addr
14 ;mov dword ptr [pa+5],0
15
16 mov dword ptr [pa],Video_Addr
17 mov dword ptr [pa+4],0
18
19 push 0 ;MmNonCached
20 push 8000h ;NumberOfBytes
21 ;push dword ptr [pa+5]
22 ;push dword ptr [pa+1]
23 push dword ptr [pa+4]
24 push dword ptr [pa]
25 call MmMapIoSpace
26
27 mov lpvmem,eax
28 mov esi,_lpstr
29 mov edi,lpvmem
30 add edi,_pos
31 mov bh,84h ;char show_attribute
32
33 .while TRUE
34 .if byte ptr [esi] != 0
35 mov bl,byte ptr [esi]
36 mov word ptr [edi],bx
37 inc esi
38 inc edi
39 inc edi
40 .else
41 .break
42 .endif
43 .endw
44
45 invoke MmUnmapIoSpace,lpvmem,8000h
46
47 ret
48
49 _DisplayString endp
50
51
最后不要忘了用MmUnmapIoSpace取消映射。
c. 为了达到延时的效果调用ntoskrnl.exe中的apiKeDelayExecutionThread,其原形如下:
1 NTSTATUS
2 KeDelayExecutionThread(
3 IN KPROCESSOR_MODE WaitMode,
4 IN BOOLEAN Alertable,
5 IN PLARGE_INTEGER Interval
6 );
其中 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种都不成问题 。我同样写了一个子函数方便使用,代码如下:
1 ;*************************************************************************
2 _WaitForInput proc
3 local al_tmp:byte
4 local interval:LARGE_INTEGER
5 local turnsNow:dword
6
7 mov dword ptr [interval],0ffffe000h
8 mov dword ptr [interval+4],0ffffffffh
9
10 mov turnsNow,0
11
12 .while TRUE
13 .if turnsNow == Turns
14 .break
15 .else
16 inc turnsNow
17
18 in al,64h
19 test al,1
20 jz Delay
21
22 in al,60h
23 mov al_tmp,al
24 movzx eax,al_tmp
25 cmp eax,1
26 jz ExitWhile
27
28 cmp eax,1eh
29 jz ExitWhile
30
31 cmp eax,0b0h
32 jz ExitWhile
33
34 cmp eax,1ch
35 jnz Delay
36
37 ExitWhile:
38 .break
39
40 Delay:
41 invoke KeDelayExecutionThread,0,0,addr interval
42 .endif
43 .endw
44
45 xor eax,eax
46 mov al,al_tmp
47 ret
48
49 _WaitForInput endp
50
51 ;*************************************************************************
52
53 d. 剩下来做的事就是在Main中判断用户输入的键码:
54
55 invoke _DisplayString,addr szhopysay,Show_Pos_Line0
56 invoke _DisplayString,addr szchoose,Show_Pos_Line1
57 invoke _WaitForInput
58
59
60 .if al == 01h
61 ;do nothing
62 .elseif al == 0b0h
63 invoke _TryBS
64 .else
65 ;do nothing
66 .endif
运行的效果如图3所示:
图3
蓝屏的代码就不给出了,因为相信每个人都能写出不同的花样来。写续篇的原因是因为正好看到驱网(www.driverdevelop.com)的一篇逆向的文章,想现学现卖一番,呵呵。暂时到这里吧(以前预告的第3篇文章因为要结合VB的界面而且又有了新的想法,所以还要等一段时间,呼呼),准备去看女足啦,哇咔咔.......(无语了,这不是我...不是我....不是我 ... ...)
侯佩|hopy
写于2007.09.18