Skip to content

FoxHunter — охота на лис (ARDF) на LoRa

Спортивная радиопеленгация (ARDF, «охота на лис») на LoRa: три роли на одинаковом железе E77-900MBL (STM32WLE5). Лиса — маяк-передатчик (9-байтовый пакет каждые 300 мс). Охотник — приёмник с зуммером «горячо-холодно» (громкость по усреднённому RSSI) и опциональным OLED. Фальшлиса — учебный «атакующий» на СВОЁМ железе в классе, показывающий три классические слабости беспроводной связи. Исходный код, методика и схема — в firmware/Projects/LORAWAN/FoxHunter.

Проект соединяет два разных курса на одном стенде: сначала физика поиска — ребёнок находит спрятанный передатчик руками, по громкости, и затухание сигнала перестаёт быть формулой; затем безопасность протокола — почему сигналу нельзя верить слепо, как подпись ловит подделку, счётчик кадров — повтор, а длина ключа решает, можно ли перебрать подпись. Порядок строгий: сначала найти лису руками, только потом лезть внутрь. Python-стенд foxhunt_sim.py даёт понять «горячо-холодно» до выхода на охоту — карта зон громкости на этаже с той же логикой усреднения и порогов, что в прошивке охотника.

Методическое пособие: FoxHunter — охота на лис на LoRa

Стенд: три роли на одинаковом железе E77-900MBL (STM32WLE5), выбираются прошивкой. Лиса — маяк-передатчик, шлёт компактный 9-байтовый пакет каждые 300 мс. Охотник — приёмник с зуммером «горячо-холодно» (громкость по усреднённому RSSI) и опциональным OLED. Фальшлиса — учебный «атакующий» на СВОЁМ железе, который показывает три классические слабости беспроводной связи.

Это спортивная радиопеленгация (ARDF — Amateur Radio Direction Finding, «охота на лис»), в которую играют десятилетиями. Проект соединяет два разных курса: сначала физика поиска (ребёнок находит спрятанный передатчик руками, по громкости), затем безопасность протокола (почему сигналу нельзя верить слепо и зачем нужна настоящая криптография). Полная методика преподавания — в METODIKA.md, железо и сборка — в README.md. Python-стенд foxhunt_sim.py даёт понять «горячо-холодно» до выхода на охоту — карта зон громкости на этаже, физика RSSI и та же логика усреднения и порогов, что в прошивке охотника.

Порядок уроков строгий: сначала найти лису руками, и только потом лезть внутрь. Магия — в находке, а не в CMAC.

Кто чем пользуется

  1. Инженер отвечает за стенд: прошивки ролей, приглушение лисы под площадку, зуммер через резистор 100 Ом, замер шума эфира ([NOISE] в мониторе).
  2. Учитель прячет лису и ведёт три урока по сценарию.
  3. Методист привязывает физику пеленгации и слабости протокола к программе.

Модуль 1. Физика пеленгации: «горячо-холодно»

Урок 1. Громкость вместо направления: почему в здании ищут так

  • Тема: логарифмическая шкала (дБм), затухание сигнала с расстоянием.
  • Стенд: foxhunt_sim.py. Параметры: ZONES[], rssi_at, показатель n.

Ход работы. Запустите uv run foxhunt_sim.py. Двигайте мышь над картой этажа — это «ходьба с охотником». Внизу справа — «зуммер»: зона RYADOM/GORYACHO/TEPLO/ HOLODNO и частота пиков. Найдите спрятанную лису, ориентируясь только на зону.

На графике:

 Зоны громкости на этаже       Профиль RSSI(расстояние)
  RYADOM ▓ (сплошной тон)        RSSI│╲
  GORYACHO ▒ (частые пики)           │ ╲___ RYADOM  −55
  TEPLO   ░ (редкие)                 │     ╲___ TEPLO −85
  HOLODNO · (очень редкие)           │         ╲___
                                     └──────────────► расстояние

Разбор. В здании радиоволна отражается от стен и арматуры — приходит со всех сторон, поэтому направление по антенне работает плохо, а дальность по громкости — отлично: ближе к лисе → выше RSSI → чаще пики зуммера. RSSI падает по лог-дистанционной модели RSSI(d) = P_tx − PL(d0) − 10·n·lg(d/d0), где n≈3 в здании. Прошивка охотника делит эту непрерывную величину на зоны (ZONES[]) и озвучивает их разной частотой пиков. Это игра «горячо-холодно» — и она честная: за ней стоит настоящая физика затухания.

Практическое задание. По профилю RSSI в стенде определите, на каком расстоянии проходит граница RYADOM (−55 дБм) при мощности лисы 2 дБм. Сверьте с реальной охотой: в какой примерно комнате зуммер перейдёт на сплошной тон?


