做蓝牙开发的朋友一定经历过这种时刻手机和设备明明连上了板子里的日志也打了几个魔数一样的状态码但两端到底交换了什么、哪个服务特征被读写了、配对到底卡在哪一步你完全只能靠猜。碰到加密链路更头疼空中抓回来的包全是一堆密文连 ATT 层的 read 请求都看不出来。我最早用双模蓝牙调试器加逻辑分析仪折腾半天只能看个连接事件协议栈内部交互一概黑盒。后来换了 Nordic 的 nRF Sniffer 配 Wireshark整套流程直接顺下来了广播、扫描、连接、配对、加密再到 GATT 层的实际读写甚至 LESC 配对过程中的 ECDH 公钥交换、密钥分发都能一条一条报文看得清清楚楚。这篇文章就围绕这套工具链讲清楚 BLE 加密报文抓包与解密到底怎么做、原理是什么以及我调 LESC 配对时踩过的一堆坑。适合正在做 BLE 从机或主机开发、想快速定位连接和配对问题的工程师也适合刚入门协议分析、想把手里的 nRF52840 Dongle 用起来的朋友。1. 为什么我选择 nRF Sniffer 做 BLE 加密抓包1.1 方案选型nRF Sniffer 和 Ellisys、Frontline 这类商业工具差在哪市面上能做 BLE 抓包的工具大概分三类一类是 Ellisys、Frontline 这种数万块的专用协议分析仪功能全面还能配合硬件做功耗分析但对个人开发者和中小团队来说预算和上手成本都太高一类是 TI 的 Packet Sniffer、Silicon Labs 的 Bluetooth Analyzer 这类芯片厂商方案只能覆盖自家生态做基础过滤还行解密配置普遍比较别扭还有一类就是 Nordic 的 nRF Sniffer基于 nRF52840 Dongle 或开发板配合 Wireshark 使用开源、免费、更新快解密功能直接集成在 Wireshark 的蓝牙协议栈里。我最终选 nRF Sniffer 有三个原因。第一Wireshark 本身对 BLE 协议栈的解码已经非常成熟从 Link Layer 到 L2CAP、ATT、GATT、SMP甚至 Mesh 的 provisioning 层都有完善解析nRF Sniffer 只是把空中的射频包送进 Wireshark剩下的分析工作全交给这个强大的工具完成。第二Nordic 的 Sniffer 固件支持自动跟踪连接事件不需要像早期方案那样手动指定信道和 Access Address它能在广播阶段捕捉设备地址连接建立后自动跟随跳频序列用起来省心很多。第三解密密钥注入非常直接Wireshark 里可以填 LTK、IRK、CSRK也可以直接在抓包过程中通过 SMP 交互自动提取密钥这对调试配对流程极其有用。如果你手里没有 nRF52840 Dongle也可以用 nRF52832 开发板刷 Sniffer 固件效果一样前提是板上要能引出天线匹配。nRF52840 Dongle 自带 USB 接口和板载天线插上就能用是性价比最高的选择。1.2 硬件准备nRF52840 Dongle 的版本区别与固件烧录思路nRF52840 Dongle 市面上有两种外观老版本是蓝色 PCB、不带外壳新版本带透明塑料外壳、体积更小两者核心都是 nRF52840 SoC。固件烧录方式略有差异老版本板载了 J-Link OB 调试器直接用 USB 线连接后在 nRF Connect for Desktop 的 Programmer 应用里选择 Sniffer 固件pca10056_sniffer.hex烧进去就行新版本 Dongle 出厂自带 Bootloader按住板上的 Reset 按键再插入 USB会枚举出一个UF2 Bootloader的 U 盘把编译好的.hex文件拖进去即可完成烧录。烧录完成后Windows 系统需要装一下 USB 驱动。nRF Sniffer 在 Wireshark 里是通过 extcap 机制调用的依赖 WinUSB 驱动访问 USB 设备。如果插上 Dongle 后系统识别成串口CDC ACM就得用 Zadig 把驱动替换为 WinUSB。这一步很多新手会卡住Wireshark 里看不到 Sniffer 接口八成就是驱动没换。具体操作打开 Zadig选择 Device 菜单里的nRF Sniffer或者nRF52840 Dongle对应的 USB 设备目标驱动选 WinUSB点击 Replace Driver等待完成即可。装好驱动后Wireshark 主界面左侧的接口列表里会出现一个nRF Sniffer开头的接口正文里的所有抓包步骤都基于这个接口完成。2. 想解密先搞懂 BLE 配对加密机制2.1 Legacy Pairing 与 LESC 的本质差异BLE 里的“加密”并不是连接一开始就有的而是通过配对协议SMP协商出来的安全属性再在 Link Layer 层开启加密。配对方式分两种传统配对 Legacy Pairing 和 LE Secure ConnectionsLESC。两者最核心的差异是密钥生成算法。Legacy Pairing 使用 AES-128 加一个简单的密钥协商方式协商过程中会生成一个短期密钥 STK再用 STK 加密后续的链路。STK 的熵来源是 TKTemporary Key。如果配对方式为 Just WorksTK 固定为 0这意味着攻击者只要抓到整个配对过程的报文就能推算出链路密钥所以 Legacy Pairing 的安全性在理论上是不足的。LESC 则基于 ECDH P-256 椭圆曲线密钥交换配对双方各生成一对临时公私钥交换公钥后计算出共享的 DHKey后续的 LTK长期密钥从这个 DHKey 派生出来。即使攻击者抓到了整个配对过程的空中报文也无法在合理时间内反推出 DHKey这相当于在蓝牙协议栈里补齐了传统配对最明显的短板。做抓包解密时这个差异直接影响你的解密策略。如果你只想分析自己设备之间的通信无论哪种配对方式都能解因为密钥在你手里但如果要分析的是第三方设备的加密流量Legacy Pairing 在抓到完整配对过程的前提下可以复现密钥LESC 则基本没戏除非你能拿到设备内部保存的 LTK。2.2 解密必须用到的几个密钥分别是谁生成的解密过程中你会遇到几个密钥缩写TK、STK、LTK、IRK、CSRK。我整理了一张速查表把每个密钥的生成位置和用途说清楚。TKTemporary Key数值可能来自用户输入的 6 位 Passkey也可能是 Just Works 模式下的全 0。只在 Legacy Pairing 中出现。STKShort Term KeyLegacy Pairing 中由 TK、双方随机数 SConfirm 和 MConfirm 经过 AES-128 计算得到只在本次连接内有效。LTKLong Term KeyLegacy Pairing 中由 STK 派生LESC 中由 DHKey 和其他临时随机数派生。它是加密链路长期可用的密钥也是 Wireshark 解密时最常手动填入的密钥。IRKIdentity Resolving Key用于解析设备的随机地址和报文内容解密关系不大但会影响你能不能把私有地址解析成真实的静态地址。CSRKConnection Signature Resolving Key用于数据签名验证抓包分析时很少用到。对于 Wireshark 解密来说Legacy Pairing 时你可以填 STK也可以填最终协商出的 LTK两者在对应阶段都能解LESC 时只需要填 LTK。这些密钥的最终来源是主机端协议栈的分发数据一般在配对完成后的 SMP 分发阶段通过LL_ENC_REQ到LL_START_ENC_REQ的流程完成交换。你有权访问设备代码时直接读协议栈绑定信息里的 LTK 填进去最省事。2.3 为什么 Wireshark 能把加密报文还原出来市面上很多抓包工具只能告诉你“这里存在一个加密连接”但 Wireshark 能把密文解成明文关键在于它掌握了几个核心参数Access Address、CRCInit、以及链路密钥。Access Address 是连接建立后每个数据包的头部都带有的 32 位地址用来标识属于哪个连接CRCInit 是 24 位初始值用于校验包的完整性。这两个参数在连接建立时通过 CONNECT_IND 广播包发给从机Sniffer 在跟踪连接时会自动解析并记录下来不需要手动配置。真正让解密成为可能的是密钥。你在 Wireshark 里填好 LTK或者通过 SMP 自动识别Wireshark 的蓝牙协议栈会用这些密钥对 Link Layer 层的数据包进行解密还原出 L2CAP 头、ATT/GATT 内容。解密过程发生在协议栈内部你不用关心它是怎么把 AES-CCM 的 nonce 拼接出来的只要保证两点一是抓包环境中能抓到完整配对过程自动提取密钥的前提二是手动填入的密钥和设备实际使用的密钥一致。这里有一个很关键的概念要区分抓包和解密是两回事。抓包只负责把空中的 bit 流收下来解密则需要额外的密钥信息。就算 nRF Sniffer 抓到了完整的加密连接没有密钥你看到的依然是密文。所以在实际调试中如果只是想快速看通信内容建议在协议栈里临时禁掉加密选项等逻辑调通后再打开加密验证安全性如果需要分析加密链路的真实行为再走完整的配对抓包解密流程。3. 5分钟抓包与解密完整实战3.1 搭建 nRF Sniffer 到 Wireshark 的链路开始之前先确认环境。硬件方面需要一块 nRF52840 Dongle或刷了 Sniffer 固件的 nRF52832 开发板一台 Windows/Linux/macOS 电脑以及一台能发起 BLE 通信的设备手机或另一块开发板都可以。软件方面Wireshark 建议装 4.0 以上版本越新越好老版本对 BLE 解密密钥管理支持较差。从 Nordic 官网下载 nRF Sniffer for BLE 压缩包解压后你会看到这几个关键目录hex针对不同硬件的 Sniffer 固件如pca10056_sniffer.hex对应 nRF52840 Dongle。extcapWireshark 插件目录里面有nrf_sniffer_ble.py和配套的 bat/sh 启动脚本。doc官方文档建议通读一遍尤其是抓包模式选择部分。把extcap目录里的所有文件复制到 Wireshark 的插件目录。Windows 默认路径是C:\Program Files\Wireshark\extcapmacOS 需要根据 Wireshark 版本找到lib/wireshark/extcap目录。复制时注意文件权限macOS 下如果提示无法执行要给 Python 脚本加一下执行权限。完成后先不要急着打开 Wireshark把所有已经打开的 Wireshark 进程关掉。重新打开主界面的接口列表里应该出现nRF Sniffer开头的接口。如果没出现依次检查驱动、插件路径、Wireshark 版本这三个环节按顺序排查。3.2 开始抓包从广播到加密连接的完整时序打开 nRF Sniffer 接口后Wireshark 会进入一个抓包界面。默认处于扫描模式你能看到周围大量的广播包。在显示过滤器里输入btle.advertising_address相关的过滤条件或者直接在 Packet List 面板里找到目标设备右键点击设备地址选择 “Apply as Filter” 可以快速找到该设备的所有广播。设备开始连接后nRF Sniffer 会自动识别 CONNECT_IND 包并切换到连接跟踪模式。你会看到连接相关的 Link Layer 数据包LL_CONNECTION_UPDATE_REQ、LL_CHANNEL_MAP_IND、LL_PAUSE_ENC_REQ、LL_ENC_REQ等。加密流程开始的标志是主机发送LL_ENC_REQ从机回复LL_ENC_RSP后双方交换随机数和确认值。之后主机发送LL_START_ENC_REQ开启加密。从这一刻起Wireshark 里看到的 DATA 包数据部分会变成一串不可读的十六进制字节。想直观对比加密前后的差异可以在 Packet List 里观察包长度和数据内容。加密开启前第一个 ATT 请求比如Read By Group Type Request明文可见协议解析树里能展开 GATT 层加密开启后同样的请求变成一段密文协议解析树只显示Encrypted Data或者直接显示为未知数据。如果你的目标是马上看到明文内容需要先把密钥信息交给 Wireshark。操作路径是菜单栏 Edit - Preferences - Protocols - Bluetooth在 Decryption Keys 里点 Add Key。Key Type 选择LTKAddress 填设备地址Key 填十六进制的 LTK 值。这里有个小坑设备地址类型要注意区分Public和Random填错了 Wireshark 匹配不上解密会失败。3.3 注入密钥并解密查看 ATT/GATT 内容密钥注入成功后你会发现原本不可读的加密数据包自动被解密Wireshark 的协议树里会出现Bluetooth ATT层你能看到具体的 opcode、handle、value。这个效果非常直观调试服务特征读写时配合过滤表达式btatt就能只看 GATT 层交互。举个例子手机连接设备后读取电池服务特征加密链路下的抓包里会依次出现Read By Group Type RequestRead By Group Type ResponseRead RequestRead Response解密之前这些都混在一堆密文里解密之后一目了然。对于定位“设备连上了但读不到数据”这类问题能直接判断是主机没发请求还是从机没回响应甚至能看出响应里的错误码。LESC 配对场景下你还能在抓包里看到 SMP 层的 ECDH Public Key、Pairing Confirm、Pairing Random 等关键包。Wireshark 会解析 ECDH 公钥的 X、Y 坐标配合代码里的日志可以确认两端是否真的协商了同一个 DHKey。3.4 没有抓到 Pairing 也能解的补救方案实际开发中有一种常见情况连接已经建立且加密了但你开始抓包的时候已经错过了配对阶段或者配对是在另一台设备上完成的你在手机端只触发了一次重连。这时候 Wireshark 里没有 SMP 交互记录自动提取密钥这条路走不通。补救方案是直接读取蓝牙协议栈的绑定数据。Android 端在/data/misc/bluetooth/下有 bonded devices 相关的数据库里面保存了每个绑定设备的 LTK、IRK 等密钥iOS 端无法直接越狱读取但如果你是设备端开发者可以直接在 nRF Connect SDK 的 flash 存储里读出 LTK。拿到 LTK 后按上一小节的路径在 Wireshark 里手动添加密钥即可解密。还有一个小技巧配对完成后设备会保存 LTK之后的重新连接会使用同一个 LTK 进行链路加密。你可以在代码里打印 LTK然后在 Wireshark 里填进去。前提是 LTK 没有发生轮换如果上层主动更新了密钥旧的 LTK 自然失效。4. LESC 配对调试技巧4.1 调试 LESC 配对过程中的关键检查点LESC 配对调试最烦的地方在于问题往往不是“能不能连”而是“配对进入了错误的安全等级”。最常见的现象是明明双方都声明支持 LE Secure Connections但实际配对结果还是 Just Works 级别没有 MITM 保护。要定位这个问题第一步是看 Pairing Request 和 Pairing Response 里的 AuthReq 字段。AuthReq 里有一个 SC 位为 1 表示支持 LE Secure Connections为 0 表示只支持 Legacy Pairing。两端必须都置 1最终才会走 LESC 流程。第二步看 IO Capabilities 组合。LESC 的配对方法由双方的 IO 能力共同决定如果两边都是 NoInputNoOutput最终会退化为 Just Works也就是虽然有 ECDH 密钥交换但没有用户确认步骤无法防中间人。如果一端有 DisplayYesNo另一端有 KeyboardOnly会采用 Passkey 方式只有两端都能显示确认才能用 Numeric Comparison也就是我们常说的“蓝牙配对时两边显示 6 位数字”的流程。调试时建议给两端的 IO Capability 明确赋值不要依赖默认值。很多 SDK 的默认 IO 能力是 NoInputNoOutput如果你希望触发 Numeric Comparison至少要把主机端配成 DisplayYesNo从机端配成 KeyboardOnly 或者 DisplayYesNo。4.2 用 Wireshark 分析 LESC 配对失败问题LESC 配对过程中Wireshark 的 SMP 解析树会显示每一步的状态。常用过滤表达式btl2cap.cid 0x0006或者smp可以把 SMP 包筛出来。一次正常的 LESC 配对流程在 Wireshark 里大致是Pairing Request / Pairing Response协商安全等级和 IO 能力。Public Key Exchange双方发送 ECDH P-256 公钥。Authentication Confirm 1 / 2交换确认值。Authentication Random 1 / 2交换随机数。DHKey Check验证 DHKey 一致性。Encryption Information / Identity Information 等分发 LTK、IRK、CSRK。如果配对失败Wireshark 里通常会看到一个Pairing Failed包错误码会告诉你原因。常见错误码包括PIN or Key Missing0x06、Encryption Key Too Short0x07、Repeated Attempts0x09等。结合错误码去查协议栈日志比盲目改代码高效得多。我曾经在自定义板子上遇到一种非常隐蔽的问题从机端的 ECDH 公钥每次配对都会变但主机端因为 flash 里存了旧的 DHKey 校验值导致 DHKey Check 永远过不了。从 Wireshark 里看整个过程卡在DHKey Check之后主机端一直回Pairing Failed。排查了半天才发现是主机端没有清理旧绑定信息重新 bind 后问题消失。4.3 解密失败的常见原因与排查速查表整理了这段时间用过的高频排查表遇到问题直接对着查Sniffer 接口没出现在 Wireshark 里检查 extcap 插件路径是否正确Wireshark 是否以管理员权限运行Zadig 驱动是否已切换为 WinUSB。能抓广播但无法跟踪连接确认设备使用的是标准跳频算法检查 CONNECT_IND 是否被完整抓到必要时在射频环境更干净的环境下重新抓包。解密失败Wireshark 显示 No Decryption Key确认密钥类型和地址类型填写正确确认该连接使用的 LTK 没有被更新。解密结果是一堆乱码很可能是密钥填错了注意大小写和字节序Wireshark 里填的格式是十六进制字符串不要加空格。LESC 配对时 Authentication Confirm 反复失败优先检查两端是否都支持 SC以及是否误开了 OOB 模式但没有真正配置 OOB 数据。抓包过程中大量丢包远离 2.4G Wi-Fi 路由器等干扰源使用 USB 延长线让 Dongle 尽量靠近被测设备。“丢包”这个问题值得多说一句。nRF Sniffer 的接收能力受环境影响很大我实测过在办公区工位上抓包信道 37 的广播包丢失率能到 20% 以上。最好把 Dongle 放在离被测设备 30 厘米以内并且避免中间有金属遮挡。如果抓包要长时间运行可以用btle过滤条件减少数据量避免 Wireshark 渲染卡顿导致丢包。4.4 关于 BLE Mesh Remote Provisioning 的一个延伸提点很多热词里常把 BLE Mesh Remote Provisioning 和抓包解密放在一起讨论。如果你的工作涉及 MeshnRF Sniffer 同样可以抓 Mesh 网络的 provisioning 包和网络层包。区别在于 Mesh 网络的加密密钥是 Network Key 和 Application Key在 Wireshark 里需要手动填入这两类密钥才能解密。这类操作和 BLE 连接加密的解密思路一致但这个扩展话题太大这里不多展开后续有机会单独写一篇。5. 一些个人经验总结整个流程跑顺之后我最大的感触是抓包解密的瓶颈往往不在工具而在对协议机制的理解。只要搞清楚了 Legacy Pairing 和 LESC 在密钥生成上的差异理解了 LTK 在整个加密链路里的作用入手 nRF Sniffer 基本上没有太多阻碍。我个人的建议是第一次练习不要直接用 LESC 加密环境先跑一遍不加密的抓包熟悉 nRF Sniffer 的界面和 Wireshark 的 BLE 过滤表达式然后开 Legacy Pairing 抓一遍手动填 STK 或 LTK 解密最后再上 LESC用自动提取或手动导入 LTK 的方式解密。三步走下来以后遇到任何 BLE 加密通信问题都不会心虚。最后分享一个平时常用的工作流写好代码后先把协议栈的安全使能关闭用明文跑通业务逻辑再用 Sniffer 确认关键数据流业务逻辑稳定后打开加密重新抓包验证解密是否正常。这个流程能省掉大量“到底是业务错了还是解密错了”的排错时间。