Всем привет. В этот раз я решил затронуть тему безопасности устройств, которыми пользуются большинство автовладельцев. Речь пойдёт о трёх самых популярных автомобильных сигнализациях (иммобилайзерах), представленных на российском рынке.

Забегая вперёд, скажу, что отчёты обо всех найденных недостатках были направлены производителям, а также во ФСТЭК России. Из ответа регулятора я узнал, что в качестве уязвимостей отчёты зарегистрированы не будут, так как «данное программно-аппаратное средство не используется на объектах ГИС и КИИ». Производители же сообщили, что описанные недостатки устранят в новых моделях сигнализаций. За информацией о том, что делать с уже существующими моделями, я рекомендую обращаться к производителям.

О проекте

Исследования проводились с большим разбросом по времени. Первое я выполнил в конце 2022 года, два других — в 2024-м, с небольшим интервалом. Целью проекта был вовсе не поиск уязвимостей, а простой азарт: если что-то зашифровано, я хочу это расшифровать. Опыт получился достаточно интересный. О нём — в этой статье.

О первом исследованном устройстве

Что меня радует в моей работе, так это возможность уделять время собственным ресёрчам. Так, в поисках подопытного я решил посмотреть содержимое одной коробочки, которая лежала на полке с артефактами от предыдущих проектов. Описание на ней гласило: «Охранно-противоугонная микросигнализация — CAN иммобилайзер PANDECT X-1000 BT», а внутри коробки лежали какие-то проводки, инструкция, кнопка на шлейфе, основной модуль сигнализации, а также брелок и «верещалка».

Коробка с сигнализацией и её содержимое
Коробка с сигнализацией и её содержимое

Первым делом нужно понять, что искать и куда копать. И проще всего начать с… запуска сигналки. А точнее — с банальной её настройки и подключения к ПК и смартфону. Читаю инструкцию, устанавливаю соответствующие приложения, втыкаю USB-шнурок и, конечно же, не забываю для всего этого добра собрать USB-трафик с помощью Wireshark для последующего анализа.

Инструкция по подключению питания и других проводков
Инструкция по подключению питания и других проводков

Так, например, одним из потенциальных векторов исследования может быть процесс обновления устройства по USB. По идее, если мне удастся разобраться, как он происходит, и найти в нём недостатки, я смогу самостоятельно формировать файлы обновления и загружать их в сигнализацию.

Интерфейс ПО от производителя
Интерфейс ПО от производителя
Интерфейс ПО от производителя
Интерфейс ПО от производителя

Процесс обновления

По утверждению Wireshark, подключенное USB-устройство распознаётся как HID, а всё общение в трафике выглядит как набор пакетов GET_REPORT/SET_REPORT. Если присмотреться к содержимому одного из первых пакетов, можно заметить, что отправляются байтики, идентичные байтам в хедере файла прошивки (его я нашёл на официальном сайте производителя). Почему я решил, что это именно хедер? Раз уж содержимое идёт отдельным пакетом, имеет совпадения с началом файла и содержит строку версии — это точно хедер!

Строка версии в одном из пакетов
Строка версии в одном из пакетов
Сравнение заголовка прошивки и отправленного пакета
Сравнение заголовка прошивки и отправленного пакета
Сравнение заголовка прошивки и отправленного пакета
Сравнение заголовка прошивки и отправленного пакета

Как можно заметить, начиная со следующего пакета отправляется уже само зашифрованное содержимое прошивки (тело) — по 64 байта (ограничение HID?). Можно ли уже попробовать что-то сделать, обладая этими знаниями?

Сравнение тела прошивки и отправленного пакета
Сравнение тела прошивки и отправленного пакета
Сравнение тела прошивки и отправленного пакета
Сравнение тела прошивки и отправленного пакета

Я решил поменять один единственный бит где-нибудь в хедере прошивки, не отправляя всё остальное (либо, если нужно, отправить первый блок тела), и посмотреть, что будет. Результат — ПО сигнализации перестало видеть устройство, хотя сам USB-девайс появляется, да и лампочки на сигналке говорят о том, что она что-то да запускает. Значит, нужно понять, что именно идёт не так и почему ПО перестало «видеть». Нормальный человек изучил бы USB-трафик, а я зачем-то полез изучать саму утилиту.

Меняю 1 бит в пакете с заголовком
Меняю 1 бит в пакете с заголовком
Меняю 1 бит в пакете с заголовком
Меняю 1 бит в пакете с заголовком

Утилита от производителя

Беглое изучение программы показало следующее:

  • Она точно использует JS.

  • Скомпилирована с использованием Awesomium SDK.

  • Не содержит строк в читаемом виде.

  • Файл data.asr на деле оказывается запароленным ZIP-архивом.

  • Без отладки не разобраться.