Урок 2. Приглушить лису: почему мощная лиса ломает охоту

  • Тема: динамический диапазон, насыщение, соотношение сигнал/масштаб.
  • Стенд: foxhunt_sim.py (ползунок мощности). Параметр: FOX_TX_PWR_DBM.

Ход работы. В стенде поставьте мощность лисы 14 дБм и походите по этажу: почти везде горит RYADOM — искать нечем. Теперь убавьте до 2 дБм: зоны стали контрастными, к лисе ведёт явный градиент.

На графике:

 Лиса 14 дБм                    Лиса 2 дБм
  RYADOM почти на весь этаж       RYADOM ▓ у самой лисы
  (96% площади — «горячо везде»)  GORYACHO ▒ вокруг, дальше TEPLO/HOLODNO
  искать нечем                    явный градиент к цели

Разбор. Чувствительность зуммера-«градусника» ограничена: если лиса мощная, её RSSI насыщает зону RYADOM уже в соседней комнате — весь этаж «горячий», разницы нет. Приглушив лису до 2 дБм, мы сжимаем зоны до масштаба этажа, и «горячо-холодно» снова различимо. Это общий инженерный принцип: измеритель полезен только в своём динамическом диапазоне — под масштаб задачи подстраивают не прибор, а источник. То же в фотографии (экспозиция), в звуке (усиление), в АЦП (диапазон входа).

Практическое задание. В стенде найдите минимальную мощность лисы, при которой на 40-метровом этаже ещё нет зоны тишины (сигнал добивает в углы), но RYADOM не разливается шире одной «комнаты» (~8 м). Это и есть оптимум под площадку.


Урок 3. Несколько лис по очереди: расписание ARDF и усреднение

  • Тема: разделение времени (TDMA), скользящее среднее, robustность.
  • Параметры: FOX_COUNT/SLOT_MS, RSSI_AVG_N, «проговор» ID.

Ход работы. По коду лисы и охотника: лисы делят цикл на слоты (SLOT_MS), вещают по очереди (FOX_ID), охотник «проговаривает» номер активной лисы пиками (лиса 1 — один пик, лиса 2 — два). В стенде поменяйте окно усреднения RSSI_AVG_N и посмотрите, как «зуммер» становится плавнее (7) или отзывчивее (3).

Разбор. Несколько передатчиков на одной частоте не мешают друг другу, потому что разделены во времени (аналог TDMA): каждая лиса вещает свой слот и молчит в чужой. Позывные MOE/MOI/MOS настоящего ARDF здесь заменены «проговором» числа пиков. Сырой RSSI LoRa скачет на ±3-5 дБ от пакета к пакету — поэтому охотник усредняет по окну из RSSI_AVG_N пакетов: среднее гасит скачки и даёт плавную громкость. Меньше окно — отзывчивее (но дёрганее), больше — плавнее (но ленивее): тот же компромисс, что в любом сглаживающем фильтре.

Практическое задание. Почему при смене активной лисы охотник СБРАСЫВАЕТ буфер усреднения (rssiReset()), а не продолжает среднее? Что было бы со стрелкой громкости в первые секунды после переключения, если бы буфер не сбрасывался?


Модуль 2. Безопасность протокола: почему сигналу нельзя верить

Только после того, как дети уверенно ищут лис. Нужна прошивка FoxHunt_FakeFox — учебный «атакующий» на СВОЁМ железе в классе. Кнопка переключает три атаки.

Урок 4. Подпись против подмены (spoofing)

  • Тема: аутентификация сообщений, контрольная подпись, целостность.
  • Параметры: foxCalcSignature(), атака ATK_SPOOF.

Ход работы. Фальшлиса (атака 1) шлёт пакет с верным fox_id и растущим fcnt, но НАМЕРЕННО испорченной подписью. Охотник печатает [SEC] REJECT ... SIG:FAIL, на экране [FAKE], зуммер молчит.

Разбор. Каждый пакет несёт подпись — байт, вычисленный из всех полей и секретного ключа (foxCalcSignature). Кто не знает ключа, не подделает подпись под свои данные. Охотник пересчитывает подпись сам и сравнивает: не сошлось — пакет отвергнут, «лиса» признана фальшивой. Так приёмник отличает подделку, не видя отправителя — только по математике. Это и есть аутентификация сообщения, основа защиты любого протокола.

Практическое задание. Вопрос классу: охотник понял, что пакет поддельный, хотя fox_id был правильный и fcnt рос. Какое единственное поле его выдало и почему атакующий не смог его подделать?


Урок 5. Счётчик кадров: replay-атака против ребута лисы

  • Тема: защита от повтора, монотонность, различение схожих причин.
  • Параметры: fcnt, логика RESYNC vs REPLAY в processBeacon.

