目标:
1. 通过蓝牙协议了解BLE蓝牙的广播帧结构
2.了解蓝牙比特流处理流程
3.使用matlab重现白噪化和CRC校验
1.蓝牙的广播帧结构:
通过蓝牙BLE协议我们可以看到,蓝牙的广播帧结构如下:
这是非编码类型的广播帧结构,在实际学习中,我们可以用wireshark软件来对蓝牙抓包,进而分析协议,wireshark的使用可以参考以下文章:
使用nRF52832开发板用作dongle抓取蓝牙数据包 - 简书 (jianshu.com)
以下是我用wireeshark抓取到的蓝牙处于扫描态时候的广播帧:
上图是ADV_IND包的广播帧结构,我们可以看到,preamble的0x55或0xAA系列并没有显示在wireshark抓取的文件中,蓝牙在发送过程中的发射顺序为D6 BE 89 6E 60 16 A7 AC F3 DD 67 F3 02 01 05 0C 09 4E 6F 72 64 69 63 5F 55 41 52 54 9D B9 A3(D6前面的数据非蓝牙数据,是wireshark加上的);我们可以通过该软件看到广播帧结构中各个部分的具体数据,非常方便。例如,Access Address为 D6 BE 89 6E。由于蓝牙的发射顺序为LSB低位先发送,因此,wireshark显示的实际的蓝牙Access Address是倒着的,在广播帧的其他部分也一样。
2.蓝牙的比特流处理
蓝牙数据在发送时,并不是直接发送原码,而是将数据进行一定的处理后再发送出去,蓝牙协议对非编码的广播包的比特流处理如下:
其中,encryption和decryption只在有些数据包才会用到,这篇文章主要分析CRC和whitening两个过程。
官方蓝牙协议对CRC处理的对象介绍如下:
应在所有链路层数据包的PDU字段上计算CRC。如果PDU已加密,则CRC应在加密后计算已执行PDU。
CRC多项式为24位CRC,PDU中的所有位应从最低有效位开始按传输顺序进行处理。多项式的形式为x24+x10+x9+x6+x4+x3+x+1。对于每个数据通道PDU,移位寄存器应预设为链路层连接设置的CRC初始化值,并在连接PDU中通信。
对于AUX_SYNC_IND PDU及其附属集,移位寄存器应预设为描述定期广播AUX_ADV_IND PDU中SyncInfo字段中设置的CRCInit值。对于所有其他广告频道PDU,移位寄存器应预设为0x555555。
操作过程如下
位置0应设置为最低有效位,位置23应设置为初始化值的最高有效位。CRC首先传输最高有效位,即从位置23传输到位置0
蓝牙官方协议对whitenig的介绍如下:
数据白化用于避免长序列的0或1,例如。0000000 B或1111111 B,在数据位流中。白化应应用于所有链路层分组的PDU和CRC字段,并在CRC之后执行。在接收器中的CRC之前执行去白化
白化和去白化的定义方式相同,使用7位线性多项式x7+x4+1的反馈移位寄存器。在白化或去白之前,移位寄存器根据频道索引(数据频道索引或广播频道索引),其中分组以以下方式传输:
•位置0设置为1。
•位置1至6设置为数据频道索引或广播频道索引。
例如,如果通道索引=23(0x17),则位置将设置如下:
位置0=1
位置1=0
位置2=1
位置3=0
位置4=1
位置5=1
位置6=1
大概了解了比特流处理过后,我们可以通过软件无线电设备截取蓝牙发射的01信号来进行进一步分析。
我使用的是USRP B210设备,在gunradio companion上搭建以下流图,并在外部使蓝牙处于扫描态,就可以接收到蓝牙的01信号了。
把中心频点设置在2.426GHz,这样就可以接收到38信道的数据。在上面对蓝牙协议的解读中,我们发现,CRC只是在PDU后面加一段校验码,而白化的对象是PDU和CRC码,所以,比特流处理并没有改变preamble和access address的值。Access Address的值是0x8e89bed6,二进制是1000 1110 1000 1001 1011 1110 1101 0110,所以,按照LSB的发送顺序,是0110 1011 0111 1101 1001 0001 1110 0001,我们先用Notepad++打开gnuradio生成的文件(哲理Notepad++要先安装Hex-Editor插件),看下是否能找到这段序列。在Notepad++中搜索 00 01 01 00 01 00 01 01 00 01 01 01 01 01 00 01 01 00 00 01 00 00 00 01 00 01 01 01 00 00 00 01,结果如下:
成功找到了这段序列,并且前面的序列是00 01 00 01 00 01 00 01,符合preamble的要求,因此,基本可以确定这个就是蓝牙发送的帧了,Access Address之后就是白化完的PDU和CRC校验码。
3.用matlab实现比特流处理
前面我们已经得到了白化前和白化后的蓝牙广播帧数据,现在我们可以按照蓝牙协议编写matlab代码以验证数据正确性。
1.白化
白化和解白的代码是一样的,我们根据以下官方给的白化电路结构编写一个matlab代码:
clear;
Data_in_hex = 'a3b99d545241555f636964726f4e090c050102f367ddf3aca71660';
Data_in_bin = str_bin(Data_in_hex);
Data_in_bin_LSB = Data_in_bin(4*length(Data_in_hex):-1:1);
Channel_index = 38;
Whitening_INIT = dec2bin (Channel_index+64);
Whitening_INIT_BIN = bitget(bin2dec(Whitening_INIT),7:-1:1);
Data_out= zeros(1,4*length(Data_in_hex));
for i = 1:4*length(Data_in_hex)
Whitening_INIT_BIN_temp = Whitening_INIT_BIN(7);
Data_out(i)= xor(Data_in_bin_LSB(i),Whitening_INIT_BIN(7));
Whitening_INIT_BIN(7)=Whitening_INIT_BIN(6);
Whitening_INIT_BIN(6)=Whitening_INIT_BIN(5);
Whitening_INIT_BIN(5)=xor(Whitening_INIT_BIN_temp,Whitening_INIT_BIN(4));
Whitening_INIT_BIN(4)=Whitening_INIT_BIN(3);
Whitening_INIT_BIN(3)=Whitening_INIT_BIN(2);
Whitening_INIT_BIN(2)=Whitening_INIT_BIN(1);
Whitening_INIT_BIN(1)=Whitening_INIT_BIN_temp;
end
上面代码中的str_bin函数是一个译码器,将输入的十六进制字符串转化为二进制:
function [bin] = str_bin(in)
bin= zeros(1,4*length(in));
for i=1:length(in)
switch in(i)
case '1'
bin(4*i-3:4*i)=[0 0 0 1];
case '2'
bin(4*i-3:4*i)=[0 0 1 0];
case '3'
bin(4*i-3:4*i)=[0 0 1 1];
case '4'
bin(4*i-3:4*i)=[0 1 0 0];
case '5'
bin(4*i-3:4*i)=[0 1 0 1];
case '6'
bin(4*i-3:4*i)=[0 1 1 0];
case '7'
bin(4*i-3:4*i)=[0 1 1 1];
case '8'
bin(4*i-3:4*i)=[1 0 0 0];
case '9'
bin(4*i-3:4*i)=[1 0 0 1];
case 'a'
bin(4*i-3:4*i)=[1 0 1 0];
case 'b'
bin(4*i-3:4*i)=[1 0 1 1];
case 'c'
bin(4*i-3:4*i)=[1 1 0 0];
case 'd'
bin(4*i-3:4*i)=[1 1 0 1];
case 'e'
bin(4*i-3:4*i)=[1 1 1 0];
case 'f'
bin(4*i-3:4*i)=[1 1 1 1];
end
end
end
白化和解白的顺序也是一样的,例如按照下图wiresharp中的广播帧,应该先白化和解白化60,最后再是a3。
上面matlab代码最终得到data_out的值如下:
0 1 1 0 1 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 1 1 1 0 0 1 1 0 0 0 1 0 1 0 1 0 1 0 1 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 1 1 1 1 1 0 1 0 0 1 1 0 0 0 0 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 0 1 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 1 0 0 0 1 1 1 1 0 0 0 1 1 1 0 1 0 0 0 0 1 0 0 0 0 0 1 0 1 1 1 1 0 0 1 1 1 0 0 1 1 1 1 0 0 1 1 1 0 1 0 1 0 1 0 0 0 1 0 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 1 1 0 1 0 1 1 0 0 0 1 0 1 1 1 1
我们把这段序列放到Notepad++中搜索:
找到了这一串序列,这就验证了我们收到的蓝牙序列和whitening的正确性。
2.CRC校验
CRC校验可以使用该网站验证CRC校验的正确性:CRC校验;
我们先将PDU输入,看下校验结果是否和wireshark对得上,多项式x24+x10+x9+x6+x4+x3+x+1对应00065B,初始值为55555,注意把输入数据翻转勾上:
和前面wiresharp结果对比下:
看得出来,一模一样。
接下来,我们用下图的LFSR结构实现CRC校验,这里也是60先进行校验,最后是54(54后面的数据就是校验完的CRC码)。注意要LSB输入LFSR中,如60的输入顺序是00000110:
clear;
Data_in_hex='545241555f636964726f4e090c050102f367ddf3aca71660';
Data_in_bin= str_bin(Data_in_hex);
Data_in_bin_LSB = Data_in_bin(4*length(Data_in_hex):-1:1);
CRC_LFSR=[1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0];
for i = 1:4*length(Data_in_hex)
X=xor(CRC_LFSR(24),Data_in_bin_LSB(i));
CRC_LFSR(24)=CRC_LFSR(23);
CRC_LFSR(23)=CRC_LFSR(22);
CRC_LFSR(22)=CRC_LFSR(21);
CRC_LFSR(21)=CRC_LFSR(20);
CRC_LFSR(20)=CRC_LFSR(19);
CRC_LFSR(19)=CRC_LFSR(18);
CRC_LFSR(18)=CRC_LFSR(17);
CRC_LFSR(17)=CRC_LFSR(16);
CRC_LFSR(16)=CRC_LFSR(15);
CRC_LFSR(15)=CRC_LFSR(14);
CRC_LFSR(14)=CRC_LFSR(13);
CRC_LFSR(13)=CRC_LFSR(12);
CRC_LFSR(12)=CRC_LFSR(11);
CRC_LFSR(11)=xor(CRC_LFSR(10),X);
CRC_LFSR(10)=xor(CRC_LFSR(9),X);
CRC_LFSR(9)=CRC_LFSR(8);
CRC_LFSR(8)=CRC_LFSR(7);
CRC_LFSR(7)=xor(CRC_LFSR(6),X);
CRC_LFSR(6)=CRC_LFSR(5);
CRC_LFSR(5)=xor(CRC_LFSR(4),X);
CRC_LFSR(4)=xor(CRC_LFSR(3),X);
CRC_LFSR(3)=CRC_LFSR(2);
CRC_LFSR(2)=xor(CRC_LFSR(1),X);
CRC_LFSR(1)=X;
end
CRC_LFSR(1:24)=CRC_LFSR(24:-1:1);
运行查看CRC_LFSR寄存器结果如下:1 0 1 1 1 0 0 1 1 0 0 1 1 1 0 1 1 1 0 0 0 1 0 1,和网址算出来的一样。
4.结语
本文按照协议内容用matlab实现了蓝牙比特流处理流程。如果有误,欢迎指出。