Доброго времени суток, друзья. В прошлой статье, мы начали создавать простейший исполняемый модуль (.exe файл) в hex-редакторе и полностью заполнили заголовки. В этой статье, мы закончим работу над нашим исполняемым файлом и получим работоспособную программу. Приступим!
Выравнивание начала секций
В прошлой статье мы заполнили заголовки, ну а в этой мы заполним секции. Как мы договорились в прошлой статье, всего у нас будет 3 секции, а именно:
- .text - секция кода
- .idata - секция импорта
- .data - секция данных
Но что делать, если у нас есть пробел между 0x1A0 (конца заголовков) и 0x200 (началом секций)? Что делать с этим пробелом в файле? Просто заполним его нулями !
Заполняем секцию .text
Первая по порядку у нас секция .text. В ней, как мы обусловились, будет храниться наш код.
Вернёмся к нашему алгоритму:
1. Показ сообщения
2. Переход к шагу 2
Как мы знаем, для показа сообщения у нас есть функция MessageBoxA. Она будет загружена загрузчиком при разборе секции импорта, а её адрес будет помещён в определённое место в памяти.
Мы пока не знаем, что это за адрес, поэтому давайте пока представим, что адрес функции будет загружен по адресу 0xAAAAAAAA (в даленьйшем мы его заменим на настоящий).
Также, мы не знаем адрес строк, которые должны быть отображены в сообщении ("Hello codeby.net!" и "Codeby.net"). Поэтому также представим, что они будут находится по адресу 0xAAAAAAAA. На самом деле эти строки будут находится в секции данных.
Программировать, как я сказал в первой статье, мы будем на языке ассемблера. Почему? Потому что именно этот язык является прямым представлением машинного языка в понятном человеку виде. Итак, на языке ассемблера наша программа будет выглядить так:
Код:
show_message:
; Передаём параметры
push 0 ; MB_OK
push 0xAAAAAAAA ; Codeby.net
push 0xAAAAAAAA ; Hello codeby.net
push 0 ; NULL
; Вызываем функцию
call [0xAAAAAAAA] ; MessageBoxA
; Переходим на шаг 1
jmp show_message
Также, нужно запомнить, что параметры когда параметры передаются через стэк (с помощью инструкций push), то они передаются в обратном порядке!
Как же нам теперь превратить код на языке ассемблера в байты? Для этого переходим по этой ссылке:
Ссылка скрыта от гостей
. Это онлайн ассемблер. Он позволяет конвертировать код на языке ассемблера в шелл-код (код в hex-представлении).Вставляем сюда код на языке ассемблера и нажимаем на кнопку "Assemble".
Теперь копируем байты из поля Raw Hex и вставляем его в hex-редактор.
Как мы помним, мы определили размер каждой секции в файле в 0x200 (512) байт. Поэтому пространство в файле от 0x200 до 0x400 должна занимать секция. Что делать с остальным пустым пространством в секции? Мы уже знаем ответ! Просто заполняем его нулями.
Заполняем .idata секцию
Сама таблица импорта - это по сути массив элементом IMAGE_IMPORT_DESCRIPTOR. А массив в памяти - просто последовательность элементов. Таблица импорта заканчивается нулевым элементом, т.е. элементом IMAGE_IMPORT_DESCRIPTOR, у которого все поля равны нулю. Структура элемента IMAGE_IMPORT_DESCRIPTOR на языке C/C++:
C:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
DWORD OriginalFirstThunk;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
Как мы помним, OriginalFirstThunk хранит относительный виртуальный адрес указателя на элемент таблицы Import Lookup Table - IMAGE_IMPORT_BY_NAME. Его структура на языке C/C++:
C:
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR[] Name;
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
Поле Hint хранит в себе индекс функции в DLL - это поле может быть равно нулю, а поле Name - это строка хранящая имя функции. Эта строка заканчивается NULL-символом.
Так, следующее интересное нам поле в структуре IMAGE_IMPORT_DESCRIPTOR - это Name. Это поле содержит указатель на строку с именем DLL.
Поле FirstThunk - хранит виртуальный адрес указатель на таблицу Import Address Table. В эту таблицу загрузчик помещает адрес импортируемой функции.
Схематична эта вся система выглядит так:
Разобрались с импортом. Теперь перейдём к деталям заполнения. Мы будем импортировать только одну функцию - MessageBoxA. Она находится в DLL'ке с именем user32.dll. Поэтому у нас будет только 2 элемента в таблице импорта. Первый - отвечающий за функцию MessageBoxA, а второй - нулевой.
В этот раз я сам заполню таблицу импорта, а после этого разберём как всё там устроено .
Каждый отдельный элемент на этом изобрежении я покрасил в отдельный цвет.
- Зелёный элемент - элемент IMAGE_IMPORT_DESCRIPTOR. Его первое поле, как мы можем заметить, содержит виртуальный адрес (0x2028) на адрес (0x2030) (бежевый элемент), который указывает на элемент IMPORT_BY_NAME (серый элемент). Также, он хранит адрес (0x2046) на имя DLL'ки (белое поле) и адрес на Import Address Table (сереневое поле) .
- Голубой элемент - нулевой элемент таблицы дескрипторов.
- Бежевый элемент - таблица указателей на элементы IMPORT_BY_NAME (серое поле).
- Серый элемент - элемент IMPORT_BY_NAME. Он хранит индекс и имя функции в DLL.
- Сиреневый элемент - таблица Import Address Table. В неё будут адресы импортируемых функций.
- Белый элемент - имя DLL'ки.
Кстати, теперь мы знаем, куда загрузится адрес нашей функции. Он загрузится по адресу 0x0040203E (ImageBase + относительный виртуальный адрес Import Address Table). Поэтому заменим его значение в секции кода.
Заполняем секцию .data
Вот и всё! Мы заполнили последнюю секцию. Также, теперь мы знаем виртуальные адреса двух нужных нам строк:
Для "Codeby.net" - 0x402000 (ImageBase + адрес начала секции (эта строка начинается в самом начале секции))
Для "Hello codeby.net!" - 0x40200B (ImageBase + адрес начала секции (эта строка начинается в самом начале секции) + размер первой строки + 1 (для NULL-байта первой строк))
Заменим и эти значения в секции кода (ранее было 0xAAAAAAAA).
Ну что же, в этой статье мы практически закрепили знания о формате PE-файла. В следующих статьях будем изучать этот вопрос с точки зрения информационной безопасности. Если у вас есть какие-либо вопросы или вы заметите кое-какие неточности, буду рад если вы напишите об этом в комментариях! Спасибо за то что прочитали эту статью до конца !
Следующая статья
Следующая статья
Последнее редактирование: