关于 usb-rea
usb-rea 分析捕获的 USB 流量以推断设备协议,并生成带数据包证据链接的 Python/libusb 驱动。它面向研究仅能通过厂商软件控制的设备的开发者;项目处于 alpha 阶段,尚未在实体设备上测试。
主要功能
- 01捕获 USB 流量
- 02按标记的操作比较数据包
- 03推断字段、计数器和校验和
- 04分析设备响应和读数
- 05生成 pyusb 驱动及测试
- 06创建 Wireshark 解析器
适用场景
- 01研究仅有厂商软件的设备
- 02识别命令与响应数据包字段
- 03根据标记的捕获生成驱动
- 04通过 MCP 工具使用 USB 分析功能
MCP 相关
其 stdio MCP 服务提供流量捕获、标记、分析、报告、驱动框架生成、HID 解码、解析器生成及经确认后重放数据包等工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
9收录的工具列表 · 更新于 今天
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| bus | integer | 否 | 用于 /dev/usbmonN 的 usbmon 总线编号 N;在 Windows/USBPcap 上忽略。默认值为 0 |
| out | string | 否 | 输出 pcapng 路径(如果已存在则覆盖) |
详细介绍
usb-rea:逆向分析任意 USB 设备
如果你觉得这个项目有用,可以购买我的另一个项目 SkinToScan 套装来支持我。这是一次性购买,收入将直接用于资助我的开源项目。

有一台 USB 设备只附带 Windows 厂商应用吗(机箱 LCD、RGB 控制器、鼠标、“智能”灯)?在厂商应用中点击操作时,usb-rea 会捕获 USB 流量(Linux 上使用 usbmon,Windows 上使用 USBPcap),按操作对数据包进行差异比较,推断协议,并编写开源 Python/libusb 驱动程序;每项结论都会关联到能够证明它的确切数据包。
状态:Alpha 阶段(v0.6.1,已发布在 PyPI)。已在合成设备、liquidctl 和 rivalcfg 文档记录的协议,以及公开的 Wireshark 捕获数据上验证(见 docs/VALIDATION.md)。尚未在实体设备上测试:欢迎提供你的捕获数据。
10 秒内试用(无需硬件)
pip install usb-rea
usb-rea demo --device fan
fan 100% 0x5a | counter (+1 per message) | 0x10 | u16be round(value * 1 + 0) | 0x00 | u16le crc16_modbus(bytes[0:6])
color 100% 0x5a | counter (+1 per message) | 0x20 | round(value * 1 + 0) | round(value[1] * 1 + 0) | round(value[2] * 1 + 0) | u16le crc16_modbus(bytes[0:6])
mode 100% 0x5a | counter (+1 per message) | 0x30 | lookup 1→0x11, 2→0x22, 3→0x47 | 0x00 | 0x00 | u16le crc16_modbus(bytes[0:6])
device replies:
fan 6/6 0xa5 | = query byte 2 (0x10) | = query byte 1 | u16le round(value * 1 + 0) | sum8(bytes[0:5])
poll 1 7/7 0xa5 | = query byte 2 (0x40) | u16le rpm = raw, seen 794..4012 | u8 temp = raw, seen 35..38 | mode: 0x00 = auto, 0x11 = silent, 0x22 = balanced, 0x47 = turbo | sum8(bytes[0:6])
21 of 21 'seen' observations matched a reply byte (mode, rpm, temp)
它会写入一份风扇控制器的合成捕获数据(其厂商应用还会轮询风扇状态,标记会注明应用屏幕上显示的内容:seen rpm 1207 temp 37 mode auto),还原协议和响应,为读数和风扇模式命名,并生成 report.md(每项结论都会关联到 Wireshark 帧编号)以及一个可用的 pyusb 驱动程序。该驱动程序能够编码从未见过的值,并解码风扇的响应。
--device lamp(默认值)是一个简单的 8 位设备;--device dial 会展示 32 位值和打包位域:
position 100% 0xa5 | 0x01 | u32le round(value * 1 + 0) | sum8(bytes[0:6])
level 100% 0xa5 | 0x02 | bits[0:4] = round(value * 1 + 0) | 0x00 | sum8(bytes[0:4])
可推断的内容
| 角色 | 示例 |
|---|---|
| 固定报头 / 命令 ID | 5a、10 与 20、30 |
| 线性值,8、16 或 32 位,小端 / 大端,每个操作可包含多个值 | fan 2650 → 0a 5a;color 255 24 0;position 50000 → 50 c3 00 00 |
| 打包位字段(位与固定标志位相邻,包括跨两个字节的 12 位跨度) | level 7 → b7 = 标志位 b0 + bits[0:4] |
| 查找表(非线性选项) | DPI 250→08,500→07…… |
| 循环计数器 | 每条消息的序列字节 +1 |
| 校验和 | sum8、xor8、neg/inv sum8、sum16、CRC8、CRC8/MAXIM、CRC16 CCITT-FALSE/XMODEM/MODBUS/KERMIT |
| 设备回复:回显的查询字节、映射到标签的值、读数 | 状态轮询 → a5 40 2a 07 26 00 3c = RPM u16le 读数、温度 u8 读数、sum8 |
命名读数,依据供应商应用显示的内容(seen rpm 1180) | 字节 2-3 u16le rpm = raw,字节 4 temp = (raw - 40) / 2 |
开 / 关状态标志(seen pump on、seen pump off) | 字节 5 bit 0: pump (1 = on) |
模式 / 枚举值(seen mode silent、seen mode turbo) | 字节 5 mode: 0x00 = silent, 0x02 = turbo,或与标志位相邻的 bits[4:6] profile: … |
有符号读数,适用于应用显示负值的情况(seen temp -5) | 字节 4 i8 temp = raw,字节 2-3 i16be … |
线性拟合必须通过留一交叉校验;否则 usb-rea 会退回到如实呈现的查找表,并说明这一点。
真实设备工作流(Linux)
sudo modprobe usbmon
usb-rea capture start -o lamp.pcapng
usb-rea mark -m lamp.pcapng.actions.json "brightness 0%" # then click it in the vendor app
usb-rea mark -m lamp.pcapng.actions.json "brightness 50%" # ...repeat for 3+ values per setting
usb-rea capture stop
usb-rea report lamp.pcapng -o report.md
usb-rea scaffold lamp.pcapng -o lamp_driver.py
usb-rea dissector lamp.pcapng -o lamp.lua # Wireshark dissector for the same protocol
在 Windows 上使用 USBPcap/Wireshark 捕获,且没有标记?依次点击每个操作,并在两次点击之间稍作停顿,然后按顺序列出操作即可;usb-rea 会根据停顿拆分流量:
usb-rea report lamp.pcapng --labels "brightness 0%; brightness 50%; brightness 100%; power off; power on"
usb-rea scaffold lamp.pcapng --labels labels.txt -o lamp_driver.py # one label per line
(如果有标记,它们是包含 {"label", "start", "end"} epoch 时间戳的 JSON 列表。)
大多数供应商应用也会自行轮询设备(每隔几百毫秒“获取状态”一次,并发送保活消息)。
usb-rea 会找出以稳定节奏发送的相同主机→设备消息,这些消息不会紧跟在你的点击之后,并会在拆分和比对前将它们单独列出;报告会在 忽略的后台流量 下列出这些消息及其周期和帧编号。始终紧跟在真实命令后面的固定“应用”数据包不会被视为后台流量。带序列号的轮询也能被识别:形状相同、仅有一个递增字节不同(以及随之变化的尾随校验和)的消息会显示为 aa 10 ?? 00 ?? 等形式。至少需要 8 个副本,因此不会把用户将数值每次递增 1 误判为轮询。
设备响应
设备也会回复:每条命令后发送 ACK,对每次轮询发送状态回复(RPM、温度、固件版本),或返回 GET_REPORT 的数据。usb-rea 会将每个查询与后续的 IN 传输配对(依据端点和典型延迟,因此在点击操作期间发送的轮询不会抢走该操作的回复),并使用相同机制解码回复:常量、回显查询内容的字节(命令 ID、序列号)、计数器、校验和,以及根据你的标签拟合的值(fan 1200 → 回复中的 u16le)。其他任何变化内容都会作为读数报告,并附上观测范围;报告会说明其含义未知。只有当一种字节序平滑变化、另一种则跳变时,才会判定为 16 位读数。生成的驱动会获得 query_<name>() 方法,用于发送查询、读取回复并返回一个字典({"reading_2": 1834, "checksum_ok": True, …})。
可使用 usb-rea demo --device fan 试用,也可对包含轮询的捕获数据试用。
为读数命名:告诉它厂商应用显示了什么
在说明读数是什么之前,它只是一串字节。采集时,记下厂商应用不时显示的内容:
usb-rea mark -m fan.pcapng.actions.json "seen rpm 1180 temp 34" # several name/value pairs are fine
seen 标记是一条观察记录,而不是操作:它不会结束正在进行的操作,不会参与差异比较,也永远不会变成命令。每条观察记录都会与紧邻它之前(应用当时显示的内容)或之后、间隔不超过 1 s 的回复进行比较。如果某个名称对应的每条观察记录(至少 2 条,误差不超过一个单位或 1%)都相等,读数就会采用该名称;或者,如果线性公式 raw = value * scale + bias 能精确拟合这些记录,并通过与命令相同的逐条留出检查(至少 3 条且数值各不相同),读数也会采用该名称。系统还会尝试将相邻的两个字节读数作为一个 16 位 LE/BE 数值、将相邻的四个字节作为一个 32 位 LE/BE 数值(若高位字节仍为 0x00,三条观察记录就足够),因此能处理能量计数器和运行时间:mark "seen energy 2.968" 会将字节 1-4 命名为 u32be energy = raw / 1000。报告会显示公式及其依据的帧和标记时间,并列出没有匹配任何内容的观察记录;驱动会返回 {"rpm": 1180, "temp": 34, "ok": True, …}。
开/关状态也适用:mark "seen pump on, alarm off"(也支持 true/false、yes/no)。如果某个名称既出现过开启状态也出现过关闭状态,它就会成为状态标志:回复字节中的一位在应用显示开启时始终为 1,显示关闭时始终为 0;多个标志可以共用一个字节,驱动会返回 {"pump": True, "alarm": False, …}。如果两位始终同时翻转,它们将不被命名;报告会指出标志字节中发生变化但从未命名的位。
模式和其他枚举值用词语表示:mark "seen mode silent"、mark "seen profile gaming"(词语值放在最后,或逗号前:seen mode turbo, rpm 3900)。如果某个名称在至少 3 条观察记录中对应至少 2 个不同的词语,它就会成为模式:当应用显示某个词语时,回复字节的原始值每次都相同;而不同词语对应的原始值也不同。如果该字节的其他位会自行变化(或其中包含已命名的标志),系统只会采用词语之间存在差异的位,例如 bits[4:6] profile。报告会列出映射表(mode: 0x00 = silent, 0x02 = turbo)及其依据,JSON/MCP 字段会以 table 形式返回映射,驱动则会返回对应词语;对于从未见过的值,则返回原始数字:{"mode": "turbo", …}。如果应用显示负数(seen temp -5),系统也会尝试将最高位为 1 的 8 位和 16 位读数解释为有符号数(i8、i16le/i16be)。
如果某个较小的数字无法匹配整个字节(例如电池格数对应的 seen battery 3),系统也会在字节的部分位中查找:已命名标志或模式旁边的空闲位(截取为发生变化的位,因此固定的保留位会跳过)或任一半字节,例如 bits[1:4] battery;驱动会返回该数字。对于带小数显示的值(seen temp 41.5),系统也会尝试按十分位、百分位或千分位解释(原始值为 415,缩放比例为 10),因此只需两个这样的标记,无须像自由线性拟合那样提供三条。数字可以按应用显示的格式输入,支持小数逗号或千位分隔符:seen temp 41,5 °C, rpm 1,180 会被解析为 41.5 和 1180。
限制:只会命名回复(不会命名命令);如果一个名称符合两个位置,则不会采用该名称;模式只能占一个字节(或其中一组连续位),且只有你见过的词语才会获得标签;32 位读数永远不会按有符号数处理;不会尝试解析与其他位打包在一起的多位数值读数;如果显示舍入的粒度大于一个单位(例如 RPM 以 10 为步进显示),则只能使用恒等映射,不能使用缩放拟合。在 --labels 模式下,seen 标签没有时间戳,因此会被跳过。
MCP 服务
pip install usb-rea,然后将 usb-rea mcp 添加为 stdio 服务。工具:capture_start、capture_stop、mark_action、diff_actions、report、scaffold_driver、hid_descriptor(解码 HID 报告描述符/报告负载)、wireshark_dissector、replay_packet(未经人工确认时会拒绝执行)。对于不带标记的捕获,分析工具接受与 CLI 相同的 labels 参数。
使用 uv 时,无需安装:
{"mcpServers": {"usb-rea": {"command": "uvx", "args": ["--from", "usb-rea", "usb-rea", "mcp"]}}}
对于 Claude Code:claude mcp add usb-rea -- uvx --from usb-rea usb-rea mcp。该服务也列在官方 MCP Registry 中,名称为 io.github.emirmrkaljevic/usb-rea,并列在 Glama 上。Glama 会使用仓库的 Dockerfile 构建该服务,并检查它能否启动及列出其工具。
工作原理
usb-rea capture start:记录流量(usbmon / USBPcap)usb-rea mark "brightness 50%":在厂商应用中为你执行的每个操作添加标签(mark "seen rpm 1180"用于记录应用显示的内容)usb-rea diff:查看每个操作改变了哪些字节、数值关联、校验和及计数器usb-rea infer:生成消息模板和证据报告usb-rea scaffold:生成 pyusb 驱动和重放测试usb-rea dissector:为推断出的协议编写 Wireshark Lua 解码器usb-rea replay:向设备发送数据包(仅在明确确认后执行)usb-rea hid:从捕获数据(GET_DESCRIPTOR 0x22)或十六进制内容中解码 HID 报告描述符,并解码报告负载usb-rea mcp:将相同工具作为 MCP 服务提供给 AI Agent
Wireshark 解码器
usb-rea dissector capture.pcapng -o lamp.lua 会将推断出的协议转换为 Wireshark Lua 插件,让你可以继续捕获流量,并将其作为命令而非十六进制内容来读取:
wireshark -X lua_script:lamp.lua capture.pcapng
tshark -X lua_script:lamp.lua -r capture.pcapng -Y 'usbrea.command == "brightness"'
每个匹配的数据包都会生成一个 usbrea 树,其中包含命令名称、解码后的数值(usbrea.brightness.value)、计数器、查找标签和校验和检查(usbrea.checksum_ok,不匹配时会显示专家警告);Info 列会显示 brightness 50。它适用于 bulk、interrupt 和 control/HID SET_REPORT 传输,可处理来自 usbmon 或 USBPcap 的数据,且不需要 bit 库(Wireshark 3.x 和 4.x)。-n NAME 可更改过滤器前缀。CI 会使用 tshark 检查每个生成的解码器。
HID 设备
大多数 RGB 键盘、鼠标、耳机和 LCD 小设备都是带有厂商自定义页的 HID 设备。如果捕获数据中包含报告描述符(开始捕获后重新插拔设备),报告中就会包含一个区块,列出每份报告的 ID、大小和字段;每个推断出的命令也会标注其使用的 HID 报告。没有捕获数据时:
usb-rea hid /sys/class/hidraw/hidraw0/device/report_descriptor --items
usb-rea hid capture.pcapng --decode "10ff0d1d000000" --type Output
路线图
- pcap/pcapng 解析器(usbmon + USBPcap)
- 操作标记和逐操作差异比对
- 校验和检测(sum8、xor8、neg/inv sum8、CRC8、CRC16 变体)
- 有证据支持的协议报告
- 驱动脚手架(pyusb、bulk/interrupt + control/HID SET_REPORT)
- 已确认的重放
- MCP 服务
- 滚动计数器、多字节(16/32 位 LE/BE)值、查找表、多值操作
- 打包位字段
- 根据已记录的协议进行验证(liquidctl、rivalcfg)
- HID 报告描述符解码(
usb-rea hid),并将捕获的命令映射到对应的 HID 报告 - 仿真设备 CI(dummy_hcd + configfs HID;脚本位于
scripts/gadget_e2e.sh) - 真实硬件案例研究
- 多数据包事务(写入 + 应用),包括对较早数据包的校验和计算
- 后台流量:检测并忽略定期状态轮询 / 保持连接信号
- 设备响应:解码对命令、后台轮询和 GET_REPORT 的回复,提供
query_*()驱动方法 - 命名读数:
seen rpm 1180标记可命名并缩放回复读数(恒等或线性,8/16/32 位) - 开/关状态标志:
seen pump on/seen pump off可命名回复字节中的单个位 - 模式 / 枚举值(
seen mode turbo)和有符号读数(seen temp -5) - 与标志位打包的小型级别值(在状态字节的第 1–3 位中使用
seen battery 3) - 状态机(多步握手)
法律声明
为实现互操作性而进行的逆向工程受到允许(欧盟指令 2009/24/EC 第 6 条、美国 DMCA §1201(f))。usb-rea 仅分析你拥有的设备的流量,绝不再分发厂商代码。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。