12、Windows驱动开发技术详解笔记(8) 基本语法回顾
8、加裁驱动,驱动与设备
1)前面我们主要通过Driver Studio和KmdManager。现在了解一下程序加裁。
Windows NT式驱动是基于服务方式加载的,可以通过修改注册表内容完成,也可以通过服务相关API完成。设备驱动程序的动态加载主要由服务控制管理程序(Service Control Manager, SCM)系统组件完成,该组件可以启动、停止和控制服务等。
具体加裁方法,参见 [1,3]。在这种情况下加载驱动需要调用OpenSCManager->CreateService->StartService,此时驱动会在服务管理器中注册自己,但这往往显得麻烦,我们还有一个不需要通过服务而直接加载驱动的方法,即使用ZwLoadDriver(这个函数通常是ring0中加载驱动时用,由于它被ntdll.dll导出,因此在ring3也可以用)进行直接加载。
及 http://hi.baidu.com/xlsdg/blog/item/93632681bf1ed4dabc3e1e97.html
2)设备与符号链接
驱动程序和系统其他组件之间的交互是通过给设备发送或者接受发给设备的请求来交互的。换句话说,一个没有任何设备的驱动是不能按规范方式和系统交互的。当然也不会收到任何IRP,分发函数也失去了意义。
但并不意味着这样的驱动程序不存在。如果一个驱动程序只是想写写日志文件、Hook某些内核函数或者是做一些其他的小动作,也可以不生成任何设备,也不需要关心分发函数的设置。
如果驱动程序要和应用程序之间通信,则应该生成设备。此外还必须为设备生成应用程序可以访问的符号链接。下面的驱动程序生成了一个设备,并设置了分发函数:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <ntifs.h>
2
3 NTSTATUS DriverEntry(
4
5 PDRIVER_OBJECT driver,
6
7 PUNICODE_STRING reg_path)
8
9 {
10
11 NTSTATUS status;
12
13 PDEVICE_OBJECT device;
14
15 // 设备名
16
17 UNICODE_STRING device_name =
18
19 RTL_CONSTANT_STRING("\\Device\\MyCDO");
20
21 // 符号链接名
22
23 UNICODE_STRING symb_link =
24
25 RTL_CONSTANT_STRING("\\DosDevices\\MyCDOSL");
26
27 // 生成设备对象
28
29 status = IoCreateDevice(
30
31 driver,
32
33 0,
34
35 device_name,
36
37 FILE_DEVICE_UNKNOWN,
38
39 0,
40
41 FALSE,
42
43 &device);
44
45 // 如果不成功,就返回。
46
47 if(!NT_SUCCESS(status))
48
49 return status;
50
51 // 生成符号链接
52
53 status = IoCreateSymbolicLink(
54
55 &symb_link,
56
57 &device_name);
58
59 if(!NT_SUCCESS(status))
60
61 {
62
63 IoDeleteDevice(device);
64
65 return status;
66
67 }
68
69 // 设备生成之后,打开初始化完成标记
70
71 device->Flags &= ~DO_DEVICE_INITIALIZING;
72
73 return status;
74
75 }
这个驱动成功加载之后,生成名为“\Device\MyCDO”的设备。给这个设备生成了一个符号链接名字“\DosDevices\MyCDOSL”。应用层可以通过打开这个符号链接来打开设备,可以调用CreateFile就像打开文件一样打开。只是路径应该是“"\\.\ MyCDOSL”。“\\.\”意味后面是一个符号链接名(用户态下),而不是普通文件。请注意,由于C语言中斜杠要双写,所以正确的写法应该是“ \\\\.\\”。
3)设备的生成安全性限制
NTSTATUS
IoCreateDevice(
IN PDRIVER_OBJECT DriverObject,
IN ULONG DeviceExtensionSize,
IN PUNICODE_STRING DeviceName OPTIONAL,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Exclusive,
OUT PDEVICE_OBJECT *DeviceObject
);
第一个参数是生成这个设备的驱动对象。
第二个参数DeviceExtensionSize非常重要;由于分发函数中得到的总是设备的指针,当用户需要在每个设备上记录一些额外的信息(比如用于判断这个设备是哪个设备的信息、以及不同的实际设备所需要记录的实际信息,比如网卡上数据包的流量、过滤器所绑定真实设备指针等等),需要指定的设备扩展区内存的大小。如果DeviceExtensionSize设置为非0,IoCreateDevice会分配这个大小的内存在 DeviceObject->DeviceExtension中。以后用户就可以从根据DeviceObject-> DeviceExtension来获得这些预先保存的信息。
DeviceName如前例,是设备的名字。目前生成设备,请总是生成在\Device\目录下。所以前面写的名字是“\Device\MyCDO”。
最后生成的设备对象指针返回到DeviceObject中。
这种设备生成之后,必须有系统权限的用户才能打开(比如管理员)。为了保证交互的成功与安全性,应该用服务程序与之交互,但是有时候必须用普通用户打开设备。必须用IoCreateDeviceSecure。
NTSTATUS
IoCreateDeviceSecure(
IN PDRIVER_OBJECT DriverObject,
IN ULONG DeviceExtensionSize,
IN PUNICODE_STRING DeviceName OPTIONAL,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Exclusive,
IN PCUNICODE_STRING DefaultSDDLString,
IN LPCGUID DeviceClassGuid,
OUT PDEVICE_OBJECT *DeviceObject)
这个函数增加了两个参数(其他的没变)。一个是DefaultSDDLString,这是一个用于描述权限的字符串。字符串“D:P(A;;GA;;;WD)”将满足“人人皆可以打开”的需求。
http://msdn.microsoft.com/en-us/library/ff563667%28VS.85%29.aspx
http://msdn.microsoft.com/en-us/library/ff548407%28VS.85%29.aspx
另一个参数是一个设备的GUID,随机手写一个GUID。不要和其他设备的GUID冲突。
// 随机手写一个GUID
const GUID DECLSPEC_SELECTANY MYGUID_CLASS_MYCDO =
{0x26e0d1e0L, 0x8189, 0x12e0, {0x99,0x14, 0x08, 0x00, 0x22, 0x30, 0x19, 0x03}};
// 全用户可读写权限
UNICODE_STRING sddl =
RLT_CONSTANT_STRING(L"D:P(A;;GA;;;WD)");
// 生成设备
status = IoCreateDeviceSecure( DriverObject,
0,
&device_name,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&sddl,
(LPCGUID)&SFGUID_CLASS_MYCDO,
&device);
使用这个函数的时候,必须连接库wdmsec.lib。
4)符号链接的用户相关性
简单的符号链接(还有一种使用GUID的符号链接)总是命名在\DosDevices\之下。但是实际上这会有一些问题。
比较高级的Windows系统(哪个版本的操作系统很难讲,可能必须判定补丁号),符号链接也带有用户相关性。如果一个普通用户创建了符号链接 “\DosDevices\MyCDOSL”,那么,其实其他的用户是看不见这个符号链接的(除在DriverEntry中外,因为 DriverEntry总是在进程“System”中执行)。当前用户总是取决于当前启动当前进程的用户。
解决方案是:任何用户都可以生成全局符号链接,让所有其他用户都能看见。路径“\DosDevices\MyCDOSL”改为“\DosDevices\Global\MyCDOSL”即可。
但是在不支持符号链接用户相关性的系统上,生成“\DosDevices\Global\MyCDOSL”这样的符号链接是一种错误。为此必须先判断一下。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 UNICODE_STRING device_name;
2
3 UNICODE_STRING symbl_name;
4
5 if (IoIsWdmVersionAvailable(1, 0x10))
6
7 {
8
9 // 如果是支持符号链接用户相关性的版本的系统,用\DosDevices\Global.
10
11 RtlInitUnicodeString(&symbl_name, L"\\DosDevices\\Global\\SymbolicLinkName");
12
13 }
14
15 else
16
17 {
18
19 // 如果是不支持的,则用\DosDevices
20
21 RtlInitUnicodeString(&symbl, L"\\DosDevices\\SymbolicLinkName");
22
23 }
24
25 // 生成符号链接
26
27 IoCreateSymbolicLink(&symbl_name, &device_name);