0%

内核驱动中拦截USB设备接入(1)

简介

有时候我们想对某一类设备进行统一管理,比如常见的磁盘设备、卷设备、USB设备等等。
在WDM驱动模型中,就可以通过对某类设备,设置 上层过滤驱动下层过滤驱动 的方式来实现。

WDM驱动模型

WDM模型中设备和驱动程序的层次结构如下图所示:

层次结构

我们这里不讨论复杂的 WDM设备驱动 如何开发,只考虑过滤驱动逻辑相对简单。
添加某类设备的过滤驱动的方法,就是在对应设备类的注册表键下,创建 LowerFilters 值来表示
下层过滤驱动,创建 UpperFilters 值来表示 上层过滤驱动

1
HKLM\SYSTEM\CurrentControlSet\Control\Class\{36fc9e60-c465-11cf-8056-444553540000}

USB设备类 为例,假定我们过滤驱动的服务名为 MyUsbFltDrv ,在如上所示注册表路径下,创建
LowerFilters 值,并设定其内容为 MyUsbFltDrv ,注意该值为 REG_MULTI_SZ 类型。

注册表路径

创建完毕后,新接入USB设备时,操作系统就会启动我们的过滤驱动。
注意:假如过滤驱动启动失败,会导致IRP无法继续传递,从而所有新接入USB设备都无法识别。

USB设备类过滤

在WDM过滤驱动中,我们在 AddDevice 函数中附加过滤设备。USB设备的启动和停止都在
IRP_MJ_PNP 中处理,为了同时兼容XP和WIN7系统,我们还要处理 IRP_MJ_POWER 功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath)
{
ULONG Index = 0;
UNREFERENCED_PARAMETER(RegPath);
for (Index = 0; Index <= IRP_MJ_MAXIMUM_FUNCTION; Index++)
{
DriverObject->MajorFunction[Index] = DispatchCommon;
}
DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;
DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
DriverObject->DriverExtension->AddDevice = MyAddDevice;
return STATUS_SUCCESS;
} // 驱动入口函数

如下为我们关注的USB关键信息结构体定义,放入到每个设备的扩展内容中

1
#define EXALLOCATE(x) ExAllocatePoolWithTag(NonPagedPool, (x), 'MEM');
1
2
3
4
5
6
typedef struct _USB_INFO {
UCHAR iType; // 设备类型
USHORT idVendor; // VID信息
USHORT idProduct; // PID信息
WCHAR szSerial[128]; // 序列号
} USB_INFO, *PUSB_INFO;
1
2
3
4
5
typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT TargetDevice; // 附加的设备
UCHAR iSerialOffset; // 序列号偏移
USB_INFO UsbInfo; // USB关键信息
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

每个新设备接入时,都会触发 AddDevice 函数,我们在这里创建过滤设备,并进行附加绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
NTSTATUS MyAddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDevice)
{
PDEVICE_EXTENSION DevExt = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT FilterDevice = NULL;
// 创建过滤设备
Status = IoCreateDevice(
DriverObject, sizeof(DEVICE_EXTENSION),
NULL, PhysicalDevice->DeviceType,
PhysicalDevice->Characteristics, FALSE, &FilterDevice);
if (!NT_SUCCESS(Status)) return Status;
FilterDevice->Flags |= PhysicalDevice->Flags;
// 设备扩展处理
DevExt = (PDEVICE_EXTENSION)(FilterDevice->DeviceExtension);
RtlZeroMemory(DevExt, sizeof(DEVICE_EXTENSION));
// 绑定物理设备
Status = IoAttachDeviceToDeviceStackSafe(
FilterDevice, // 创建的过滤设备
PhysicalDevice, // 目标设备栈中的设备
&DevExt->TargetDevice); // 最终绑定的设备
if (!NT_SUCCESS(Status))
{
IoDeleteDevice(FilterDevice);
return Status;
}
FilterDevice->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}

DispatchCommon 中不做任何处理,直接转发到下层设备

1
2
3
4
5
6
7
8
NTSTATUS DispatchCommon(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PDEVICE_EXTENSION DevExt = NULL;
// 转发到下层设备
DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(DevExt->TargetDevice, Irp);
}

