首页
社区
课程
招聘
[原创] 实战解析 USB HID 协议
发表于: 2天前 325

[原创] 实战解析 USB HID 协议

2天前
325

最近在深入研究 USB HID 协议, 翻了不少教材和Spec文档,发现很多内容写得过于”照本宣科”----堆砌了一堆 Usage Page/Collection 的定义,读完却依然不知道这串 Hex 数据到底代表什么.


我认为, 理解 HID 最好的方式不是死磕枯燥的定义, 而是结合工具看疗效. 我将抛开晦涩的理论, 以最常见的 USB Keyboard 为例, 通过 Wireshark 抓包实战, 从 Report Descriptor 的解析入手, 一步步拆解 Input Report Raw Data. 当你亲手利用 Descriptor 将那串 0x00 00 20... 的 16 进制代码精准映射到具体的按键动作时, 那些原本枯燥的协议定义也就瞬间”活”过来了. 希望这篇基于实战的总结, 能帮到同样在啃 HID 协议的读者.

  1. HID Report Descriptor


从一个简单的Mouse HID “Report Descriptor”开始. 这段Report Descriptor是微软HID Descriptor Tool提供的一个样例:

From:MSDEV\Projects\test\Mouse.hid


再来看一个实际的Mouse Report Descriptor, 一个多功能鼠标(USB Node下有多个HID Node)

(DevMgr view)


Winddk HidClient.exe会列出大量相同VID&PID的设备,需要根据UsagePage & Usage来确定当前操作的是哪个HID设备。


对于此例中的USB Mouse,可以通过Wireshark的USB Capture功能获得其Hid Report Descriptor(须拔插USB发射器):

读者可下载附件中的usbkeyboardInput.pcapng文件, 打开后在Display filter输入usbhid(只显示usbhid协议), 即可得到HID Report Descriptor。(这是 USB Control Transfer 的一部分)


USB MouseHID Report Descriptor中有5UsagePage/Usage/Collection & End Collection,  可能每一组对应DevMgr/USB Input device下的DevNode(另外设备命名也相似)

相较而言,USB  keyboardHID Report Descriptor及其DevNode则简洁很多, 只有一组:

另外, 仔细观察KeyboardMouseHID Report Descriptor, 除了UsagePage/Usage/Collection & End Collection组数数量差异, Collection & End Collection中还有一个细小的差异:


USB KeyboardHIDReport Descriptor中只有一组UsagePage/Usage/Collection & End Collection, 基础款USB Keyboard CollectionEnd Collection之间通常没有Report ID;

USB MouseHIDReport Descriptor中有5UsagePage/Usage/Collection & End Collection, 每组的CollectionEnd Collection之间中都有单独的Report ID.  Report ID的作用将在后面的章节中阐述.

 

 

 2. HID Report Descriptor的作用----解析HID Report

 

引用<HID跨接口设计与开发>书中(第九章P84)的一段话: 报告描述符用于描述报告的数据格式与数值定义, 一个实例的所有报告信息都包含在同一个报告描述符内.

下图是书中HID MouseInput Report的示例。鼠标会以此格式为模板, 当发生按键/移动之类的事件, FW会将按键状态/位移相对值填入该模板, 形成一个3字节的Input Report, 发送给OS. 


OS收到Input Report, 以此模板从Input Report中解析各字段, 做出相应的动作. 这段话引出了HID Report Descriptor的模板化的作用:  device,  deviceHID Report Descriptor的规定, 生成Input Report; OS, OSHID Report Descriptor的规定, 解读Input Report中各个字段的含义.

 

下图, 是书中表9-2 HID Mouse  Input Report对应的HID Report Descriptor:


以上仅是书本中的例子(书是照本宣科的烂书, 远不如问AI有用), 以现实中USB HID Mouse移动引起的HID Input report为例: USBCap抓到的HID Data(Raw data)0x0201000000000000(下图中红框部分); Wireshark根据USB Mouse汇报的HID Report Descriptor, 解析了Raw data各个字段, 并最终呈现给分析者(下图绿框部分).

