逆向工程实战:用usbmon解码USB键盘的原始通信数据
当你的手指在键盘上敲击时,一组神秘的十六进制代码正在USB总线上飞驰。这些看似随机的数字背后,隐藏着从物理按键到操作系统事件的完整转化链条。本文将带你深入USB HID协议的底层,使用Linux内核内置的usbmon工具,捕获并解析键盘每一次击键产生的原始数据包。
1. 搭建USB数据捕获环境
1.1 内核模块准备
现代Linux发行版通常已内置usbmon支持,首先验证内核模块状态:
ls /sys/module/usbmon若目录不存在,需加载模块:
sudo modprobe usbmondebugfs是usbmon的依赖文件系统,检查挂载状态:
mount | grep debugfs未挂载时执行:
sudo mount -t debugfs none /sys/kernel/debug1.2 设备总线定位
使用lsusb定位目标设备所在总线:
lsusb -v | grep -A5 "Keyboard"典型输出示例:
Bus 003 Device 002: ID 046d:c31c Logitech USB Keyboard这里003即为总线编号,记下设备地址002用于后续过滤。
2. 捕获键盘原始数据流
2.1 实时监控数据包
针对特定总线启动监控(以总线3为例):
sudo cat /sys/kernel/debug/usb/usbmon/3u > keyboard_capture.log按下几个按键后Ctrl+C终止捕获,得到原始数据样本:
ffff8a1b12a8d400 1638297565 S Ci:3:002:0 s a1 01 0000 0001 0008 8 < ffff8a1b12a8d400 1638297567 C Ci:3:002:0 0 8 = 0000000000000000 ffff8a1b12a8d400 1638298512 S Ci:3:002:0 s a1 01 0000 0001 0008 8 < ffff8a1b12a8d400 1638298514 C Ci:3:002:0 0 8 = 00000400000000002.2 数据包结构解析
usbmon输出的关键字段说明:
| 字段位置 | 示例值 | 含义 |
|---|---|---|
| 1 | ffff8a1b12a8d400 | URB唯一标识符(内核地址) |
| 3 | Ci | 控制输入传输(Control Input) |
| 4 | 3:002:0 | 总线:设备:端点号 |
| 7 | 8 < | 数据长度8字节,方向为设备到主机 |
| 9 | = 00000400... | 实际传输的HID报告数据 |
3. HID报告描述符解码
3.1 报告描述符获取
USB HID设备通过描述符定义数据格式,获取键盘描述符:
sudo lsusb -v -d 046d:c31c | grep -A50 "HID Device Descriptor"关键输出片段:
HID Device Descriptor: bLength 9 bDescriptorType 33 bCountryCode 0 bNumDescriptors 1 bDescriptorType 34 wDescriptorLength 65 Report Descriptor: (length is 65) Item(Global): Usage Page, data= [ 0x01 ] 1 Generic Desktop Controls Item(Local): Usage, data= [ 0x06 ] 6 Keyboard Item(Main): Collection, data= [ 0x01 ] 1 Application3.2 按键码映射表
标准USB HID键盘的键码对应关系(部分):
| 十六进制 | 十进制 | 按键 |
|---|---|---|
| 0x04 | 4 | A/a |
| 0x05 | 5 | B/b |
| 0x06 | 6 | C/c |
| 0x07 | 7 | D/d |
| 0x1A | 26 | [/ { |
| 0x1B | 27 | ]/ } |
| 0x28 | 40 | Enter |
| 0x29 | 41 | Escape |
4. 实战数据包分析
4.1 单按键按下事件
捕获到的数据样本:
ffff8a1b12a8d400 1638298514 C Ci:3:002:0 0 8 = 0000040000000000解析步骤:
- 数据部分
0000040000000000按字节分组:00 00 04 00 00 00 00 00 - 第1字节
00:修饰键状态(Shift/Ctrl/Alt等) - 第3字节
04:对应键码表可知是字母A - 其余字节
00:保留位或同时按下的其他键
4.2 组合键事件分析
Shift+A组合击键的典型数据:
ffff8a1b12a8d400 1638301123 C Ci:3:002:0 0 8 = 0200040000000000关键区别:
- 第1字节变为
02:左Shift键的修饰位标志 - 第3字节仍为
04:物理A键的扫描码
4.3 数据包时序分析
连续击键的时间戳差值计算:
timestamp1 = 1638297565 timestamp2 = 1638298512 delta_ms = (timestamp2 - timestamp1) / 1000 print(f"按键间隔:{delta_ms:.1f}毫秒")输出示例:
按键间隔:946.7毫秒5. 高级分析技巧
5.1 使用Python解析数据
自动化解析脚本示例:
import re def parse_usbmon(line): pattern = r'.* C Ci:\d+:\d+:\d+ \d+ \d+ = ([0-9a-f]+)' match = re.match(pattern, line) if match: return bytes.fromhex(match.group(1)) return None sample = "ffff8a1b12a8d400 1638298514 C Ci:3:002:0 0 8 = 0000040000000000" print(parse_usbmon(sample)) # 输出:b'\x00\x00\x04\x00\x00\x00\x00\x00'5.2 Wireshark联合分析
- 将usbmon输出转换为pcapng格式:
text2pcap -T 147,147 keyboard_capture.log keyboard.pcap- 在Wireshark中应用USB HID解析器:
右键数据包 → Decode As → USBHID5.3 异常数据诊断
常见问题数据模式及含义:
| 数据模式 | 可能原因 |
|---|---|
| 0000000000000000 | 按键释放事件 |
| FF00FF0000000000 | 全键按下(防鬼跳测试) |
| 0100000000000000 | 左Ctrl键按下 |
| 持续相同非零数据 | 按键卡住或硬件故障 |
在虚拟机环境中测试时,可能会观察到额外的控制传输数据包,这是虚拟化层与主机通信的正常现象。实际物理键盘的数据流通常更简洁,主要集中在中断传输端点上。