蓝牙推送协议实测逆向:外置串口当旁路探头,五个疑点一次修完
Contents
蓝牙模块挂在主机的 UART 上:手机连上蓝牙放歌,模块把曲名、艺术家、进度等状态以文本行推给主机,主机再转发到控制面板显示。没有正式协议文档,厂商给的"说明"和实际行为对不上。这篇记录怎么用外置串口把协议抓明白,以及五个疑点的修复——其中三个修复后来直接变成了协议设计。
一、方法论:外置串口当旁路探头
飞线把 USB 串口适配器的 RX 并联到蓝牙模块的 TX 脚——只听不打扰:主机和模块之间的对话原样进行,我在旁边抄底稿。然后让手机做一套标准动作:连接、播放、暂停、切歌、断开,把每个动作触发的原始行录下来。
比"在主机代码里加日志"强在三点:
- 不改任何代码——加日志本身可能改变接收时序,掩盖问题;
- 主机侧解析还没写好的阶段也能先积累语料,协议认识和代码实现解耦;
- 出争议时它是权威仲裁——“芯片到底发了没有"一锤定音。
后来查串口 RX 失聪问题(另篇)时,这个探头又立了一功:芯片在发、主机收不到,硬件和软件的责任当场切开。
二、建立词汇表
抓了两天语料,整理出 21 个词条的词汇表。每个词条三要素:样本、触发动作、语义推断。例如:
| 词条 | 样本(节选) | 语义 |
|---|---|---|
| TITLE | 0,歌名 |
曲名(带流索引前缀) |
| ARTIST / ALBUM | 0,歌手 |
艺术家/专辑 |
| PLAYPOS | 毫秒数值 | 播放进度(单位是毫秒!) |
| DURATION | 毫秒数值 | 总时长 |
| MEDIAINFO | START … END 块 |
编码/采样率/比特率复合块 |
| CONI / DISI | 事件行 | 连接/断开事件 |
| NUMBER | 号码串 | 来电号码 |
词汇表是后续一切的根:面板显示映射、主机解析、测试用例都从它抄。
三、五个疑点与修复
1. “0,” 前缀剥离。 曲名行恒以 0, 开头——那是模块的流索引前缀,不是歌名的一部分。解析层剥掉,显示层永远不该见到它。
2. MEDIAINFO 块合并(这个成了设计)。 模块把编码/采样率/比特率拆成多行、用 START/END 包起来发。如果逐行透传,面板会闪中间态:先只看到编码、再蹦出采样率、最后比特率——一行"44.1kHz FLAC"拆成三次 UI 抖动。改成攒齐 END 才推最终形态,一次成型。协议推送的最小单位应该是"一条完整信息”,不是"一行"。
3. ms→s 转换放协议层。 PLAYPOS/DURATION 推的是毫秒,显示要 mm:ss。转换放在主机协议层而不是面板显示层——显示层不该知道单位的历史包袱,以后加第二种显示终端时不用再转一遍。
4. 0x06 参数变化事件(这个也成了设计)。 采样率/编码的变化以前靠面板轮询发现,慢半拍还费查询流量。协议增加 0x06(AUDIO_PARAMS_CHANGED)事件:主机一收到就主动把最新参数推给面板——变轮询为推送。事件驱动这招在私有协议里性价比极高:一个事件号换来一整类轮询的删除。
5. CONI/DISI 正名。 最初按字面猜"连接/断开",实测事件序列发现语义对不上。推翻重录——词汇表里每个词条都必须有"触发动作 → 观测样本"的成对证据,字面猜测一律标注"未证实",拿到证据才能转正。
四、协议权威:只有一份真相
面板和主机是两套代码各自实现同一份私有协议,最怕"两份都像真的"文档各说各话。这次立下规矩:枚举权威在下位机的协议头文件(一条词条一个常量,注释里带样本),面板要改映射、加词条,先查它再动手。文档会过期,代码里的枚举不会。
五、小结
- 旁路探头(只听不打扰)是私有协议逆向的第一工具;
- 词汇表三要素:样本、触发动作、语义——缺一不可;
- 推送的最小单位是完整信息块,不是一行;
- 能用事件推送解决的,不要留给轮询;
- 私有协议必须有单一权威定义点,且它在代码里。
Author 软件开发大郭
LastMod 2026-09-26