本文的意图亦是如此:根据USB device 汇报的HID Report Descriptor, 解析Raw Data中的含义(诚然, USB HID device尚可通过Wireshark捕获, Wireshark解析将止步与其他使用HID协议的设备—I2C Touch Panel/I2C Touch Pad).



2.1.  USB HID Keyboard的输入报告(Input Report)一例

 

用键盘在Wireshark Display Filter输入字符1~8a~k, 见下图:


27, Wireshark解析的HID Data如下:

Frame 27:

HID Data: 0000200000000000

 

本节结合USB KeyboardHID Report Descriptor分析这串Raw data其对应的按键操作.


2.1.1.   解析HID Report Descriptor的步骤

解析HID Report Descriptor, 就是构造/解释HID Report模板的过程.

a.       HID Report DescriptorUsagePage/Usage/Collection & End Collection进行分组, 每个分组代表一个DevNode(或者说一个功能), 因此第一步是从多个UsagePage/Usage/Collection & End Collection分组中挑选出一组UsagePage/Usage/Collection & End Collection, Collection & End Collection之间的内容进行分析;

(先选蓝框,再选绿框)


b.       Collection & End Collection之间又有3Main Item, 分别为Input Item /Output Item /Feature Item. 把相同Main Item合并到一起, 描述一类Report. 如所有的Input()合到一起, 用于描述Input ReportRaw Data; 所有的Output()合到一起, 用于描述Output Report Raw Data.

 

附注: Input Report是设备发送给OS的数据. 对于HID Keyboard按下/抬起数字键/字母键, 属于Input Report;

Output ReportOS发送给设备的数据. 对于HID Keyboard,按下/抬起CapsLocks/NumLock/ScrollLock(这类OS指示键盘指示灯发生变化), 属于Output Report;

 

除了Main Item, Collection & End Collection之间还有2Global Item: Report SizeReport Count.

Report Size: 字段在Report中数据占了多少bit;


Report Count: 上述字段在Report中的数量. Report Size* Report Count=字段在Report中总 Bit

Report Descriptor 解析为Report, 有个简单的规则: 从上往下解析Report Descriptor, Report Size/Report Count作为字段(Report Count=1)/字段数组(Report Count>1)的开始;  遇到Main Item, 作为字段(Report Count=1)/字段数组(Report Count>1)的结束 (其他内容如Usage Page/Usage Min&Usage Max当做描述性语句, 暂时忽略).

以下USB Keyboard Report Descriptor是上述结论的示例. 红框中的内容可用于生成Input Report各个字段; 而黄框中的内容可用于生成Output Report各个字段.


根据上图可知每次按键(普通数字键/字母键)将产生一个8字节的Input Report:

Byte7
Byte6Byte5Byte4Byte3Byte2Byte1Byte0
6*Input(Data, Array, Abs)1*Input(Const, Var, Abs)1*Input(Data, Var, Abs)

1. Input Report中各偏移

 

附注: Byte1 上的Input(Const, Var, Abs)前面没有Report SizeReport Count, 因此它将沿用Byte0Report SizeReport Count.

 

c.       解读各个字段含义. 迄今为止, 已解读的Main Item/Global Item可以规划Report大小/字段范围, 而字段的含义则需要结合Global Item中的Usage Page以及Local Item中的Usage Minimum/Usage Maximum来解读.



解读表1). Byte 0: 用来定义修饰键, 每一个修饰键用一个bit表示 (对照SPEC逐个Item翻译意义不大, 直接AI…)


ItemNote
Usage Page(Keyboard)Usage Minimum~ Usage Maximum使用了Usage ID 0xE0~0xE7.它们是Keyboard/Keypad Usage Page(0x07) 下定义的标准编号
Usage Minimum(0xE0)Left Control
Usage Maximum(0xE7)Right GUI (Win/Cmd)
Logical Minimum(0)按键松开
Logical Maximum(1)按键按下



解读表1). Byte 1.  根据Boot Keyboard Protocol 定义, Byte 1被称为 Reserved Byte (保留字节) 它的作用主要是为了字节对齐.

 

解读表1). Byte 2~7: 用来定义普通按键, 不过这次使用一个Byte来描述165个普通按键的状态

