使用SDR重放RF信号的技术指南

本文详细介绍了如何使用软件定义无线电(SDR)设备如RTL-SDR、HackRF One和Yardstick One捕获和重放RF信号,包括信号分析、编码转换和Python脚本实现,适用于无线门铃等设备的信号重放测试。

使用SDR重放RF信号

免责声明:在传输任何数据之前,请务必使用法拉第袋或笼子,以免意外在受监管频率上非法传输而违反法律。此外,拦截和解密他人数据是非法的,因此在研究流量时要小心。

前言:最近,我受邀与几位同事合作(非常感谢BB King邀请我加入他的项目),涉及RF信号重放实验室的故障排除。虽然我拥有便宜的RTL加密狗(20美元)和价格较高的HackRF One设备(350美元),但我没有BB King实验室中使用的Yardstick One加密狗(100美元)。此外,我对Yardstick One加密狗特有的一些工具(RfCat)和脚本不熟悉。

如你所料,我立即订购了Yardstick One,并在当地零售店购买了一个便宜的无门铃(12美元)。在等待Yardstick交付期间,我决定启动我的HackRF One,尝试捕获门铃遥控器的RF信号并使用HackRF重放。

意识到有几种不同的方法执行RF信号重放攻击,我决定记录我的发现,以便其他人可以从我的探索中受益。希望有了这些信息,你可以根据需求、成本、复杂性和可用设备及工具的多功能性选择方法。

SDR USB设备

  • RTL-SDR – 便宜(20美元),仅接收(频率范围:500KHz至1.75GHz)
  • Yardstick One – 中等价格(100美元),接收和传输(频率范围:300-348MHz、391-464MHz和782-928MHz),半双工
  • HackRF One – 较高价格(350美元),接收和传输(频率范围:1 MHz至6 GHz),半双工
  • BladeRF – 较高价格(420美元),接收和传输(频率范围:47MHz至6GHz,61.44MHz采样率,2×2 MIMO流),全双工

使用无线门铃的RF信号重放示例

