PE-формат. Часть 2 — Немного практики: выводим информацию о секциях исполняемого файла

Тем, кто мало знаком с PE-форматом, или совсем с ним не знаком, наверное, было непросто понять то море информации, которое было излито в моей предыдущей статье, поэтому я решил пока немного отложить изучение экспортов, импортов и прочих служебных таблиц в PE-файлах. В этой статье мы займемся практикой: напишем программку, которая выведет список секций произвольного исполняемого файла и некоторую информацию о них. Впоследствии, когда дойдет дело до написания упаковщика, этим кодом мы воспользуемся, ведь упаковывать мы будем как раз данные секций.


Писать будем на C++. Никаких извратов не будет, поэтому код должен быть понятен, тем более, я его досконально прокомментирую.
Начнем с инклюдов:

Далее - несколько макросов, которые предоставил Крис Касперски в своей статье про формат PE. Мы будем чаще всего использовать ALIGN_UP - макрос для выравнивания числа на заданную границу.

Итак, тело главной функции. В качестве единственного аргумента нашей программе будет передаваться путь к исполняемому файлу для анализа.

Теперь пришла пора открыть файл, имя которого нам передали через консоль.

Как я писал в предыдущей статье, в самом начале файла должна лежать структура IMAGE_DOS_HEADER. Считаем ее и немного проверим.

Теперь необходимо считать структуру IMAGE_NT_HEADERS. Я программу писал исключительно под PE32, хотя сделать ее для PE64 или вообще универсальной труда никакого не составляет. Читать будем, соответственно, структуру IMAGE_NT_HEADERS32 (это 32-разрядная версия IMAGE_NT_HEADERS, они все определены в глубине Windows.h). Сейчас я пропускаю множество необходимых проверок полей заголовка PE-файла (например, не проверяю выравнивания), потому что они сейчас не являются критичными.

Теперь нам необходимо переместиться к таблице секций, которую мы и будем читать, чтобы получить информацию о секциях исполняемого файла. Можно было бы воспользоваться макросом IMAGE_FIRST_SECTION, но я сделал это руками, чтобы было понятнее:

Немного подготовим консоль для удобного вывода информации. Выставим выравнивание текста по левому краю и вывод чисел в 16-ричной системе счисления. std::showbase добавит перед 16-разрядными числами "0x" автоматически.

Теперь начнем читать таблицу секций. Количество секций лежит в IMAGE_NT_HEADERS.FileHeader.NumberOfSections.

Дальше я добавил всевозможные проверки корректности таблицы секций. Разберем их.

Если вам сейчас трудно вспомнить, что это всё такое - виртуальный размер, реальный адрес, выравнивание, то советую вернуться к первой статье и всё повторить.

Пришло время вывести информацию о секции - раз уж она прошла все проверки :)

Вот и все, наша программа готова, и ей можно через консоль скормить любой исполняемый файл (PE32), чтобы получить информацию о его секциях. На первом скриншоте как раз показан вывод этой программы при анализе самой себя, собранной в Visual Studio 2010 в отладочной версии.

Полная версия кода (без комментариев): скачать (txt).

PE-формат. Часть 2 — Немного практики: выводим информацию о секциях исполняемого файла: 17 комментариев

    1. Ну напишите, включите туда все проверки, которые включил я, потом сравним. Не думаю, что он был бы проще в 5 раз, разве что совсем немного) Да и я писал на C++ же, без винапи.

  1. Если у кого на Win7 не пашет, меняем 0x108 на IMAGE_NT_OPTIONAL_HDR32_MAGIC.

    По крайней мере у меня не работало, покопавшись в MSDN нашел решение проблемы.

  2. Еще забыл добавить, для версии x64 используем IMAGE_NT_HEADERS64 вместо IMAGE_NT_HEADERS32. Вроде по все баги которые у меня полезли.

    Спасибо за гайд. =)

  3. Как правильно запускать программу?
    Пишу в консоли sections.exe sections.exe, ошибка: Error reading section header

    1. Запускаешь вроде правильно. Но не исключено, что в примере есть ошибка, тогда надо разбираться. А каким компилятором собирал пример?

  4. Visual C++ 2013.
    В коде, где считывается заголовок секции IMAGE_SECTION_HEADER, закоментировал проверку и стало нормально отрабатывать.

    1. Дошли руки проверить. Странно, но у меня всё нормально отрабатывает на бинарнике, собранном в VS2013. Был бы благодарен, если зальёшь куда-нибудь тот бинарник, на котором у тебя не сработало.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *