最简单的分层驱动程序模型(汇编语言描述)(转)

首先声明一下不是什么新东西,这里只是对最近的学习做个总结而已。

一、什么是分层驱动
这里的分层驱动,是指过滤器驱动程序,搞windows驱动的应该非常清楚windows的驱动架构是
分层的,一个实际的物理设备,可以有多个不同的驱动设备对象,当然,一个驱动设备对象
对应一个驱动程序,而这些驱动设备对象的排列结构是分层的,即一个IRP包过来了,这个IRP
先交给最上层的设备对象对应的驱动程序处理,第一层处理完了,再调用下一层设备的驱动程序
来处理这个IRP,直到最底下的那层(WDM称为PDO,Physicl Device Object)处理完这个IRP,然后呢,内核再检查一下某一个驱动是否注册了OnCompletion Routine,如果是的话,再从下往上依次调用他们注册的OnCompletion Routine,然后,这个IRP完成了自己的使命,可以升天了。所以,分层,就必然可以过滤,这一可以过滤,有人就要干坏事喽....

二、最简单的分层驱动模型
嗯,WDM和KMD两种驱动程序模型下,分层驱动程序的写法是不太一样的,
WDM可参看<<Programming the Microsoft Windows Driver Mode>>第9章第一节
KMD的话,就来忍受我的唠叨吧
下面主要从3个方面展开说明
DriverEntry的编写
IRP分派函数的编写
DriverUnload的编写

1.DriverEntry
a.调用IoCreateDevice创建未命名的设备对象,类型指定为你想过滤的设备类型,如键盘,TCPIP
invoke IoCreateDevice,pDriverObject,sizeof DEVICE_EXTENSION,0,/
   FILE_DEVICE_KEYBOARD,0,TRUE,addr @lpNewDevObj
要注意的地方
第2个参数指定为一个结构体的大小,内核在创建此设备对象的时候会帮我们分配好指定大小的内存,
并把这块内存的地址写入返回的设备对象的DeviceExtension字段
至于这个结构体的定义,完全是由我们自己定义的,但必须要留一个字段用来存放我们创建的设备
的底层设备对象的地址,因为这个地址相当重要,放在局部变量或放在全局变量中都不方便后面我们
编写分派函数的需要
然后是设备类型,这里以键盘驱动为例,即为FILE_DEVICE_KEYBOARD,Windows定义的所有可用值如下:
FILE_DEVICE_BEEP                equ 01
FILE_DEVICE_CD_ROM              equ 02
FILE_DEVICE_CD_ROM_FILE_SYSTEM equ 03
FILE_DEVICE_CONTROLLER          equ 04
FILE_DEVICE_DATALINK            equ 05
FILE_DEVICE_DFS                 equ 06
FILE_DEVICE_DISK                equ 07
FILE_DEVICE_DISK_FILE_SYSTEM    equ 08
FILE_DEVICE_FILE_SYSTEM         equ 09
FILE_DEVICE_INPORT_PORT         equ 0ah
FILE_DEVICE_KEYBOARD            equ 0bh
FILE_DEVICE_MAILSLOT            equ 0ch
FILE_DEVICE_MIDI_IN             equ 0dh
FILE_DEVICE_MIDI_OUT            equ 0eh
FILE_DEVICE_MOUSE               equ 0fh
FILE_DEVICE_MULTI_UNC_PROVIDER equ 10h
FILE_DEVICE_NAMED_PIPE          equ 11h
FILE_DEVICE_NETWORK             equ 12h
FILE_DEVICE_NETWORK_BROWSER     equ 13h
FILE_DEVICE_NETWORK_FILE_SYSTEM equ 14h
FILE_DEVICE_NULL                equ 15h
FILE_DEVICE_PARALLEL_PORT       equ 16h
FILE_DEVICE_PHYSICAL_NETCARD    equ 17h
FILE_DEVICE_PRINTER             equ 18h
FILE_DEVICE_SCANNER             equ 19h
FILE_DEVICE_SERIAL_MOUSE_PORT   equ 1ah
FILE_DEVICE_SERIAL_PORT         equ 1bh
FILE_DEVICE_SCREEN              equ 1ch
FILE_DEVICE_SOUND               equ 1dh
FILE_DEVICE_STREAMS             equ 1eh
FILE_DEVICE_TAPE                equ 1fh
FILE_DEVICE_TAPE_FILE_SYSTEM    equ 20h
FILE_DEVICE_TRANSPORT           equ 21h
FILE_DEVICE_UNKNOWN             equ 22h
FILE_DEVICE_VIDEO               equ 23h
FILE_DEVICE_VIRTUAL_DISK        equ 24h
FILE_DEVICE_WAVE_IN             equ 25h
FILE_DEVICE_WAVE_OUT            equ 26h
FILE_DEVICE_8042_PORT           equ 27h
FILE_DEVICE_NETWORK_REDIRECTOR equ 28h
FILE_DEVICE_BATTERY             equ 29h
FILE_DEVICE_BUS_EXTENDER        equ 2ah
FILE_DEVICE_MODEM               equ 2bh
FILE_DEVICE_VDM                 equ 2ch
FILE_DEVICE_MASS_STORAGE        equ 2dh
FILE_DEVICE_SMB                 equ 2eh
FILE_DEVICE_KS                  equ 2fh
FILE_DEVICE_CHANGER             equ 30h
FILE_DEVICE_SMARTCARD           equ 31h
FILE_DEVICE_ACPI                equ 32h
FILE_DEVICE_DVD                 equ 33h
FILE_DEVICE_FULLSCREEN_VIDEO    equ 34h
FILE_DEVICE_DFS_FILE_SYSTEM     equ 35h
FILE_DEVICE_DFS_VOLUME          equ 36h
FILE_DEVICE_SERENUM             equ 37h
FILE_DEVICE_TERMSRV             equ 38h
FILE_DEVICE_KSEC                equ 39h
总共有58个,但是要除去FILE_DEVICE_UNKNOWN,因为我们写过滤器驱动程序总得有个过滤目标吧
所以我们总共有57种过滤对象

