2、BLE协议栈基础:物理层(PHY)与射频基础、链路层(LL)状态机、HCI接口与数据包格式

做蓝牙抓包分析,你得先搞清楚协议栈里每一层在干什么。说白了,抓包就是监听空中的报文,但如果你不懂物理层怎么调频、链路层怎么切换状态、HCI接口怎么封装数据,那抓回来的包就是一堆乱码。

我个人习惯,把BLE协议栈分成三层来看:物理层(PHY)负责发信号,链路层(LL)负责管连接,主机控制器接口(HCI)负责传数据。今天咱们就把这三层掰开揉碎了讲清楚。

2.1 物理层(PHY)与射频基础

物理层是协议栈的最底层,它直接跟天线打交道。BLE工作在2.4GHz ISM频段,这个频段大家都能用——Wi-Fi、Zigbee、微波炉都挤在这里。所以BLE设计了一套跳频机制来避开干扰。

2.1.1 频段与信道

BLE把2.4GHz频段划分成40个信道,每个信道带宽2MHz。这40个信道分成两类:

信道类型 信道编号 中心频率 用途
广播信道 37、38、39 2402MHz、2426MHz、2480MHz 设备发现、广播、连接建立
数据信道 0~36(共37个) 2404MHz ~ 2478MHz 连接后的数据传输

为什么广播信道只有3个?我刚开始学的时候也纳闷。后来做项目才明白——这3个信道故意避开了Wi-Fi最常用的1、6、11信道。你想想看,如果广播信道跟Wi-Fi信道重叠,那手机搜设备时可能半天搜不到。这是BLE设计者早就考虑好的。

抓包要点:抓广播包时,监听37、38、39三个信道就够了。但抓连接后的数据包,你得跟着跳频走,否则会丢包。

2.1.2 调制与速率

BLE使用GFSK(高斯频移键控)调制,说白了就是用频率变化来表示0和1。BLE 4.0/4.1/4.2的速率是1Mbps,BLE 5.0引入了2Mbps模式,速率翻倍,但传输距离会缩短。

我在项目中遇到过一个问题:用2Mbps模式抓包,抓到的数据总是有CRC错误。后来发现是射频环境太差,2Mbps对信噪比要求更高。所以实际项目中,如果距离远或者干扰大,我建议降回1Mbps。

2.1.3 发射功率与接收灵敏度

BLE的发射功率范围一般是-20dBm到+10dBm。接收灵敏度通常在-90dBm到-97dBm之间。这两个参数直接影响通信距离。

嗯,这里要注意:发射功率不是越大越好。功率大了,功耗也大,而且可能干扰其他设备。我一般先设0dBm,测试时再根据实际距离调整。

2.2 链路层(LL)状态机

链路层是BLE协议栈的核心。它管理设备的状态切换,所有抓包行为本质上都是在跟踪链路层的状态变化。

BLE链路层有5种状态:

  • 待机(Standby):初始状态,不发送也不接收数据
  • 广播(Advertising):发送广播包,让别人发现自己
  • 扫描(Scanning):监听广播包,寻找设备
  • 发起(Initiating):主动连接某个广播设备
  • 连接(Connection):已建立连接,双方收发数据

状态切换的规则很简单:待机是起点,其他状态都能回到待机。但广播、扫描、发起这三个状态不能直接互相切换,必须经过待机。

个人经验:抓包时,如果你看到设备频繁在广播和待机之间切换,说明它可能在省电。如果一直广播不停,那可能是配置了持续广播模式。

下面这张图展示了链路层状态机的完整切换流程:

BLE链路层状态机 待机 (Standby) 广播 (Advertising) 扫描 (Scanning) 发起 (Initiating) 连接 (Connection) 开始广播 开始扫描 发起连接 停止广播 停止扫描 连接失败 收到连接请求 连接成功 断开连接 注意:广播、扫描、发起三个状态不能直接互相切换,必须经过待机 连接状态只能从广播或发起状态进入

2.2.1 连接状态详解

连接状态是BLE最常用的状态。一旦连接建立,主设备和从设备会按照固定的时间间隔交换数据,这个间隔叫连接间隔(Connection Interval)。

连接间隔的范围是7.5ms到4s,步进1.25ms。我一般设30ms到50ms,既能保证实时性,功耗也不高。如果你做的是传感器类产品,数据量小,可以设到100ms以上。

避坑指南:我曾经在一个项目中把连接间隔设成了7.5ms,结果抓包发现丢包率高达30%。后来排查发现,连接间隔太短,设备来不及处理完上一个包,下一个包就到了。所以连接间隔不是越短越好,得看设备处理能力。

2.3 HCI接口与数据包格式

HCI(Host Controller Interface)是主机和控制器之间的通信接口。在BLE协议栈中,主机跑的是GAP、GATT、L2CAP等上层协议,控制器跑的是链路层和物理层。HCI就是连接这两部分的桥梁。

2.3.1 HCI数据包类型

HCI定义了4种数据包:

包类型 用途 方向
命令包 主机发送命令给控制器 主机→控制器
事件包 控制器回复事件给主机 控制器→主机
ACL数据包 传输用户数据 双向
同步数据包 传输音频等同步数据 双向

抓包时,我们最常看到的是ACL数据包和事件包。命令包一般在连接建立前出现,比如发送连接请求。

2.3.2 HCI数据包格式

每个HCI数据包都有一个固定的头部格式。以ACL数据包为例:

ACL数据包格式:
┌─────────┬──────────┬────────────┬──────────────┐
│ 头部(4B)│ 长度(2B) │ 数据(可变) │ 可选字段      │
├─────────┼──────────┼────────────┼──────────────┤
│ 0x02    │ 0x0020   │ ...        │ ...          │
└─────────┴──────────┴────────────┴──────────────┘

头部格式:
┌───────┬───────┬───────┬───────┐
│ 句柄  │ 标志  │ 长度  │       │
│ (2B)  │ (2B)  │ (2B)  │       │
└───────┴───────┴───────┴───────┘

句柄(Handle)标识连接对象,标志(Flags)表示数据包类型(比如起始包还是中间包),长度(Length)是数据部分的字节数。

抓包技巧:用Wireshark抓HCI数据时,注意看标志位。如果标志位是0x02,说明这是L2CAP的起始包;如果是0x01,说明是中间包。我曾经因为没注意这个标志,把一个长数据包拆开分析,结果完全对不上。

2.3.3 HCI命令与事件示例

我举个实际例子。当主机想扫描设备时,会发送HCI命令:

HCI命令:LE Set Scan Parameters
Opcode: 0x200B (OGF=0x08, OCF=0x000B)
参数:
  - 扫描类型:0x01(主动扫描)
  - 扫描间隔:0x0060(60个时间单位,约60ms)
  - 扫描窗口:0x0030(30个时间单位,约30ms)
  - 扫描策略:0x00(接受所有广播包)

控制器收到命令后,会回复一个事件包:

HCI事件:Command Complete
事件码:0x0E
参数:
  - 命令完成码:0x00(成功)
  - Opcode:0x200B(对应刚才的命令)

嗯,这里要注意:HCI命令和事件是一一对应的。如果你抓包时发现发了命令但没收到事件,那可能是控制器卡死了,或者通信链路有问题。

2.4 本章小结

物理层管信号,链路层管状态,HCI管通信。这三层是BLE协议栈的骨架,也是抓包分析的基础。我个人觉得,链路层状态机是最值得花时间搞懂的——你只要看懂了设备在哪个状态,就能猜出它下一步要干什么。