使用SDR对ASK调制信号的监听和重放

0x01 软硬件使用清单

  • hackrf one 或者 rtlsdr 等sdr工具
  • gnuradio-companion
  • python
  • stm32单片机
  • 433MHZ或者其他频率的ASK发射模块

0x02 确定监听频率

  • 拆解遥控器查看RF部分的晶振频率
  • 或者使用gqrx监听空中信号,寻找遥控器按键按下时出现波峰的频率值

0x03 录制信号

  • 文件 rec_sig.grc 为 gnuradio-companion 使用的信号录制模块,用来录制原始信号的数据并存入硬盘,原始IQ通道数据占用磁盘空间比较大,录制后可以通过audacity打开来简单分析,或者在下一步解调分析。
  • 2018-12-24 19.23.51

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出现的次数代表原始信号持续的时间,这个时间可以通过出现次数*采样周期来计算出来。
  • 2018-12-24 19.18.12
  • 2018-12-24 19.19.42

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变量设置,目的是减小生成文件的大小。

  • 目前接触到三种编码方式:

    1. 第一种调制方式属于时长编码,非归零编码方法。每个高电平时间单位为400us,代表一个1。则800us高电平代表11,1200us高电平代表111,同样的低电平时长代表对应的0,以此类推。第一种编码方式,在小区车道门禁中常见:image-20181224202755132

    2. 第二种调制方式属于曼彻斯特编码,归零编码方法,每个时间片单位为200us,200us高400us低代表1,400us高200us低代表0。image-20181224202828315

    3. 第三种编码方式,固定码遥控芯片PT2262或EV1527,详细的数据编码时序图见datasheet,audacity分析如下:
      image-2018-12-25-00-54-03

  • 文件 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'
  • 2018-12-24 19.31.06

  • 解码数据为FF01F0F00111 ,为固定地址码FF01F0F0和遥控器键值 0111,具体可参见PT2262的datasheet,此处不复赘述。

0x06 信号重放

  • 为了设备的便携性,重放装置的RF部分使用超再生模块,某宝4块一对收发都有了,注意选对频率。控制部分使用Arduino或者其他单片机。测试过树莓派,由于ubuntu系统操作GPIO在us级别延迟抖动比较大,因此没有采用树莓派。
  • 控制GPIO为高低电平,并按照编码规则进行延时即可即可发送信号。
  • 2018-12-24 20.47.54
  • 本测试仅为安全研究,重放代码不再放出。
  • 已知存在安全隐患的设备:
    • 使用PT2262或者EV1527芯片的设备,例如:某厂家门禁控制器、某厂家电动自行车遥控器
    • 未使用以上芯片但使用了自定义固定编码方案的遥控器:小区车道门禁蓝牙识别系统

0x07 源代码