设备FCC标识符的FCC搜索(https://fccid.io/)结果:

RTL-SDR – 使用Gqrx接收和捕获RF信号:(注意:RTL-SDR无法传输!)

在调整中心频率以获得最佳增益和清晰度(433.89MHz)后,我们可以单击“REC”单选按钮,然后按下外围遥控按钮,最终将RF信号突发捕获到文件中。

显然,由于RTL-SDR加密狗的限制,我们无法传输此信号,但捕获的文件可以由Yardstick One或HackRF传输,前提是我们将.wav文件转换为这些设备可以识别的原始文件。例如,HackRF需要一个8位有符号IQ原始文件,无头信息。

除了GQRX,还有许多其他SDR GUI应用程序可用,如SDRSharp、SIGINTOS等(更多信息请参阅我的BHIS博客“软件定义无线电和GSM/LTE简介”)。

HackRF One – 使用简单命令行重放RF信号

无疑,当信号中心频率已知时,重放RF信号的最快方法之一是使用HackRF工具“hackrf_transfer”。

通过提供所需参数,HackRF可以捕获所需的传输(在按下外围遥控按钮时),然后将原始数据保存到文件中。此外,HackRF可以重放(传输)保存文件中的原始RF信号,从而在没有物理遥控器的情况下调用所需的外围活动。

不幸的是,尽管这种方法快速高效,但它是“盲目”进行的,因为对信号的了解很少。它完成了预期的任务,但在暴露可能的攻击向量或漏洞方面不足。

HackRF One – 使用GNURADIO流图重放RF信号

尽管不是最快的RF信号重放方法,但HackRF也可以与GNURADIO流图结合使用来捕获、保存和重放RF信号。理解GNURADIO的学习曲线可能相当广泛,但这种复杂性带来了巨大的能力和多功能性。

以下屏幕截图显示了我使用GNURADIO流图进行的先前信号重放项目(车辆FOB重放)(更多详细信息请参阅我的BHIS博客“GNU RADIO入门”)。可以看出,该项目中的中心频率为315MHz,但过程相同。

以下显示了一个文件接收块,用于保存捕获的信号。

以下显示了重放流图(灰显的块已禁用)。

Yardstick One – 使用RfCat重放RF信号

安装RFCat和依赖项(libusb、pyusb)

1
2
3
4
5
6
7
8
9
git clone https://github.com/atlas0fd00m/rfcat.git
cd rfcat/
sudo python setup.py install
cd ../
git clone https://github.com/walac/pyusb.git
cd pyusb/
sudo python setup.py install
easy_install pip
pip install libusb

插入设备并运行以下命令进行验证:

1
rfcat -r

验证安装和加密狗检测:

将保存的.wav文件(我们使用GQRX捕获)加载到Audacity中,我们可以快速识别传输的开关键控数据突发。

突出显示传输的数据突发并放大,我们可以看到较小突发的重复模式。

放大任何重复的较小突发,我们可以识别实际的OOK(开关键控)PWM(脉冲宽度调制)数据。短脉冲被视为(Mark = 1),宽脉冲被视为(Space = 0)。

解码波形脉冲,我们得到以下数字足迹:

二进制比特流: 为了在Yardstick上重放波形,每个数字足迹脉冲值需要使用以下比特表编码: 足迹值 = 0 编码比特 = 1110 足迹值 = 1 编码比特 = 1000 编码比特流: 11101000 11101000 11101110 10001110 11101110 10001000 10001110 10001110 10001110 11101000 11101000 10001000 10000000

为了使用Yardstick One重放此比特流,我们需要将此二进制数据转换为十六进制。 这导致以下十六进制值:E8 E8 EE 8E EE 88 8E 8E 8E E8 E8 88 80 然后我们需要在每个十六进制值前加上“\x”。 Yardstick重放字符串 = \xE8\xE8\xEE\x8E\xEE\x88\x8E\x8E\x8E\xE8\xE8\x88\x80 (用零填充)= \xE8\xE8\xEE\x8E\xEE\x88\x8E\x8E\x8E\xE8\xE8\x88\x80\x00\x00\x00\x00\x00\x00

安装Yardstick One后,我们现在可以运行RfCat实例:$ sudo python doorbell.py (我的脚本doorbell.py)

注意:波特率使用方程近似:波特率 = 倒数(1/t),其中时间是1位的周期 根据以下屏幕截图,我们可以看到3位脉冲的长度约为630微秒,除以3得到每位210微秒的时间。使用最短脉冲的时间(210uS)并取其倒数,我们得到近似的波特率4800。

执行doorbell.py脚本成功导致门铃响铃!

附加信息

RFCat作为频谱分析仪: 安装频谱分析仪先决条件:

1
2
sudo pip install PySide2 
sudo apt-get install ipython 

执行 >d.specan(433920000) 并按下遥控按钮

最终说明

为了自动化整个重放过程(而不是在交互模式下手动输入十六进制字符串),我编写了一个简短的Python脚本来重现由GQRX捕获并在Audacity中分析的数字足迹。

如前所述,数字足迹的编码仅基于以下比特表: 足迹值 = 0 编码比特 = 1110 足迹值 = 1 编码比特 = 1000

然而,需要提到的是,尽管我成功响铃,但我的同事在他的硬件上运行相同的脚本却失败了。我的捕获数字足迹和他的之间的差异如下所示:

注意,我的足迹以“Space”(编码1110)开始。然而,BB King的足迹以“Mark”开始,通过查看脉冲,它应该被编码为0001。不幸的是,我没有考虑这种情况,因此我的脚本将其编码为1000,导致BB King的测试失败。

为了纠正这种情况,我修改了脚本,检查数字足迹的第一个脉冲,并根据以下比特表编码整个足迹: 足迹的第一个脉冲 = Space(‘0’) 足迹值 = 0 编码比特 = 1110 足迹值 = 1 编码比特 = 1000 足迹的第一个脉冲 = Mark(‘1’) 足迹值 = 0 编码比特 = 0111 足迹值 = 1 编码比特 = 0001

Python代码: 理解我对我Python编码的优雅性不做任何声称,并承认我以效率和技术为代价快速拼凑了这一点。此外,由于目前python3中没有对rflib模块的支持,我选择在python 2.7中编码。 因此,欢迎你使用和编辑代码,但请记住,你也有责任不通过在受监管频率上非法传输而违反法律。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import sys
import time
from rflib import *
from struct import *

# 用户定义参数
_digital_footprint = "0101001000111010100101111" # 我的足迹:(从space到mark的短过渡)
#_digital_footprint = "11000111010000" # BB King的足迹:(从space到mark的长过渡)
_frequency = 433890000
_baudrate = 4800
_modulation = "MOD_ASK_OOK"  # 调制类型(此版本中不可更改)
_mult = 3  # 传输RF信号的次数
_pad_bytes = 3 # 用于尾随填充的零字节数
_method = "1" # 足迹中从space到mark过渡时的“谷”间隙时间周期:
 # 短周期(1位时间周期):_method = "0"
 # 长周期(3位时间周期):_method = "1"

# 配置rf_cat
d = RfCat()

d.setFreq(_frequency)
d.setMdmModulation(MOD_ASK_OOK)
d.setMdmDRate(_baudrate)

print "SIGNAL INFORMATION"
print "------------------"
print "Frequency:                 ", _frequency
print "Baud rate:                 ", _baudrate
print "ModulationType:            ", _modulation
print "Repeat transmission count: ", _mult
print "Digital footprint:         ", _digital_footprint

mark_space = str(_digital_footprint)
xmt_stream = ""

# 扫描每个数字足迹位(脉冲)并将其转换为适当的4位值
# Mark = 1(短脉冲)和 Space = 0(长脉冲)
mark_space = str(_digital_footprint)
xmt_stream = ""

if (_method == "0"): # 足迹中从space到mark的过渡短(1位时间周期)
 for i in mark_space:

 if (i == "0"): # Space
 _pulse = "1110"

 if (i == "1"): # Mark
 _pulse = "1000"

 xmt_stream = xmt_stream + _pulse

if (_method == "1"): # 足迹中从space到mark的过渡长(3位时间周期)
 for i in mark_space:

 if (i == "0"): # Space
 _pulse = "0111"

 if (i == "1"): # Mark
 _pulse = "0001"

 xmt_stream = xmt_stream + _pulse

# 如果数字足迹的长度为奇数,则用“0000”填充以使其为8位
if (len(_digital_footprint) % 2 != 0):
 xmt_stream = xmt_stream + "0000"

# 填充零以用于传输之间的间隙长度
_padding = ""
for i in range(0, _pad_bytes):
 xmt_stream = xmt_stream + "00000000"
 _padding = _padding + "00000000"
print "Trailing padding:          ", _padding
 
print "RF transmit binary stream: ", xmt_stream

# 将二进制传输流转换为十六进制等效
hex_xmt_stream = str(('%08X' % int(xmt_stream, 2)))
print "RF transmit hex stream:    ", hex_xmt_stream


mod_xmt_stream = ""
for i in xrange(0, len(hex_xmt_stream), 2):
 ch = "\\x"
 mod_xmt_stream = mod_xmt_stream + (ch + (hex_xmt_stream[i:i+2]))
print "Modified RF hex stream:    ", mod_xmt_stream

# -------------------发送传输 ----------------------------------------#
print "Starting transmission ..."

hex_data = bytearray.fromhex(hex_xmt_stream)
d.RFxmit(hex_data, repeat = _mult)

d.setModeIDLE()
print " "
print "Transmission Complete"

后续测试

在我尝试理解为什么我需要为两个不同的硬件件编码时有两个单独的比特表(仅仅因为一个足迹以space开始,一个以mark开始),我决定进一步研究。我使用我已知工作的数字足迹运行了一个测试,但使用BB King的编码,假设它可能工作,我可以消除对两个比特表的需求。它没有工作,所以我深入研究了为什么。在仔细关注细节后,问题变得清晰。问题不是我的足迹以space开始,他的以mark开始,差异在于从space到mark的过渡时间周期!

比较两个数字足迹(我早先发布的):

分析我的足迹,从space到mark的过渡时间是1位长度,而BB King的space到mark过渡时间是3位长度。

基于这些知识,我再次修改了我的Python代码,以在选择使用哪个比特表时考虑这种差异。不再关心足迹是以space还是mark开始,我 instead 查看了过渡周期,并使用此信息选择适当的比特表。成功!!!

结束思考

第一次使用Yardstick对我来说是一个非常有益的经历,有机会与BB King合作,同时我们在自己独特的硬件上工作,对我非常有益。它让我保持专注和正轨,高度 motivated,并暴露了我可能在不同(但相似)测试实验室中发生的变异。

此外,以我 humble 的意见,我认为Michael Ossmann的Yardstick One(Great Scott Gadgets)是一个很棒的工具,相对便宜,易于交互, yet versatile enough to automate if the need arises。它的频率限制(低于1GHz)在捕获和重放滚动或固定代码设备(如车辆fobs、车库门开启器、无线门铃、安全设备或几乎任何在该频率范围内的无线RF信号)时几乎无关紧要。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计