Reverse engineering STM32 firmware

Andrey Voloshin
TechMaker
Published in
5 min readSep 14, 2018
BlackPill з подругою

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

Проте, маючи на руках незашифровану прошивку зацікавлена особа може проаналізувати хід її виконання і навіть змінити його на свій розсуд.

Для прикладу візьмемо BlackPill та невеличку прошивку, що перевіряє секретний ключ (передається по UART) та або блокує пристрій (рядки 30–37) або виконує основний функціонал — щосекунди відправляє інформацію з АЦП по USB.

Наш embedded crackme :)

Повний код проекту для SystemWorkbench знаходиться тут.

0x00 Firmware dump

Першим кроком ми отримуємо дамп пам’яті пристрою. Це можливо зробити завантаживши оновлення прошивки з офіційного сайту або вичитуванням flash пам’яті самого пристрою.

Наступна команда OpenOCD викачає перші 32кБ флеш пам’яті пристрою.

openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "init" -c "reset init" -c "flash read_bank 0 firmware.bin 0 0x8000" -c "exit"
Результат виконання команди

Спробуйте змінити команду таким чином, щоб вигрузити усі 64кБ флеш пам’яті BlackPill. Між іншим, там насправді 128кБ а обмеження в 64кБ можна обійти ;)

0x01 Quick analysis

Швидкий аналіз дасть нам розуміння:

  • чи зашифровано вміст flash?
  • які строкові літерали присутні в прошивці?

З цією роботою справляться strings, binwalk, https://binvis.io.

strings firmware.bin
😳секретний токен не такий вже й секретний

Візуалізація файлу дає змогу оцінити ентропію різних ділянок файлу та виявити присутність текстової інформації. Скористайтеся сервісом https://binvis.io/ та завантажте firmware.bin для візуального аналізу.

0x02 Disassembly

Нам підійде radare2 в якості дизасемблера. ARM Cortex-M використовують Thumb набір інструкцій. З офіційної документації ARM:

The Thumb instruction set is a subset of the most commonly used 32-bit ARM instructions. Thumb instructions are each 16 bits long, and have a corresponding 32-bit ARM instruction that has the same effect on the processor model. Thumb instructions operate with the standard ARM register configuration, allowing excellent interoperability between ARM and Thumb states.

On execution, 16-bit Thumb instructions are transparently decompressed to full 32-bit ARM instructions in real time, without performance loss.

Thumb code is typically 65% of the size of ARM code, and provides 160% of the performance of ARM code when running from a 16-bit memory system.

Іншими словами, інструкції 16-бітні, хардварно розширюються у 32-бітні.

Освіжимо в пам’яті як виглядають виклики функцій та умовні переходи в ARM:

+------------------+------------+
| Instruction | Relativity |
+------------------+------------+
| b label | Relative |
| bx register | Absolute |
| bl label | Relative |
| blx label | Relative |
| blx register | Absolute |
| pop {..., pc} | Absolute |
| ldr pc, =address | Absolute |
+------------------+------------+

Дві інструкції, котрі ми зустрінемо, використовуються для організації умовних переходів та циклів:
cbnz (compare, branch on non-zero),
cbz (compare, branch on zero).

Entry point
Для мікроконтролерів stm32f1xx документація дає наступну інформацію про хід виконання та точку входу

Код, з якого починається виконання — reset handler. В таблиці векторів переривань знаходиться по зміщенню 0x04 від початку флеша
(Reset_Handler @ 0x0800 0004).

Talk is cheap. Show me the code!

r2 -a arm -b 16 -m 0x08000000 -w firmware.bin

Radare2 запускаємо та вказуємо архітектуру ARM, 16-бітний набір інструкцій, зміщення 0x08000000, дозволяємо редагування прошивки та власне вказуємо шлях до неї.

Перші 32 інструкції прошивки

Команда ааапроаналізувати виклики, переходи та символи і роздати їм автогенеровані імена.

Команда pd 32 — вивести 32 команди по поточному зміщенню. Поточне зміщення вказано лівіше від курсора (жовтим, 0х08000000 — початок файла).

Знайшли зміщення, де розташовано ResetHandler

Переходимо до аналізу ResetHandler. Зміщуємося на 0x08003ac4 та виводимо дизассемблінг:

s aav.0x08003ac4
pd 32
ResetHandler закінчується по зміщенню 0x08003af6 (bx lr)

Одразу бачимо 3 виклики функцій (bl fcn.08003xxx). Що це за функції? Заглянемо в проект, в файл /startup/startup_stm32f103xb.s і знайдемо відповідності.

Три виклики функцій, це SystemInit (налаштування тактування та ініціалізація flash), libc_init та main. Зміщуємося до main та виводимо лістинг функції:

s fcn.0800348c
pdf
Лістинг main

На початку зберігається вказівник адреси виклику, виділяється стек для локальних змінних (росте вниз, тому від вказівника стека просто віднімається необхідна кількість байт).

Далі йдуть виклики функцій, котрі ініціалізують різну периферію. Після чого, організовано два цикли і умовний перехід на один з них. Простіше їх розібрати на візуалізації графу переходів:

VV

Якщо в регістрі r0 після виклику функції 33а4 ненульове значення, то хід виконання піде праворуч. Якщо нуль — ліворуч.

0x03 Patch

Зліва в регістр r0 завантажується вказівник на щось… (aav.0x08004430) Гляньмо, що ж там. Виводимо 32 значення по зміщенню aav.0x08004430:

x 32 @ aav.0x08004430
А там — строкові літерали.

Таким чином, йдучи ліворуч по графу викликів ми потрапляємо в цикл, що блокує роботу пристрою. Нам потрібно змінити умовний перехід.

Варіанти:

  • інвертувати його (cbnz -> cbz)
  • замінити на безумовний перехід на зміщення 0x80034c8 (там код виконання основного функціоналу пристрою)

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

Запишемо безумовний перехід на місце cbnz :

wa b 0x80034c8 @ 0x080034ac

wa — записати опкоди, що відповідають асемблерному коду “b 0x80034c8” по зміщенню 0x080034ac.

0x080034ac тепер 0ce0 замість 60b9

0x04 Upload & test

Завантажимо змінену прошивку в пристрій та переконаємося в її працездатності.

openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "program path/to/firmware.bin verify reset exit 0x08000000"screen /dev/cu.usbmodem1421
Пристрій працює

0x05 DIY

Функція перевірки ключа може викликатися в різних місцях прошивки. Тому ефективніше пропатчити саме функцію isUnlocked щоб вона повертала ненульове значення незалежно від наявності ключа. Спробуйте зробити це самостійно 😎

Materials

Про такі речі ми пишемо на нашій сторінці TechMaker в Facebook та розповідаємо на курсах.

--

--