Ход работы. Учитель заранее ловит реальный пакет лисы (строка [RAW] в мониторе охотника → байты в CAPTURED_PACKET). Фальшлиса (атака 2) шлёт этот пакет снова и снова БЕЗ ИЗМЕНЕНИЙ. Подпись верна, но fcnt СТОИТ. Охотник: [SEC] REJECT ... SIG:OK FCNT:REPLAY. Затем перезагрузите настоящую лису — её fcnt тоже низкий, но РАСТЁТ, и охотник сам ресинхронизируется ([RESYNC]).

На графике:

 Настоящая лиса (ребут)          Replay-атака
  fcnt: 1,2,3,... (растёт)        fcnt: 127,127,127,... (застыл)
  → RESYNC, лиса принята          → REJECT, атака отбита

Разбор. Подпись ловит подделку, но НЕ ловит повтор: атакующий может записать чужой валидный пакет и переиграть его. Защита — монотонный счётчик кадров fcnt: охотник помнит последний и принимает только больший. Красивый момент: и у replay-атаки, и у перезагруженной лисы fcnt низкий — но у атаки он ЗАСТЫЛ, а у ребута РАСТЁТ. Один и тот же механизм (динамика счётчика) отличает «честная лиса перезагрузилась» от «злоумышленник переигрывает старое»: три растущих подряд → ребут (ресинк), застывший → атака (отказ). Так работает защита от replay в реальных протоколах (LoRaWAN, TLS, IPsec).

Практическое задание. Почему нельзя просто «принимать любой fcnt, если подпись верна»? И почему одного растущего пакета мало для ресинка — зачем требовать три подряд (RESYNC_N)?


Урок 6. Брутфорс подписи: почему 1 байт — не защита, а 4 байта — защита

  • Тема: пространство ключей, вычислительная стойкость, экспонента.
  • Параметры: атака ATK_BRUTE, мостик к AES-CMAC (4-байтовый MIC).

Ход работы. Фальшлиса (атака 3) перебирает все 256 значений однобайтовой подписи для одного пакета, раз в секунду шлёт следующее. Рано или поздно совпадёт — и охотник примет фальшивку как настоящий [RX]! Наблюдайте счётчик перебора в мониторе фальшлисы.

На графике:

 Подпись 1 байт = 256 вариантов        Подпись 4 байта (MIC) = 2³² вариантов
  перебор за ~минуты на 1 Гц             ~4.3 млрд — перебор нереален
  █ пробита                              🔒 держит

Разбор. Наша подпись — намеренно слабая: 1 байт = всего 256 вариантов, перебор совпадёт в среднем за 128 попыток. Это не баг, а учебный материал: он показывает, ЗАЧЕМ настоящие системы берут 4-байтовый MIC (Message Integrity Code) на базе AES-CMAC — 2³² ≈ 4.3 млрд вариантов, перебор физически нереален. Каждый добавленный байт умножает стойкость на 256 (экспонента) — вот почему длина ключа решает. Это прямой мостик к «взрослой» LoRaWAN-ветке курса, где подпись честная.

Практическое задание. Если однобайтовую подпись перебирают за ~2 минуты (1 Гц), сколько времени займёт перебор 4-байтового MIC при той же скорости? (Ответ в годах переведите в понятную величину.) Почему в реальности атакующий не может слать «быстрее» без ограничений?


Приложение: таблица переноса концепций в индустрию

Концепция из проектаГде встречается за пределами лаборатории
Лог-дистанционная модель RSSISite survey, планирование покрытия, локализация
Динамический диапазон измерителяФотография, АЦП, аудио, любые сенсоры
Скользящее среднее (сглаживание)Обработка сигналов, датчики, финансы
Разделение времени (TDMA)Сотовые сети, промышленные шины, ARDF
Аутентификация сообщения (подпись/MIC)TLS, LoRaWAN, банковские протоколы
Защита от replay (счётчик кадров)LoRaWAN, TLS, IPsec, платёжные системы
Стойкость ключа, пространство перебораВся криптография; выбор длины ключа

Заключение

FoxHunter ведёт ученика от осязаемого к абстрактному: сначала он находит спрятанный передатчик руками, по громкости зуммера, — и физика затухания перестаёт быть формулой, становится игрой «горячо-холодно». Затем на том же железе он видит, как легко подделать сигнал, если протокол наивен, и как три механизма (подпись, счётчик кадров, длина ключа) по очереди эту наивность лечат — вплоть до понимания, зачем существует настоящая криптография. Python-стенд foxhunt_sim.py даёт прожить первую половину — почему мощную лису не найти и как приглушение возвращает контраст, — а фальшлиса на своём железе честно, безопасно и наглядно показывает вторую. Главный принцип курса тут звучит буквально: сигналу нельзя верить слепо — ни его громкости (мощная лиса «горячо везде»), ни его содержимому (подделка и повтор).