DispatchPower 中,针对不同的操作系统,使用的函数有所区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NTSTATUS DispatchPower(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PDEVICE_EXTENSION DevExt = NULL;
// 转发到下层设备
DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
#if (NTDDI_VERSION < NTDDI_VISTA)
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(DevExt->TargetDevice, Irp);
#else
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(DevExt->TargetDevice, Irp);
#endif
}

DispatchPnp 中,我们只关注子功能 IRP_MN_START_DEVICEIRP_MN_REMOVE_DEVICE 的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
NTSTATUS DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PDEVICE_EXTENSION DevExt = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT TargetDevice = NULL;
PIO_STACK_LOCATION IrpStack = NULL;
// 删除设备后,设备扩展内容可能会无效
DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
TargetDevice = DevExt->TargetDevice; // 保存下层设备
// 处理子功能
IrpStack = IoGetCurrentIrpStackLocation(Irp);
switch (IrpStack->MinorFunction)
{
case IRP_MN_START_DEVICE: // 设备启动
Status = GetUsbType(DeviceObject); // 获取设备类型
if (!NT_SUCCESS(Status)) break;
Status = GetUsbSerial(DeviceObject); // 获取序列号
if (!NT_SUCCESS(Status)) break;
// 可以发送给应用层,来自行决定如何处理USB信息
if (DevExt->UsbInfo.iType == 0x8) // 大容量存储类型
{
// 拦截本次IRP请求
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
break;
// case IRP_MN_QUERY_REMOVE_DEVICE: // 安全移除
// case IRP_MN_SURPRISE_REMOVAL: // 异常移除
// 安全移除和异常移除,最终都触发设备移除
case IRP_MN_REMOVE_DEVICE: // 设备移除
IoDetachDevice(DevExt->TargetDevice); // 解绑设备
IoDeleteDevice(DeviceObject); // 删除过滤设备
break;
}
// 转发到下层设备
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(TargetDevice, Irp);
}

如何查询USB信息

查询USB信息,需要向 总线设备驱动 发送 URB 请求,所以在 下层过滤驱动 处进行处理。
具体查询步骤为,首先申请一段 URB 空间,初始化 URB 为查询相关信息的请求,
然后创建一个 IRP 请求,把 URB 绑定到 IRP 中,发送到 总线控制设备 进行查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
NTSTATUS UsbCallUSBDI(PDEVICE_OBJECT DeviceObject, PURB Urb)
{
PIRP Irp = NULL;
KEVENT Event = { 0 };
NTSTATUS Status = STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatus = { 0 };
PIO_STACK_LOCATION IrpStack = NULL;
PDEVICE_EXTENSION DevExt = NULL;
// 构建IRP请求
KeInitializeEvent(&Event, NotificationEvent, FALSE);
DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
Irp = IoBuildDeviceIoControlRequest(
IOCTL_INTERNAL_USB_SUBMIT_URB, DevExt->TargetDevice,
NULL, 0, NULL, 0, TRUE, &Event, &IoStatus);
if (Irp == NULL) Status = STATUS_UNSUCCESSFUL;
// 设置URB并发送IRP请求
IrpStack = IoGetNextIrpStackLocation(Irp);
IrpStack->Parameters.Others.Argument1 = Urb;
Status = IoCallDriver(DevExt->TargetDevice, Irp);
// 调用IoCallDriver时,其实并没有结束本函数
if (Status == STATUS_PENDING)
{
Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatus.Status;
}
return Status;
} // 构建IRP并发送

