news 2026/9/28 17:02:17

nRF Sniffer + Wireshark 实战:BLE加密报文抓包与LESC配对解密

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
nRF Sniffer + Wireshark 实战:BLE加密报文抓包与LESC配对解密

做蓝牙开发的朋友一定经历过这种时刻:手机和设备明明连上了,板子里的日志也打了几个魔数一样的状态码,但两端到底交换了什么、哪个服务特征被读写了、配对到底卡在哪一步,你完全只能靠猜。碰到加密链路更头疼,空中抓回来的包全是一堆密文,连 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 Connections(LESC)。两者最核心的差异是密钥生成算法。

Legacy Pairing 使用 AES-128 加一个简单的密钥协商方式,协商过程中会生成一个短期密钥 STK,再用 STK 加密后续的链路。STK 的熵来源是 TK(Temporary Key)。如果配对方式为 Just Works,TK 固定为 0,这意味着攻击者只要抓到整个配对过程的报文,就能推算出链路密钥,所以 Legacy Pairing 的安全性在理论上是不足的。

LESC 则基于 ECDH P-256 椭圆曲线密钥交换,配对双方各生成一对临时公私钥,交换公钥后计算出共享的 DHKey,后续的 LTK(长期密钥)从这个 DHKey 派生出来。即使攻击者抓到了整个配对过程的空中报文,也无法在合理时间内反推出 DHKey,这相当于在蓝牙协议栈里补齐了传统配对最明显的短板。

做抓包解密时,这个差异直接影响你的解密策略。如果你只想分析自己设备之间的通信,无论哪种配对方式都能解,因为密钥在你手里;但如果要分析的是第三方设备的加密流量,Legacy Pairing 在抓到完整配对过程的前提下可以复现密钥,LESC 则基本没戏,除非你能拿到设备内部保存的 LTK。

2.2 解密必须用到的几个密钥分别是谁生成的

解密过程中你会遇到几个密钥缩写:TK、STK、LTK、IRK、CSRK。我整理了一张速查表,把每个密钥的生成位置和用途说清楚。

  • TK:Temporary Key,数值可能来自用户输入的 6 位 Passkey,也可能是 Just Works 模式下的全 0。只在 Legacy Pairing 中出现。
  • STK:Short Term Key,Legacy Pairing 中由 TK、双方随机数 SConfirm 和 MConfirm 经过 AES-128 计算得到,只在本次连接内有效。
  • LTK:Long Term Key,Legacy Pairing 中由 STK 派生,LESC 中由 DHKey 和其他临时随机数派生。它是加密链路长期可用的密钥,也是 Wireshark 解密时最常手动填入的密钥。
  • IRK:Identity Resolving Key,用于解析设备的随机地址,和报文内容解密关系不大,但会影响你能不能把私有地址解析成真实的静态地址。
  • CSRK:Connection 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。
  • extcap:Wireshark 插件目录,里面有nrf_sniffer_ble.py和配套的 bat/sh 启动脚本。
  • doc:官方文档,建议通读一遍,尤其是抓包模式选择部分。

把extcap目录里的所有文件复制到 Wireshark 的插件目录。Windows 默认路径是C:\Program Files\Wireshark\extcap,macOS 需要根据 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 选择LTK,Address 填设备地址,Key 填十六进制的 LTK 值。这里有个小坑:设备地址类型要注意区分Public和Random,填错了 Wireshark 匹配不上,解密会失败。

3.3 注入密钥并解密查看 ATT/GATT 内容

密钥注入成功后,你会发现原本不可读的加密数据包自动被解密,Wireshark 的协议树里会出现Bluetooth ATT层,你能看到具体的 opcode、handle、value。这个效果非常直观,调试服务特征读写时,配合过滤表达式btatt就能只看 GATT 层交互。

举个例子,手机连接设备后读取电池服务特征,加密链路下的抓包里会依次出现:

  • Read By Group Type Request
  • Read By Group Type Response
  • Read Request
  • Read 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 里大致是:

  1. Pairing Request / Pairing Response:协商安全等级和 IO 能力。
  2. Public Key Exchange:双方发送 ECDH P-256 公钥。
  3. Authentication Confirm 1 / 2:交换确认值。
  4. Authentication Random 1 / 2:交换随机数。
  5. DHKey Check:验证 DHKey 一致性。
  6. Encryption Information / Identity Information 等:分发 LTK、IRK、CSRK。

如果配对失败,Wireshark 里通常会看到一个Pairing Failed包,错误码会告诉你原因。常见错误码包括PIN or Key Missing(0x06)、Encryption Key Too Short(0x07)、Repeated Attempts(0x09)等。结合错误码去查协议栈日志,比盲目改代码高效得多。

我曾经在自定义板子上遇到一种非常隐蔽的问题:从机端的 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 和抓包解密放在一起讨论。如果你的工作涉及 Mesh,nRF 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 确认关键数据流;业务逻辑稳定后打开加密,重新抓包验证解密是否正常。这个流程能省掉大量“到底是业务错了还是解密错了”的排错时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 17:02:13

Excel学习笔记:从故障排查到Python自动化实战

翻开2026年3月16日这份Excel学习笔记,我盯着屏幕想了想,决定不再让这些零散的知识点躺在草稿箱里吃灰。从“复制粘贴没反应”这种基础故障,到“Python批量写入Excel”再到“栅格数据转换导出”这类跨工具操作,过去一段时间我攒了不…

作者头像 李华
网站建设 2026/9/28 17:00:54

CLI-Anything:将一切操作变成终端命令的脚手架

经常有人在群里问我:有没有一个办法,把乱七八糟的日常任务都塞进终端,一个命令搞定?说实话,我之前一直没找到特别满意的,直到我折腾出了CLI-Anything。严格来说,CLI-Anything不是一个单一的工具…

作者头像 李华
网站建设 2026/9/28 17:00:40

UDS诊断0x1906服务详解:Python模拟实现与ECU故障统计应用

1. 0x1906服务到底在诊断链路里扮演什么角色很多人做UDS诊断开发,一上来就盯着0x19服务的各个子功能,觉得读故障码就是0x1902、清故障码就是0x1904、快照就是0x1906,但真正在项目里跑起来才发现,0x1906这个子功能用的人少&#xf…

作者头像 李华
网站建设 2026/9/28 17:00:21

Python爬虫实战:Ajax接口与JSON解析,轻松抓取金融行情数据

上个月有个做股票复盘的朋友找我,说他每天夜里都要在某财经网站上翻行情列表,手动翻页、复制粘贴、再粘到Excel里,光是整理数据就花一个小时。我听了直接摇头:这种重复劳动,早就该交给Python爬虫了。他犹豫了一下&…

作者头像 李华
网站建设 2026/9/28 16:59:52

Python爬虫实战:Ajax接口定位与JSON解析,搞定金融行情数据

前两年帮一个做量化研究的朋友搭建数据采集流程,打开某行情网站的电脑端页面,习惯性地用Requests把首页HTML抓下来,结果翻遍源码,连一个行情数字的影子都没找到——页面结构里全是空容器和一大段压缩过的JavaScript。那一刻我意识…

作者头像 李华