Пароль от data.asr
Пароль от data.asr

После непродолжительной отладки приложения мне удалось выяснить пароль от архива: aoeuiqjkxb. Далее я добавил в js-файл (уже не помню какой) строчку, которая помогла бы понять различия между двумя сигналками: почти окирпиченной и нормальной.

Отладочные принты
Отладочные принты

Результат такой отладки показал различия в поле версии. В одном случае содержимое поля version было нормальным, а в другом — нечитаемым. Исходя из этого можно сделать следующие выводы:

  • Содержимое USB-пакета с модифицированным хедером сразу же записалось во внутреннюю память устройства.

  • При взаимодействии по USB содержимое читается из памяти, ПО не может сравнить его с нормальной строкой версии прошивки, и устройство перестаёт быть видимым.

Результат патча одного бита
Результат патча одного бита
Результат патча одного бита
Результат патча одного бита

Для дальнейших экспериментов мне хотелось восстановить работу сигналки, поэтому я поставил бряк на выход из ReadFile() и просто подменил нужные байтики в lpBuffer на корректные. Благодаря этому мне удалось запустить процесс обновления целиком и прошить нормальный хедер заново, после чего сигнализация ожила.

Указатель на буфер с результатом
Указатель на буфер с результатом

Продолжаем обновляться

Если позволить процессу обновления отработать полностью, при этом оставив модифицированный бит в заголовке, можно выяснить: какой бы ни была контрольная сумма (цифровая подпись), она проверяется в самом конце, после отправки всех байт. Об этом будет свидетельствовать сообщение об ошибке в программе (извините, но скрин у меня не сохранился за давностью лет).

Можно ли теперь собрать все полученные знания в скрипт и заменить прошивку сигнализации на свою? К сожалению, пока ещё нет. Играть в «раскирпичь меня» после каждой попытки мне не очень-то хотелось. Поэтому было решено наконец-таки разобрать девайс и посмотреть, что можно сделать, обладая, так сказать, некоторыми навыками пайки.

Внутренний мир

В основном блоке сигнализации обнаружились чипы с собственной прошивкой:

  • STM32 (уже не помню, какая именно).

  • nRF52832 (основной модуль).

  • nRF51822 (брелок).

Также на основном модуле легко нашлись две гребёнки по пять выводов каждая: одна ближе к STM32, вторая — к nRF52. Прозвонка показала, что два вывода из пяти — земля и питание на обоих группах контактов. Остальные же (не помню почему, но, возможно, потому что на тот момент контакты чипов были жутко мелкими для меня) я не стал вызванивать, а сразу решил, что это SWD. В результате непродолжительного перебора различных вариантов так оно и оказалось.

Отладка

Подключившись отладчиком JLink к STM32, мне удалось выяснить, что внутренняя память чипа не читается, а вот SRAM читать можно — это говорит о включенном режиме RDP1. В принципе, с этим уже можно работать. Идея была такая:

  1. Запускаю режим обновления прошивки.

  2. Отправляю первый блок.

  3. Подключаю отладчиком.

  4. Читаю SRAM в файл dump1.bin.

  5. Отправляю второй блок.

  6. Читаю SRAM в файл dump2.bin.

  7. Сравниваю файлы и ищу закономерности.

С этой последовательностью возникла проблема: после подключения отладчиком сигнализация не смогла принимать USB-пакеты — это связано с работой режима RDP1, не позволяющего делать полноценную «паузу» на STM32. Пришлось адаптировать алгоритм: на этапе отправки второго блока сначала перезагружать устройство, отправлять первый блок, затем второй и уже после этого читать SRAM.

Автоматизация

Чтобы автоматизировать процесс, я попросил коллегу написать небольшой «дропер» питания на Arduino, который бы ожидал на одном из GPIO-пинов команду ресета и при помощи MAX4619 (переключатель) на некоторое время обрывал линию 5V на USB-шнурке.

MAX4619
MAX4619
Скрипт для Arduino
Скрипт для Arduino

В результате, кроме больших участков с неинициализированной памятью, удалось обнаружить две области, в которых происходило что-то интересное. Но сказать что-либо однозначное по ним я пока не мог — нужно было набрать больше дампов, порядка тысячи.

Базовый сетап
Базовый сетап

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

Изменённые байты в SRAM после получения заголовка
Изменённые байты в SRAM после получения заголовка
Сравнение SRAM после получения первого зашифрованного блока
Сравнение SRAM после получения первого зашифрованного блока

Убер-автоматизация

К счастью, на полках с артефактами обнаружились ещё две сигнализации этой модели (итого — три). К тому же у нас на работе было целых два J-Link, и один EDU я притащил из дома. Что ещё может понадобиться для качественной автоматизации, кроме трёх препарированных USB-кабелей, как вы думаете?

