
Извлечение прошивки из микроконтроллера – задача, требующая точного понимания архитектуры чипа и доступных инструментов. Современные микроконтроллеры, такие как STM32, AVR или ESP32, часто оснащены защитой от чтения памяти, но существуют методы обхода этих ограничений. Выбор способа зависит от модели чипа, наличия отладочных интерфейсов (SWD, JTAG, UART) и уровня защиты.
Для микроконтроллеров с открытым доступом к интерфейсам программирования (например, STM32 через SWD) достаточно использовать отладчики типа ST-Link или J-Link. Подключение выполняется через стандартные пины: SWDIO, SWCLK, VCC и GND. Программы вроде OpenOCD или STM32CubeProgrammer позволяют считывать flash-память напрямую. Однако при активированной защите уровня RDP (Readout Protection) потребуются дополнительные меры.
Если интерфейсы заблокированы, применяются методы аппаратного вмешательства. Для чипов с внешней flash-памятью (например, ESP8266) прошивку можно извлечь через SPI-интерфейс, подключив логический анализатор или программатор. В случае встроенной памяти (как у STM32) используют атаки по сторонним каналам: анализ потребляемого тока (Power Analysis) или электромагнитных излучений. Инструменты типа ChipWhisperer позволяют восстанавливать данные даже при частичной защите.
Для микроконтроллеров с аппаратной защитой (например, ARM TrustZone) требуется комбинация методов: реверс-инжиниринг печатной платы, поиск уязвимостей в bootloader или использование эксплойтов. В некоторых случаях помогает перепрошивка через UART с помощью заводских команд, если они не заблокированы. Важно учитывать, что несанкционированное извлечение прошивки может нарушать законодательство и лицензионные соглашения.
Инструменты для считывания прошивки через JTAG и SWD интерфейсы

Для работы с JTAG и SWD чаще всего используют адаптеры на базе микросхем FTDI (FT2232H, FT232H) или специализированные отладчики, такие как J-Link (SEGGER), ST-Link (STMicroelectronics) и CMSIS-DAP (ARM). J-Link поддерживает более 5000 микроконтроллеров, включая Cortex-M, RISC-V и AVR, с тактовой частотой до 12 МГц для SWD и 50 МГц для JTAG. ST-Link v3 работает с STM32, обеспечивая скорость до 24 МГц, а CMSIS-DAP – открытый стандарт, совместимый с OpenOCD и поддерживаемый многими производителями, включая NXP и Microchip. Для низкоуровневого доступа применяют Bus Pirate или Black Magic Probe, последний интегрирует GDB-сервер напрямую, устраняя необходимость в промежуточном ПО.
Программное обеспечение зависит от выбранного адаптера: OpenOCD (поддерживает большинство JTAG/SWD-устройств, конфигурируется через скрипты TCL), J-Flash (для J-Link, оптимизирован под массовое производство), STM32CubeProgrammer (для ST-Link, работает с защищёнными прошивками через RDP-механизмы). Для автоматизации используют Python-библиотеки pyOCD или pyswd, позволяющие считывать память по адресам с проверкой контрольных сумм. При работе с защищёнными чипами (например, STM32 с активированным RDP Level 1) требуется предварительное стирание флеш-памяти или использование уязвимостей, таких как Fault Injection через JTAG/SWD-пины.
Методы дампа памяти с использованием программаторов типа ST-Link и J-Link

ST-Link и J-Link поддерживают прямое чтение памяти микроконтроллеров через интерфейсы SWD или JTAG. Для дампа флеш-памяти через ST-Link используйте утилиту st-flash из пакета stlink-tools с командой:
st-flash read <output_file.bin> 0x08000000 0x10000– чтение 64 КБ с адреса 0x08000000 (типичный стартовый адрес флеш STM32).- Для J-Link применяйте
J-Link Commanderс последовательностью команд:connect(выбор устройства и интерфейса),savebin <output_file.bin>, 0x08000000, 0x10000.
Оба программатора требуют корректной настройки тактовой частоты (обычно 4 МГц для SWD) и подключения питания к целевому устройству. При ошибках доступа проверьте конфигурацию защиты памяти (RDP) – на STM32 она может блокировать чтение.
Для автоматизации процесса используйте скрипты на Python с библиотеками pyocd (для J-Link) или stlink (для ST-Link). Пример для pyocd:
from pyocd.core.helpers import ConnectHelper
with ConnectHelper.session_with_chosen_probe() as session:
flash = session.target.memory_map.get_boot_memory()
data = session.target.read_memory_block8(flash.start, flash.length)
with open("dump.bin", "wb") as f:
f.write(data)
Этот метод позволяет обойти ограничения штатных утилит, например, при работе с нестандартными адресами или секторами памяти. Для микроконтроллеров с зашифрованной флеш (например, STM32H7 с TrustZone) потребуется предварительное снятие защиты через STM32CubeProgrammer или аналоги.
При дампе ОЗУ учитывайте, что его содержимое теряется при перезагрузке. Для сохранения данных используйте команды halt (остановка ядра) перед чтением. В J-Link Commander это реализуется так:
halt,mem32 0x20000000 0x2000(чтение 8 КБ с адреса 0x20000000),savebin ram_dump.bin 0x20000000 0x2000.
Для ST-Link аналогичный результат достигается через st-util в связке с GDB: dump binary memory ram_dump.bin 0x20000000 0x20020000. При работе с динамическими данными (например, стеком) фиксируйте момент дампа, чтобы избежать искажений из-за выполнения кода.
Извлечение прошивки через UART и другие последовательные порты

Многие микроконтроллеры поддерживают загрузку прошивки через UART в режиме бутлоадера. Например, STM32 использует встроенный загрузчик USART, активируемый при старте с зажатым BOOT0 или через команды STM32_Programmer_CLI. Для AVR-микроконтроллеров (ATmega, ATtiny) применяется avrdude с параметром -c arduino или -c stk500, если используется внешний программатор. ESP8266 и ESP32 позволяют считывать прошивку через UART с помощью esptool.py командой read_flash.
Перед извлечением необходимо определить скорость передачи (baud rate) и формат данных (8N1, 7E1 и т.д.). Часто встречаются значения 9600, 115200 или 1 Мбит/с. Для анализа трафика используйте логический анализатор (Saleae, DSLogic) или осциллограф с декодером UART. Если микроконтроллер шифрует данные, потребуется предварительный дамп памяти через JTAG/SWD или аппаратный взлом (например, сброс защиты через лазер или glitching).
В некоторых случаях прошивка передается по UART в виде дампа памяти или HEX-файла. Для захвата используйте скрипты на Python с библиотекой pyserial или специализированные утилиты вроде Flashrom для SPI-флэш-памяти, подключенной к микроконтроллеру. Пример команды для чтения 1 МБ данных с адреса 0x000000: esptool.py --baud 115200 read_flash 0x0 0x100000 firmware.bin. Убедитесь, что прошивка не фрагментирована – иначе потребуется сборка из нескольких блоков.
Помимо UART, для извлечения прошивки применяются другие последовательные интерфейсы: SPI, I2C и 1-Wire. SPI часто используется для доступа к внешней флэш-памяти (Winbond, Macronix), где прошивка хранится в открытом виде. Для считывания подойдет программатор CH341A с ПО Flashrom или NeoProgrammer. I2C встречается реже, но поддерживается утилитами вроде i2cdump из пакета i2c-tools. 1-Wire (например, в DS2431) требует адаптера и ПО owfs.
При работе с защищенными устройствами UART может быть заблокирован на уровне прошивки. В таких случаях помогает аппаратный сброс защиты через перепрошивку boot-сектора или использование уязвимостей в загрузчике. Например, в некоторых STM32 можно обойти защиту чтения (RDP) через JTAG, если она не установлена на уровень 2. Для ESP32/ESP8266 применяется метод efuse – считывание битов защиты перед попыткой дампа.
Если микроконтроллер использует зашифрованный UART-трафик, потребуется реверс-инжиниринг протокола. Инструменты: Wireshark с плагином для UART, UARTDecoder для логических анализаторов или скрипты на Python для парсинга пакетов. Часто встречаются простые XOR-шифры или AES с жестко закодированными ключами. Пример поиска ключа: дамп памяти через SWD и поиск строк вроде 0xA0, 0xA1, 0xA2... – типичные маркеры AES-ключей.
Для автоматизации процесса используйте скрипты на Bash или Python. Пример для STM32 с stm32flash: stm32flash /dev/ttyUSB0 -r firmware.bin -S 0x08000000:0x10000. Для AVR: avrdude -p m328p -c arduino -P /dev/ttyUSB0 -U flash:r:firmware.hex:i. При работе с несколькими устройствами удобно применять expect для автоматизации диалоговых протоколов. Всегда проверяйте контрольные суммы дампов – даже однобитовая ошибка может сделать прошивку неработоспособной.
Работа с загрузочными загрузчиками (bootloaders) для доступа к памяти

Загрузочные загрузчики (bootloaders) – специализированные программы, размещаемые в защищённой области памяти микроконтроллера, часто в первых секторах flash-памяти. Они позволяют обновлять прошивку без внешних программаторов, используя интерфейсы UART, SPI, I2C или USB. Пример: STM32 с заводским bootloader поддерживает загрузку через USART1 (PA9/PA10) с фиксированной скоростью 115200 бод, игнорируя пользовательские настройки тактовой частоты.
Протоколы взаимодействия с bootloader стандартизированы для конкретных семейств. В STM32 используется протокол STMicroelectronics, где команды передаются в виде пакетов с контрольной суммой CRC. Например, команда 0x7F инициирует соединение, а 0x02 запрашивает идентификатор чипа. Для ESP32 загрузчик работает через UART с протоколом Espressif, где данные передаются в пакетах с заголовком 0x02 и длиной до 256 байт. Ошибки в контрольных суммах или тайм-ауты приводят к сбросу соединения.
Извлечение прошивки через bootloader возможно только при отсутствии защиты памяти. В STM32 защита реализована через биты RDP (Readout Protection), где уровень RDP1 блокирует чтение flash через debug-интерфейсы, но оставляет доступ через bootloader. Уровень RDP2 полностью запрещает чтение, включая bootloader. Для проверки состояния защиты используйте команду 0x92 (Get ID) и анализируйте ответ: если возвращается 0x55, защита отключена.
Инструменты для работы с bootloader включают утилиты производителей и сторонние решения. STMicroelectronics предоставляет STM32CubeProgrammer, поддерживающий все интерфейсы (UART, USB, SPI). Для ESP32 используется esptool.py, который через UART загружает или считывает прошивку с адреса 0x00000. Для AVR подходит avrdude с параметром -c arduino, если используется Optiboot. Ключевой параметр – корректная настройка тактовой частоты и задержек: например, для STM32 при скорости 115200 бод тайм-аут между байтами не должен превышать 10 мс.
При работе с bootloader критически важно учитывать ограничения по объёму передаваемых данных. STM32 позволяет считывать память блоками до 256 байт за одну команду (0x11 – Read Memory), ESP32 – до 4096 байт. Превышение лимита приводит к ошибке протокола. Для автоматизации процесса используйте скрипты на Python с библиотеками pyserial или pyusb, где можно задать цикл чтения с динамическим расчётом контрольных сумм и проверкой ответов.
Обход защиты от чтения и способы разблокировки микроконтроллеров
Многие микроконтроллеры (МК) оснащены механизмами защиты от чтения прошивки, реализованными на аппаратном или программном уровне. Наиболее распространённые методы защиты включают:
- Блокировку доступа к памяти через интерфейсы отладки (JTAG, SWD, UART).
- Шифрование содержимого flash-памяти (например, AES-256 в STM32 с TrustZone).
- Физическое разрушение тестовых точек после программирования (например, в автомобильных ЭБУ).
- Использование однократно программируемых (OTP) битов конфигурации.
Для обхода защиты часто применяют анализ уязвимостей в реализации протоколов отладки. Например, в некоторых МК семейства AVR можно восстановить доступ к JTAG, сбросив fuse-биты через недокументированные команды или переполнение буфера в загрузчике. В STM32 с активированной защитой уровня RDP2 (Read Protection Level 2) возможен частичный дамп памяти через уязвимости в обработке исключений, если МК переходит в режим HardFault при попытке чтения защищённых областей.
Аппаратные методы разблокировки включают:
- Лазерная микрообработка – удаление защитных слоёв кристалла для доступа к шинам памяти. Требует оборудования стоимостью от $50 000 и применяется в основном для чипов с топологией менее 90 нм.
- Электромагнитный анализ (EMA) – снятие побочных электромагнитных излучений для восстановления данных. Эффективен против МК без аппаратного шифрования, например, PIC16F.
- Fault Injection – внесение сбоев в работу МК через скачки напряжения, лазерное облучение или электромагнитные импульсы. Позволяет обойти проверки RDP в STM32, если сбой происходит в момент проверки флага защиты.
Программные атаки нацелены на уязвимости в загрузчиках и протоколах. В ESP32, например, можно извлечь прошивку через UART, если не отключён заводской загрузчик, или использовать переполнение стека в пользовательском коде для выполнения произвольного кода. Для МК с ARM Cortex-M распространён метод Vector Catch – перехват исключений для получения контроля над исполнением, если отладчик не заблокирован полностью.
В случае использования TrustZone (например, в STM32H7) обход защиты усложняется из-за разделения памяти на защищённые и незащищённые области. Однако возможны атаки через:
- Уязвимости в SAU (Security Attribution Unit) – неправильная конфигурация может позволить доступ к защищённым данным.
- Side-channel атаки на реализацию криптографических алгоритмов (например, анализ времени выполнения AES).
- Эксплуатацию ошибок в обработке прерываний, если они не изолированы в защищённой зоне.
Для МК с аппаратным шифрованием (например, NXP LPC55S69) ключевую роль играет извлечение ключей. Методы включают:
- Атаки на PUF (Physically Unclonable Function) – анализ уникальных физических характеристик чипа для восстановления ключей.
- Glitching атаки на генерацию случайных чисел (TRNG), если они используются для формирования ключей.
- Извлечение ключей из дампа ОЗУ через холодный перезапуск (cold boot attack), если ключи временно хранятся в незащищённой памяти.
При работе с автомобильными ЭБУ (например, Infineon Tricore) часто применяют методы реверс-инжиниринга печатных плат. Основные шаги:
- Идентификация тестовых точек (например, через анализ трассировки дорожек).
- Подключение логического анализатора к шинам памяти (SPI, Parallel Flash).
- Использование специализированных инструментов (например, PCBite или JTAGulator) для поиска скрытых интерфейсов.
- Обход защиты через эмуляцию внешней памяти, если МК загружает прошивку из внешнего flash.
Для успешного обхода защиты критически важно комбинировать методы. Например, при работе с STM32 с RDP2 можно сначала попытаться вызвать HardFault через некорректные команды, затем использовать fault injection для сброса флага защиты, и наконец, извлечь прошивку через SWD. В каждом случае требуется анализ документации на конкретный МК, так как реализация защиты варьируется даже внутри одного семейства.
Анализ и восстановление прошивки из дампа памяти

Восстановление прошивки из поврежденного дампа требует учета специфики архитектуры. Например, для AVR-микроконтроллеров (ATmega) критически важно восстановить fuse-биты, определяющие тактовую частоту и режим загрузки. В таблице ниже приведены ключевые параметры для анализа дампов разных архитектур:
| Архитектура | Типичный размер дампа | Ключевые сигнатуры | Инструменты восстановления |
|---|---|---|---|
| ARM Cortex-M | 16–2048 КБ | Векторы прерываний (0x00000000), Flash-конфигурация (0x0800XXXX) | Ghidra, OpenOCD, STM32CubeProgrammer |
| AVR (ATmega) | 1–256 КБ | Fuse-биты (0x00–0x03), таблица векторов (0x0000) | avrdude, Atmel Studio, AVRDUDESS |
| PIC (PIC16/PIC18) | 1–64 КБ | Конфигурационные слова (0x2007), ID-локации (0x2000) | MPLAB X, pk2cmd, PICkit |
При восстановлении фрагментированных дампов используйте скрипты на Python с библиотеками capstone (дизассемблирование) и keystone (ассемблирование). Для STM32 с поврежденной таблицей векторов восстановите первые 4 байта (адрес сброса) вручную, сравнив с эталонной прошивкой. В случае шифрованных дампов ищите ключи в EEPROM или внешней памяти – часто они хранятся в незащищенных областях (например, 0x080E0000 для STM32F4). Для проверки целостности используйте контрольные суммы (CRC32 в секции .data) или сравнение с резервными копиями.