如下为获取 设备描述符 信息的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
NTSTATUS UsbGetDeviceDescriptor(
PDEVICE_OBJECT DeviceObject,
PUSB_DEVICE_DESCRIPTOR *pDeviceDescriptor)
{
PURB Urb = NULL;
NTSTATUS Status = STATUS_SUCCESS;
// 申请URB内存空间
Urb = (PURB)EXALLOCATE(sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
if (Urb == NULL) return STATUS_UNSUCCESSFUL;
// 申请Device Descriptor信息内存空间
*pDeviceDescriptor = (PUSB_DEVICE_DESCRIPTOR)EXALLOCATE(
sizeof(USB_DEVICE_DESCRIPTOR));
if (*pDeviceDescriptor == NULL)
{
ExFreePool(Urb);
return STATUS_UNSUCCESSFUL;
}
// 构造URB请求,并获取设备描述信息
UsbBuildGetDescriptorRequest(
Urb, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, *pDeviceDescriptor,
NULL, sizeof(USB_DEVICE_DESCRIPTOR), NULL);
// 发送内部IRP请求
Status = UsbCallUSBDI(DeviceObject, Urb);
ExFreePool(Urb); // 先释放URB空间
if (!NT_SUCCESS(Status)) return Status;
return STATUS_SUCCESS;
}

如下为获取 配置描述符 信息的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
NTSTATUS UsbGetConfigDescriptor(
PDEVICE_OBJECT DeviceObject,
PUSB_CONFIGURATION_DESCRIPTOR *pConfigDescriptor)
{
PURB Urb = NULL;
USHORT wTotalLength = 0;
NTSTATUS Status = STATUS_SUCCESS;
// 申请URB内存空间
Urb = (PURB)EXALLOCATE(sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
if (Urb == NULL) return STATUS_UNSUCCESSFUL;
// 申请Config Descriptor内存空间
*pConfigDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)EXALLOCATE(
sizeof(USB_CONFIGURATION_DESCRIPTOR));
if (*pConfigDescriptor == NULL)
{
ExFreePool(Urb);
return STATUS_UNSUCCESSFUL;
}
// 构造URB请求,并获取配置描述信息
UsbBuildGetDescriptorRequest(
Urb, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, *pConfigDescriptor,
NULL, sizeof(USB_CONFIGURATION_DESCRIPTOR), NULL);
// 发送内部IRP请求
Status = UsbCallUSBDI(DeviceObject, Urb);
if (!NT_SUCCESS(Status))
{
ExFreePool(Urb);
return Status;
}
// 第2次申请Config Descriptor信息内存空间
wTotalLength = (*pConfigDescriptor)->wTotalLength;
ExFreePool(*pConfigDescriptor);
*pConfigDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)EXALLOCATE(wTotalLength);
if (*pConfigDescriptor == NULL)
{
ExFreePool(Urb);
return STATUS_UNSUCCESSFUL;
}
// 第2次构造URB请求,并获取配置描述信息
UsbBuildGetDescriptorRequest(
Urb, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, *pConfigDescriptor,
NULL, wTotalLength, NULL);
// 第2次发送内部IRP请求
Status = UsbCallUSBDI(DeviceObject, Urb);
ExFreePool(Urb); // 先释放URB空间
if (!NT_SUCCESS(Status)) return Status;
return STATUS_SUCCESS;
}

通过 设备描述符配置描述符 可以得到该设备的VID、PID和设备类型信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

NTSTATUS GetUsbType(PDEVICE_OBJECT DeviceObject)
{
PDEVICE_EXTENSION DevExt = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PUSB_DEVICE_DESCRIPTOR DeviceDescriptor = NULL;
PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor = NULL;
PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptor = NULL;
DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
do
{
// 获取Device Descriptor信息
Status = UsbGetDeviceDescriptor(DeviceObject, &DeviceDescriptor);
if (!NT_SUCCESS(Status)) break;
// 保存关键的USB信息
DevExt->UsbInfo.idVendor = DeviceDescriptor->idVendor; // VID信息
DevExt->UsbInfo.idProduct = DeviceDescriptor->idProduct; // PID信息
DevExt->iSerialOffice = DeviceDescriptor->iSerialNumber; // 序列号偏移
// 获取Configuration Descriptor信息(有可能不存在)
Status = UsbGetConfigDescriptor(DeviceObject, &ConfigDescriptor);
if (!NT_SUCCESS(Status)) break;
// 分析Interface Descriptor信息
InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
ConfigDescriptor, ConfigDescriptor,
-1, -1, 0x8, -1, -1); // 查询大容量存储类型
if (InterfaceDescriptor != NULL)
{
DevExt->UsbInfo.iType = 0x8;
break;
}
InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
ConfigDescriptor, ConfigDescriptor,
-1, -1, 0xFF, -1, -1); // 查询自定义设备类型
if (InterfaceDescriptor != NULL)
{
DevExt->UsbInfo.iType = 0xFF;
break;
}
Status = STATUS_SUCCESS;
} while (0);
// 释放申请的空间
if (DeviceDescriptor != NULL) ExFreePool(DeviceDescriptor);
if (ConfigDescriptor != NULL) ExFreePool(ConfigDescriptor);
return Status;
}

