Молодогвардейцев 454015 Россия, Челябинская область, город Челябинск 89085842764
MindHalls logo

Алгоритм загрузки PE файлов в Windows

Привет всем, кто забрел на эту заметку. Мной будет затронута не самая любимая тема: системное программирование. Дело в том, что волей судьбы я с ним столкнулся, в университете, если быть точнее. Пришлось знатно покопаться в самых низких слоях программирования, алгоритм разгрузки pe файлов оттуда. Надеюсь, что кому-нибудь пригодится мой опыт в этом деле, что вряд ли. Но тем не менее, я им поделюсь.

Если вам не интересен краткий экскурс в теорию работы pe загрузчика, то добро пожаловать сразу к реализации загрузчика PE файлов на C. Конечно же на Си, никаких скриптов, абстракций и прочего высокоуровнего кайфа, только хардкор!

Что такое PE файл

PE(Portable Executable) это основной формат исполняемых файлов для операционных систем семейства Windows NT. Используется для представления исполняемых файлов (.exe), динамических библиотек (.dll) и драйверов (.sys). Является расширением формата исполняемых файлов для DOS, то есть PE файл является корректной программой для системы DOS, но это нам не интересно. Мы будем запускать PE файлы под Windows собственноручно написанным загрузчиком.

Зачем? Для разных целей. Например, мне просто дали такое задание в университете для затравочки и подготовки к изучению методов взлома и защиты программных данных. Кто-то пишет всякие читы, трейнеры для игр(это очень плохо, я их осуждаю). Есть и такие, для кого это просто хобби, как писать на ассемблере. В любом случае, сейчас я вкратце расскажу последовательность действий операционной системы при загрузке pe файла.

Алгоритм загрузки PE файла в Windows

Очень краткое описание алгоритма, достаточное для понимания реализации. Итак, в работе загрузчика можно выделить два основных этапа.

1) физическое копирование секций файла в память;
2) логическая обработка скопированного образа программы.

Понятно, что второй этап возможен только в случае успешного завершения первого. Теперь подробно рассмотрим шаги каждого этапа.

Физическое копирований секций файла в память

Проще простого, файлы копируются в область памяти без учета их назначения. Пара слов о формате PE файла. Первым расположен DOS-заголовок и сразу за ним лежит значение смещения до NT-заголовка. После этих товарищей расположены секции загружаемого файла. Их может быть разное количество в зависимости от типа файла(процесс, библиотека или драйвер). На первом этапе мы пробежимся по каждой секции и загрузим ее содержимое в оперативную память.

Действия первого этапа:
1) считать в память DOS-заголовок и определить смещение до NT-заголовка;
2) считать NT-заголовок;
3) определить из NT-заголовка начальный адрес загрузки (ImageBase) и размер образа исполняемого файла(SizeOfImage). ImageBase это адрес, по которому PE файл хочет загрузиться, но на деле редко ему это удается.
4) попытаться выделить память по указанному адресу(ImageBase) указанного размера (VirtualAlloc). Если по этому адресу выделить память не удается, проверить наличие таблицы релоков(таблицы перемещений). Если релоки присутствуют, выделить память по новому адресу с с учетом смещения. Память выделяется с правами для чтения и записи без учета прав доступа, указанных в таблице секций;
5) проверить корректность заголовков и таблицы секций;
6) скопировать данные секций из файла в память;

Логическая обработка скопированного образа программы

Все скопировано в память, пора пробежаться по образу и выполнить необходимые манипуляции вплоть до вызова точки входа.

Действия второго этапа:
1) настроить таблицу релоков, если был изменен адрес загрузки(подробнее в реализации);
2) обработать таблицу импорта, если она присутствует. Для этого необходимо загрузить все библиотеки, которые еще не были загружены. Они грузятся рекурсивно с первого шага первого этапа, следовательно во время их загрузки могут быть загружены другие библиотеки. Каждая библиотека должна быть загружена только один раз, для этого следует вести список загруженных библиотек;
3) в таблице экспорта библиотеки необходимо найти все импортируемые из нее функции. Должны быть найдены все импортируемые функции всех библиотек, иначе процесс загрузки прерывается с ошибкой. При загрузке библиотеки должна вызываться ее точка входа (AddressOfEntryPoint), которая является адресом функции: BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); Эта функция вызывается при загрузке и перед выгрузкой библиотеки. Во втором параметре передается константа, указывающая причину вызова. В случае библиотеки это DLL_PROCESS_ATTACH. Если она вернула FALSE, значит библиотека загрузилась с ошибкой и следует прерывать процесс загрузки;
4) выставить права доступа в соответствии с указанными в таблице секций(VirtualProtect);
5) передать управление точке входа.

На этом шаги наконец закончились. Если все прошло успешно, наша программа запустится. Естественно система будет ругаться, мы вторгаемся во владения ядра и берем на себя его функции.

Заключение

Почувствовали себя теперь настоящим хозяином операционной системы? Я немножко да. Но это все цветочки, на этот алгоритм я буду опираться во время непосредственной реализации загрузчика pe файлов. Приходите, почитайте, как я это сделал.