OpenMouse逆向调试实录:从8万包USB抓包到破解鼠标协议的完整过程
【免费下载链接】openmouseBrowser-based control panel for supported gaming mice — change DPI, polling rate, and sensor settings without installing a driver.项目地址: https://gitcode.com/gh_mirrors/ope/openmouse
OpenMouse 是一个基于浏览器的游戏鼠标控制面板:插上鼠标、无需安装任何厂商驱动,就能直接读取并修改 DPI、轮询率、传感器等参数。而这篇文章,要讲的正是一次典型的鼠标协议逆向实战——OpenMouse 团队如何从8 万包 USB 抓包出发,最终破解了一款游戏鼠标的私有通信协议。整个流程对任何想做硬件逆向、协议分析的新手都非常有参考价值。
为什么一个"网页面板"要去抓 USB 包?
很多人以为"改鼠标 DPI"只是点一下网页按钮。但现实是:每一家厂商的鼠标协议都是私有且互不兼容的。罗技、雷蛇、Attack Shark、Lamzu……同一根 USB 线插进来,底层走的 HID 报文格式却完全不同。
OpenMouse 的做法,是把这些私有协议在浏览器里用 WebHID 重新实现一遍。它本身不依赖厂商软件,而是通过逆向工程,搞清楚"鼠标到底在听哪条命令",然后自己照着发。
正因如此,每一次新增鼠标支持,几乎都是一次小型逆向工程。而本文记录的,就是其中最有代表性的一个"翻车—抓包—破案"完整过程。
故障现场:X8 SE 被误标成"已支持"
故事的主角是Attack Shark X8 SE。它当时在支持列表里被标成了"已支持",但实际连上后却纹丝不动——DPI、轮询率、电池全部返回 0 或 null。
这种"看起来能连、数据全是空"的症状,是协议不匹配的典型案例:浏览器确实能打开设备,但发出的命令根本不是这台鼠标听得懂的方言。
面对这种问题,靠猜是没有出路的。OpenMouse 团队选择最硬核的办法——把整段 USB 流量抓下来,一个字节一个字节地看。
第一步:抓取 8 万包 USB 流量
团队使用 USBPcap 工具导出了完整的抓包文件(.pcapng,linkType 249 即 USBPcap 格式),解析出80,502 个 USB 报文。
从枚举阶段就能看到这台 X8 SE 的"身份证":
| 项目 | 值 |
|---|---|
| VID | 0x1d57 |
| PID | 0xfa60 |
| 总线 / 设备 | bus 2, device 11 |
| HID 接口数 | 4 个 |
四个接口分别是:Boot Keyboard(EP0x81)、Boot Mouse(EP0x82,贡献了 27,471 包、每包 7 字节的位移数据)、一个 Non-boot HID(EP0x83,54 包,内容恒为03 02 40 01 64),以及另一个 Boot Keyboard(EP0x84)。
设备档案和枚举逻辑散落在 hid-filters.ts 与各厂商驱动里,而这次完整排查过程被记录在 session.md。
关键发现:整段抓包里,没有一条 Feature Report
翻遍这 8 万包,团队发现了一个决定性的事实:
整个抓包里,没有发生任何一次 HID Feature Report 的交互。
这意味着驱动从未成功通过 feature report 与设备通信。而 OpenMouse 的常规读取路径恰恰依赖 feature report 去问"你的 DPI 是多少""你的轮询率是多少"。一问得不到答,返回值自然全是空。
这一步非常关键:它把问题从"某个参数读错了"缩小成了"我们问错了门"。
定位根因:同一个 VID,藏着两套协议
继续深挖后,真正的坑浮出水面。
OpenMouse 里负责"认人"的函数detectFamily(),会把所有 VID 为0x1d57的鼠标一律归到"1d57"家族(也就是 R1 / X11 协议)。可问题是——X8 SE 虽然共用这个 VID,走的却是 GearHub(25a7)协议。同一个 VID 下面,藏着两套完全不同的方言:
1d57家族:feature report0x06读轮询率、0xa0读 DPI,电池签名是[0x03, 0x55, 0x40, 0x01];25a7GearHub:64 字节 MU 类命令、0x80读固件版本、0xD4读 DPI 档位、0x04读轮询率。
驱动照着1d57的报文去敲 X8 SE 的门,自然石沉大海——这就是"全返回 0"的根因。
修复:用 usagePage0xffff给家族分家
既然两套协议共用一个 VID,就必须找一个更细的区分信号。团队的答案,是去检查设备的厂商控制集合(usagePage0xffff):GearHub 接口会暴露这个特征,而 R1/X11 不会。修复后的判断逻辑非常简洁:
if (device.vendorId === VID_1D57) { if (device.collections.some(hasVendorControl)) return "25a7"; // GearHub return device.collections.some(hasFeatureReports) ? "1d57" : null; }认出正确的家族后,再补上25a7家族的 DPI 档位预设(400 → 26000)。改完后重新构建,全部 463 个协议测试用例一次通过,X8 SE 的 DPI、轮询率、电池瞬间"活"了过来。
修复后的完整判定表(节选):
| VID | 集合判定 | 家族 | 协议 |
|---|---|---|---|
0x1d57 | 有0xffff厂商控制 | 25a7 | GearHub |
0x1d57 | 有 feature report | 1d57 | R1/X11 |
0x25a7 | 有厂商控制 | 25a7 | GearHub |
OpenMouse 自带的逆向工具箱
这次能"破案",靠的不只是运气,而是一套沉淀下来的逆向工具。它们就放在仓库里,可直接复用:
- 浏览器内抓包器—— hid-diagnostics.ts 会挂钩 WebHID 的
sendFeatureReport/receiveFeatureReport等方法,把OUT / SET / GET / IN四个方向的报文连同 reportId、字节和耗时全部记录下来(上限 10,000 条),还能一键导出成可下载的诊断轨迹。相当于把"桌面抓包"搬进了网页。 - 字节级 diff 法—— capture-format.ts 封装了协议逆向最核心的技巧:先用厂商 App 改一个设置,再对整段 profile 做快照对比,哪个字节变了,就说明那个设置对应哪个字段。这比盯着原始流量猜要高效得多。
- 硬件验证套件—— hardware-test-report.ts 定义了一套"证据标准":控制接口读回、Flash/EEPROM 读回、无线链路、写回往返校验,全部通过才算真正"支持"。
从一台鼠标,到一整个协议家族
X8 SE 只是冰山一角。OpenMouse 真正的"大脑"——所有厂商的报文编解码器和 WebHID 驱动——都集中在独立库@openmouse/protocol里,应用层(如 controller.ts)只负责调度 UI 和快照。
这种"协议与界面分离"的架构,正是它能从"修一台 X8 SE"平滑扩展到支持几十种品牌、并衍生出Desktop 桌面版和OpenMouse-Bridge(为 Chrome 153+ 的雷蛇保护接口提供原生 HID 通道)的根本原因。对想参与贡献的人,仓库里 noir-s1.md 等文档记录了真实硬件上的验证清单,是很好的入门参考。
小结:逆向的通用四步
把这次 X8 SE 的排查抽出来,其实就是任何私有硬件协议逆向都能套用的四步法:
- 复现异常:确认"能连但读不到值"这类症状;
- 全量抓包:用 USBPcap 等工具导出完整流量,做枚举分析;
- 找决定性证据:像"整段没有 Feature Report"这样能一锤定音的观察;
- 找更细的区分信号并修复:用 usagePage、集合特征等做精确判定,再用测试兜底。
下一次当你面对一台"连得上却读不出数据"的设备时,不妨从 8 万包抓包开始——答案往往就藏在那些最不起眼的字节里。
【免费下载链接】openmouseBrowser-based control panel for supported gaming mice — change DPI, polling rate, and sensor settings without installing a driver.项目地址: https://gitcode.com/gh_mirrors/ope/openmouse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考