Reverse Engineering ESP8266 — часть 2

Продолжаем исследование модуля ESP8266. В этот раз рассмотрим процесс загрузки прошивки для дизассемблирования.Первая часть статьи здесь.64b220a7ece04cf5848be80f367ed546.pngСодержание Введение Архитектура ESP8266Карта памяти (адресного пространства) Формат прошивки Процесс запуска Инструменты Загрузка прошивки для исследования Ассемблер XtensaРегистры Базовые операторы Функции Условные переходы Заключение Ссылки 4. Загрузка прошивки для исследования Подготовив необходимые инструменты, мы подошли к самой интересной части — загрузке и дизассемблированию прошивки.ELF Начнем с самого простого — загрузки файла app.out — прошивки в формате ELF, созданной с помощью SDK.Как правило, файл app.out доступен в случае, когда у вас есть исходный код прошивки, который изучать гораздо проще и логичнее. Однако, чтобы познакомиться с особенностями компилятора, расположением сегментов и увидеть наименования функций, предлагаю начать именно с него.

После компиляции и сборки в папке build у нас появится файл app.out, который представляет собой скомпилированный пользовательский код, данные, библиотеки и отладочную информацию. В таком виде прошивку загрузить в модуль нельзя, поэтому после сборки ELF-файла SDK преобразует app.out в один или два файла в папке firmware — 0×00000.bin и 0×40000.bin, которые можно непосредственно прошить.

Открыв app.out в HIEW и посмотрев на таблицу сегментов (enter, F8, F6) мы увидем следующую картину:

beb344f54f0142c780c7d1f4c8f2cb2f.png Колонка VirtAddr содержит адреса начала сегментов в адресном пространстве Xtensa. Обратите внимание, что три сегмента (.data, .rodata и .bss) будут загружены в область оперативной памяти, сегмент .text будет записан по адресу пользовательского исполняемого кода, а сегмент .irom0.text — по адресу кода библиотек SDK. Остальные сегменты, имеющие начальный адрес, равный нулю, содержат служебную информацию и в прошивку, готовую к заливке в модуль добавлены не будут.Забегая вперед скажу, что сегмент .irom0.text будет в исходном виде скопирован в файл 0×40000, а сегменты .data, .rodata, .bss и .text будут собраны в файл 0×00000.bin с учетом формата, который был рассмотрен выше.

Для загрузки app.out в IDA необходимо проделать следующую последовательность действий:

1. Открываем IDA Pro 6.6 или выше2. Нажимаем «Go» — никаких файлов пока открывать не будем3. Открываем пункт меню File — Script file и выбираем скрипт определения процессора xtensa.py4. Загружаем файл app.out, здесь необходимо выбрать тип процессора и нажать «Set»:

a1a1a13e0dd5495ebced2b7828e173cd.png 5. В следующих окнах с предупреждением о неизвестном типе машины и предложениями загрузки отладочной информации нажать «Yes»6. В результате мы получим готовый к исследованию файл: b1d51f5e379d4f099c8a12df03a85d26.png Чего в нем не хватает? Не хватает системного ROM, содержащего базовые функции модуля. При необходимости его можно загрузить вручную, это мы и проделаем в следующем разделе.Системная прошивка модуля Мы рассмотрели довольно простую загрузку в IDA прошивки в виде ELF-файла. Однако на практике зачастую требуется изучить уже готовые прошивки, извлеченные из flash модуля (методом подключения к flash напрямую) или распространяемые в виде файлов 0×00000.bin и 0×40000.bin. Здесь придется проделать немного ручной работы. Начнем с загрузки образа системного ROM. В первой части я давал ссылку на архив с файлом 40000000.bin — это он и есть. Последовательность действий такая:1. Открываем IDA Pro 6.6 или выше2. Нажимаем «Go»3. Открываем пункт меню File — Script file и выбираем скрипт определения процессора xtensa.py4. Открываем файл 40000000.bin5. Выбираем тип процессора Tensilica Xtensa [xtensa] и нажимаем «Set»6. Далее необходимо указать организацию памяти для правильной загрузки двоичного файла. Здесь мы создаем сегмент кода по адресу 0×40000000 и загружаем в него наш файл:

ca893946f57b4d0eb71fb295cd513a7b.png 7. Образ ROM загружен, но он плохо читаем из-за отсутствия названий функций. Теперь загрузим скрипт 40000000.idc, который произведет дополнительную работу — определит имена функций и создаст дополнительные сегменты в адресном пространстве: File — Script file — 40000000.idc. Вот результат: 58b7e84790b04d7a9b563b0cba0f07da.png На этом загрузку системного ROM можно считать завершенной, можно переходить к его исследованию. Скрипт определил названия функций системного ROM, теперь можно разобраться, что выплолняет та или иная функция, вызываемая из SDK.А вот, кстати, функция, которая копирует пользовательскую прошивку из flash в память SoC:

25f30b88953d4e8dbf67e78d980b3786.png Такой функции в SDK нет, поэтому название я ей дал произвольное.Но прошивка не полна без загрузки пользовательской части — файлов 0×00000.bin и 0×40000.bin. Поэтому подгрузим эти файлы к системному ROM.

Пользовательская прошивка Мы загрузили в IDA системный ROM модуля, а скрипт подготовил нам несколько сегментов для загрузки остальных частей. Начнем с простого — загрузки кода библиотек.Как я говорил выше, файл прошивки 0×40000.bin представляет собой образ сегмента кода без всякой служебной информации и напрямую мапируется в адресное пространство процессора по адресу 0×40240000. Чтобы подгрузить его в IDA проделаем следующее:

1. Убедимся, что у нас открыта база данных 40000000.bin и скрипт 40000000.idc создал дополнительные сегменты: RAM, ROM, IRAM, IROM2. Выбираем в меню File — Load file — Additional binary file, открываем файл прошивки 40000.bin3. В следующем окне выбираем параметры загрузки. Обратите внимание, что загрузка производится по смещению в параграфах, т.е. вместо адреса указываем значение в 10h раз меньше (отбрасываем последний ноль). Галку создания сегмента можно снять, он у нас уже создан:

85cffa06459944729c75743f5bf9e830.png 4. Файл загружен. После указания начала кода (в данном случае это 4024000Ch) мы получаем примерно следующую картину: 43e0b3d39f9c4865b0987ac2d188fbde.png В отличие от ELF-файла, здесь не будут определены названия функций и переменных, но с этим уже ничего не поделаешь.Небольшое лирическое отступление Как исследовать голый ассемблерный код? Как понять, что выполняет та или иная функция? Для многих известных SDK IDA имеет сигнатуры FLIRT, которые определят названия стандартных функций. В нашем случае это не поможет, так как IDA не знает данный SDK. Так что эту работу придется проделать вручную. Для примера приведу несколько методов в порядке возрастания сложности:1. Найти сигнатуру исследуемой функции в дизассемблере ELF-файла, скомпилированного той же версией SDK. Есть вероятность, что вы ее найдете, и у нее будет имя (из отладочной информации). В том числе для этого я рассматривал загрузку прошивки в ELF.2. Известные константы — функция может ссылаться на текстовые строки или двоичные данные. С опытом многие такие константы запоминаешь наизусть, если константа незнакома — гуглим. Вот пример:

46c04c1595ee4e068c6f88e608bd3f69.png Видим две примечательные константы. Гугл первой же ссылкой выдает описание алгоритма strlen, использующего эти константы: 30e2d8c75c7c4f3aa4f0bfaed97ed76a.png Сравнив реализации алгоритма, можно с уверенностью сказать, что по адресу 40100E70h расположена функция strlen.Или вот такой кусок кода сразу выдает функцию деления: 55d68981fc1e45ce9ca9977d4da9b33d.png 3. Собственно, изучение ассемблерного кода и попытка понять, что выполняет функция. Иногда можно определить, что перед нами «местная» реализация знакомого алгоритма, а иногда нет.В любом случае, навык понимания ассемблерного кода приходит с опытом, так что дерзайте! Теперь переходим к загрузке файла 00000.bin. Мы помним, что это не просто образ, а файл со структурой, описывающий сегменты данных и кода. Вот так он может выглядеть при просмотре в шестнадцатеричном виде: 92bb53827824494a9c5dbd82e69a3a9f.png Сначала идет 8 байт общего заголовка прошивки, в которой определяется количество сегментов и точка входа. Потом идут сами сегменты, также имеющие 8-ми байтовые заголовки с адресом и длиной.Чтобы правильно загрузить их в IDA я вырезал данные каждого сегмента (без заголовков) в отдельные файлы, назвав их по адресу загрузки: 296ac0e8cfc14dc4885e5ada62771ca2.png Теперь остается подгрузить их в IDA. Для каждого файла выполняем последовательность действий, аналогичных загрузке системного ROM:1. File — Load file — Additional binary file, выбираем файл данных2. В параметрах загрузки указываем сегмент (по имени файла без последнего нуля), сегмент не создаем.

Все, теперь у нас есть полностью загруженная и готовая к исследованию прошивка!

16e498d426454fd9a1879288d0173c36.png В этой части статьи мы рассмотрели процедуры загрузки различных видов прошивок ESP8266 для дизассемблирования в IDA Pro. В заключительной части мы рассмотрим особенности процессора Xtensa, отличия от архитектуры x86, набор регистров и команд.

© Habrahabr.ru