ItemNote
Usage Page(Keyboard)Usage Minimum~ Usage Maximum使用了Usage ID 0x00~0xA4. 它们是Keyboard/Keypad Usage Page (0x07) 下定义的标准编号
Usage Minimum(0x00)N/A
Usage Maximum(0xA4)N/A
Logical Minimum(0)
Logical Maximum(164)

2.1.2.   Hid Keyboard Input Report实例

结合以上a),b),c)三点, 我相信读者已经可以解析USB HID keyboardInput Report. 仍然以上文附带的usbkeyboardInput.pcapng文件为例, 来解析其CaptureInput Report.

 

(分析前, 还需要引入一个新话题: 我的 USB 接收器是一个 USB Composite Device (复合设备), 即在同一个 USB 设备下包含多个 Interface, Wirshark中怎么区分哪些FrameHID Data是发送给键盘的? 哪些是发送给鼠标的? 这涉及到USB Device Endpoint的概念, 我打算开个番外篇介绍了. 有个简单的区分方法是根据HID Report Descriptor计算出来的长度差异作依据. HID data长度为8字节,Keyboard产生的数据; HID data长度为3字节, 大概率是Mouse产生的数据. 另外在附件usbkeyboardInput.pcapng, usb.src==”2.4.1”对应Keyboard; usb.src==”2.4.2”对应Mouse)

 

a.       解析27# / 29# FrameHID Data含义

Frame 27# Byte 0=0x00 修饰键处于抬起状态, Byte 2=0x20, UsageID=0x20处于按下状态. 查询后得知是Page=0x07, UsageID=0x20对应按键3按下;


Frame 29# Byte 0=0x00 修饰键处于抬起状态, Byte 2~7=0x00, 所有的普通按键都处于按下状态. 结合Frame #27可知按键3抬起;

 

b.       解析31# / 33# FrameHID Data含义


Frame 31# Byte 0=0x00 修饰键处于抬起状态, Byte 2=0x21, UsageID=0x21处于按下状态. 查询后得知是Page=0x07, UsageID=0x21对应按键4按下;


Frame 33# Byte 0=0x00 修饰键处于抬起状态, Byte 2~7=0x00, 所有的普通按键都处于按下状态. 结合Frame #31可知按键4抬起;


c.       解析55# / 57# FrameHID Data含义

Frame 55# Byte 0=0x00 修饰键处于抬起状态, Byte 2=0x16, UsageID=0x16处于按下状态. 查询后得知是Page=0x07, UsageID=0x16对应按键s按下;


Frame 57# Byte 0=0x00 修饰键处于抬起状态, Byte 2~7=0x00, 所有的普通按键都处于按下状态. 结合Frame #57可知按键s抬起;

d.       额外实验Shift+s/Ctrl+d

(附件usbkeyboardInput.pcapng没有包含这2个动作, 忘了记录, 留给读者自己实验了)


Frame 139# Byte 0=0x02 修饰键LeftShift处于按下状态, Byte 2=0x16, UsageID=0x16处于按下状态. 查询后得知是Page=0x07, UsageID=0x16对应按键LeftShift+s按下;

Frame 159# Byte 0=0x01 修饰键LeftCtrl处于按下状态, Byte 2=0x07, UsageID=0x07处于按下状态. 查询后得知是Page=0x07, UsageID=0x07对应按键LeftCtrl+d按下;



这篇文章看看反响, 如果评优了, 计划更新3篇番外篇: USB Mouse Hid input report / I2C touch Pad Hid input report / USB endpoint


[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 18小时前 被hyjxiaobia编辑 ,原因:
上传的附件:
收藏
免费 2
支持
分享
最新回复 (2)
雪    币: 2790
活跃值: (5592)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
2
学习了。之前还是做模拟锁驱动时研究过点,但是没有向你一样系统性整理。
9小时前
0
雪    币: 802
活跃值: (4498)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
3
我正好是遇到i2c touch pad有问题,它底层也是hid协议,所以看了一遍
7小时前
0
游客
登录 | 注册 方可回帖
返回