Представим, что вы поймали, перехватили и расшифровали сигнал управления каким-то устройством. Теперь настала пора его подделать! А для этого нужно научиться передавать любые сигналы. Для этого мы нам понадобится радиопередатчик, который мы и сделаем.
Железо для передачи радиосигнала
В качестве примера будем рассматривать сигналы от пульта, работающего на частоте 434 МГц.
Такой пульт для наших целей хорош по двум причинам:
- Его сигнал очень прост для изучения и представляет собой модулированный нужной частотой бинарный код (так называемая модуляция OOK — On-Off Keying).
- По такому принципу работают многие устройства, так что, разобравшись с одним, нетрудно управлять и другими.
Для приема сигнала мы пользовались дешевым приемником RTL SDR V3, купить который можно за 30 долларов. Он хорош для приема и анализа сигналов, но вот передавать, увы, RTL SDR не умеет. Тут нам пригодится что-нибудь более функциональное, например LimeSDR или HackRF.
Подробнее об этом я писал в статье «Перехват и анализ радиосигнала».
ПО для передачи радиосигнала
Главное отличие Software Defined Radio от радио обычного в том, что вся обработка сигнала выполняется в «цифре». Задача самого устройства сводится к тому, чтобы передать цифровой поток на ЦАП, который транслирует все в эфир. Скорость оцифровки и ширина полосы пропускания связаны теоремой Шеннона — Котельникова: чем больше число отсчетов в секунду, тем более широкополосный сигнал можно передать.
Скорости современных ЦАП и АЦП таковы, что SDR может записывать одновременно весь эфир FM в диапазоне от 88 до 108 МГц целиком. Или передавать сразу несколько сигналов одновременно на разных частотах — главное, эти сигналы правильно сформировать программно.
Привет из Голландии
В качестве примера возможностей SDR хочу поделиться записью, которую я сделал в Амстердаме. Она содержит все FM-радиостанции сразу в диапазоне от 91 до 105 МГц, десять секунд такой записи занимают 660 Мбайт. Можешь скачать этот файл и послушать, что играется на голландском радио.
Проиграть файл можешь в бесплатной программе SDR# — в ней можно открыть запись и слушать любую станцию, как с обычного приемника. Но в отличие от приемника все декодирование идет в «цифре», а на входе лишь цифровой поток. Думаю, теперь ты представляешь, какая вычислительная мощность у современных SDR.
Вернемся к беспроводному пульту. Наша задача — сформировать нужный нам сигнал, а SDR передаст его в эфир. Классикой для цифровой обработки сигналов считается программа GNU Radio. Это не просто программа, а целый фреймворк с огромным количеством компонентов для работы с сигналами и возможностью добавлять собственные модули. Раньше эта программа была доступна только в Linux, но последние версии работают и в 64-битных версиях Windows. Пользователи macOS могут установить GNU Radio с помощью brew.
В общем, устанавливаем GNU Radio, подключаем SDR и приступаем к работе с сигналами.
Прием радиосигнала
Чтобы передать сигнал, сначала нужно его принять и сохранить как образец. Запускаем GNU Radio Companion и собираем из блоков схему.
GNU Radio ориентирована на поточную обработку данных, в ней можно создавать из блоков схему, которая будет выполнять операции. В данном случае мы имеем только два блока: Source (источник) и Sink (приемник, буквально «слив»). Я использую SDR USRP, поэтому в качестве источника — USRP Source. В твоем случае устройство может быть другим — ищи в документации к твоему SDR. В качестве приемника используется FFT Sink — блок визуализации данных, который позволит нам увидеть наличие сигнала. Он не обязателен для записи, но без него будет непонятно, принимаем мы сигнал или нет.
В свойствах приемника я также указал частоту, на которой мы хотим принимать, — 434 МГц. Частоту дискретизации (sample rate) я установил равной 128 000, этого достаточно для записи сигнала. Запускаем проект в GNU Radio, подносим пульт поближе к антенне, нажимаем на нем любую кнопку, и, если все было сделано правильно, мы увидим хорошо заметный всплеск сигнала на спектре.
Если сигнал виден нормально, мы можем его записать. Добавляем блок File Sink.
GNU Radio для Windows требует абсолютные пути файлов, иначе ничего не работает. В Linux такой проблемы нет.
Запускаем программу, нажимаем кнопку на пульте, закрываем программу. В указанной нами папке должен появиться файл 433_signal.iq размером примерно 5 Мбайт, содержащий наш сигнал. Его можно открыть в любом аудиоредакторе, например в Cool Edit, если выбрать тип файла «стерео» (в SDR пишутся два канала, называемых I и Q) и тип данных float. Мы же откроем его с помощью Python и библиотеки для научных расчетов NumPy.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import numpy as np import matplotlib.pyplot as plt data = np.fromfile('433_signal.iq', dtype=np.float32) data_2ch = np.reshape(data, (-1, 2)) data_left = data_2ch[:, -1] rate = 128000 plt.figure(1) time = np.linspace(0, len(data_left)/rate, num=len(data_left)) plt.plot(time, data_left) plt.tight_layout() plt.show() |
Записанный файл не имеет заголовка wav, а содержит непосредственно данные float, так что частоту дискретизации и тип данных необходимо указать вручную. Также с помощью np.reshape входной одномерный массив преобразуется в двумерный: запись у нас содержит два канала. Для удобства отображения я вывожу только один канал.
Запускаем программу и видим наш сигнал.
Чтобы избежать искажения, уровень записи не должен быть слишком высоким и упираться в 1.0. При этом он не должен и быть слишком низким, иначе качество передаваемого сигнала будет плохим. Уровень сигнала между 0.5 и 1.0 — оптимальный. В моем случае было достаточно держать пульт при записи в нескольких сантиметрах от антенны.
Если ты не видишь ничего, а вместо этого выдается ошибка отсутствия библиотеки NumPy или Matplotlib, то нужно их установить:
- в Windows: C:\Python3\Scripts\pip.exe install numpy matplotlib;
- в Linux: $ sudo pip install numpy scipy matplotlib;
- в последних версиях macOS: $ pip3 install numpy scipy matplotlib --user.
Передача радиосигнала
После того как мы записали сигнал, нужно собрать блок-схему для его передачи. Принцип такой же, но с небольшими отличиями.
Блок File Source — источник нашего сигнала — для этого мы его и записывали. Блок Throttle указывает, с какой скоростью сигнал будет подаваться на передатчик. В нашем случае частота дискретизации равна 128 000. Multiply Const слегка увеличивает уровень сигнала, чтобы он был близок к единице: это позволяет максимально использовать мощность передатчика. И наконец, блок USRP Sink передает данные в реальный SDR. Уровень сигнала на выходе (Gain) я установил равным 70, для других моделей SDR это число может отличаться.
Это зависит еще и от удаленности от управляемого устройства — в моем случае беспроводной пульт управляет лампой, которая висит в другой комнате. Поэтому мощность я установил побольше. Частота передачи указана та же, что использовалась для приема. Модуль Scope Sink не обязательный и служит для контроля передаваемых данных.
РЕКОМЕНДУЕМ:
Как сделать робота для сборки кубика Рубика
Проверяем. Запускаем программу, и на SDR включается светодиод TX, лампа загорается.
После того как программа готова и отлажена, GNU Radio Companion можно не использовать. Для этого в настройках проекта меняем WX GUI на No GUI и выбираем опцию Run to Completion, убираем блок Scope Sink. Затем выбираем в меню Run → Generate, создается файл top_block.py, который и содержит готовое приложение на Python. Его можно использовать из командной строки, как любую другую программу на Python.
Можно открыть файл top_block.py и убедиться, что код понятен и при желании его можно изменить.
GNU Radio Python Flow Graph
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 |
from gnuradio import blocks from gnuradio import eng_notation from gnuradio import gr from gnuradio import uhd from gnuradio.eng_option import eng_option from gnuradio.filter import firdes from optparse import OptionParser import time class top_block(gr.top_block): def __init__(self): gr.top_block.__init__(self, "Top Block") # Variables self.samp_rate = samp_rate = 128000 # Blocks self.uhd_usrp_sink_0 = uhd.usrp_sink( ",".join(("", "")), uhd.stream_args(cpu_format="fc32", channels=range(1),), ) self.uhd_usrp_sink_0.set_samp_rate(samp_rate) self.uhd_usrp_sink_0.set_center_freq(434000000, 0) self.uhd_usrp_sink_0.set_gain(70, 0) self.uhd_usrp_sink_0.set_antenna('TX/RX', 0) self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, samp_rate,True) self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vcc((1.8, )) self.blocks_file_source_0 = blocks.file_source(gr.sizeof_gr_complex*1, 'D:\\Temp\\1\\433_signal.iq', False) # Connections self.connect((self.blocks_file_source_0, 0), (self.blocks_throttle_0, 0)) self.connect((self.blocks_multiply_const_vxx_0, 0), (self.uhd_usrp_sink_0, 0)) self.connect((self.blocks_throttle_0, 0), (self.blocks_multiply_const_vxx_0, 0)) def get_samp_rate(self): return self.samp_rate def set_samp_rate(self, samp_rate): self.samp_rate = samp_rate self.uhd_usrp_sink_0.set_samp_rate(self.samp_rate) self.blocks_throttle_0.set_sample_rate(self.samp_rate) def main(top_block_cls=top_block, options=None): tb = top_block_cls() tb.start() tb.wait() if __name__ == '__main__': main() |
Если ты знаешь Python, можешь модифицировать программу по своему усмотрению. Например, ты можешь передавать имя записанного файла через параметр командной строки, что позволит симулировать нажатие разных кнопок пульта.
Проверяем: открываем Power Shell и запускаем программу, лампа должна загореться.
Таким способом можно принимать и передавать разные сигналы — не только от пульта, но и от любого устройства, работающего по аналогичному принципу. Простор для экспериментов огромный.
Передавать с помощью SDR мы можем любой записанный сигнал — можно записать FM-станцию в городе, затем «ретранслировать» ее на даче. Сигнал будет передаваться как есть, включая RDS и стерео. Можно даже увеличить частоту дискретизации и передавать сразу несколько FM-станций параллельно. Все зависит только от того, сколько дискового места под запись тебе не жалко и какова максимальная ширина полосы пропускания твоего SDR. Единственный недостаток — выходная мощность SDR довольно мала, и дальность будет небольшой.
Перехват слабого сигнала
Рассмотрим теперь более сложную задачу. Представим, что физического доступа к пульту у нас нет. Для примера я записал сигнал с пульта, находящегося в другой комнате. Открываем его с помощью Python и смотрим на результат.
Если сравнить с предыдущей записью, сигнал слабее в двести раз, уровень шумов выше. Наш прежний подход тут не сработает: SDR ведь не знает, что ему нужно передать, и он передаст слабый и шумный сигнал. Попробовать можно, но результат очевиден: лампа не загорается.
Есть целых три способа решить эту задачу.
Вариант 1 — просто увеличим уровень сигнала. У нас ведь Software Defined Radio: что хотим в цифре, то и делаем. Изменим параметр Multiply Const с 1.8, ну, например, на 300. Если точнее, нам нужно открыть Scope Sink и подобрать параметр так, чтобы сигнал стал в диапазоне от 0.5 до 0.9.
Результат: запускаем программу, лампа горит. Но такой способ я не могу рекомендовать. Причина проста: вместе с полезным сигналом мы передаем в эфир весь усиленный шум и мусор, что снижает эффективность передачи и заполняет ненужными помехами радиоэфир.
Посмотри, что передается в эфир: сверху сигналы от настоящего пульта, жирный сигнал снизу — это наш, усиленный в триста раз вместе с шумом сигнал.
Вариант 2 — добавим полосовой фильтр. Он просто обрежет все лишнее.
Вернемся к спектру сигнала: пик приходится на частоту 6,5 кГц.
Все вокруг него — шум, который можно и нужно убрать. Заменяем блок Multiply Const на блок Band Pass Filter, указываем параметры верхней и нижней границы в 4 и 9 кГц — центральная частота с небольшим запасом влево и вправо. Усиление в триста раз зададим в параметре Gain.
Запускаем: все работает, лампа загорается. Интересно посмотреть на спектр сигнала — он получился гораздо компактнее по занимаемой полосе (верхний сигнал), чем сигнал оригинального пульта (нижний сигнал).
Неудивительно — в дешевые китайские пульты ставят простейшие компоненты ценой менее доллара, и на качество тут рассчитывать не приходится.
Мы убрали шум вокруг сигнала, но в самой полосе сигнала данные так и передаются вместе с шумом, который был записан из эфира. Кардинальный способ улучшить это — отказаться от передачи записанного сигнала в эфир и сформировать цифровой сигнал заново, без шумов.
Вариант 3 — сформируем сигнал заново, с нуля.
Не пугайся. Если рассматривать прохождение сигнала слева направо, то все становится проще.
Мы знаем, что наш пульт передает в АМ, так что первым шагом мы выполняем демодуляцию АМ. Для этого исходный сигнал пульта, находящийся на частоте 6,5 кГц от центра, смещается на нулевую частоту умножением на синусоидальный сигнал. Вспоминаем школьную формулу умножения косинусов — результатом будет сумма и разность частот.
Затем мы убираем лишнее с помощью Low Pass Filter и выделяем амплитуду сигнала с помощью Complex to Mag. Как ты помнишь, из SDR приходят два потока данных, называемых I и Q, которые в GNU Radio представлены как комплексные числа. На выходе после Complex to Mag мы имеем демодулированный сигнал АМ.
Теперь нужно преобразовать наш сигнал в бинарный, для чего используется блок Threshold. Он делает то, ради чего это затевалось, — преобразует шумный входной сигнал в чистый выходной. Посмотри на разницу.
Мы имеем неплохой запас по отношению сигнал/шум, а значит, могли бы принять еще более слабые сигналы.
И последнее: передача сформированного сигнала обратно в эфир. Для этого мы сдвигаем его обратно на те же 6,5 кГц, обрезаем все лишнее с помощью Band Pass Filter (исходный цифровой сигнал прямоугольный, так что он имеет много ненужных нам гармоник), и окончательные данные поступают на USRP Sink.
Последний тест: запускаем программу, лампа загорается.
Если убрать Band Pass Filter, то на выходе мы получаем широкий спектр, точно такой же, как у оригинального пульта. Из этого мы можем сделать вывод: китайцы сэкономили, и никакого выходного фильтра в этом пульте нет. Они просто взяли генератор на 434 МГц и модулируют его цифровым управляющим сигналом. Скорее всего, простейший микроконтроллер, который генерирует цифровые сигналы при нажатии кнопок, напрямую подключен ко входу Enable микросхемы генератора ВЧ.
Так, не вскрывая пульт, с помощью одной только математики, мы выяснили, что у него внутри.
Можно ли принять сигнал, который будет еще слабее и расположен еще дальше? Да, можно, если воспользоваться тем, что пульт передает повторяющиеся одинаковые посылки. Для этого уже одного GNU Radio будет мало, придется написать функцию накопления и усреднения сигнала. Тогда отношение сигнал/шум можно будет повысить. Примерно так астрономы принимают слабые сигналы на радиотелескопы. Или можно сделать направленную антенну на 433 МГц.
Способы есть, как программные, так и аппаратные, если этот сигнал тебе действительно очень нужен.
Что делать, если ничего не работает?
Допустим, ты сделал все, как написано выше, но ничего не работает. Где копать?
Электроника — наука точная, и чудес здесь не бывает. Для начала стоит проверить прием: убедиться, что в записанном файле есть сигнал и его уровень хорош. В записанном файле должен быть четко различимый сигнал. Если сигнала нет, проверь усиление приемника и частоту. Возможно, частота пульта другая или сигнал инфракрасный, а не радио.
Чтобы не возиться с файлами, запусти SDR# и просто посмотри, что есть на экране во время нажатия кнопок пульта. Если ты не знаешь точную частоту, настрой максимальную полосу обзора в твоем SDR, например 10 МГц, и нажимай кнопки пульта — где-то этот сигнал должен быть. Разумеется, пока не получишь сигнал, дальше двигаться бесполезно.
Когда частота известна и сигнал записан, можно проверять передачу. Важные параметры: антенна и мощность сигнала. От этого зависит дальность передачи. Мощность задается в настройках SDR, этот параметр обычно называется Gain. Антенну в идеале лучше использовать готовую на 433 МГц, например от портативной радиостанции. Если такой антенны нет, сойдет и кусок провода, воткнутый в антенное гнездо, но с ним дальность передачи будет гораздо меньше. Для начала лампу (или то, чем мы управляем) желательно разместить рядом с SDR на рабочем столе, а убедившись, что все работает, уже можно сделать тесты на дальность.
Качество передачи удобно проверять с помощью SDR# и контрольного приемника RTL SDR, спектр сигнала можно вывести на второй монитор. Если передачи нет совсем, смотри в консоли GNU Radio сообщения об ошибках — возможно, не установлен правильный драйвер или в настройках передатчика выбрано другое антенное гнездо. Главное, чтобы ты не пытался передавать с помощью RTL SDR.
Все это наверняка будет работать только с простыми устройствами, которые передают жестко зашитые сигналы. Теоретически, если сигнал не содержит динамически изменяемых полей, способ передачи заранее записанного сигнала будет работать, если же устройства обмениваются данными или ключами, то ничего не выйдет.
Что касается дальности, то с SDR и антенной от рации на 433 МГц мне удавалось зажечь лампу, находящуюся в другом конце квартиры, а если специально сделать направленную антенну, то дальность можно увеличить.
Заключение
Управлять некоторыми беспроводными устройствами с помощью SDR возможно и даже интересно в плане программирования и математики.
С точки зрения уязвимости: еще раз повторю, что простые пульты, которые разбирались в тексте, никакой защиты не имеют. Это плата за дешевизну и простоту конструкции. С другой стороны, ничего серьезного к таким пультам обычно и не подключают. Гораздо более вероятна проблема не «хакер с ноутбуком», а банальное совпадение каналов.
РЕКОМЕНДУЕМ:
Как из видеоадаптера сделать SDR-передатчик
Мощный обогреватель я бы подключал к радиоуправлению с большой осторожностью и обесточивал бы при длительных отъездах в отпуск, а канал лучше сменить с дефолтного на другой в настройках пульта. А если твои друзья или соседи накупили радиоуправляемых лампочек — ты знаешь, как их разыграть на первое апреля.
Здравствуйте! А не подскажите как формировать файл с IQ данными для передачи сигналов произвольной формы? Не могу разобраться в каком виде записывать данный IQ в файле.