b.必须为每种类型的IRP都设置一个派遣函数
mov esi,pDriverObject
assume esi:ptr DRIVER_OBJECT
mov ecx,IRP_MJ_MAXIMUM_FUNCTION
lea edi,[esi].MajorFunction
mov eax,offset _DispatchAny
cld
rep stosd

c.设置驱动的Unload函数
mov [esi].DriverUnload,offset _DriverUnload

d.为需要额外处理的IRP设置派遣函数
比如要处理IRP_MJ_READ,就这样编写
mov [esi].MajorFunction[IRP_MJ_READ*4],offset _DispatchRead
这里只是给定出一个简单的模型,就没有去特别的处理某种类型的IRP
我会在后面的一个实例中详细说明这一块

e.设置设备属性和原有设备的属性一致,原有设备属性可通过DeviceTree查看
mov edi,@lpNewDevObj
assume edi:ptr DEVICE_OBJECT
mov eax,[edi].Flags
or eax,DO_BUFFERED_IO
or eax,DO_POWER_PAGABLE
mov ecx,DO_DEVICE_INITIALIZING
not ecx
and eax,ecx
mov [edi].Flags,eax

f.清空系统为DEVICE_EXTENSION结构分配的内存空间
xor eax,eax
mov edi,[edi].DeviceExtension
mov ecx,sizeof DEVICE_EXTENSION
cld
repz stosb
当然,你使用RtlZeroMemory也可以

g.把当前设备加入到这种设备类型所在的设备链中
mov edi,@lpNewDevObj
mov esi,[edi].DeviceExtension
invoke IoAttachDevice,@lpNewDevObj,addr szDevKeyboard,esi
第2个参数的定义是:
stUnicode "//Device//KeyboardClass0",szDevKeyboard,4
stUnicode是我在那个俄国人的基础上改写的一个宏,用来定义一个UNICODE_STRING类型的结构体
第3个参数要注意,必须是我们的DEVICE_EXTENSION结构的某个字段的地址
这样DriverEntry就完工了

