0、前言
該教程詳細介紹了從電磁波級別開始,反向嗅探藍牙低功耗廣播包的全過程。雖然這裏面涉及極其多的專業知識,但是一步一步去閱讀,小白應該也可以瞭解個大概 _。
如果能將本文完全理解,將會大大提升你對藍牙協議棧的理解(是藍牙協議棧最最底層級別的瞭解)、將會大大提升你對通信原理的理解、將會大大提升你對 GNU Radio 工具的理解、將會大大提升你對於無線通訊黑客嗅探的理解!
那麼,讓我們開始 ba~
1、體驗
1)在 linux 電腦 clone 代碼,並且進入 ble_adv_rx
目錄:
git clone [email protected]:nbtool/auto_test_tool.git
cd auto_test_tool/app/app_sdr_ble_adv_rx
2)將 hackrf 插入電腦 USB3.0 的端口
3)運行 make , 可以看到 hackrf 收取到了 BLE 廣播包
2、代碼解析
2.1 目錄結構
➜ app_sdr_ble_adv_rx git:(master) ✗ tree
.
├── app_frame.py # BLE 廣播包協議解析邏輯
├── BT.xlsx # 藍牙 BLE 包格式說明
├── grc # GNU RADIO 流程圖
│ ├── gr_ble.grc
│ ├── gr_ble.py
│ └── __init__.py
├── main.py # 主邏輯
├── makefile # MAEKFILE
└── readme.md
2 directories, 8 files
2.2 main.py
def analysis_cmd(str):
print("analysis_cmd:[%02X][%02X]" %(str[0],str[1]),end=' ')
print("mac:",end = '')
for d in reversed(str[2:8]):
print('%02X' %(d), end='')
print(" data:",end = '')
for d in str[8:]:
print('%02X' %(d), end=' ')
print(' ')
# Initialize Gnu Radio
gr_block = gr_block() # 實例化 gnu radio 流程圖
gr_block.start() # 啓動 gnu radio 流程圖
gr_block.set_ble_channel(app_frame.BLE_CHANS[37]) # 設置 BLE SCAN 通道爲 37
zmq1 = bsp_zmq.bsp_zmq("tcp://127.0.0.1:55555") # 藉助我自己封裝的 zmq 類,用於從 gnu radio 流程圖中通過 socket 接收數據
frame = app_frame.FRAME(analysis_cmd) # 藉助我自己封裝的 app_frame 類,用於分析處理從 gnu radio 流程圖
# 中獲取的數據,從而過濾、去白、格式分析出 BLE 廣播包數據,
# 通過 analysis_cmd 回調給應用層
try: # 不斷判斷 zmq 的 socket 端口是否有數據,如果有讀取並放入 frame 的 fifo 中
while 1<2:
if zmq1.iswaiting() != 0:
x = zmq1.read()
frame.insert_data(x)
frame.run()
except KeyboardInterrupt:
zmq1.close() # close port
gr_block.stop()
gr_block.wait()
print("safe exit")
main 文件是此程序的入口,實例並啓動 gnu radio; 實例 zmq,用於該 python 程序與 gnu radio 通信; 實例 app_fram 用於分析處理 gnu radio 傳來的數據。
注:如果想要更細瞭解 ZMQ 的機制,參考《[SDR] GNU Radio 系列教程(十四) —— GNU Radio 低階到高階用法的分水嶺 ZMQ 的使用詳解》
2.3 grc gnu radio 流程圖
這裏的流程圖如下:
這個流程圖和《SDR 教程 —— 利用 GNU Radio + HackRF + WireShark 做藍牙抓包器(超低成本)》中的一樣。
- 數據源採用 RTL-SDR Source,設備選擇 hackrf =0, 其頻率對應的是藍牙廣播掃描的信道。
- 源數據出來後,過一個閾值過濾 -70dB 過濾器
- 然後再過一個低通濾波器
- 然後送到 GFSK 解碼模塊
- 最後送到 ZMQ 將數據發佈出去
本質是一個 GFSK 解碼接收機,類似一個 nRF24L01+ 模塊《如何爲編程愛好者設計一款好玩的智能硬件(十)——無線2.4G通信模塊研究·一篇說完》。當然,我們也能用一個純具備 GFSK 制式的 2.4G 接收模塊,實現 ble beacon 的接收!!!
2.4 如何從 01 數據流中解析出 BLE 廣播包
想要知道如何從 01 數據流中解析出 BLE 廣播包,我們首先需要看看 BLE 協議棧從下到上有哪些層:
最下面物理層是載波和制式相關的電磁波相關要求,這一層要求了電磁波的頻率、編碼、數據率等,實現 01 與電磁波的互相轉換;次高一層是 Link Layer 層,該層約定了 01 數據流以怎樣的幀組織方式是合法的。這兩層組成了 BLE Controller 層,是藍牙芯片廠家至少要實現的層。
2.4.1 物理層
1)信道
我們如果想要抓取廣播信道,只要關注:37、38、39 三個信道,起頻率分別爲:2400KHz、2426KHz、2480KHz
注:反過來看上一節的流程圖中 RTL-SDR Source 默認的頻率爲 2.426GHz,意味着默認採集 38 信道的數據
注:編碼方式採用 GFSK
2.4.2 數據鏈路層
2.4.2.1 角色
在 37、28、39 信道發送信息的叫做 advertiser ; 接收信息的叫做 scanner。
2.4.2.2 數據格式
這有份 BLE 數據鏈路層超全的格式說明:https://github.com/nbtool/auto_test_tool/blob/master/app/app_sdr_ble_adv_rx/BT.xlsx
我們以 4.0/4.1 版本爲例:
其中:
1)Preamble
所有鏈路層數據包都有一個 8 位前導碼。 在接收機中使用前導碼來執行頻率同步,符號定時估計和自動增益控制(AGC)訓練。
- 廣告信道數據包應具有 10101010b 作爲前導碼。
- 數據信道分組前導碼是 10101010b(0xAA)或 01010101b(0x55),具體取決於接入地址的 LSB。 如果接入地址的 LSB 是 1,則前導應爲 01010101b,否則前導應爲 10101010b。
2)Access Address
由發起者生成,用來在兩個設備之間識別一個LL層連接
- 所有廣播數據包的訪問地址都是 10001110100010011011111011010110b (0x8E89BED6)。
- 所有連接數據包的訪問地址都是隨機值,並遵循一定規則,每次連接重新生成。
3)PDU
Protocol Data Unit,協議數據單元
PDU 有兩種,廣播信道傳輸的是廣播 PDU,連接信道傳輸的是連接 PDU。
4)CRC
每個 Link Layer 數據包的結尾都有 24 位的 CRC 校驗數據,它通過 PDU 計算得出。
2.4.3 加密相關
這裏有個至關重要的流程,需要看藍牙協議棧 《CoreSpecification_v5.0.pdf》:
2.4.3.1 Cyclic Redundancy Check(循環冗餘檢查)
循環冗餘校驗作用於數據鏈路層 PDU 部分
If the PDU is encrypted, the CRC is calculated after encryption.
這裏的 CRC 多項式是一個 24 bit 多項式:x^24+x^10+x^9+x^6+x^4+x^3+x^1+x^0
其物理上對應一個線性反饋移位寄存器 (LFSR) with XOR taps at bit 0, 1, 3, 4, 6, 9, 10, and 24.
數據從最低有效位開始移位到移位寄存器。移位寄存器用已知的共享值或 0x555555 進行初始化。
線性反饋移位寄存器中的CRC編碼示例,預設爲0x555555(10101010101010101010)—— Animation by Mark Hughes
當接收到數據包時,在CRC之後檢查訪問地址。如果其中一個不正確,則數據包將被拒絕,並停止處理。
2.4.3.2 Data Whitening
數據白化防止重複位(00000000或11111111)的長序列。它被應用於發射機的 CRC 之後的鏈路層的 PDU 和 CRC 字段。在接收器中的 CRC 之前執行去白化。白化器和去白化器都使用在比特 4 和比特 7 處具有抽頭的7比特線性反饋移位寄存器(LFSR)。
The shift register is initialized with a sequence that is derived from the channel index:
- 位置 0 設置爲 1
- 位置 1 到 6 被設置成 the channel index of the channel used when transmitting or receiving, with the MSB in position 1 and the LSB in position 6
Bits are shifted along the shift register from 0→1, 1→2, 2→3, 4→5, 5→6, 6→0. Bit 3 and bit 6 are processed with the XOR ⊕ operator to determine bit 4 (0⊕0=0,0⊕1=1,1⊕0=1,1⊕1=0)
圖示用通道26 = 0x1A (1011010)初始化的數據白化線性反饋移位寄存器內部的邏輯圖像 —— Animation by Mark Hughes
1011010
https://www.allaboutcircuits.com/uploads/articles/CoreSpecification_v5.0.pdf [3.2 DATA WHITENING]
通道 26 = 00[01 1010]
position0 = 1(固定的)
position 1 = 0(通道的最高有效位MSB)
position 2 = 1
position 3 = 1
position 4 = 0
position 5 = 1
position 6 = 0(通道最低有效位LSB)
第一次移位寄存器爲:
1011 010 (取position6 和 position3 進行異或得到下次的 position 4數據)
0101 101
1010 010
0101 001
...
算法實現:
- 1)channel bit 左右反轉,右數第二位置1 (00[01 1010] -> [010110]10)形成 lfsr
- 2)對於每一字節的輸入,bits 左右反轉成 d,循環 8 次:
- 從左往右取出 lfsr 和 d 的每一 bit,亦或運算賦值給 d 的對應 bit
- 取position6 和 position3 進行異或得到下次的 position 4數據
- 注:下面代碼裏用了比較巧妙的方式,做到了上面兩點)
PYTHON 代碼爲:
# Swap bits of a 8-bit value
# ➜ sdr4iot-ble-rx git:(master) ✗ python test_swap.py
# 0b11010101
# 0b10101011
def swap_bits(value):
return (value * 0x0202020202 & 0x010884422010) % 1023
# (De)Whiten data based on BLE channel
def dewhitening(data, channel):
ret = []
lfsr = swap_bits(channel) | 2
for d in data:
d = swap_bits(ord(d[:1]))
for i in 128, 64, 32, 16, 8, 4, 2, 1:
if lfsr & 0x80:
lfsr ^= 0x11
d ^= i
lfsr <<= 1
i >>= 1
ret.append(swap_bits(d))
return ret
測試代碼:
for xx in self.gr_buffer[pos:pos + BLE_PDU_HDR_LEN]:
print(hex(ord(xx)),end=' ')
print('<--PRE (%d)', self.current_ble_chan)
ble_header = dewhitening(
self.gr_buffer[pos:pos + BLE_PDU_HDR_LEN], self.current_ble_chan)
for xx in ble_header:
print(hex(xx),end=' ')
print('<--AFTER')
輸入輸出:
0xcd 0xf7 <--PRE (%d) 37
0x40 0x25 <--AFTER
0xcd 0xf7 <--PRE (%d) 37
0x40 0x25 <--AFTER
0x3a 0x79 <--PRE (%d) 37
0xb7 0xab <--AFTER
0x93 0xf7 <--PRE (%d) 37
0x1e 0x25 <--AFTER
0x85 0x9f <--PRE (%d) 37
0x8 0x4d <--AFTER
0x60 0x15 <--PRE (%d) 37
0xed 0xc7 <--AFTER
0x6b 0xdf <--PRE (%d) 37
0xe6 0xd <--AFTER
0xea 0x95 <--PRE (%d) 37
0x67 0x47 <--AFTER
0xaa 0x6d <--PRE (%d) 37
0x27 0xbf <--AFTER
0xaa 0x86 <--PRE (%d) 37
0x27 0x54 <--AFTER
0xdb 0xb3 <--PRE (%d) 37
0x56 0x61 <--AFTER
0xd5 0x96 <--PRE (%d) 37
0x58 0x44 <--AFTER
0xa3 0xf4 <--PRE (%d) 37
0x2e 0x26 <--AFTER
0xcd 0xf8 <--PRE (%d) 37
2.4.4 在 ellsys 中的一個藍牙 LL 層數據
下面是一個 ellsys 中的數據:
2.5 app_frame.py 之 BLE Beacon 數據解析代碼實現
- insert_data 就是將 ZMQ 從 GNU Radio 中獲取的數據放入 FIFO(data_buf) 中
- run 是不斷被執行的函數
- 首先調用 frame_ok 進行幀解析判斷,如果解析出 ble beacon 就調用 fun_analysis 同知應用層
因此,重點顯而易見都在 frame_ok 函數中:
2.5.1 判斷是否滿足最小幀要求
str_len = len(str)
if str_len < FRAME.P_MIN_LEN:
return (-2,start_pos,end_pos)
2.5.2 找到固定幀頭
while start_pos<str_len:
pos = start_pos
if(str[pos:].startswith('\xAA\xD6\xBE\x89\x8E')):
break
start_pos = start_pos+1
注:Preamble + Access Address
2.5.3 對 PDU HEADER 進行去白及驗證 PDU TYPE 合法性
# Dewhitening received BLE Header
ble_header = bsp_algorithm.bt_dewhitening(str[start_pos+FRAME.P_PDU_HEADER:start_pos+FRAME.P_PDU_HEADER+BLE_PDU_HDR_LEN],37)
ll_pdu_header = (ble_header[0] << 8) | ble_header[1]
ll_pdu_type = ble_header[0] & 0x0f
ll_pdu_txadd = (ble_header[0] >> 6) & 0x01
ll_pdu_rxadd = (ble_header[0] >> 7) & 0x01
ll_pdu_lenght = ble_header[1] & 0x3f
head_pos = start_pos+FRAME.P_PDU_HEADER
adva_pos = start_pos+FRAME.P_PDU_PAYLOAD_ADVA
advdata_pos = start_pos+FRAME.P_PDU_PAYLOAD_ADVDATA
crc_pos = start_pos+FRAME.P_PDU_PAYLOAD_ADVA+ll_pdu_lenght
end_pos = start_pos+FRAME.P_PDU_PAYLOAD_ADVA+ll_pdu_lenght+BLE_CRC_LEN
# Check BLE PDU type
if ll_pdu_type not in BLE_PDU_TYPE.values():
# print("Invalid ll_pdu_type: {:x}".format(ll_pdu_type))
return (-1,start_pos,end_pos)
2.5.4 對 PDU 進行去白及驗證 CRC 合法性
# Dewhitening BLE packet
self.ble_data = bsp_algorithm.bt_dewhitening(str[head_pos:crc_pos],37)
if self.ble_data[-3:] != bsp_algorithm.bt_crc(self.ble_data, 2 + ll_pdu_lenght):
if ll_pdu_type == 0:
'''
print("->head:%04x [T:%02x T:%d R:%d L:%d] adva_pos:%d advdata_pos:%d crc_pos:%d end_pos:%d str_len:%d" \
%(ll_pdu_header,ll_pdu_type,ll_pdu_txadd,ll_pdu_rxadd,ll_pdu_lenght, \
adva_pos,advdata_pos,crc_pos,end_pos,str_len))
for x in self.ble_data:
print('%02X ' %x, end = '')
print('\n')
'''
return (0,start_pos,end_pos)
參考鏈接
[1].MICROCHIP Developer Help 關於藍牙協議棧的簡單介紹
[2].What is Bluetooth 5? Learn about the Bit Paths Behind the New BLE Standard
教程列表
- [1]. GNU Radio 系列教程(一) —— 什麼是 GNU Radio
- [2]. GNU Radio 系列教程(二) —— 繪製第一個信號分析流程圖
- [3]. GNU Radio 系列教程(三) —— 變量的使用
- [4]. GNU Radio 系列教程(四) —— 比特的打包與解包
- [5]. GNU Radio 系列教程(五) —— 流和向量
- [6]. GNU Radio 系列教程(六) —— 基於層創建自己的塊
- [7]. GNU Radio 系列教程(七)—— 創建第一個塊
- [8]. GNU Radio 系列教程(八)—— 創建能處理向量的 Python 塊
- [9]. GNU Radio 系列教程(九)—— Python 塊的消息傳遞
- [10]. GNU Radio 系列教程(十)—— Python 塊的 Tags
- [11]. GNU Radio 系列教程(十一)—— 低通濾波器
- [12]. GNU Radio 系列教程(十二)—— 窄帶 FM 收發系統(基於ZMQ模擬射頻發送)
- [13]. GNU Radio 系列教程(十三)—— 用兩個 HackRF 實現 FM 收發
- [14]. GNU Radio 系列教程(十四)—— GNU Radio 低階到高階用法的分水嶺 ZMQ 的使用詳解
- [14]. SDR 教程實戰 —— 利用 GNU Radio + HackRF 做 FM 收音機
- [15]. SDR 教程實戰 —— 利用 GNU Radio + HackRF 做藍牙定頻測試工具(超低成本)
- [16]. SDR 教程實戰 —— 利用 GNU Radio + HackRF + WireShark 做藍牙抓包器(超低成本)
視頻和博客
: 如果覺得不錯,幫忙點個支持哈~