八、USB 协议分析与调试实战
USB 协议学习目录
点击标题可跳转到对应的网络文章
- 一、USB 协议结构详解
- 二、USB 协议中的设备类
- 三、USB 协议通信过程
- 四、USB 协议中的描述符
- 五、USB 协议中的请求
- 六、USB 协议中的接口与端点
- 七、USB 协议中的事务
- 八、USB 协议分析与调试实战
目录
- 一、概述
- [1.1 为什么需要抓包分析](#1.1 为什么需要抓包分析)
- [1.2 知识闭环:从规范到信号](#1.2 知识闭环:从规范到信号)
- [二、USB 抓包工具介绍](#二、USB 抓包工具介绍)
- [2.1 工具对比](#2.1 工具对比)
- [2.2 Wireshark + usbpcap](#2.2 Wireshark + usbpcap)
- [2.3 USBlyzer](#2.3 USBlyzer)
- [2.4 Bus Hound](#2.4 Bus Hound)
- [2.5 Ellisys USB Explorer](#2.5 Ellisys USB Explorer)
- 三、环境搭建
- [3.1 Windows 环境](#3.1 Windows 环境)
- [3.2 Linux 环境](#3.2 Linux 环境)
- 四、枚举过程抓包分析
- [4.1 枚举全景抓包](#4.1 枚举全景抓包)
- [4.2 请求 1:GET_DESCRIPTOR(Device, 8)](#4.2 请求 1:GET_DESCRIPTOR(Device, 8))
- [4.3 请求 2:SET_ADDRESS](#4.3 请求 2:SET_ADDRESS)
- [4.4 请求 3:GET_DESCRIPTOR(Device, 18)](#4.4 请求 3:GET_DESCRIPTOR(Device, 18))
- [4.5 请求 4:GET_DESCRIPTOR(Configuration, 9)](#4.5 请求 4:GET_DESCRIPTOR(Configuration, 9))
- [4.6 请求 5:GET_DESCRIPTOR(Configuration, wTotalLength)](#4.6 请求 5:GET_DESCRIPTOR(Configuration, wTotalLength))
- [4.7 请求 6:SET_CONFIGURATION](#4.7 请求 6:SET_CONFIGURATION)
- 五、控制传输抓包分析
- [5.1 Setup 阶段分析](#5.1 Setup 阶段分析)
- [5.2 Data 阶段分析](#5.2 Data 阶段分析)
- [5.3 Status 阶段分析](#5.3 Status 阶段分析)
- 六、四种传输类型抓包分析
- [6.1 批量传输(Mass Storage)](#6.1 批量传输(Mass Storage))
- [6.2 中断传输(HID 键盘)](#6.2 中断传输(HID 键盘))
- [6.3 同步传输(UVC 摄像头)](#6.3 同步传输(UVC 摄像头))
- 七、常见错误与异常排查
- [7.1 STALL 错误](#7.1 STALL 错误)
- [7.2 NAK 风暴](#7.2 NAK 风暴)
- [7.3 枚举失败](#7.3 枚举失败)
- [7.4 设备无法识别](#7.4 设备无法识别)
- 八、实战练习
- [8.1 练习 1:分析 HID 键盘枚举](#8.1 练习 1:分析 HID 键盘枚举)
- [8.2 练习 2:分析 U 盘批量传输](#8.2 练习 2:分析 U 盘批量传输)
- [8.3 练习 3:分析 CDC 串口数据](#8.3 练习 3:分析 CDC 串口数据)
- 九、总结
一、概述
1.1 为什么需要抓包分析
前面的七份文档构建了 USB 协议的理论知识体系 ,但协议的真实行为只有在总线上才能被完整观察。抓包分析是验证理论、排查问题、理解细节的唯一手段。
验证
发现问题
加深理解
理论学习
抓包分析
调试修复
抓包分析能解决的核心问题:
- 枚举过程中哪一步失败了?
- 设备返回的 STALL 是哪个端点?为什么?
- 批量传输的吞吐量为什么低于预期?
- 中断传输的轮询间隔是否符合描述符声明?
- 驱动加载后发送了什么类特定请求?
1.2 知识闭环:从规范到信号
验证
实践
理论
文档一:协议结构
文档三:通信过程
文档五:请求
文档六:接口端点
文档七:事务
抓包工具
原始信号
逐包解析
问题定位
枚举流程匹配
Setup 包字段验证
Data Toggle 检查
描述符内容核对
二、USB 抓包工具介绍
2.1 工具对比
| 工具 | 平台 | 价格 | 抓包层级 | 分析深度 | 推荐指数 |
|---|---|---|---|---|---|
| Wireshark + usbpcap | Windows/Linux | 免费 | 驱动层 | 中等 | ★★★★★ |
| USBlyzer | Windows | 免费试用 | 驱动层 | 深 | ★★★★☆ |
| Bus Hound | Windows | 付费 | 驱动层 | 中等 | ★★★☆☆ |
| Ellisys USB Explorer | Windows | 昂贵 | 物理层 | 极深 | ★★★★★ |
| Total Phase Beagle | 跨平台 | 中等 | 物理层 | 深 | ★★★★☆ |
| Linux usbmon | Linux | 免费 | 内核层 | 中等 | ★★★★☆ |
抓包层级说明:
| 层级 | 能看到的 | 不能看到的 |
|---|---|---|
| 物理层 | 差分信号波形、PID、CRC、位填充 | - |
| 驱动层 | URB(USB Request Block)、Setup包、数据 | 底层信号时序 |
| 应用层 | 设备插入/移除、驱动加载 | 具体事务 |
2.2 Wireshark + usbpcap
Wireshark 是最常用的网络/USB 协议分析工具,配合 usbpcap 驱动可抓取 USB 总线数据。
优点:
- 完全免费,开源
- 支持 USB 协议解析(Setup 包、描述符、事务类型自动解析)
- 强大的过滤和搜索功能
- 支持导出和统计
安装步骤(Windows):
- 安装 Wireshark(勾选安装 usbpcap)
- 插入目标 USB 设备
- Wireshark → 选择
USBPcapX接口 - 开始抓包
常用显示过滤器:
| 过滤器 | 说明 |
|---|---|
usb.bus_id == 1 |
只显示总线 1 |
usb.device_address == 5 |
只显示地址 5 的设备 |
usb.setup.bRequest == 0x06 |
只显示 GET_DESCRIPTOR 请求 |
usb.endpoint_address == 0x81 |
只显示端点 0x81 |
usb.transfer_type == 0x02 |
只显示批量传输 |
2.3 USBlyzer
USBlyzer 是专门针对 USB 的抓包和分析工具,对 Windows 用户非常友好。
特点:
- 自动解析描述符层次结构
- 以树形视图显示 URB
- 支持设备和驱动的详细信息
- 可导出为 HTML/CSV
2.4 Bus Hound
老牌 USB/I2C/SPI 抓包工具,适合嵌入式开发。
特点:
- 轻量级,占用资源少
- 支持多种总线
- 可设置触发条件
- 适合长时间监控
2.5 Ellisys USB Explorer
专业级 USB 协议分析仪,可抓取物理层信号。
特点:
- 支持 USB 2.0/3.0/3.1/3.2
- 可查看差分信号波形
- 支持 OTG/PD 分析
- 价格昂贵(数万元)
三、环境搭建
3.1 Windows 环境
USB Device
USB Hub
PC USB Port
USBPCap Driver
Wireshark
步骤:
- 下载并安装 Wireshark(https://www.wireshark.org/)
- 安装过程中勾选 USBPCap
- 重启计算机
- 打开 Wireshark,选择
USBPcap1(或对应端口) - 插入设备,开始抓包
注意事项:
- 某些 Hub 可能不支持 USBPCap 过滤,建议直接插在 Root Hub 上
- Root Hub 对应
USBPcap1,外部 Hub 对应USBPcap2等 - 捕获前设置过滤器避免数据过多:
usb.device_address != 0排除未分配地址的噪声
3.2 Linux 环境
Linux 内核自带 usbmon 模块,无需额外安装。
bash
# 加载 usbmon 模块
sudo modprobe usbmon
# 查看可用的监控总线
ls /sys/kernel/debug/usb/usbmon
# 使用 tcpdump 抓取
sudo tcpdump -i usbmon0 -w usb_capture.pcap
# 或用 cat 直接查看文本
sudo cat /sys/kernel/debug/usb/usbmon/0u
usbmon 输出格式:
ffff888123456780 2847713975 S Ci:1:005:0 s 80 06 0100 0000 0012 18 <
│ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ └── 数据方向
│ │ │ │ │ │ │ │ │ │ │ │ └──── 数据长度
│ │ │ │ │ │ │ │ │ │ │ └───────── wLength
│ │ │ │ │ │ │ │ │ │ └────────────── wIndex
│ │ │ │ │ │ │ │ │ └─────────────────── wValue
│ │ │ │ │ │ │ │ └────────────────────── bRequest
│ │ │ │ │ │ │ └───────────────────────── bmRequestType
│ │ │ │ │ │ └──────────────────────────── 端点
│ │ │ │ │ └────────────────────────────── 设备地址
│ │ │ │ └────────────────────────────────── 总线号
│ │ │ └──────────────────────────────────── 阶段 (S=提交, C=完成)
│ │ └────────────────────────────────────── 时间戳
│ └───────────────────────────────────────────────── URB 地址
四、枚举过程抓包分析
4.1 枚举全景抓包
以下是一次 Full Speed HID 键盘枚举的 Wireshark 抓包摘要:
| No | 时间 | 源 | 目标 | 协议 | Info |
|---|---|---|---|---|---|
| 1 | 0.000000 | host | 0.0 | USB | GET DESCRIPTOR Request DEVICE |
| 2 | 0.000234 | 0.0 | host | USB | GET DESCRIPTOR Response DEVICE |
| 3 | 0.000456 | host | 0.0 | USB | SET ADDRESS Request |
| 4 | 0.000678 | 0.0 | host | USB | SET ADDRESS Response |
| 5 | 0.000890 | host | 5.0 | USB | GET DESCRIPTOR Request DEVICE |
| 6 | 0.001123 | 5.0 | host | USB | GET DESCRIPTOR Response DEVICE |
| 7 | 0.001345 | host | 5.0 | USB | GET DESCRIPTOR Request CONFIGURATION |
| 8 | 0.001567 | 5.0 | host | USB | GET DESCRIPTOR Response CONFIGURATION |
| 9 | 0.001789 | host | 5.0 | USB | GET DESCRIPTOR Request CONFIGURATION |
| 10 | 0.002234 | 5.0 | host | USB | GET DESCRIPTOR Response CONFIGURATION |
| 11 | 0.002456 | host | 5.0 | USB | GET DESCRIPTOR Request STRING |
| 12 | 0.002678 | 5.0 | host | USB | GET DESCRIPTOR Response STRING |
| 13 | 0.002890 | host | 5.0 | USB | SET CONFIGURATION Request |
| 14 | 0.003123 | 5.0 | host | USB | SET CONFIGURATION Response |
注意:
0.0表示地址 0 端点 0(枚举前),5.0表示地址 5 端点 0。
4.2 请求 1:GET_DESCRIPTOR(Device, 8)
Wireshark 解析视图:
Frame 1: 64 bytes on wire
USB URB
URB id: 0xffff888123456780
URB type: URB_SUBMIT ('S')
URB transfer type: URB_CONTROL (0x02)
Endpoint: 0x00, Direction: OUT
Device: 0
Endpoint: 0x00
URB setup
bmRequestType: 0x80 (Device-to-Host, Standard, Device)
bRequest: GET_DESCRIPTOR (0x06)
wValue: 0x0100 (Descriptor Type: DEVICE, Index: 0)
wIndex: 0x0000
wLength: 8
Data: <empty>
Frame 2: 64 bytes on wire
USB URB
URB id: 0xffff888123456780
URB type: URB_COMPLETE ('C')
URB transfer type: URB_CONTROL (0x02)
Endpoint: 0x80, Direction: IN
Device: 0
Endpoint: 0x80
Data (8 bytes)
0000 12 01 00 02 00 00 00 08 ........
逐字节解析响应数据:
| 偏移 | 值 | 字段 | 说明 |
|---|---|---|---|
| 0 | 0x12 | bLength | 18 字节 |
| 1 | 0x01 | bDescriptorType | 设备描述符 |
| 2~3 | 0x0200 | bcdUSB | USB 2.0 |
| 4 | 0x00 | bDeviceClass | 每接口定义 |
| 5 | 0x00 | bDeviceSubClass | - |
| 6 | 0x00 | bDeviceProtocol | - |
| 7 | 0x08 | bMaxPacketSize0 | EP0 最大包 = 8 字节 |
关键发现:
bMaxPacketSize0 = 8,说明后续 EP0 通信每次最多 8 字节。
4.3 请求 2:SET_ADDRESS
Frame 3: SETUP OUT
bmRequestType: 0x00 (Host-to-Device, Standard, Device)
bRequest: SET_ADDRESS (0x05)
wValue: 0x0005
wIndex: 0x0000
wLength: 0
Frame 4: COMPLETE IN
Data: <empty> (ZLP)
解析:
wValue = 0x0005→ 分配地址 5wLength = 0→ 无数据阶段- 响应为空 → Status 阶段的 ZLP
从此帧开始,后续通信使用地址 5。
4.4 请求 3:GET_DESCRIPTOR(Device, 18)
Frame 5: SETUP OUT (ADDR=5)
bmRequestType: 0x80
bRequest: 0x06
wValue: 0x0100
wLength: 18
Frame 6: COMPLETE IN (ADDR=5)
Data (18 bytes)
0000 12 01 00 02 00 00 00 08 83 04 10 57 00 01 01 02
0010 00 01
解析:
| 偏移 | 值 | 字段 | 说明 |
|---|---|---|---|
| 0~1 | 12 01 | 头部 | 长度18,类型设备 |
| 2~3 | 00 02 | bcdUSB | USB 2.0 |
| 4~6 | 00 00 00 | Class/Sub/Proto | 每接口定义 |
| 7 | 08 | bMaxPacketSize0 | 8 字节 |
| 8~9 | 83 04 | idVendor | 0x0483 = ST Microelectronics |
| 10~11 | 10 57 | idProduct | 0x5710 |
| 12~13 | 00 01 | bcdDevice | 版本 1.00 |
| 14 | 01 | iManufacturer | 字符串 1 |
| 15 | 02 | iProduct | 字符串 2 |
| 16 | 00 | iSerialNumber | 无 |
| 17 | 01 | bNumConfigurations | 1 个配置 |
4.5 请求 4:GET_DESCRIPTOR(Configuration, 9)
Frame 7: SETUP OUT (ADDR=5)
wValue: 0x0200
wLength: 9
Frame 8: COMPLETE IN (ADDR=5)
Data (9 bytes)
0000 09 02 22 00 01 01 00 a0 32
解析:
| 偏移 | 值 | 字段 | 说明 |
|---|---|---|---|
| 0 | 0x09 | bLength | 9 |
| 1 | 0x02 | bDescriptorType | 配置描述符 |
| 2~3 | 22 00 | wTotalLength | 0x0022 = 34 字节 |
| 4 | 0x01 | bNumInterfaces | 1 个接口 |
| 5 | 0x01 | bConfigurationValue | 配置值 = 1 |
| 6 | 0x00 | iConfiguration | 无字符串 |
| 7 | 0xA0 | bmAttributes | 自供电 + 远程唤醒 |
| 8 | 0x32 | bMaxPower | 50 × 2mA = 100mA |
关键发现:
wTotalLength = 34,下一次将请求 34 字节获取完整配置。
4.6 请求 5:GET_DESCRIPTOR(Configuration, wTotalLength)
Frame 9: SETUP OUT (ADDR=5)
wValue: 0x0200
wLength: 34
Frame 10: COMPLETE IN (ADDR=5)
Data (34 bytes)
0000 09 02 22 00 01 01 00 a0 32 09 04 00 00 01 03 01
0010 01 00 09 21 11 01 00 01 22 3f 00 07 05 81 03 08
0020 00 0a
完整解析(34 字节):
| 偏移 | 长度 | 描述符类型 | 关键字段 | 值 |
|---|---|---|---|---|
| 0~8 | 9 | 配置描述符 | wTotalLength=34, bNumInterfaces=1 | - |
| 9~17 | 9 | 接口描述符 | bInterfaceClass=0x03, bInterfaceSubClass=0x01, bInterfaceProtocol=0x01 | HID Boot Keyboard |
| 18~26 | 9 | HID 描述符 | bcdHID=0x0111, wDescriptorLength=63 | HID 1.11 |
| 27~33 | 7 | 端点描述符 | bEndpointAddress=0x81, bmAttributes=0x03, wMaxPacketSize=8, bInterval=10 | EP1 IN, 中断, 8B, 10ms |
4.7 请求 6:SET_CONFIGURATION
Frame 13: SETUP OUT (ADDR=5)
bmRequestType: 0x00
bRequest: 0x09
wValue: 0x0001
wLength: 0
Frame 14: COMPLETE IN (ADDR=5)
Data: <empty>
解析:
bRequest = 0x09→ SET_CONFIGURATIONwValue = 0x0001→ 激活配置 1- 响应为空 → 设备进入 Configured State
枚举完成! 此后 EP1 (0x81) 开始周期性传输中断数据(按键报告)。
五、控制传输抓包分析
5.1 Setup 阶段分析
控制传输的 Setup 阶段总是由 SETUP + DATA0 + ACK 三个包组成。
USB Device USB Host USB Device USB Host PID=0x2D ADDR+ENDP+CRC5 PID=0xC3 bmRequestType+bRequest+ wValue+wIndex+wLength CRC16 PID=0xD2 TOKEN: SETUP DATA0: 8B Setup HANDSHAKE: ACK
5.2 Data 阶段分析
Data OUT(Host → Device):
USB Device USB Host USB Device USB Host TOKEN: OUT DATA1: 数据 ACK TOKEN: OUT DATA0: 数据 ACK
Data IN(Device → Host):
USB Device USB Host USB Device USB Host TOKEN: IN DATA1: 数据 ACK TOKEN: IN DATA0: 数据 ACK
5.3 Status 阶段分析
Status 阶段总是使用 与 Data 阶段相反方向 的零长度包(ZLP)。
| Data 阶段方向 | Status 阶段事务 | 说明 |
|---|---|---|
| 无 Data 阶段 | IN + DATA1(ZLP) + ACK | 如 SET_ADDRESS |
| Data OUT | IN + DATA1(ZLP) + ACK | Host 确认设备已接收 |
| Data IN | OUT + DATA1(ZLP) + ACK | 设备确认 Host 已接收 |
六、四种传输类型抓包分析
6.1 批量传输(Mass Storage)
# CBW 发送
host → 5.2: OUT + DATA0: CBW (31 bytes)
5.2 → host: ACK
# 数据阶段(Host → Device 写数据)
host → 5.2: OUT + DATA1: 数据 (512 bytes)
5.2 → host: ACK
host → 5.2: OUT + DATA0: 数据 (512 bytes)
5.2 → host: ACK
...
host → 5.2: OUT + DATA1: 短包 (128 bytes) # 传输结束
5.2 → host: ACK
# CSW 接收
host → 5.1: IN
5.1 → host: DATA0: CSW (13 bytes)
host → 5.1: ACK
批量传输特征识别:
- URB transfer type:
URB_BULK (0x03) - 大量连续的 IN 或 OUT 事务
- 数据包大小通常为端点最大包大小(64/512)
- 以短包或 ZLP 结束
6.2 中断传输(HID 键盘)
# 周期性轮询,每 10ms 一次
host → 5.1: IN
5.1 → host: NAK # 无按键
host → 5.1: IN
5.1 → host: DATA0: 00 00 00 00 00 00 00 00 # 无按键
host → 5.1: ACK
host → 5.1: IN
5.1 → host: DATA1: 00 00 04 00 00 00 00 00 # 'a' 键按下
host → 5.1: ACK
host → 5.1: IN
5.1 → host: DATA0: 02 00 04 00 00 00 00 00 # Left Shift + 'a' = 'A'
host → 5.1: ACK
host → 5.1: IN
5.1 → host: DATA1: 00 00 00 00 00 00 00 00 # 释放
host → 5.1: ACK
中断传输特征识别:
- URB transfer type:
URB_INTERRUPT (0x01) - 周期性出现,间隔 = bInterval
- 大量 NAK(无数据时)
- 数据包很小(通常 8B)
按键数据解析(Boot Protocol):
| 字节 | 值 | 含义 |
|---|---|---|
| 0 | 0x00 | 修饰键(Ctrl/Shift/Alt/GUI) |
| 1 | 0x00 | 保留 |
| 2 | 0x04 | 按键 1:'a' (0x04) |
| 3 | 0x00 | 按键 2:无 |
| 4 | 0x00 | 按键 3:无 |
| 5 | 0x00 | 按键 4:无 |
| 6 | 0x00 | 按键 5:无 |
| 7 | 0x00 | 按键 6:无 |
6.3 同步传输(UVC 摄像头)
# 每帧一次,数据包很大
host → 5.5: IN
5.5 → host: DATA0: MJPEG 帧头 (12 bytes)
# 无 ACK!
host → 5.5: IN
5.5 → host: DATA1: MJPEG 数据 (1023 bytes)
# 无 ACK!
host → 5.5: IN
5.5 → host: DATA0: MJPEG 数据 (1023 bytes)
# 无 ACK!
...
同步传输特征识别:
- URB transfer type:
URB_ISOCHRONOUS (0x00) - 无握手包(看不到 ACK/NAK/STALL)
- 数据包大(最大 1023B FS / 1024B HS)
- 连续多个 IN 事务,无间隔
七、常见错误与异常排查
7.1 STALL 错误
现象 :抓包中看到 STALL 响应。
host → 5.1: IN
5.1 → host: STALL # ← 异常!
排查流程:
EP0
非零端点
GET_DESCRIPTOR
SET_FEATURE
类请求
抓包发现 STALL
STALL 发生在哪个端点?
控制请求错误
端点挂起
什么请求?
描述符索引越界
不支持该特性
类请求参数错误
批量/中断端点错误
Host 发送 CLEAR_FEATURE
Data Toggle 复位
通信恢复
常见原因:
| 场景 | 原因 | 解决 |
|---|---|---|
| EP0 STALL | 设备不支持该请求 | 检查 bRequest 和 bmRequestType |
| EP0 STALL | wValue/wIndex 越界 | 检查描述符索引/接口号/端点号 |
| 批量端点 STALL | 设备协议错误 | Host 发送 CLEAR_FEATURE 恢复 |
| 中断端点 STALL | 设备未准备好 | 检查设备固件状态机 |
7.2 NAK 风暴
现象:连续大量 NAK,无 ACK。
host → 5.1: IN
5.1 → host: NAK
host → 5.1: IN
5.1 → host: NAK
host → 5.1: IN
5.1 → host: NAK
# ... 持续数十次 NAK
排查流程:
| NAK 场景 | 正常/异常 | 原因 | 处理 |
|---|---|---|---|
| 中断 IN 端点 | 正常 | 设备暂无数据 | 周期性轮询,等待数据 |
| 批量 OUT 端点 | 可能正常 | 设备缓冲区满 | 稍后重试 |
| 控制端点 EP0 | 异常 | 设备忙或死锁 | 检查设备固件 |
| 持续 NAK 无 ACK | 异常 | 设备挂死 | 重新插拔或复位 |
判断标准:
- 中断传输:NAK 是正常现象,占 90% 以上也是正常的
- 批量/控制传输:连续 NAK > 100 次 → 设备可能挂死
7.3 枚举失败
现象:设备插入后,Host 反复复位,无法完成枚举。
# 失败模式 1:GET_DESCRIPTOR 无响应
host → 0.0: SETUP + GET_DESCRIPTOR
# ... 超时,无响应
host → 0.0: SETUP + GET_DESCRIPTOR
# ... 超时,无响应
# Host 放弃,报告设备故障
# 失败模式 2:SET_ADDRESS 后失联
host → 0.0: SETUP + GET_DESCRIPTOR (8B)
0.0 → host: DATA0 (8B)
host → 0.0: ACK
host → 0.0: SETUP + SET_ADDRESS(5)
0.0 → host: ACK
# 此后 Host 用地址 5 通信,设备无响应
host → 5.0: SETUP + GET_DESCRIPTOR
# ... 超时
枚举失败排查表:
| 失败阶段 | 可能原因 | 排查方法 |
|---|---|---|
| GET_DESCRIPTOR 无响应 | D+/D- 上拉电阻错误 | 检查原理图 |
| GET_DESCRIPTOR 无响应 | 晶振未起振 | 示波器测时钟 |
| SET_ADDRESS 后失联 | 设备未正确切换地址 | 检查固件地址切换时机 |
| 获取配置描述符失败 | wTotalLength 计算错误 | 核对描述符总长度 |
| SET_CONFIGURATION 失败 | 端点配置错误 | 检查端点地址/属性 |
| 驱动加载失败 | VID/PID 不匹配 | 检查设备描述符 |
7.4 设备无法识别
现象:Windows 显示"无法识别的 USB 设备"。
未知设备
代码 43
无反应
无
有
设备无法识别
设备管理器显示什么?
枚举失败
设备报告错误
硬件问题
抓包看枚举停在第几步
GET_DESCRIPTOR 响应?
检查 D+/D- 上拉、晶振
检查 SET_ADDRESS 后通信
抓包找 STALL
CLEAR_FEATURE 后是否恢复
测量 VBUS 电压
测量 D+/D- 电平
检查 PCB 焊接
八、实战练习
8.1 练习 1:分析 HID 键盘枚举
目标:根据以下抓包数据,还原设备的描述符结构。
# 抓包数据(简化)
1. SETUP: 80 06 00 01 00 00 08 00
RESP: 12 01 00 02 00 00 00 08
2. SETUP: 00 05 05 00 00 00 00 00
RESP: (ZLP)
3. SETUP: 80 06 00 01 00 00 12 00
RESP: 12 01 00 02 00 00 00 08 83 04 10 57 00 01 01 02 00 01
4. SETUP: 80 06 00 02 00 00 09 00
RESP: 09 02 22 00 01 01 00 a0 32
5. SETUP: 80 06 00 02 00 00 22 00
RESP: 09 02 22 00 01 01 00 a0 32 09 04 00 00 01 03 01 01 00
09 21 11 01 00 01 22 3f 00 07 05 81 03 08 00 0a
6. SETUP: 00 09 01 00 00 00 00 00
RESP: (ZLP)
问题:
- 该设备的 VID 和 PID 是什么?
- bMaxPacketSize0 是多少?
- 该设备是什么类型?(根据 bInterfaceClass/SubClass/Protocol)
- 配置中有多少个接口?
- 接口 0 有多少个端点?
- 端点的地址、类型、最大包大小和轮询间隔分别是多少?
答案
- VID = 0x0483, PID = 0x5710
- bMaxPacketSize0 = 8
- HID Boot Keyboard (0x03/0x01/0x01)
- 1 个接口
- 1 个端点
- EP1 IN (0x81), 中断, 8 字节, 10ms
8.2 练习 2:分析 U 盘批量传输
抓包片段:
host → 5.2: OUT + DATA0: 55 53 42 43 12 34 56 78 ... (31 bytes = CBW)
5.2 → host: ACK
host → 5.2: OUT + DATA1: [512 bytes 数据]
5.2 → host: ACK
host → 5.2: OUT + DATA0: [512 bytes 数据]
5.2 → host: ACK
host → 5.2: OUT + DATA1: [128 bytes 短包]
5.2 → host: ACK
host → 5.1: IN
5.1 → host: DATA0: 55 53 42 53 12 34 56 78 00 00 00 00 00 (13 bytes = CSW)
host → 5.1: ACK
问题:
- CBW 的签名是什么?(前 4 字节)
- 这次批量传输发送了多少字节数据?
- CSW 的 bCSWStatus 是多少?(最后一个字节)
- 数据传输是否成功?
答案
- CBW 签名 = 0x43425355 ('USBC')
- 数据量 = 512 + 512 + 128 = 1152 字节
- bCSWStatus = 0x00(最后一个字节)
- 成功(Status = 0)
8.3 练习 3:分析 CDC 串口数据
抓包片段:
# SET_LINE_CODING 请求
host → 5.2: SETUP: 21 20 00 00 00 00 07 00
5.2 → host: ACK
host → 5.2: OUT + DATA0: 00 C2 01 00 00 00 08
5.2 → host: ACK
host → 5.2: IN
5.2 → host: DATA1: (ZLP)
host → 5.2: ACK
# 后续数据发送
host → 5.4: OUT + DATA0: 48 65 6C 6C 6F 0A ("Hello\n")
5.4 → host: ACK
问题:
- SET_LINE_CODING 的波特率是多少?
- 数据位、停止位、校验分别是什么?
- Host 向设备发送了什么字符串?
答案
- 波特率 = 0x0001C200 = 115200
- 停止位=1, 校验=None, 数据位=8
- "Hello\n"(十六进制 48 65 6C 6C 6F 0A)
九、总结
问题排查
分析对象
工具
Wireshark
- usbpcap
USBlyzer
Linux usbmon
枚举过程
6 个标准请求
控制传输
Setup/Data/Status
批量传输
CBW/CSW
中断传输
周期性 IN
同步传输
无握手
STALL
NAK
枚举失败
无法识别
学习路径回顾:
| 阶段 | 文档 | 能力 |
|---|---|---|
| 理论学习 | 一~七 | 理解协议规范 |
| 工具掌握 | 本章 2~3 节 | 能独立抓包 |
| 枚举分析 | 本章 4 节 | 能逐包解析枚举流程 |
| 传输分析 | 本章 5~6 节 | 能识别四种传输类型 |
| 问题排查 | 本章 7 节 | 能定位常见故障 |
| 实战验证 | 本章 8 节 | 能独立完成抓包分析 |
下一步建议 :有了抓包分析能力后,可以尝试自己实现一个简单的 USB 设备固件(如 STM32 的 USB Device 库),然后用抓包工具验证自己的实现是否正确。