IoCreateDevice设备的安全性限制
上一节的例子只是简单的例子。很多情况下那些代码会不起作用。为了避免读者在实际编程中遇到哪些特殊情况的困绕,下面详细说明生成设备和符号链接需要注意的地方。生成设备的函数原型如下:
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”。其他路径也是可以的,但是这在本书描述范围之外。
- DeviceType表示设备类型。目前的范例无所谓设备类型,所以填写FILE_DEVICE_UNKNOWN即可。
- DeviceCharacteristics目前请简单的填写0即可。
- Exclusive这个参数必须设置FALSE。文档没有做任何解释。
- 最后生成的设备对象指针返回到DeviceObject中。
这种设备生成之后,必须有系统权限的用户才能打开(比如管理员)。所以如果编程者写了一个普通的用户态的应用程序去打开这个设备进行交互,那么很多情况下可以(用管理员登录的时候)。但是偶尔又不行(用普通用户登录的时候)。结果困绕很久。其实是权限问题。
为了保证交互的成功与安全性,应该用服务程序与之交互。
但是依然有时候必须用普通用户打开设备。为了这个目的,设备必须是对所有的用户开放的。此时不能用IoCreateDevice。必须用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)”将满足“人人皆可以打开”的需求。
另一个参数是一个设备的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。