0x01 软硬件使用清单
- hackrf one 或者 rtlsdr 等sdr工具
- gnuradio-companion
- python
- stm32单片机
- 433MHZ或者其他频率的ASK发射模块
0x02 确定监听频率
- 拆解遥控器查看RF部分的晶振频率
- 或者使用gqrx监听空中信号,寻找遥控器按键按下时出现波峰的频率值
0x03 录制信号
- 文件
rec_sig.grc
为 gnuradio-companion 使用的信号录制模块,用来录制原始信号的数据并存入硬盘,原始IQ通道数据占用磁盘空间比较大,录制后可以通过audacity打开来简单分析,或者在下一步解调分析。
0x04 解调信号
- 文件
mk_sig.grc
用来使用上一步录制的信号进行ASK信号的解调。 - 如果不需要录制信号的话,可以不进行上一步操作,在这里直接disable掉file source模块并enable osmocom source,直接对SDR采集的数据进行解调,并生成解调后的数据文件,这里生成的文件存储的是解调后的数据,数据量很小。
- 流程图中使用AM Demod对信号解调,使用Add Count和Multiply Count和Threshold对解调后的信号归一化处理,解调后的数据文件为二进制数据,只包含0x00和0xFF两种字节。其中0x00字节代表原始信号的0,0xFF代表原始信号的1。0x00和0xFF出现的次数代表原始信号持续的时间,这个时间可以通过出现次数*采样周期来计算出来。
0x05 解码信号
解调出来的文件使用xxd打开,形如
0027ecd0: 0000 0000 0000 0000 0000 00ff ffff ffff ................
序列,其中00代表0,ff代表1,该序列代表0000000000011111的序列。可以根据文件中0或者1出现的次数,结合采样率和波形图来分析原始数据。也可以使用audacity打开来分析,比较直观。0或者1连续出现的次数x采样周期x分频系数20 等于该信号持续出现的时间,分频系数在mk_sig.grc
文件中由dec_num
变量设置,目的是减小生成文件的大小。目前接触到三种编码方式:
第一种调制方式属于时长编码,非归零编码方法。每个高电平时间单位为400us,代表一个1。则800us高电平代表11,1200us高电平代表111,同样的低电平时长代表对应的0,以此类推。第一种编码方式,在小区车道门禁中常见:
第二种调制方式属于曼彻斯特编码,归零编码方法,每个时间片单位为200us,200us高400us低代表1,400us高200us低代表0。
第三种编码方式,固定码遥控芯片PT2262或EV1527,详细的数据编码时序图见datasheet,audacity分析如下:
文件
get_sig_time_data.py
为第三种编码方式的解码程序。需要注意的是,由于不同遥控器中使用的阻容振荡器参数不一,导致码元周期也不一致,程序中使用time参数进行调整。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
96
97
98#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from sys import argv
#ori time seq
ori_data = []
#all 01 seq
data = []
#time div
time = 50
def decode_sig(sig):
#last status
prv = 0
#cnt time
cnt = 0
#decode per 300us
decode = ''
#ori status
ori_seq = []
#this status
seq = []
fin = open(sig,'r')
for i in fin.read():
if ord(i) == prv:
cnt += 1
else:
tmp = int(cnt/time)+int(2*cnt/time)%2
if prv/255 == 1:
decode = tmp * '1'
else:
decode = tmp * '0'
#normal 2000
#if cnt > 2000:
#jpzx 800
if cnt > 800:
#print 'seq is %s'%(seq)
#print '++mark+++++++++++++++++++++++++++++++++++++++++++'
ori_data.append(ori_seq)
data.append(seq)
ori_seq = []
seq = []
else:
ori_seq.append(cnt)
seq.append(decode)
#print '%2s for %5s *10us for %5s *300us decode is %10s'%(prv/255,cnt,tmp,seq[-1])
prv = ord(i)
cnt = 0
fin.close()
def decode_str(strs):
data = ''
ptr = 0
start = '10'*212
#gap = '0000000000000000000000000000000000000000'
#gap = '0000000000000000000000000000000000000'
while ptr < len(strs):
if strs[ptr:ptr+len(start)] == start:
data += '\nstart_seq\n'
ptr += len(start)
else:
data += strs[ptr]
ptr += 1
return data
def decode_2262(strs):
data = ''
ptr = 0
Hpin = '10001000'
Lpin = '11101110'
Fpin = '10001110'
while ptr < len(strs):
if strs[ptr:ptr+8] == Hpin:
data += '1'
ptr += 8
elif strs[ptr:ptr+8] == Lpin:
data += '0'
ptr += 8
elif strs[ptr:ptr+8] == Fpin:
data += 'F'
ptr += 8
else:
data += 'e'
ptr += 1
return data
decode_sig(argv[1])
print len(ori_data)
print len(data)
for i in xrange(len(data)):
print '++mark+++++++++++++++++++++++++++++++++++++++++++'
print '--time len is:%s\n%s'%(len(ori_data[i]),repr(ori_data[i]))
print '--data len is:%s\n%s'%(len(data[i]),''.join(data[i]))
#print '--dec str is:\n'+decode_str(strs)+'\n'
print '--dec str is:\n'+decode_2262(''.join(data[i]))
print '\n'解码数据为
FF01F0F00111
,为固定地址码FF01F0F0和遥控器键值 0111,具体可参见PT2262的datasheet,此处不复赘述。
0x06 信号重放
- 为了设备的便携性,重放装置的RF部分使用超再生模块,某宝4块一对收发都有了,注意选对频率。控制部分使用Arduino或者其他单片机。测试过树莓派,由于ubuntu系统操作GPIO在us级别延迟抖动比较大,因此没有采用树莓派。
- 控制GPIO为高低电平,并按照编码规则进行延时即可即可发送信号。
- 本测试仅为安全研究,重放代码不再放出。
- 已知存在安全隐患的设备:
- 使用PT2262或者EV1527芯片的设备,例如:某厂家门禁控制器、某厂家电动自行车遥控器
- 未使用以上芯片但使用了自定义固定编码方案的遥控器:小区车道门禁蓝牙识别系统