2.IRP分派函数的编写
下面是我的任意IRP分派函数
_DispatchAny proc _lpDevObj,_lpIrp
IoSkipCurrentIrpStackLocation _lpIrp
mov esi,_lpDevObj
assume esi:ptr DEVICE_OBJECT
mov ecx,[esi].DeviceExtension
mov ecx,dword ptr [ecx]
fastcall IofCallDriver,ecx,_lpIrp
assume esi:nothing
ret
_DispatchAny endp
比较短,就直接贴出来了
a.调用IoSkipCurrentIrpStackLocation
因为当IRP传递到我这一层驱动时,这个IRP我们并不关心,所以不需要额外地处理它,交给下一层驱动
处理就行了,但是我们也不能直接就把这个IRP交给下一层驱动处理,必须要调用
IoSkipCurrentIrpStackLocation宏
当驱动被分层以后,他们被注册到一个chain中,IRP会在这个chain中传递,从最上面,到最下面,再回到最上面
为适应这种体制,IRP的结构的大小是不固定的,大体结构如下:
    --------------------
    |   IRP header     |
    --------------------
    |IO_STACK_LOCATION |<-----lowest driver stack location #index1
    --------------------
    |IO_STACK_LOCATION |<-----next higher stack location    #index2
    --------------------
    |IO_STACK_LOCATION |<-----topmost driver stack location #index3
    --------------------
也就是说,chain的最顶层的驱动,对应的IO_STACK_LOCATION是在最下面
IRP头中存放着当前驱动对应的IO_STACK_LOCATION的索引,是从1开始的,不是从0开始
同时,也保存着当前驱动对应的IO_STACK_LOCATION的地址
最上面的驱动处理完这个IRP后,调用IoCallDriver,这会使索引加1,地址也修正为下一个IO_STACK_LOCATION的地址,
然后IRP被交给下一个Driver处理,
而IoSkipCurrentIrpStackLocation宏的作用就是使IO_STACK_LOCATION指针少前进一步
而IoCallDriver函数会使IO_STACK_LOCATION指针向前一步,中和的结果就是IO_STACK_LOCATION2006-11-20指针不变
当下一个驱动程序的派遣例程调用IoGetCurrentIrpStackLocation时,它将收到与我们正使
用的完全相同的IO_STACK_LOCATION指针,因此,它所处理的将是同一个请求(相同的主副功
能代码)以及相同的参数。
b.调用IofCallDriver,把IRP交给下一层驱动处理
注意这里用了fastcall,其实你可以不用fastcall,但为了性能,还是用fastcall吧
fastcall把第一,二个参数分别放入ecx,edx,剩下的参数才使用堆栈传递,所以性能较cdecl和stdcall
有所提高.当然,这个fastcall关键字是那个俄国人定义的宏

3.Unload函数的编写
也比较短,就直接贴出来了
_DriverUnload proc pDriverObject:PDRIVER_OBJECT
push esi
mov esi,pDriverObject
assume esi:ptr DRIVER_OBJECT
mov eax,[esi].DeviceObject
mov eax,(DEVICE_OBJECT PTR [eax]).DeviceExtension
mov eax,dword ptr [eax]
invoke IoDetachDevice,eax
invoke IoDeleteDevice,[esi].DeviceObject
assume esi:nothing
pop esi
ret
_DriverUnload endp

a.调用IoDetachDevice
要把当前设备从设备链中删除,所以它就一个参数,下层设备对象的地址
b.调用IoDeleteDevice

三、用汇编写分层驱动要注意的地方
在DriverEntry函数中,一定要事先保存寄存器,返回前恢复,要不然,会死得很惨
在其它分派函数中,也要注意寄存器的保存,这是过滤器驱动程序,可能会在任意上下文环境中
被调用
对函数的形参不要使用(DEVICE_OBJECT ptr [形参])这种类型的引用,要不然,你也会死得奶惨!

相关参考资料:
<<Programming the Microsoft Windows Driver Mode>>第9章第一节
<<Rootkits>>第6章第1节第1小节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值