如何在串流云桌面中实现外设的远程控制输入——HID人机接口设备驱动简介(键鼠、手写板等)

虚拟HID设备

对于HID设备的虚拟化,主要包括:

  1. 虚拟化鼠标。
  2. 虚拟化键盘。
  3. 虚拟化触摸板(手写笔)。

本文我们来探讨上述HID设备虚拟化的基本实现原理(关于HID设备的虚拟化,需要了解HID描述符相关基础知识,可以参见另外一篇文章:关于HID描述符)。

1. HID架构

在Windows下面HID的基本架构如下:
在这里插入图片描述

HID Clients包括有驱动,服务以及应用程序,他们都和HIDClass.sys进行通信;通常来说每一次通信的对象都是一个指定的设备(例如键盘,鼠标等)。通常通过hardware ID或者HID Collection来标识一个设备,通过通信遵循如下规则:

  • 用户模式驱动或者应用程序通过HIDClass提供的HidD_xxx来获取HID Collection。
  • 内核驱动或者用户驱动以及应用程序通过HID分析支持程序HidP_xxx,内核驱动程序使用HID 类驱动的IOCTL来处理HID报表。
ModeDriversApplications
User ModeHidD_XxxHidP_Xxx
Kernel ModeHidD_Xxx
IOCTL_HID_xxx
N/A

HID Transport是具体的硬件设备,我们HID Transport向上提供硬件相关信息,HID Client从来不主动和HID Transport直接通信,都是通过HIDClass.sys来进行中转的,这样给我们HID Client和HID Transport的开发都带来了非常大的便利;这也是Windows MiniPort驱动实现的基本框架。

对于HID的设备的虚拟,我们主要是需要实现HID Transport。

2. HID接口函数

Windows提供了如下的HID函数接口来或者和操作HID设备:

  1. Device Discovery and Setup。
  2. Data Movement。
  3. Report Creation and Interpretation。

2.1 Device Discovery and Setup

API描述
HidD_GetAttributes请求获得HID设备的厂商ID、产品ID和版本号
HidD_GetHidGuid请求获得HID设备的GUID
HidD_GetIndexString请求获得由索引识别的字符串
HidD_GetManufactureString请求获得设备制造商字符串
HidD_GetPhysicalDescriptor请求获得设备实体字符串
HidD_GetPreparsedData请求获得与设备能力信息相关的缓冲区的代号
HidD_GetProductString请求获得产品字符串
HidD_GetSerialNumberString请求获得产品序列号字符串
HidD_GetNumInputBuffer获得驱动程序用于存储输入报表的环形缓冲区的大小,默认值是8
HidD_SetNumInputBuffer设置驱动程序用于存储输入报表的环形缓冲区的大小

2.2 Data Movement

API描述
HidD_GetInputReport从设备读取一个特征报表
HidD_SetFeature向设备传送一个特征报表
HidD_SetOutputReport向设备传送输出报表
WriteFile向设备传送输出报表
ReadFile从设备读取输入报表

2.3 Report Creation and Interpretation

API描述
HidP_GetButtonCaps请求获得HID报表中所有按钮的能力
HidP_GetButtons从设备读取包含每个按下的按钮的用法(Usage)的缓冲区的指针,该请求可以设定一个Usage Page
HidP_GetButtonEx从设备读取包含每个按下的按钮的Usage和Usage Page的缓冲区的指针
HidP_GetCaps请求获得用于描述设备能力的结构的指针
HidP_GetLinkCollectionNotes请求获得描述在顶层集合中的连接集合(Link Collection)关系的结构的数组
HidP_GetSpecificButtonCaps请求获得报表中按钮的能力,该请求可以设定一个Usage Page、Usage或是Link Collection
HidP_GetSpecificValueCaps请求获得报表中数值的能力,该请求可以设定一个Usage Page、Usage或是Link Collection
HidP_GetValueCaps请求获得 HID 报表中所有数值的能力
HidP_MaxUsageListLength请求获得 HID 报表中可以回传的按钮的最大数目,该请求可以设定一个Usage Page
HidP_UsageListDifference比较两个按钮列表,并且求出在一个列表中设定而在另一个列表中没有设定的按钮
HidP_GetScaledUsageValue从设备读取一个已经经过比例因子调整的有符号数值
HidP_GetUsageValue从设备读取一个指向数值的指针
HidP_GetUsageValueArray从设备读取包含多个数据项的Usage的数据
HidP_SetButtons向设备传送设置按钮的数据
HidP_SetScaledUsageValue将一个实际数值转换成设备使用的逻辑数值,并将其插入到报表中
HidP_SetUsageValue向设备传送数据
HidP_SetUsageValueArray向设备传送包含多个数据项的Usage的数据

3. 虚拟设备

3.1 HidRegisterMinidriver

