3. Wireshark基础操作:捕获过滤器与显示过滤器、BLE协议栈分层解析、Packet Details面板解读
好,咱们正式开始动手。前两章我们把蓝牙的物理层和协议栈骨架搭起来了,现在该真刀真枪地打开Wireshark了。说实话,我第一次用Wireshark抓BLE包的时候,看着满屏花花绿绿的报文,脑子是懵的。后来摸清了门道,才发现它就是个「剥洋葱」的工具——你只要知道每一层该看什么,它就变得特别听话。
3.1 捕获过滤器 vs 显示过滤器:别搞混了
这两个东西名字像,但干的活完全不同。我见过不少新手在这上面栽跟头,所以咱们先把这个理清楚。
| 对比项 | 捕获过滤器 | 显示过滤器 |
|---|---|---|
| 作用时机 | 抓包开始前,网卡驱动层 | 抓包完成后,Wireshark解析层 |
| 语法 | BPF(Berkeley Packet Filter) | Wireshark专有语法(display filter) |
| 性能影响 | 直接减少抓取的数据量,省CPU | 只影响显示,不减少抓取量 |
| 典型场景 | 只抓BLE广播包,过滤掉所有数据包 | 在已抓的10000个包里,只看某个设备的连接请求 |
捕获过滤器,说白了就是「抓之前就筛掉」。它的语法是BPF,比较古老。比如我只想抓BLE广播信道(37/38/39)上的包,可以这样写:
# 只抓BLE广播信道(37/38/39)上的包
# 注意:这需要你的抓包硬件支持信道过滤
# 以nRF Sniffer为例,通常用Wireshark的接口选项设置
但说实话,我现在很少用捕获过滤器了。为什么?因为现在的抓包硬件(比如nRF52840 DK + Sniffer固件)性能足够强,我宁愿把所有包都抓下来,再用显示过滤器慢慢分析。你想想看,万一你漏掉了某个关键包,还得重新抓一次,多麻烦。
显示过滤器才是我们日常的主力。它的语法更灵活,而且支持自动补全。我常用的几个:
# 只看BLE广播包
btle.advertising_header.pdu_type == 0
# 只看某个特定设备的连接请求
btle.advertising_header.pdu_type == 5 && btle.advertising_address == 11:22:33:44:55:66
# 只看LL层的数据包(连接建立后)
btle.type == LL_DATA_PDU
# 只看ATT写请求
att.opcode == 0x52
我的小技巧:在Wireshark的显示过滤器输入框里,你输入 btle. 之后按Tab键,它会弹出所有可用的字段。这个功能我用了三年才发现,后悔没早点知道。
3.2 BLE协议栈分层解析:从空中到应用
好,现在我们来抓一个实际的BLE连接过程。假设你有一个蓝牙手环在广播,你用手机去连它。Wireshark里会看到什么?
我习惯把BLE协议栈分成四层来看,每一层在Wireshark里都有对应的展开项:
- 物理层(PHY) —— 你看不到,但Wireshark会告诉你信道号、RSSI
- 链路层(LL) —— 这是最核心的,负责设备发现、连接建立、数据重传
- L2CAP层 —— 负责数据分段重组,以及信道管理
- ATT/GATT层 —— 应用层,读写特征值、收发通知
咱们用一张图来理解这个分层关系:
嗯,这里要注意:Wireshark里显示的「分层」其实是我们人为划分的。实际抓到的包是一个完整的PDU,Wireshark帮我们拆开了。你点开一个包,在Packet Details面板里从上往下看,就是「从物理层到应用层」的顺序。
3.3 Packet Details面板解读:逐层剥开
咱们拿一个实际的广播包来举例。假设你在Wireshark里双击一个广播包,Packet Details面板会展开成这样的结构:
Frame 1: 47 bytes on wire (376 bits)
[Protocols in frame: btle:btle_rf:btle_ll:btle_hci]
Bluetooth
[Source: xx:xx:xx:xx:xx:xx]
[Destination: Broadcast]
Bluetooth Low Energy
[Access Address: 0x8e89bed6] ← 广播包的AA固定值
[Packet Type: Advertising]
[Channel Index: 37] ← 广播信道37
[RSSI: -65 dBm] ← 信号强度
[CRC Status: OK]
Bluetooth Low Energy Link Layer
Advertising Header
PDU Type: ADV_IND (0)
ChSel: 2
TxAdd: Random
RxAdd: Random
Length: 31
Advertising Address: xx:xx:xx:xx:xx:xx
Advertising Data (31 bytes)
[AD Type: Flags]
[AD Type: Complete Local Name]
[AD Type: TX Power Level]
你看,从上到下,每一层都对应一个「+」号展开项。我个人的习惯是:
- 先看Frame层 —— 确认包大小、时间戳、有没有被截断
- 再看Bluetooth Low Energy层 —— 看信道、RSSI、CRC状态。CRC失败?那这个包大概率是干扰或者重传
- 最后看Link Layer层 —— 这才是真正的内容。PDU类型是什么?广播地址对不对?AD数据里有没有我们关心的服务UUID?
我曾经踩过的坑:有一次我抓到一个设备的广播包,但怎么也搜不到它的服务UUID。折腾了半天,才发现它用的是「Service Data」而不是「Service UUID」来广播。Wireshark里这两个字段长得像,但含义完全不同。所以,别只看字段名,要展开看具体的AD Type值。
3.4 实战:用显示过滤器定位连接过程
好,理论说完了,咱们来点实际的。假设你抓了一大堆包,现在想找到「设备A连接设备B」的那个瞬间。怎么做?
连接过程在BLE里其实就几个关键包:
- ADV_IND —— 广播包,设备在说「我在这儿」
- CONNECT_IND —— 连接请求,发起方说「咱俩连吧」
- LL_DATA_PDU —— 连接建立后的数据包
用显示过滤器可以这样定位:
# 只看连接请求包(PDU Type = 5)
btle.advertising_header.pdu_type == 5
# 或者更直接,看LL层的连接指示
btle.ll.opcode == 0x05
找到CONNECT_IND包之后,双击它。在Packet Details里展开Link Layer,你会看到:
- InitA —— 发起方的设备地址
- AdvA —— 被连接方的设备地址
- Access Address —— 连接后使用的AA值(不再是0x8e89bed6了)
- CRCInit —— 连接后的CRC初始值
- WindowSize / WindowOffset —— 连接参数,决定跳频时序
拿到这些信息后,你就可以用它们来过滤后续的连接数据包了:
# 只看这个连接上的数据包
btle.access_address == 0x12345678
核心要点:连接建立后,Access Address会从固定的广播AA(0x8e89bed6)变成一个随机值。这个随机值就是你在CONNECT_IND包里看到的。后续所有数据包都使用这个AA,所以它是过滤连接数据包的最佳钥匙。
3.5 避坑指南:常见问题与排查
最后,分享几个我实际工作中遇到的坑:
- 抓不到广播包? 检查一下你的抓包硬件是不是在正确的信道上。nRF Sniffer默认只扫描37/38/39三个广播信道,但有些设备会在数据信道上发扩展广播。
- CRC一直失败? 可能是两个设备离得太远,或者有同频干扰。试试把设备靠近抓包器,或者换个环境。
- Wireshark显示「Malformed Packet」? 别慌,这通常不是你的问题。可能是抓包硬件丢了一个字节,或者固件版本不匹配。先更新一下Sniffer固件试试。
嗯,这一章的内容就到这儿。记住,Wireshark只是个工具,真正值钱的是你脑子里对协议栈的理解。多抓、多看、多对比,慢慢你就能一眼看出问题在哪儿了。