本文是对USBIPSTUB项目进行改造,添加了类过滤机制,提升了驱动对于USB设备的兼容性。
开源项目USB重定向地址:933K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6U0k6i4A6S2L8X3&6W2i4K6u0r3N6i4y4T1K9i4m8Q4x3X3c8%4K9h3^5`.
USBIPSTUB驱动模块分为两部分,分别是在本地设备端瘦客户端,还有远程机器端。瘦客户端驱动为STUB,远端驱动为VHCI以及子设备VHUB,虚拟出来的USB设备属于VHUB管辖。
USBIPSTUB瘦客户端驱动的逻辑:usbip.exe先主动将某个USB设备安装STUB驱动,后续usbipd.exe程序接收到USB重定向请求后,遍历系统中的STUB设备,根据id找到设备后,打开此设备通信。
usbip.exe程序主动为USB设备安装STUB驱动的过程称为bind,需要先在本机上安装USB/IP 测试证书,然后程序根据设备id修改对应的cat文件以及添加设备id到对应的inf文件,接着调用UpdateDriverForPlugAndPlayDevicesA(W)函数为该设备安装STUB驱动。在客户机器上安装测试证书可能会引发安全问题,如果不使用测试证书不希望动态生成cat文件和inf文件,那么就需要提前容纳尽可能多的设备id到inf文件,交给微软签名,生成cat文件。
由于是为特定vid(供应商id)和特定pid(产品id)安装STUB驱动,因此如果系统中存在两个同类型同厂商的usb设备,那么这两个设备都会安装STUB驱动,如果只需要重定向一个设备,那么另一个设备也会安装STUB驱动无法在本地使用了。
兼容性问题,需要在inf文件中包含尽可能多的设备id,每需要多添加一个设备id,需要修改inf文件后重新交给微软签名才能兼容信的USB设备。
将瘦客户端的USBIPSTUB驱动修改为类过滤驱动(需要提供AddDevice回调函数),当系统中存在USB设备(如USB Hub和部分USB设备)时操作系统会帮忙调用这个STUB驱动的AddDevice的回调函数。
使用instdrv工具安装STUB驱动,例如服务名称为usbip_stub
需要在框架上做出的改动如下:
在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{36fc9e60-c465-11cf-8056-444553540000}右侧添加多字符串值类型的数据,值名称为UpperFilters,这是固定的,这个值不能修改,值Value为usbip_stub 取决于你的驱动服务名称。
可以采用重启系统的方式,或者执行一段代码对系统上的Hub设备执行软插拔操作,来触发STUB类过滤驱动中的AddDevice回调函数。
下面的这段代码来自于USBDK开源项目,调用ResetDeviceByClass传入的GUID为GUID_DEVINTERFACE_USB_HOST_CONTROLLER。
上面代码需要在安装时如添加注册表后 执行一次,就会调用AddDevice回调函数。还有在卸载时先删除注册表内容再执行这个代码即可触发Hub设备的Remove回调例程,此时STUB驱动可以释放资源,后续需要停止驱动卸载驱动。
接下来是Attach Hub设备,AddDevice函数的部分代码:
在AddDevice中 调用了FilterDevice函数,之后就返回了STATUS_SUCCESS
在FilterDevice中流程如下:
1. 根据pdo查询一个链表,判断这个pdo是否已经被保存到了链表中,如果已经存在那么直接返回,表明已经记录下了这个设备不需要处理。
2. 根据pdo查询BusQueryDeviceID属性,如果deviceid满足如下条件之一那么就认为是hub,
(1)开头的字符串是USB\\ROOT_HUB、USB\\ROOT_HUB20、USB\\ROOT_HUB30、NUSB3\\ROOT_HUB30、IUSB3\\ROOT_HUB30 ,那么就是一个hub设备
(2) 开头是USB\\ 并且设备描述符的bDeviceClass字段是USB_DEVICE_CLASS_HUB,那么也是一个hub设备
3. 根据pdo查询BusQueryInstanceID属性,记录下instanceid
4. 如果deviceid开头是USB\\字符串,此时需要从另一个全局链表中判断当前的deviceid和instanceid是否保存在链表中,这个链表是需要重定向的设备链表。
5. 如果一个设备既不是hub设备(即不满足步骤2)也不是需要重定向的usb设备(即不满足步骤4),那么就认为这个设备不需要attach,不需要处理,直接返回。
接下来 创建一个设备attach到pdo设备栈上,并缓存设备描述符以及产品字符串信息(方便后续应用层程序枚举)。
然后将创建的设备信息以及pdo信息保存到全局链表中,如果是hub设备那么保存到链表即可,如果是usb设备,那么还需要获取usb设备的parent对象即usb设备是在哪个hub上,这个后续说明,将usb设备插入到全局链表时还需要对parent设备的数据结构进行调整例如对子设备数量+1,并插入到parent设备对应的child设备链表中,维护这样一个父子关系。
如果是需要重定向的usb设备,那么还需要调用USBD_CreateHandle函数获取设备的usbd句柄记录下来。
接下来是拦截Hub设备的请求,当MajorFunction为IRP_MJ_PNP,并且MinorFunction为IRP_MN_QUERY_DEVICE_RELATIONS时,如果类型如下:
BusRelations == irpstack->Parameters.QueryDeviceRelations.Type
那么先下发这个请求,等待请求完成后,如果返回成功,那么此时的irp->IoStatus.Information指向的是结构体PDEVICE_RELATIONS
typedef struct _DEVICE_RELATIONS {
ULONG Count;
_Field_size_(Count) PDEVICE_OBJECT Objects[1]; // variable length
} DEVICE_RELATIONS, *PDEVICE_RELATIONS;
这个结构体中保存的就是hub返回的子设备,此时需要完成如下两个步骤
(1)丢弃已经移除的usb设备,比如hub上原来插入了3个usb设备,此时只有2个,那么需要从保存的hub数据结构子设备中移除对应的pdo对象(子设备数目减1并且从子设备链表移除),以及从全局设备链表中移除这个pdo数据释放对应内存,如果是一个
(2)添加新插入的usb设备,如果这个是一个usb hub,那么先不处理,因为后续系统会调用AddDevice传入此pdo,如果这个是一个usb设备,比如hub上插入了一个新的usb设备,之前没有的usb设备(此处判断如果新插入的设备需要重定向,通过一个全局重定向的链表来判断,如果此pdo对应的deviceid和instanceid需要重定向,那么将此pdo对应的parent hub对象保存到全局链表中),如果这个usb设备不需要重定向,那么获取deviceid和instanceid后添加到全局链表中并修改parent对应的数据结构,如果需要重定向,那么手动调用此驱动的AddDevice回调函数传入此pdo(在AddDevice函数中会Attach此设备)。
完成hub上的IRP_MN_QUERY_DEVICE_RELATIONS的请求拦截后,我们现在已经Attach上了usb设备,Usb设备给上层系统传入设备id等信息,操作系统根据设备id信息为设备安装合适的驱动,如果要重定向usb设备,还需要为设备patch deviceid信息,这样系统就不会安装对应驱动,不会将这个设备识别为对应的usb设备,需要拦截usb设备的MinorFunction请求为IRP_MN_QUERY_ID,需要patch BusQueryDeviceID、BusQueryInstanceID、BusQueryHardwareIDs、BusQueryCompatibleIDs
还需要修改拦截MinorFunction为IRP_MN_QUERY_CAPABILITIES的请求
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-10-13 01:05
被0346954编辑
,原因: