往期回顾
软件无线电安全之GUN Radio基础(上)
背景
在上一小节中,我们简单介绍和使用了GNU Radio软件的基础功能和模块,同时通过GNU Radio Companion(GRC)创建了简单的流程图,展示了信号生成、处理和输出的流程。最后通过制作一个FM receiver来加深对GNU Radio的了解。在这一小节中我们将更加深入的了解和使用GNU Radio软件的功能,并制作一个简单的蓝牙抓包器。
GNU Radio使用
Bits的打包和解包
Bits的打包与解包对于表示二进制数据(与数字化 RF 样本相对)以及使用调制器模块(Constellation调制器、GFSK 调制器和 OFDM 发射器)非常有用。我们创建一个新流程图并将 Random Source 模块添加到工作区,把他的输出设置为byte,此时输出端口由紫色显示。
然后将 Throttle、Pack K Bits、Char to Float 和 QT GUI Histogram Sink 模块按照下图添加到流程图并连接它们:
编辑 Pack K Bits 的属性,设置K为4
编辑QT GUI Histogram Sink的属性,设置Number of Bins: 1024,Max x-axis: 16。
编辑另一个QT GUI Histogram Sink的属性,设置Number of Bins: 1024,Max x-axis: 16。
此时我们开始运行流程图, 4 Bit的直方图显示 0 到 15 的值,而1 Bit的直方图显示 0 和 1 的值 。
随后我们将 Unpack K Bits 模块添加到工作区,并将其连接在 Pack K Bits 模块和 Char to Float 模块之间。编辑 Unpack K Bits 模块属性并输入 K: 4,流程图如下。
此时如果我们运行一下,就会得出上面框图是0-15的数值范围,下面的1bit只有0和1。
流和向量
GNU Radio 中的块可以使用流或向量进行连接。流为每个时间单位携带 1 个实例样本,且必须具有数据类型,例如 Float 32 或 Byte。向量是能够在单个时间实例中携带多个采样,代表了多个数据的平行存在。GRC 使用较浅的颜色表示流,使用较暗的颜色表示向量。我们用下面的流程图来更深刻的理解一下流,第一个数据源是一个1k的cosine函数,这个输出的stream类型的数据就是数据流。然后我们使用节流阀进行一个流量控制,然后可以将这个流送到一个QT GUI Time Sink中来展示出来。运行效果如下:
然后我们将第一个流程图的信号源复制出三份,然后分别将它们的输出类型设置成complex、float和short。此时我们可以用一个stream to vector的,将流转化为向量,我们可以收集128个数据,然后合成一个向量。
此时运行效果如下:
我们使用一个流转成向量的典型例子,更形象地展示一下。下面这个流程图引入了两个数据源,一个是1KHz的cosine信号源,另一个是100Hz的cosine信号源,然后第一个信号源设置浮动为正负1,第二个信号源设置浮动为正负0.1,相当于我们有两个数据流,此时我们用一个strings to vector,把俩个数据源的流留合成为一个向量,然后接着我们再把合成的向量转换成一个数据,然后把它显示出来。
我们可以运行流程图查看一下效果,第一个是我们的100Hz的cosine信号源正负0.1的效果图,第二个是1KHz的cosine信号源正负1的一个数据源。第三个图为把两个交替信号源显示的效果图。
类似byte的打包解包,我们可以用vector to stream模块将两个stream打包成一个vector,那我们也可以利用vector to streams模块将数据拆解回原来的样子。下面流程图中,两个数据源经过streams to vector,然后打包把它先显示出来,然后调用Vector to Streams 模块反序列化向量样本并将其转换为流,执行与Streams to Vector 模块的相反操作。
我们可以运行流程图查看一下效果,此时经过打包之后的一个合成数据源已分为两个流:
层次块
当我们想要在设计其他流程图中,如何自己设计一个模块并使用呢?这时就用到了层次块。在GNU Radio Companion软件中点击就File-New-Hier Block,即可创建层次块流程图。在新的 GRC 选项卡中创建流程图如下:
双击选项块并编辑属性,设置Id: FrequencyShifter,Title: Frequency Shifter Block。
此时我们需要注意变量与 GNU Radio 中的参数不同。参数为 hier 块创建一个接口以接受来自外部源的值,而变量仅存在于 hier 块内部。
例如, samp_rate 变量只能从 hier 块内访问。
那么我们就需要删除 samp_rate 变量,将其替换成为参数,我们将俩个参数块添加到 GRC 工作区中,以便可以从较大流程图中的另一个块进行更新。
设置第一个参数属性:Id: samp_rate,Label: Sample Rate,Type: Float
设置第二个参数属性:Id: frequency,Label: Frequency,Type: float
流程图设置完成后,需要generate保存。但是此时从右侧功能模块中搜索,并不能搜索到我们保存的层次块。
我们需要单击重新加载块按钮,此时再次从右侧功能模块中搜索,就可以搜索到我们保存的层次块了。
创建一个python块
python块是什么功能呢,我们要实现一个功能块,它可以中有多个输入,并且会根据属性中的特定参数进行相应处理。通常,Python 块有两个属性:代码(代码,一个点击框,其中包含该块的 Python 代码的链接)和参数(块的输入参数)。在下面的例子中,我们要实现一个可以实现输入相加或者相乘的一个块。该功能块的参数可以决定是相加还是相乘。我们搜索 Python 块并将其添加到工作区:
单击 Open in Editor 编辑 Python 代码:
此时编辑器窗口中会显示 Python 块的 Python 代码:
注意: __init__() 和 work() 函数必须符合 GNU Radio 软件框架的规则和期望进行修改,该框架控制块输入和输出之间的数据传输。
我们可以在编辑器菜单中选择“查找和替换”,将默认example_param参数重命名以方便我们自己查看,这里我全部替换为additionFlag。
接下来就是修改功能实现部分了,该python文件中定义了默认块有一个输入和一个输出,但是我们需要该块的两个输入,我们需要将第二个 np.complex64 添加到 in_sig列表中。同时,python块在处理条件分支时,处理逻辑分别为加和乘,那么我们需要修改work函数。最终修改的代码如下:
根据我们设计的逻辑,在“加法”或“乘法”模块中参数选择“True”处理逻辑为两个信号源的相加。运行流程图给出以下两个图:
当我们设置参数为 Additionflag 属性输入 False,处理逻辑为相乘。这时两个复数正弦曲线相乘会在两个频率之和处产生一个正弦曲线。因此,频率为 1,000 的信号源与频率 3,000 的乘积是频率为 4,000 的复正弦曲线。运行流程图时可以看到这个复杂的正弦曲线:
实验:制作一个简单的蓝牙抓包器
实验的github仓库地址为"https://github.com/oldprogram/sdr4iot-ble-rx",该工具可以捕获的 BLE 数据包并通过命名管道 (FIFO) 直接显示在 Wireshark 中。我们使用Gnu Radio 用于接收和解调传入的 BLE 数据包。我们打开仓库中的流程图进行查看,流程为使用hackrf one设备收集到的数据,然后送到一个阈值滤波,-70的db的一个阈值滤波进行静噪和滤波。再送到GFSK模块(蓝牙的一种调制方式为GFSK)中进行个解码,解码后的数据再进行一个打包。然后数据会通过ZMQ模块(ZMQ模块,下小节中会进行讲解)pub出去。这样我们就可以用这个流程图实现一个功能,就是拿到了蓝牙最原始的GFSK数据包,并把它解出来就拿到蓝牙的物理层级别的数据。那接下来再写一个数据链路层的一个数据包的解析(ble_dump.py文件实现此功能,下一小节中讲解),就能把蓝牙的广播包提取出来。
我们打开终端,运行下面的命令进行蓝牙抓包:
mkfifo /tmp/fifo1
wireshark -S -k -i /tmp/fifo1
./ble_dump.py -o /tmp/fifo1
抓包效果如下:
总结
这一小节,我们更加深入的了解和使用GNU Radio软件的功能,同时对流程图基础知识也有了更好的拓展。从这一小节开始我们将接触与python的交互,方便我们更加高效和快捷的使用GNU Radio。在下一小节中,我们会学习ZMQ模块的使用以及蓝牙抓包器的原理。