Если вы изучали микроконтроллеры, вы, вероятно, слышали о битах конфигурации. Для разных семейств они называются по-разному: в AVR это предохранители, а в PIC — слово конфигурации. Сегодня мы углубимся в них и рассмотрим их применение для защиты прошивки в контроллере. И покажем как обходить такую защиту. Погнали!
AVR
В семействе AVR для регулировки используются предохранители. Предохранители (от англ. Fuse — предохранитель) — это особые биты в микроконтроллере, которые, как и все биты, хранят информацию. Его основные особенности заключаются в следующем:
- они хранятся и прошиваются отдельно от остальной памяти;
- изменяются только извне;
- управляют работой микроконтроллера на самом низком уровне.
Лучше всего их можно объяснить на примере дозиметра РКСБ-104.
Дозиметр РКСБ-104
Его базовая настройка осуществляется одним переключателем на передней панели. Но для более точных настроек снятие защитной крышки с задней стенки и использование небольших переключателей (белые в центре).
Задняя панель со снятой крышкой
В AVR эти биты для удобства объединены в байты: старший, младший, защитный и дополнительный. Доступен каждый битовый байт → бит. Младший байт обычно отвечает за часы, а старший байт — за плюшки. Биты различаются от чипа к чипу, поэтому в идеале каждый чип следует рассматривать отдельно с использованием документации.
Значение битов этого семейства инвертировано: 1 означает, что бит был очищен, а 0 означает, что он установлен. Но программы для прошивки МК работают по разному. Для разных программ необходимо уточнить логику работы с предохранителями.
Что могут фьюзы в этом семействе МК:
- управление тактированием (частота генератора, внешний или внутренний генератор);
- разрешение на чтение прошивки микроконтроллера (самое интересное, но об этом позже);
- управление таймерами;
- защита EEPROM;
- более специфичные функции, их надо уточнять к конкретному чипу.
Самые «популярные» биты:
CKSEL
— их четыре, и они отвечают за тактирование;SUT
— их два, и они управляют режимом запуска тактирования;CKOPT
— конфигурирует внутренний генератор;RSTDISBL
— режим работы ножки RESET МК;SPIEN
— разрешение SPI;EESAVE
— защита EEPROM;BOOTRST
— адрес, откуда начать исполнять код;BODEN
— контроль питания;SELFPRGEN
— разрешение записи в память изнутри;OCDEN
— вот он, бит, разрешающий чтение прошивки.
Считывают фьюзы обычно не вручную, а с помощью специальных калькуляторов. Вот один из них — Fusecalc.
Софт
warning
При работе с фьюзами будь предельно внимателен. Неправильно выставленный бит может превратить чип в «кирпич». Перед прошивкой уточняй логику работы с фьюзами в твоей программе.
Linux
Я обычно работал с программой avrdude. Приведу пару команд без дополнительных параметров (чип, программатор). Считывание прошивки из чипа в файл:
$
avrdude -U flash:r:flash_dump.hex:i
Считывание энергонезависимой памяти в файл:
$
avrdude -U eeprom:r:eeprom_dump.raw:r
Запись прошивки из файла в чип:
$
avrdude -U flash:w:flash_dump.hex
Запись энергонезависимой памяти из файла:
$
avrdude -U eeprom:w:eeprom_dump.raw
Запись фьюзов (0xc3
; 0x99
):
$
avrdude -U lfuse:w:0xc3:m -U hfuse:w:0x99:m
Чтение фьюзов в файлы:
$
avrdude -U hfuse:r:hfuse.txt:h -U lfuse:r:lfuse.txt:h
Есть дополнительные параметры -с
и -p
. Первый отвечает за программатор, а второй — за чип. В качестве примера — команда для прошивки контроллера ATmega328p с помощью USBASP:
$
avrdude -U flash:w:flash_dump.hex -c usbasp -p m328p
В командах есть странные строки вида flash:
. Это строки в специальном формате для avrdude
. Для чего такое решение — не знаю ни я, ни кто‑либо еще.
Части этих строк разделены двоеточиями:
- первая часть — область памяти в МК (например
flash
илиlfuse
); - вторая — направление (
w
— write илиr
— read); - третья — файл на локальном устройстве (например файл с прошивкой);
- последняя (опциональная) — формат файла (например
r
— raw илиi
— ihex, интеловский шестнадцатеричный).
Более специфичные случаи применения этой строки выходят за рамки данной статьи.
Windows
Оконщики обычно пользуются программами с GUI. Например, AVRdude GUI.
Первая вкладка предназначена для загрузки прошивки. Мы видим и можем выбрать целевое устройство, формат файлов прошивки и пути к самим файлам. Один для программы, другой для энергонезависимой памяти.
Вторая вкладка конфигурирует программатор: какая используется модель и на каком порте она сидит.
Вкладка управления защитными битами.
А вот наконец и фьюзы. Задаются они как байты.
Как видишь, все просто, и использовать фьюзы можно, даже не открывая терминал!
info
На Arduino можно конфигурировать МК, не задумываясь о работе фьюзов. Этим занимается Arduino IDE в автоматическом режиме.
STM
Семейство STM использует биты в специальных регистрах для конфигурации. Информацию об этих регистрах и их назначении ищите в документации. Можно и необходимо изменять значения этих регистров на лету, но, в отличие от AVR, здесь настраивается не только самый низкий уровень (например, часы), но и небольшие периферийные устройства.
Настраивать надо много, даже если проект в духе Hello world, поэтому обычно это делается не ручной записью регистров, а с помощью красивого и мощного софта.
Софт
STM32CubeMX — это официальное и бесплатное программное обеспечение, созданное в компании STMicroelectronics. У нее есть и другая и тоже бесплатная IDE для своих МК — Atollic TrueSTUDIO. Но начнем мы с «Кубика».
CubeMX
Разобраться с этим просто: вы создаете проект, выбираете контроллер — и вперед. Порты настраиваются прямо на интерактивном изображении, а периферийные устройства — в меню слева. Большинство опций подписаны: вам больше не нужно заходить в таблицу и разбираться с битами регистра. И в конце, нажав соответствующую кнопку, программа сгенерирует код на языке программирования в виде подключаемого файла.
Atollic TrueSTUDIO — это еще одна навороченная IDE для микроконтроллеров STM. В ней можно и код писать, и отлаживать. А еще она хорошо стыкуется с «Кубом».
www
Есть еще System Workbench for STM32 и, конечно, Arduino IDE с ее неисчислимыми модулями.
Посторонним вход запрещен!
Если ты не хочешь использовать софт производителя МК, можно все сделать руками. Благо это относительно нетрудно. Приведу пример кода из одного своего проекта.
void init_uart()
{
UART1_CR2 |= UART_CR2_TEN; // Transmitter enable
UART1_CR2 |= UART_CR2_REN;
UART1_CR2 |= UART_CR2_RIEN;
UART1_CR3 &= ~(UART_CR3_STOP1 | UART_CR3_STOP2);
UART1_BRR2 = (F_CPU/UART_BAUD) & 0x000F;
UART1_BRR2 |= (F_CPU/UART_BAUD) >> 12;
UART1_BRR1 = ((F_CPU/UART_BAUD) >> 4) & 0x00FF;
}
Библиотеки
Прошивки STM обычно пишутся с использованием специальных библиотек. Есть как официальные, так и кастомные (HAL и SPL). Проще говоря, это стандартный набор библиотек. Можно, конечно, и без них — мне даже больше нравится: ты лучше понимаешь, как работает твой код.
Приведу команды для компиляции и прошивки STM8 из упомянутого проекта.
$ sdcc --Werror --std-sdcc99 -mstm8 -DSTM8S103 -lstm8 -mstm8 --out-fmt-ihx ../devctrl.c
$ stm8flash -c stlinkv2 -p stm8s103f3 -w devctrl.ihx
Здесь мы видим две программы — stm8flash для прошивки и sdcc для компиляции. Может, кому‑то пригодится.
- stm8flash — это программа c GitHub. По назначению — тот же AVRdude, только для STM8. Я использовал три аргумента:
-с
для указания программатора, в моем случаеst-linkv2
(программатор ST-Link, подробнее — в моей прошлой статье),-p
для указания целевого чипа (у меня это была платка с AliExpess с чипом STM8S103F3);-w
для направления работы (write, записать) и файл с прошивкой. - sdcc (small device C compiler) — компилятор языка C для «маленьких» устройств, то есть для микроконтроллеров. Это аналог GCC, но для МК.
- —Werror — считать все предупреждения ошибками. Странно, наверное, ведь обычно программисты забивают на предупреждения, а тут такое. Еще есть
--std-sdcc99
— это стандарт языка. Я использовал этот, потому что он ошибок не выдавал.
warning
В низкоуровневом программировании стандарты языка C очень важны, и код хорошо компилируется по одному стандарту и либо не компилируется вообще, либо не будет работать должным образом в соответствии с другим стандартом. Чтобы ответить на этот вопрос, начните с руководств, которые научили вас программировать. Обычно во введении указывается, какой стандарт будет включен в книгу.
Остальные аргументы, кроме файла кода, — это для какого МК компилировать код.
PIC
Здесь вместо предохранителей используется специальное конфигурационное слово из двух байтов. У этого слова есть адрес в памяти, который необходимо объяснить в документации. Там же вы можете найти структуру слова и назначение битов. Кстати, логика битов в этом семействе неоднозначная. Вы можете иметь в виду как «включено», так и «выключено». Пожалуйста, внимательно прочтите документацию.
Приведу вырезку из документации на чип PIC16F627A.
Вырезка из документации
Тут мы видим следующие настройки:
- CP — защита flash-памяти, то есть прошивки;
- CPD — защита области с данными;
- LVP — низковольтная прошивка;
- и так далее.
Слово конфигурации функционирует как статическая глобальная переменная на языке C. Существует фрагмент кода, который, когда он мигает, записывается непосредственно в область памяти, отвечающую за настройку микроконтроллера. Очевидно, вы не можете изменить это на ходу. Работа аналогична контроллерам AVR.
Софт
Здесь покажем лишь особенности при конфигурировании этих самых контроллеров.
Тут используется язык ассемблера, и мы видим строчку __CONFIG
. Возможно, это макрос или что‑то подобное, но нам важен смысл — это число записывается как конфигурационное слово и кодирует в себе настройки МК, в комментариях к коду они даже подписаны.
И вот еще один пример кода. Он уже из статьи на «Хабре» (кстати, настоятельно рекомендую с ней ознакомиться).
— This program assumes a 20 MHz resonator or crystal
-- is connected to pins OSC1 and OSC2.
pragma target clock 20_000_000 -- oscillator frequency
-- configuration memory settings (fuses)
pragma target OSC HS -- HS crystal or resonator
pragma target WDT disabled -- no watchdog
pragma target LVP disabled -- no Low Voltage Programming
pragma target MCLR external -- reset externally
Он написан на языке JAL, который был создан специально для программирования этого семейства МК. Этот код явно читабельнее ассемблера. Чего уж говорить — почти все читабельнее ассемблера! Хотя PIC традиционно программируются именно на нем.
www
Защита и взлом
Мы проанализировали теорию. Теперь поговорим о защите. Как вы понимаете, в семействе AVR бит предохранителя служит защитой. Если защита установлена, контроллер не позволит вам загрузить программное обеспечение. А если сбросить биты защиты, возможно ли это? Возможно, но даже здесь вас поджидает поросенок: при сбросе бита защиты МК очистит всю память.
Ну из-за софта не вышло — пройдемся по железу. Электроника не программирование — есть что потрогать. Итак, перейдем к истории: раньше были микросхемы памяти с УФ-стиранием.
Чип M27c256B — память с УФ‑стиранием
На корпусе чипа было специальное окошко, которое обычно чем-то заклеивалось. Вы записываете данные при программировании встроенными средствами, а стираете их с помощью специального устройства, похожего по конструкции на лампу для ногтей.
Лампа-коготь
Лампа для коготков
Открываешь окно, вставляешь микросхему в устройство — и через некоторое время забираешь чистую микросхему. С лампой все понятно — можно одолжить у жены. Главное — не замечать, иначе не объяснишь, что нужна лампа для стирания памяти через окошко.
А вот с окошком что делать? На современных чипах его нет. Если дырки нет — надо ее сделать! Я знаю три метода: механический, химический и лазерный.
- При механическом методе, как выразился польский блогер, szlifierka precyzyjna — прецизионной шлифмашинкой, то есть дремелем, надо твердосплавной насадкой прогрызть дырочку в корпусе.
- При химическом методе применяется смесь кислот (царская водка, если я не ошибаюсь), и ей протравливается все то же отверстие. Но раздобыть азотную кислоту проблематично: ее оборот под контролем, поскольку она используется при производстве взрывоопасных веществ, так что придется поискать другой способ. Если, конечно, у тебя не завалялась баночка азотной кислоты.
- Последний метод использует лазер. Им просто прожигается окошко. Лазер можно найти на ЧПУ‑граверах, но хватит ли их мощности — я не знаю.
Короче, будем считать, что окошко к подложке чипа ты проделал.
Получится что‑то похожее
Далее нужно понять, где та область, которую нужно защитить, а где — стереть. По сути, нам нужно облучить все, кроме Flash и EEPROM. Определять где что, придется интуитивно. Можно смотреть каналы Lisin YT и CuriousMarc — есть вскрытие и изучение фишек. Вы можете посмотреть типичные ошибки и технологию вскрытия фишек.
Итак, что защищать, мы придумали, и затем защищаем чем-то светорезистивным. Например, святая синяя изолента. Ну а после вставляем фишку в ластик и немного ждем.
info
Если что, про лампу для ногтей — это шутка. Нужна лампа с определенной длиной волны и мощностью.
Все, исхода два: либо все сделано правильно и аккуратно и защита снята, либо что‑то пошло не так и тебе придется искать, что именно.
PIC и STM
PIC такие же, как AVR. Условия такие же, метод тот же.
С STM все несколько интереснее. Способы защиты описаны в документации. Короче говоря, они не запрещают считывать чип, и создают условия, чтобы это было невозможно.
Но, собирая информацию для этой статьи, я наткнулся на страницу с интересным названием — «Считывание защищенной прошивки из флеш‑памяти STM32F1xx с использованием ChipWhisperer». Смысл этого метода в программной атаке на прошивку. Этот метод ближе к тематике журнала.
Вместо напутствия
Прежде чем я побегу украсть программное обеспечение, имейте в виду: даже если вы загрузите его, оно будет в виде двоичного кода, который все равно нужно проанализировать, изучить и восстановить логику работы, настолько это сложно. Так что, на мой взгляд, проще взломать компьютер программиста, написавшего прошивку, и скачать ее исходный код. Но это не тема моей статьи!