HID键盘设备数据抓包分析实践
本文使用 Bus Hound 工具对 USB HID 设备数据包进行分析,并结合官方手册及网上文章进行整理。
在进行数据分析前,我们先回顾一下USB相关的基础知识。
USB描述符
USB 主机是通过各种描述符来识别设备的,有设备描述符,接口描述符,端点描述符,字符描述符,报告描述符(HID)等。
和普通的USB设备一样,USB主机获取设备的描述符一般先是设备描述符,然后是配置描述符(包括接口描述符和端点描述符),而对于HID设备,还需要获取HID描述符和报表描述符。
USB HID 设备是通过报告来传送数据的,报告有:输入报告、输出报告、特性报告。
- 输入报告:是设备发送给主机的,例如 usb鼠标将鼠标移动和鼠标点击的信息返回给电脑,键盘将按键数据返回给电脑。输入报告是通过中断输入端点输入的。
- 输出报告:是主机发送给USB设备的,例如键盘上的数字键盘锁定灯和大写字母锁定灯等。报告是一个数据包,里面包含的是所要传送的数据。
- 报告描述符:是描述一个报告以及报告里面的数据是用来干什么的。通过它,USB 主机可以分析出报告里面的数据所要表达的意思。
HID设备相关的描述符有:
USB设备类别bDeviceClass和接口类别:
- 0x00:接口描述符中提供类的值
- 0x02:通信类
- 0x03:HID类
…
HID接口描述符中bInterfaceProtocol:
- 0:NONE
- 1:键盘
- 2:鼠标
- 3~255:保留
HID 报告类别:
- 1:输入报告
- 2:输出报告
- 3:特征报告(feature report)
- 04-ff:保留
使用Bus Hound进行HID键盘设备抓包分析
1.首先Bus Hound的设备树静表中选择需要抓包的设备。这里我们可以不选任何设备。(将设备树前的选中按钮全取消掉,然后在左下角选中 Auto Select hot plugged devices,这样当新的设备插入后,会自动选中)
2.在抓包配置界面中选择对抓包进行参数配置。
3.然后在Capture界面中击 Run按钮开始抓包。这时我们将键盘从电脑中拨出并再次插入。
这时BusHound会自动选中我们新插入的键盘,并对其进行抓包。
分析Bus Hound抓到的数据
Device - Device ID (followed by the endpoint for USB devices)
(31) USB Composite Device
(34) USB Input Device
(35) USB Input Device
(36) HID Keyboard Device
(37) HID-compliant consumer control device
(38) HID Keyboard Device
(39) HID Keyboard Device
Phase - Phase Type
CTL USB control transfer
IN Data in transfer
OUT Data out transfer
Data - Hex dump of the data transferred
Descr - Description of the phase
Cmd... - Position in the captured data
Device Phase Data Description Cmd.Phase.Ofs(rep)
------ ----- ------------------------ ---------------- ------------------
31.0 CTL 80 06 00 01 00 00 12 00 GET DESCRIPTOR 1.1.0
31.0 IN 12 01 10 01 00 00 00 08 ........ 1.2.0
6d 02 02 00 01 01 00 00 m....... 1.2.8
00 01 .. 1.2.16
第一部分是描述保存的数据格式,第二部分是真正的数据。第2列 Phase 表示数据的类型,最后一列表示数据的序号(如1.x表示第1次发送/返回的信息,2.x表示第2次,以此类推)。为方便分析 USB 请求流程,下面从开始的数据包进行分析——亦从 USB 设备插入到 USB 主机开始(最后一列从1.x开始)。
设备描述符
一个USB设备只有一个设备描述符。设备描述符主要记录的信息有:设备所使用的USB协议版本号、设备类型、端点0的最大包大小、厂商ID(VID,由 USB 组织分配)和产品ID(PID)、设备版本号、厂商字符串索引、产品字符串索引、设备序列号索引、可能的配置数等。
数据如下:
31.0 CTL 80 06 00 01 00 00 12 00 GET DESCRIPTOR 1.1.0
31.0 IN 12 01 10 01 00 00 00 08 ........ 1.2.0
6d 02 02 00 01 01 00 00 m....... 1.2.8
00 01 .. 1.2.16
请求数据为:
31.0 CTL 80 06 00 01 00 00 12 00
数据解析如下:
- bmRequestType 80:数据方向从设备端到主机端;标准的请求;USB设备接收
- bRequest 06:请求为 GET_DESCRIPTOR
- wValue 00 01:设备描述符
- wIndex 00 00:从偏移地址0开始读取设备描述符
- wLength 12 00:下一阶段数据的长度为18个字节(小端格式,实际为0x0012,即18)
返回数据字段说明可参见USB规范中的设备描述符自行进行分析。
31.0 IN 12 01 10 01 00 00 00 08 ........ 1.2.0
6d 02 02 00 01 01 00 00 m....... 1.2.8
00 01 .. 1.2.16
解析如下:
12 长度为18
01 表示设备描述符
10 01 转换后为0x0110,表示USB协议版本1.10 (注:USB协议版本使用bcd表示)
00 设备类型(USB分配)
00 设备子类
00 协议码
08 端点0的最大包为8(注:仅有8、16、32、64这几个值)
6d02 VID,转换后为0x026d
0200 PID,转换后为0x0002
0101 设备版本号
本文使用的键盘信息如图7所示(主要核对VID和PID):
配置描述符解析
设备描述符里决定了该设备有多少种配置,每种配置都有一个配置描述符。配置描述符主要记录的信息有:配置所包含的接口数、配置的编号、供电方式、是否支持远程唤醒、电流需求量等。
数据如下:
31.0 CTL 80 06 00 02 00 00 3b 00 GET DESCRIPTOR 3.1.0
31.0 IN 09 02 3b 00 02 01 00 a0 ..;..... 3.2.0
32 09 04 00 00 01 03 01 2....... 3.2.8
01 00 09 21 11 01 00 01 ...!.... 3.2.16
22 41 00 07 05 81 03 08 "A...... 3.2.24
00 0c 09 04 01 00 01 03 ........ 3.2.32
00 00 00 09 21 11 01 00 ....!... 3.2.40
01 22 5b 00 07 05 82 03 ."[..... 3.2.48
08 00 0c ... 3.2.56
注:由于配置描述符是通过2次获取的,这里我们只看第2次获取的全配置描述符内容。
31.0 CTL 80 06 00 02 00 00 09 00
- bmRequestType 80:数据方向从设备端到主机端;标准的请求;USB设备接收
- bRequest 06:请求为 GET_DESCRIPTOR
- wValue 00 02:?
- wIndex 00 00:从偏移地址0开始读取设备描述符
- wLength 09 00:下一阶段数据的长度为9个字节(小端格式,实际为0x0009,即9)
返回数据解析如下所示。
31.0 IN 09 02 3b 00 02 01 00 a0
32 09 04 00 00 01 03 01
01 00 09 21 11 01 00 01
22 41 00 07 05 81 03 08
00 0c 09 04 01 00 01 03
00 00 00 09 21 11 01 00
01 22 5b 00 07 05 82 03
08 00 0c
- 09 本描述符数据长度
- 02 类型,表示配置描述符
- 3b00 即003b,表示此次数据长度。包括其它描述符(配置、接口、终端和HID)的总长度
- 02 本配置支持的接口数量为2
- 01 设置配置命令(Set Configuration)的参数值
- 00 字符串描述符索引值,0表示没有
- a0 电源和唤醒方式 a0表示总线供电(Bus Powered),远程唤醒(Remote Wakeup)
- 32:耗电电流,单位为2mA,此值表示50(0x32)*2=100mA
接口描述符
配置描述符之后紧接着就是接口描述符,接口描述符指明了接口的类型,对应的端点的数量。
在每个配置描述符中又定义了该配置有多少个接口,每个接口都有一个接口描述符。接口描述符主要记录的信息有:接口的编号、接口的端点数、接口所使用的类、子类、协议等。
09 04 00 00 01 03 01 01 00 (1)
09 04 01 00 01 03 00 00 00 (2)
- 09 本描述符长度
- 04 类型值,表示接口描述符
- 00 接口数量为0
- 00 备用的接口描述符编号
- 01 接口终端数量为1
- 03 接口类型值,3表示HID(由USB分配)
- 01 子类型
- 01 协议码,1表示键盘。2为鼠标,0为无
- 00 本接口字符串描述符索引
可以看到,这里接口描述符指定的接口类别为 HID。注意,此处显示的是2个描述符数据,(1) 表示是键盘,但(2)却不是,原因为何,暂无深究。
HID描述符
HID 描述符指定了 HID 规范版本、HID 相关描述符类型(注:物理描述符不是必须的)。
09 21 11 01 00 01 22 41 00
09 21 11 01 00 01 22 5b 00
- 09 本描述符长度
- 21 类别,21为HID描述符
- 1101 转换后为0111,表示USB协议版本为1.11(bcd码)
- 00 国家码
- 01 HID描述符数量为1
- 22 描述符类型,0x22为报告描述符,0x21为HID描述符,0x23为物理描述符
- 4100 描述符长度,此处为0x0041
端点描述符
端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。
07 05 81 03 08 00 0c
07 05 82 03 08 00 0c
- 07 本描述符长度
- 05 类别,5表示端点描述符
- 81 端点地址,Bit7表示方向,1为输入,0为输出,低4比特为端点号。81为输入的1号,82为输入的2号
- 03 端口属性,00表示控制,01为同步,02为批量,03为中断
- 0800 转换后为0x0008,表示最大包长度为8
- 0c 轮询时间间隔,单位ms
报告描述符
HID 特有的描述符共6种,本节分析其中的2种(另外的拿不到数据),捕获的数据来源一款 HID 设备。
HID 请求类别只有0x21或0xa1两种。6种描述符请求如图所示。
设置报告描述符
47.0 CTL 21 09 00 03 00 00 20 00 SET REPORT 19.1.0
- 21 请求类别 0x21最高比特为0,表示数据方向从主机到设备(即输出)
- 09 请求,9表示设置报告
- 0003 低字节为报告ID,其值为0,高字节为报告类别,3表示 feature,1为输入报告,2为输出报告
- 0000 索引值
- 2000 转换后为0x0020,表示报告数据长度为32字节
本描述符字段说明如下图:
设置的输出数据示例如下:
47.0 OUT 55 55 01 4c 61 74 65 01 UU.Late. 19.2.0
02 c2 00 00 00 00 00 00 ........ 19.2.8
00 00 00 00 00 00 00 00 ........ 19.2.16
00 00 00 00 00 00 00 00 ........ 19.2.24
获取报告描述符
47.0 CTL a1 01 00 03 00 00 20 00 GET REPORT 20.1.0
本描述符字段说明如图
- a1 请求类别 0xa1最高比特为1,表示数据方向从设备到主机(即输入)
- 01 请求,1表示获取报告
后面数据同上
本描述符字段说明
输入的数据示例如下:
47.0 IN 55 55 01 4c 61 74 65 01 UU.Late. 20.2.0
03 c3 00 00 00 00 00 00 ........ 20.2.8
00 00 00 00 00 00 00 00 ........ 20.2.16
00 00 00 00 00 00 00 00 ........ 20.2.24
在开发中,报告ID是十分重要的,前面示例的ID为0,下面给出给出报告ID为9的数据:
47.0 CTL 21 09 09 03 00 00 21 00 SET REPORT 3.1.0
47.0 OUT 09 55 55 04 30 00 bb 00 .UU..... 3.2.0
f6 00 77 00 00 00 00 00 ..w..... 3.2.8
00 00 00 00 00 00 00 00 ........ 3.2.16
00 00 00 00 00 00 00 00 ........ 3.2.24
47.0 CTL a1 01 09 03 00 00 21 00 GET REPORT 5.1.0(3)
47.0 IN 09 3c 3c 3c 3c 3c 3c 3c .<<<<<<< 5.2.0
3c 3c 3c 3c 3c 3c 3c 3c <<<<<<<< 5.2.8
3c 3c 3c 3c 3c 3c 3c 3c <<<<<<<< 5.2.16
3c 3c 3c 3c 3c 3c 3c 3c <<<<<<<< 5.2.24
3c < 5.2.32
可以看到,ID 为9时,数据前面多了 ID,而 ID 为0时则没有。然而,在使用 hidapi 库设置 feature 报告时,必须额外添加1字节的 ID,否则会失败。获取时,真正数据在 ID 之后,所以要跳过1字节。