对于一个HID的Miniport硬件驱动来说,都是通过HidRegisterMinidriver函数来完成注册的,这个函数是HIDClass.sys提供,也就是说HIDClass.sys提供了框架,给我们MiniPort驱动的开发。

对于HidRegisterMinidriver工作大致可以总结为如下:

  1. IoAllocateDriverObjectExtension创建驱动的上下文HIDCLASS_DRIVER_EXTENSION
  2. 保存MiniPort驱动的相关信息到HIDCLASS_DRIVER_EXTENSION结构中,如下:
RtlCopyMemory(hidDriverExtension->MajorFunction,
    minidriverObject->MajorFunction,sizeof( PDRIVER_DISPATCH ) * (IRP_MJ_MAXIMUM_FUNCTION + 1) );

minidriverObject->DriverUnload = HidpDriverUnload;

hidDriverExtension->AddDevice = driverExtension->AddDevice;

3.2 IRP_MJ_INTERNAL_DEVICE_CONTROL

对于HID的虚拟硬件设备,对上层实现的接口都是通过IRP_MJ_INTERNAL_DEVICE_CONTROL来提供的,HID需要实现的相关IOCTL如下:

//
// Internal IOCTLs for the class/mini driver interface.
//

#define IOCTL_HID_GET_DEVICE_DESCRIPTOR             HID_CTL_CODE(0)
#define IOCTL_HID_GET_REPORT_DESCRIPTOR             HID_CTL_CODE(1)
#define IOCTL_HID_READ_REPORT                       HID_CTL_CODE(2)
#define IOCTL_HID_WRITE_REPORT                      HID_CTL_CODE(3)
#define IOCTL_HID_GET_STRING                        HID_CTL_CODE(4)
#define IOCTL_HID_ACTIVATE_DEVICE                   HID_CTL_CODE(7)
#define IOCTL_HID_DEACTIVATE_DEVICE                 HID_CTL_CODE(8)
#define IOCTL_HID_GET_DEVICE_ATTRIBUTES             HID_CTL_CODE(9)
#define IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST    HID_CTL_CODE(10)

我们知道HID最重要的两个描述符是:

  1. HID设备描述符。HID设备描述符通过IOCTL_HID_GET_DEVICE_DESCRIPTOR向上层提供。
  2. HID报表描述符。HID报表描述符通过IOCTL_HID_GET_REPORT_DESCRIPTOR向上层提供。

其他操作包括IOCTL_HID_READ_REPORT实现用来读取报表描述符,IOCTL_HID_WRITE_REPORT用来写入报表描述符。

这里我们需要定义两个描述符,例如可以定义如下:

HID_REPORT_DESCRIPTOR           DefaultReportDescriptor[] = {
    0x06,0x00, 0xFF,                // USAGE_PAGE (Vender Defined Usage Page)     
    0x09,0x01,                          // USAGE (Vendor Usage 0x01)      
    0xA1,0x01,                           // COLLECTION (Application)        
    0x85,CONTROL_FEATURE_REPORT_ID,           // REPORT_ID (1)                      
    0x09,0x01,                          // USAGE (Vendor Usage 0x01)              
    0x15,0x00,                          // LOGICAL_MINIMUM(0)                   
    0x26,0xff, 0x00,                // LOGICAL_MAXIMUM(255)               
    0x75,0x08,                          // REPORT_SIZE (0x08)                     
    0x95,0x01,                          // REPORT_COUNT (0x01)                    
    0xB1,0x00,                          // FEATURE (Data,Ary,Abs)
    0x09,0x01,                          // USAGE (Vendor Usage 0x01)              
    0x75,0x08,                          // REPORT_SIZE (0x08)                     
    0x95,INPUT_REPORT_BYTES,           // REPORT_COUNT (0x01)                    
    0x81,0x00,                      // INPUT (Data,Ary,Abs)
    0xC0                                // END_COLLECTION                       

};

HID_DESCRIPTOR              DefaultHidDescriptor = {
    0x09,   // length of HID descriptor
    0x21,   // descriptor type == HID  0x21
    0x0100, // hid spec release
    0x00,   // country code == Not Specified
    0x01,   // number of HID class descriptors
    { 0x22,   // report descriptor type 0x22
    sizeof(DefaultReportDescriptor) }  // total length of report descriptor
};

3.3 效果

通过实现相关的HID硬件特性之后,我们可以虚拟化如下HID设备:

在这里插入图片描述

并且我们可以直接操作虚拟化的驱动来模拟各种HID设备信息,如下我们利用虚拟HID键盘向系统开始菜单输入一个Hello:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cr9X42xM-1689647709368)(assets/images/6ed7b51b8b100367f5f1552b31f4dec7e5ea5e924d8d853f4411b7d4d331f008.gif)]

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值