Правильный ответ: три зажима для бумаги, обмотанные изолентой. Не спрашивайте меня почему — я не вспомню. Но вот фото с пруфом, что это точно было нужно.

Такого вы точно нигде не видели
Такого вы точно нигде не видели

После отладки этой конструкции я оставил её работать на целую неделю… Так думал я, пока не пришёл на работу на следующий день. Там меня ожидал сюрприз: окошко JLink с предложением принять условия использования EDU-версии. Многие, кто работают с этим отладчиком, видят окно постоянно, но наверняка, как и я, абсолютно машинально нажимают кнопку Accept и забывают о нём. Как оказалось, сам JLink вспоминает о нём ровно в полночь. Как я это обходил — отдельный вопрос и, пожалуй, не стоящий упоминания в этой статье.

То самое окно
То самое окно

Какие-то успехи

Спустя несколько дней набралось достаточно дампов SRAM, чтобы обнаружить и атрибутировать в них:

  • зашифрованные 8 байт текущего блока,

  • 8 байт XOR-ключа для этого блока,

  • расшифрованные 8 байт.

Таким образом, если набрать достаточное количество байт XOR-ключа для первых N-байт прошивки, можно зашифровать ими свою собственную, которая, например, блок за блоком сдампит туда же в SRAM содержимое загрузчика (который, предположительно, будет содержать и алгоритм шифрования целиком). Сказано — сделано! Детали будут в конце статьи, а пока перейдём ко второму чипу на плате сигнализации — nRF52.

nRF52 и nRF51

На момент работы над проектом у меня, к сожалению, не было опыта с глитчами по питанию (fault injection), а вот у моего коллеги, к счастью, он был. Поэтому с его помощью, а также опираясь на публично доступную статью с описанием уязвимости в чипе nRF52 (Wayback Machine), я получил на руки полный дамп прошивки беспроводной части сигнализации.

Посадочные места под nRF52 и сглаживающие конденсаторы
Посадочные места под nRF52 и сглаживающие конденсаторы
Типичная обвязка для nRF52
Типичная обвязка для nRF52

Что касается брелока: в нём установлен похожий чип nRF51, который не требует таких продвинутых техник обхода защиты. Ну и, конечно же, статья о том, как сдампить с него прошивку (Wayback Machine).

Брелок
Брелок
Процесс эксплуатации уязвимости в nRF51
Процесс эксплуатации уязвимости в nRF51

Вместо выводов

Алгоритмом, который использовался для шифрования прошивки, оказался ГОСТ 28147-89. Благодаря возможности пошагово получать содержимое SRAM и некорректному использованию алгоритма (промежуточные результаты работы оставались в памяти) мне удалось полностью восстановить прошивку основного блока и изучить механизм его обновления по USB. А ещё я написал скрипт, который позволяет полностью расшифровывать файлы обновлений и шифровать их обратно.

Ручная реализация ГОСТ 28147-89
Ручная реализация ГОСТ 28147-89

Взаимодействие с производителем

В конце 2022 года результаты, которые можно было бы со спокойной душой отправить вендору, были сформированы. Я написал письмо с подробным описанием проделанных шагов, а также указал рекомендации по устранению найденных проблем. Производитель сообщил, что в курсе выявленных уязвимостей, но иммобилайзер уже снят с производства. Исправления же учтут в будущих моделях.

Результаты

Что мы получаем в итоге:

  • Алгоритм шифрования ГОСТ используется некорректно (в памяти остаются следы применяемого ключа).

  • Проверка подписи файлов не реализована.

  • Можно загрузить в сигнализацию модифицированную прошивку (включая низкоуровневые загрузчики) либо изменив файлы обновлений, либо через SWD-пины.

P. S. Спасибо за внимание. Скоро опубликую статью о следующем производителе автомобильных сигнализаций — Starline. Остаёмся на связи!

Комментарии (3)


  1. jaha33
    01.07.2026 07:41

    Чтобы получать более безопасные устройства лучше таки использовать контроллеры на M23/33 и правильно пользоваться возможностями ядра. Если разработчик все правильно сделает, то и к swd не прицепиться будет, и гличи по питанию не помогут. В отдельных вариациях M33 даже есть фиксация физического вскрытия корпуса микросхемы с автоочитской памяти

    Насколько я помню, с 22г, когда западные микросхемы резко перестали быть доступны, Пандора перешли на какие то китайские. Как там с безопасностью - не очень понятно

    Сам встречал китайскую микросхему на Corex M0, где разъем программирования не лочится в принципе


    1. DrMefistO Автор
      01.07.2026 07:41

      Ядро от всего не защитит. Алгоритмы тоже должны быть написаны грамотно, с учётом специфики конкретной железки


  1. dmitryrf
    01.07.2026 07:41

    Очень интересно и легко читается, спасибо!