码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • [SDR] SDR 教程实战 —— 利用 GNU Radio + HackRF 手把手深入了解蓝牙协议栈(从电磁波 -> 01数据流 -> 蓝牙数据包)



    目录
    • 0、前言
    • 1、体验
    • 2、代码解析
      • 2.1 目录结构
      • 2.2 main.py
      • 2.3 grc gnu radio 流程图
      • 2.4 如何从 01 数据流中解析出 BLE 广播包
        • 2.4.1 物理层
        • 2.4.2 数据链路层
          • 2.4.2.1 角色
          • 2.4.2.2 数据格式
        • 2.4.3 加密相关
          • 2.4.3.1 Cyclic Redundancy Check(循环冗余检查)
          • 2.4.3.2 Data Whitening
        • 2.4.4 在 ellsys 中的一个蓝牙 LL 层数据
      • 2.5 app_frame.py 之 BLE Beacon 数据解析代码实现
        • 2.5.1 判断是否满足最小帧要求
        • 2.5.2 找到固定帧头
        • 2.5.3 对 PDU HEADER 进行去白及验证 PDU TYPE 合法性
        • 2.5.4 对 PDU 进行去白及验证 CRC 合法性
    • 参考链接
    • 教程列表
    • 视频和博客


    0、前言

    该教程详细介绍了从电磁波级别开始,反向嗅探蓝牙低功耗广播包的全过程。虽然这里面涉及极其多的专业知识,但是一步一步去阅读,小白应该也可以了解个大概 _。

    如果能将本文完全理解,将会大大提升你对蓝牙协议栈的理解(是蓝牙协议栈最最底层级别的了解)、将会大大提升你对通信原理的理解、将会大大提升你对 GNU Radio 工具的理解、将会大大提升你对于无线通讯黑客嗅探的理解!

    那么,让我们开始 ba~


    1、体验

    1)在 linux 电脑 clone 代码,并且进入 ble_adv_rx 目录:

    git clone git@github.com: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_posif(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 做蓝牙抓包器(超低成本)

    视频和博客



    : 如果觉得不错,帮忙点个支持哈~

  • 相关阅读:
    BFF避坑指南
    【LeetCode】792. 匹配子序列的单词数
    Hash(哈希)选做
    从业多年,我总结出软件测试工程师必须掌握的技能,你不可错过!
    计算机网络---概述
    国庆作业 10月1 用select实现服务器并发
    电感单位亨利H单位换算
    中国车用氮氧传感器行业研究与投资前景报告(2022版)
    Linux 网络配置
    如何使用 saplink 安装其他网站上提供的 ABAP 程序
  • 原文地址:https://www.cnblogs.com/zjutlitao/p/17698384.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号