Продолжаем исследование модуля ESP8266. В этот раз рассмотрим процесс загрузки прошивки для дизассемблирования.
Первая часть статьи здесь.
Содержание
- Введение
- Архитектура ESP8266
- Карта памяти (адресного пространства)
- Формат прошивки
- Процесс запуска
- Инструменты
- Загрузка прошивки для исследования
- Ассемблер Xtensa
- Регистры
- Базовые операторы
- Функции
- Условные переходы
- Заключение
- Ссылки
4. Загрузка прошивки для исследования
Подготовив необходимые инструменты, мы подошли к самой интересной части – загрузке и дизассемблированию прошивки.
ELF
Начнем с самого простого – загрузки файла app.out – прошивки в формате ELF, созданной с помощью SDK.
Как правило, файл app.out доступен в случае, когда у вас есть исходный код прошивки, который изучать гораздо проще и логичнее. Однако, чтобы познакомиться с особенностями компилятора, расположением сегментов и увидеть наименования функций, предлагаю начать именно с него.
После компиляции и сборки в папке build у нас появится файл app.out, который представляет собой скомпилированный пользовательский код, данные, библиотеки и отладочную информацию. В таком виде прошивку загрузить в модуль нельзя, поэтому после сборки ELF-файла SDK преобразует app.out в один или два файла в папке firmware – 0x00000.bin и 0x40000.bin, которые можно непосредственно прошить.
Открыв app.out в HIEW и посмотрев на таблицу сегментов (enter, F8, F6) мы увидем следующую картину:
Колонка VirtAddr содержит адреса начала сегментов в адресном пространстве Xtensa. Обратите внимание, что три сегмента (.data, .rodata и .bss) будут загружены в область оперативной памяти, сегмент .text будет записан по адресу пользовательского исполняемого кода, а сегмент .irom0.text – по адресу кода библиотек SDK. Остальные сегменты, имеющие начальный адрес, равный нулю, содержат служебную информацию и в прошивку, готовую к заливке в модуль добавлены не будут.
Забегая вперед скажу, что сегмент .irom0.text будет в исходном виде скопирован в файл 0x40000, а сегменты .data, .rodata, .bss и .text будут собраны в файл 0x00000.bin с учетом формата, который был рассмотрен выше.
Для загрузки app.out в IDA необходимо проделать следующую последовательность действий:
1. Открываем IDA Pro 6.6 или выше
2. Нажимаем «Go» — никаких файлов пока открывать не будем
3. Открываем пункт меню File – Script file и выбираем скрипт определения процессора xtensa.py
4. Загружаем файл app.out, здесь необходимо выбрать тип процессора и нажать «Set»:
5. В следующих окнах с предупреждением о неизвестном типе машины и предложениями загрузки отладочной информации нажать «Yes»
6. В результате мы получим готовый к исследованию файл:
Чего в нем не хватает? Не хватает системного ROM, содержащего базовые функции модуля. При необходимости его можно загрузить вручную, это мы и проделаем в следующем разделе.
Системная прошивка модуля
Мы рассмотрели довольно простую загрузку в IDA прошивки в виде ELF-файла. Однако на практике зачастую требуется изучить уже готовые прошивки, извлеченные из flash модуля (методом подключения к flash напрямую) или распространяемые в виде файлов 0x00000.bin и 0x40000.bin. Здесь придется проделать немного ручной работы. Начнем с загрузки образа системного ROM. В первой части я давал ссылку на архив с файлом 40000000.bin – это он и есть. Последовательность действий такая:
1. Открываем IDA Pro 6.6 или выше
2. Нажимаем «Go»
3. Открываем пункт меню File – Script file и выбираем скрипт определения процессора xtensa.py
4. Открываем файл 40000000.bin
5. Выбираем тип процессора Tensilica Xtensa [xtensa] и нажимаем «Set»
6. Далее необходимо указать организацию памяти для правильной загрузки двоичного файла. Здесь мы создаем сегмент кода по адресу 0x40000000 и загружаем в него наш файл:
7. Образ ROM загружен, но он плохо читаем из-за отсутствия названий функций. Теперь загрузим скрипт 40000000.idc, который произведет дополнительную работу – определит имена функций и создаст дополнительные сегменты в адресном пространстве: File – Script file – 40000000.idc. Вот результат:
На этом загрузку системного ROM можно считать завершенной, можно переходить к его исследованию. Скрипт определил названия функций системного ROM, теперь можно разобраться, что выплолняет та или иная функция, вызываемая из SDK.
А вот, кстати, функция, которая копирует пользовательскую прошивку из flash в память SoC:
Такой функции в SDK нет, поэтому название я ей дал произвольное.
Но прошивка не полна без загрузки пользовательской части – файлов 0x00000.bin и 0x40000.bin. Поэтому подгрузим эти файлы к системному ROM.
Пользовательская прошивка
Мы загрузили в IDA системный ROM модуля, а скрипт подготовил нам несколько сегментов для загрузки остальных частей. Начнем с простого – загрузки кода библиотек.
Как я говорил выше, файл прошивки 0x40000.bin представляет собой образ сегмента кода без всякой служебной информации и напрямую мапируется в адресное пространство процессора по адресу 0x40240000. Чтобы подгрузить его в IDA проделаем следующее:
1. Убедимся, что у нас открыта база данных 40000000.bin и скрипт 40000000.idc создал дополнительные сегменты: RAM, ROM, IRAM, IROM
2. Выбираем в меню File – Load file – Additional binary file, открываем файл прошивки 40000.bin
3. В следующем окне выбираем параметры загрузки. Обратите внимание, что загрузка производится по смещению в параграфах, т.е. вместо адреса указываем значение в 10h раз меньше (отбрасываем последний ноль). Галку создания сегмента можно снять, он у нас уже создан:
4. Файл загружен. После указания начала кода (в данном случае это 4024000Ch) мы получаем примерно следующую картину:
В отличие от ELF-файла, здесь не будут определены названия функций и переменных, но с этим уже ничего не поделаешь.
1. Найти сигнатуру исследуемой функции в дизассемблере ELF-файла, скомпилированного той же версией SDK. Есть вероятность, что вы ее найдете, и у нее будет имя (из отладочной информации). В том числе для этого я рассматривал загрузку прошивки в ELF.
2. Известные константы – функция может ссылаться на текстовые строки или двоичные данные. С опытом многие такие константы запоминаешь наизусть, если константа незнакома – гуглим. Вот пример:
Видим две примечательные константы. Гугл первой же ссылкой выдает описание алгоритма strlen, использующего эти константы:
Сравнив реализации алгоритма, можно с уверенностью сказать, что по адресу 40100E70h расположена функция strlen.
Или вот такой кусок кода сразу выдает функцию деления:
3. Собственно, изучение ассемблерного кода и попытка понять, что выполняет функция. Иногда можно определить, что перед нами «местная» реализация знакомого алгоритма, а иногда нет.
В любом случае, навык понимания ассемблерного кода приходит с опытом, так что дерзайте!
Теперь переходим к загрузке файла 00000.bin. Мы помним, что это не просто образ, а файл со структурой, описывающий сегменты данных и кода. Вот так он может выглядеть при просмотре в шестнадцатеричном виде:
Сначала идет 8 байт общего заголовка прошивки, в которой определяется количество сегментов и точка входа. Потом идут сами сегменты, также имеющие 8-ми байтовые заголовки с адресом и длиной.
Чтобы правильно загрузить их в IDA я вырезал данные каждого сегмента (без заголовков) в отдельные файлы, назвав их по адресу загрузки:
Теперь остается подгрузить их в IDA. Для каждого файла выполняем последовательность действий, аналогичных загрузке системного ROM:
1. File – Load file – Additional binary file, выбираем файл данных
2. В параметрах загрузки указываем сегмент (по имени файла без последнего нуля), сегмент не создаем.
Все, теперь у нас есть полностью загруженная и готовая к исследованию прошивка!
В этой части статьи мы рассмотрели процедуры загрузки различных видов прошивок ESP8266 для дизассемблирования в IDA Pro. В заключительной части мы рассмотрим особенности процессора Xtensa, отличия от архитектуры x86, набор регистров и команд.
Автор: codezero