如下为获取 字符串描述符 信息的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
NTSTATUS UsbGetStringDescriptor(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR DescriptorIndex,
IN USHORT LanguageID,
OUT PUSB_STRING_DESCRIPTOR StringDescriptor,
IN UCHAR StringSize)
{
PURB Urb = NULL;
NTSTATUS Status = STATUS_SUCCESS;;
// 判断参数是否是空指针
if ((StringDescriptor == NULL) || (StringSize == 0))
return STATUS_UNSUCCESSFUL;
// 申请URB内存空间
Urb = (PURB)EXALLOCATE(sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
if (Urb == NULL) STATUS_UNSUCCESSFUL;
// 构造URB请求,并获取设备描述信息
UsbBuildGetDescriptorRequest(
Urb, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_STRING_DESCRIPTOR_TYPE, DescriptorIndex, LanguageID,
StringDescriptor, NULL, StringSize, NULL);
// 发送内部IRP请求
Status = UsbCallUSBDI(DeviceObject, Urb);
ExFreePool(Urb); // 先释放URB空间
if (!NT_SUCCESS(Status)) return Status;
return STATUS_SUCCESS;
}

字符串描述符 中可能存在 多种语言 字符串,我们这里只获取 第1种语言 的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
NTSTATUS GetUsbString(PDEVICE_OBJECT DeviceObject)
{
PDEVICE_EXTENSION DevExt = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PUSB_STRING_DESCRIPTOR StringLanguage = NULL;
PUSB_STRING_DESCRIPTOR StringDescriptor = NULL;
USHORT NumLanguageIDs = 0; // 语言的数量
PUSHORT LanguageIDs = NULL; // 语言ID的指针
do
{
DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
// 首先检验序列号偏移是否为空
if (DevExt->iSerialOffset == 0) break;
// 申请String Descriptor的语言列表内存空间
StringLanguage = (PUSB_STRING_DESCRIPTOR)EXALLOCATE(256);
if (StringLanguage == NULL) break;
// 获取String Descriptor的语言列表
Status = UsbGetStringDescriptor(
DeviceObject, 0, 0, StringLanguage, 256);
if (!NT_SUCCESS(Status)) break;
// 检测语言列表信息长度
if (StringLanguage->bLength < 2) break;
// 语言ID的换算处理
NumLanguageIDs = (StringLanguage->bLength - 2) / 2; // 数量
LanguageIDs = (PUSHORT)&(StringLanguage->bString[0]); // 地址
// 申请String Descriptor的内存空间
StringDescriptor = (PUSB_STRING_DESCRIPTOR)EXALLOCATE(256);
if (StringDescriptor == NULL) break;
// 循环语言ID数量,去读取USB的序列号(这里只1次)
Status = UsbGetStringDescriptor(
DeviceObject, DevExt->iSerialOffset,
*LanguageIDs, StringDescriptor, 256);
if (!NT_SUCCESS(Status)) break;
// 复制序列号(需要减2个字节,bLength指总长度,而不是字符串长度)
if (StringDescriptor->bLength > 2)
{
RtlCopyMemory(DevExt->UsbInfo.szSerial,
StringDescriptor->bString, StringDescriptor->bLength - 2);
}
Status = STATUS_SUCCESS;
} while (0);
// 释放申请的空间
if (StringLanguage != NULL) ExFreePool(StringLanguage);
if (StringDescriptor != NULL) ExFreePool(StringDescriptor);
return Status;
}