Практический гид по мотивам доклада Nord Husky
AVR сегодня не выглядит самой модной архитектурой для embedded-разработки. Вокруг много ARM, много дешёвых MCU других семейств, много готовых плат и SDK, которые обещают закрыть большую часть низкоуровневых вопросов. Но для реверсера популярность архитектуры не равна её практической важности. Если устройство уже выпущено, стоит на объекте, управляет периферией, живёт в промышленной коробке или работает в чьём-то самодельном контроллере, то неважно, насколько современно оно выглядит на фоне рынка. Его всё равно придётся понимать.
Доклад Nord Husky как раз про этот слой работы: не про красивый декомпилятор и не про «магическую» кнопку анализа, а про реальный путь к AVR-прошивке. Сначала надо понять плату, потом найти способ получить firmware, потом разобраться с dump, а уже после этого открывать Ghidra, IDA или radare2 и смотреть, что получилось. И почти на каждом шаге оказывается, что datasheet важнее привычного комфорта reverse engineering-инструментов.
Почему AVR всё ещё имеет смысл изучать
AVR — самостоятельная архитектура микроконтроллеров, исторически связанная с Atmel. Сейчас Atmel находится внутри Microchip, но в практическом разговоре о старых и живых устройствах оба имени всё ещё встречаются. Для реверсера важно не столько корпоративное дерево, сколько то, что AVR — это не x86, не ARM и не MIPS. У него свои инструкции, своя модель памяти, свои особенности адресации и свой набор привычек, которые приходится учитывать при анализе bare metal-кода.
Главная причина держать AVR в поле зрения проста: такие чипы всё ещё попадаются. Не обязательно в новых флагманских изделиях и не обязательно в сложных вычислительных устройствах. Скорее в небольших embedded-системах, периферийных платах, IoT-устройствах, самодельных или полупромышленных решениях, иногда в PLC/ПЛК и похожем оборудовании управления. Самый узнаваемый пример для широкой аудитории — Arduino, где многие платы исторически построены на ATmega. Для многих инженеров и исследователей именно Arduino стала первым контактом с AVR, пусть даже без глубокого погружения в архитектуру.
Внутри семейства тоже есть разные масштабы. ATtiny — маленькие MCU/МК: меньше выводов, меньше памяти, меньше периферии. В докладе упоминались ATtiny12 и ATtiny85 как примеры небольших микроконтроллеров. ATmega — более крупная линейка, которая подходит для задач посерьёзнее. По меркам современных систем десятки или сотни килобайт flash выглядят скромно, но в мире bare metal-микроконтроллеров этого достаточно, чтобы спрятать заметную прикладную логику.
Вывод для hardware security простой: если вы занимаетесь устройствами, а не только серверными бинарями, AVR нельзя списывать как «археологию». Он может оказаться в конкретной плате на вашем столе.
С чего начинается реверс платы
Реверс AVR-устройства начинается не с дизассемблера. Он начинается с корпуса, платы, маркировок и трасс.
Первый практический барьер — физический доступ. Устройство может быть залито компаундом, эпоксидкой, силиконом или другим защитным материалом. Иногда это просто технологическое решение, иногда защита от влаги и вибрации, иногда сознательная попытка усложнить исследование. В любом случае для реверсера это означает риск повредить плату ещё до анализа firmware.
Когда плата доступна, надо понять её топологию. Где питание. Где земля. Где MCU/МК. Какие микросхемы рядом с ним. Какие линии уходят к внешним разъемам. Есть ли тестовые площадки. Есть ли разведенные интерфейсы программирования или отладки. Какие компоненты похожи на питание, связь, память, драйверы, датчики или исполнительные элементы.
В AVR-устройствах часто нет отдельной внешней flash, которую можно выпаять и спокойно прочитать программатором. Прошивка обычно находится во внутренней flash самого микроконтроллера. Это принципиально меняет подход: физическое наличие платы ещё не означает, что dump можно получить простым чтением отдельной микросхемы памяти. Надо взаимодействовать с самим чипом.
На этом этапе datasheet становится рабочим инструментом, а не справочным приложением. По маркировке микроконтроллера нужно найти документацию, проверить pinout, понять, какие пины отвечают за питание, reset, ISP/SPI/JTAG или другие интерфейсы конкретной модели, какие функции мультиплексированы на выводах и какие условия нужны для режима программирования.
Почему прошивку сложно достать
Есть несколько типичных путей к firmware, но ни один не гарантирован.
Первый путь — считать прошивку напрямую с микроконтроллера. В идеальном мире вы находите интерфейс программирования, подключаете программатор, выбираете правильный чип и читаете flash. В реальном мире на пути часто стоят lock bits и readout protection. Эти механизмы запрещают чтение памяти программы или ограничивают доступ к содержимому flash и EEPROM. Вендоры иногда забывают включить защиту, но строить исследование на надежде не стоит.
Второй путь — поиск официального обновления. Иногда firmware можно найти на сайте производителя, в архиве поддержки или внутри утилиты обновления. Но у небольших embedded-устройств сайт может исчезнуть, обновлений может не быть, а пользовательский update-канал мог вообще не предполагаться.
Третий путь — перехват процесса обновления. Если устройство обновляется через внешний интерфейс, сеть, сервисную утилиту или другой канал, можно попытаться получить образ прошивки в момент передачи. Но и здесь нет гарантии, что файл окажется готовым бинарем для анализа. Он может быть упакован, зашифрован, подписан, разбит на блоки или завернут в формат, который сначала придётся восстановить.
Четвёртый путь — чужой dump. Иногда прошивку уже кто-то считал и выложил. Для массовых устройств такое возможно. Для редкого промышленного модуля, старой платы или неприятного в разборе изделия — скорее удача, чем метод.
Поэтому практический вопрос звучит не «чем открыть AVR-бинарь», а «откуда вообще взять бинарь». Если lock bits выставлены, интерфейсы отключены, обновлений нет, а устройство залито компаундом, исследование быстро превращается в аппаратную задачу с риском, стоимостью и неопределенностью.
Защиты: что проверять на чипе
Когда доступ к MCU/МК всё-таки есть, важно не путать наличие пинов с наличием доступа.
ISP/SPI, JTAG и другие интерфейсы зависят от конкретного AVR-чипа и его конфигурации. На плате могут быть тестовые площадки, похожие на программаторский разъем, но это ещё не значит, что чтение разрешено. Отладочный интерфейс может быть не разведен, отключён или занят другой функцией. Пины могут быть доступны только после выпайки. Линии могут быть подтянуты, нагружены внешними компонентами или использоваться схемой так, что подключение программатора прямо на плате будет нестабильным.
Главные защитные сущности, на которые надо смотреть, — lock bits, fuse/configuration-биты и режимы readout protection, если они есть у конкретной модели. Они определяют, можно ли читать flash, как работает память, какие интерфейсы активны, как стартует устройство и какие опции программирования доступны.
EEPROM тоже не стоит забывать. Внутренняя EEPROM может хранить калибровки, ключи, настройки, счётчики, серийные данные или параметры, без которых один flash dump будет неполным. Но доступ к EEPROM также может зависеть от настроек защиты и режима чтения.
В более сложных сценариях появляется криптография. Например, если firmware update передаётся по недоверенному каналу, устройство может получать зашифрованный образ, расшифровывать его внутри и записывать во flash. В таком случае найденный update-файл ещё не является понятным кодом. Нужно разбирать формат, искать место расшифровки, понимать, где лежат ключи и какие проверки выполняются перед записью.
Что делать, если dump получен
Допустим, firmware удалось получить. Это важная победа, но не конец работы. AVR dump обычно приводит реверсера в мир bare metal: нет операционной системы, нет привычного загрузчика, нет PE/ELF-структуры с богатыми метаданными, нет символов и импортов. Есть последовательность байтов, которую микроконтроллер исполняет согласно своей архитектуре и конфигурации.
Первый шаг — правильно загрузить бинарь в инструмент. Можно пробовать Ghidra, IDA, radare2 и другие дизассемблеры. Но ожидания стоит сразу настроить трезво. По опыту из доклада, инструменты для AVR дают ассемблер, но не дают того уровня удобного псевдокода, к которому привыкли реверсеры x86 или ARM. Декомпиляция либо отсутствует, либо работает ограниченно, либо требует большого ручного сопровождения.
В IDA и похожих инструментах важно выбрать конкретный чип или хотя бы близкую модель. От этого зависят адресные пространства, регистры, interrupt vectors, периферия и интерпретация обращений к памяти. Ошибка на этом уровне может сделать дальнейший анализ шумным: код вроде бы дизассемблируется, но смысл обращений к регистрам и периферии теряется.
Дальше начинается ручная разметка. Нужно понять, где таблица interrupt vectors, где стартовый код, где инициализация стека, портов, таймеров, UART/SPI/I2C-подобной периферии, где основные циклы и обработчики прерываний. В bare metal-прошивках логика часто живёт вокруг инициализации регистров и бесконечного цикла, а важные реакции спрятаны в interrupt handlers.
Одна из задач — найти аналог main. В обычной C-программе с ELF, символами и runtime это проще. В голом dump приходится искать паттерны старта и инициализации. В докладе упоминалось практическое наблюдение: иногда полезно искать инструкцию sei, которая включает глобальные прерывания. Это не универсальное правило и не доказанный способ «найти main», но хороший пример мышления реверсера: искать момент, когда устройство завершило базовую настройку и разрешило interrupt-driven-логику.
Почему datasheet важнее красивого декомпилятора
При анализе AVR легко попасть в ловушку: открыть dump в инструменте, увидеть ассемблер и ждать, что дальше всё станет похоже на привычный reverse engineering. Но без datasheet дизассемблер показывает только половину реальности.
В микроконтроллерной прошивке обращение к адресу часто означает не «работу с переменной», а запись в hardware register. Один байт может включать порт, настраивать направление GPIO, запускать таймер, менять режим SPI, сбрасывать флаг прерывания или включать периферийный блок. Если не знать карту registers, такой код выглядит как бессмысленные in, out, lds, sts и битовые операции.
Что смотреть в datasheet в первую очередь:
- pinout конкретного корпуса, а не только семейства;
- карту памяти и разделение flash, SRAM, EEPROM и регистров;
- interrupt vectors и порядок обработчиков;
- описание registers периферии;
- режимы программирования и отладки;
- fuse bits, lock bits и ограничения чтения;
- особенности reset, clock source и старта;
- описание инструкций или ссылку на AVR instruction set.
Datasheet также помогает связать firmware с платой. Если код пишет в регистр порта, надо понять, какой физический вывод за ним стоит. Если этот вывод на плате уходит к драйверу, датчику или разъему, появляется смысл. Так постепенно ассемблер превращается в модель устройства: этот блок включает питание, этот читает вход, этот отвечает за связь, этот обрабатывает состояние.
Именно поэтому в AVR-реверсе красивый декомпилятор вторичен. Хороший псевдокод был бы удобен, но без аппаратного контекста он всё равно не ответит, что делает конкретный вывод и почему запись в один бит меняет поведение платы.
Как мыслить реверсеру AVR
AVR-анализ полезно вести от железа к коду и обратно.
Сначала фиксируйте аппаратный контекст: маркировка MCU/МК, корпус, pinout, питание, интерфейсы, соседние микросхемы, тестовые площадки, разъемы, заметные цепи. Затем поднимайте документацию: datasheet, memory map, registers, interrupt vectors, режимы ISP/SPI/JTAG, lock bits и EEPROM. Потом уже загружайте dump в Ghidra, IDA или radare2 и начинайте разметку.
В коде стоит искать несколько классов мест:
- старт и инициализацию стека;
- таблицу interrupt vectors;
- настройку портов ввода-вывода;
- инициализацию таймеров и интерфейсов связи;
- чтение и запись EEPROM;
- бесконечный главный цикл;
- обработчики прерываний;
- проверки состояния, ошибок и режимов;
- операции с данными, похожими на ключи, счётчики или конфигурацию.
Полезная дисциплина — сразу переименовывать адреса и регистры по datasheet. Если инструмент позволяет описывать структуры, регистры или именованные области памяти, это окупается быстро. В обсуждении после доклада прозвучал комментарий о GitHub-плагине для IDA, который помогает работать с AVR и генерировать вспомогательные структуры. Название из транскрипта не восстановлено, но сама мысль важная: любые средства, которые подтягивают аппаратный контекст в дизассемблер, резко уменьшают ручную боль.
При этом не стоит слепо доверять автоматике. Для AVR и bare metal автоматический анализ часто ошибается в границах функций, переходах, данных, таблицах и обработчиках. Надёжнее считать инструмент помощником, а источником истины держать datasheet, плату и проверяемые гипотезы.
Где может помочь LLM
LLM можно использовать как ассистента, но не как замену аппаратному анализу.
Модель может объяснить фрагмент AVR-ассемблера, помочь разложить последовательность инструкций, предложить гипотезу о функции, аккуратно пересказать смысл работы со стеком, вызовами и переходами. Она может быть полезна при черновом комментировании кода и при сопоставлении повторяющихся паттернов.
Но LLM не видит вашу плату и не знает автоматически, что подключено к конкретному выводу микроконтроллера. Ей нужен контекст: точная модель AVR, фрагменты datasheet, pinout, карта registers, сведения о соседних микросхемах, куски dump и ваши наблюдения. Без этого модель будет рассуждать слишком общо и может уверенно ошибаться.
Хороший способ работы — давать модели маленькие, проверяемые задачи: «объясни этот обработчик прерывания с учётом таких-то регистров», «помоги переименовать обращения к портам по datasheet», «проверь, на что похожа эта инициализация таймера». Плохой способ — загрузить неизвестный бинарь и ждать полного отчёта о поведении устройства.
Практические выводы
AVR-реверс — это не только знание архитектуры. Это цепочка аппаратных и программных шагов, где слабое место на любом участке может остановить исследование.
Если прошивка внутри MCU/МК защищена lock bits или readout protection, dump может оказаться недоступен простым программатором. Если update-файл зашифрован, его ещё надо превратить в исполняемый образ. Если dump получен, bare metal-код всё равно требует ручной разметки, понимания interrupt vectors, registers и периферии. Если открыть бинарь в Ghidra, IDA или radare2 без datasheet, получится много ассемблера и мало смысла.
Главный урок доклада Nord Husky: в embedded-реверсе выигрывает не тот, у кого самый красивый декомпилятор, а тот, кто умеет связать три слоя — плату, документацию и firmware. Для AVR это особенно заметно. Архитектура достаточно понятная, документация Atmel/Microchip в целом пригодна для работы, а инструменты дают стартовую точку. Но финальный анализ всё равно собирается руками: через pinout, registers, interrupt vectors, EEPROM, flash, lock bits и проверку каждой гипотезы на конкретном устройстве.