
Сегодня, мы изучим под микроскопом структуру исполняемых файлов Windows (Portable EXEcutable, или просто PE).
Portable Executable файл (PE-файл) — это отдельный исполняемый файл с расширением .exe (или .dll), получаемый в процессе сборки (компиляции) программы для выполнения в Windows. В него включены код, ресурсы (иконки и другие данные), библиотеки, данные программы и т.д..
Что бы заглянуть внутрь файла мы будем использовать популярный редактор двоичных файлов или просто HEX-редактор, ориентированный на работу с кодом — Hiew ( простонародье Хью ). За одно получиться руководство к нему )))
Запускаем редактор, выбираем файл который будем использовать. Например этот crack.exe из прошлой статье.
- Нажимаем F4(Mode).
- Выбираем режим HEX.

Заголовок.
Структура исполняемого файла состоит из двух частей:
- Заголовки.
- Секции.
Заголовки идут в начает файли и содержат всю техническую информацию для запуска файла. Сколько исполняемый код и данные занимают памяти, описание данных которые хранятся в секциях , сколько секций и т.д.
Структура заголовков:
- DOS заголовок.
- DOS стаб, небольшая DOS программа.
- PE заголовок.
- Таблица секций.
DOS заголовок.
Это заголовок для DOS программы. В полях заголовка хранятся данные для запуска в MS-DOS, если кто-то попытается запустить PE-файл. Не кто не стал переписывать формат из за того что многие уже не знают что это такое. Структура заголовка выглядит так :
- e_magic;
- e_cblp;
- e_cp;
- e_crlc;
- e_cparhdr;
- e_minalloc;
- e_maxalloc;
- e_ss;
- e_sp;
- e_csum;
- e_ip;
- e_cs;
- e_lfarlc;
- e_ovno;
- e_res[4];
- e_oemid;
- e_oeminfo;
- e_res2[10];
- e_lfanew;
Для нас интересны только два поля: e_magic — двухбайтовое поле хранит в себе специальную сигнатуру. Эта сигнатура нужна, чтобы указать что это действительно исполняемый файл. Вот она — «MZ» (0x4D 0x5A) . Каждый PE-файл обязан начинаться с неё.
Последнее поле e_lfanew — четырёхбайтовое поле хранит в себе смещение до заголовка PE относительно начала файла. Только этот адрес хранится в обратном порядке : B8 00 00 00 —> 00 00 00 B8

Вы можете нажать клавишу F5 и ввести адрес «B8» что бы сразу попасть на заголовок PE
DOS стаб.
Дальше у нас идет область DOS-стаб которая продолжается до PE заголовка. Это код программы который выполниться если мы попытаемся запустить программу в MS-DOS. Она выводит надпись что эта программа не может работать в DOS и закроет ее.

PE — заголовок.
Теперь мы переходим к самому важному для нас месту, PE-заголовок, который на самом деле, состоит из трёх частей: сигнатуры, файлового подзаголовка и дополнительного подзаголовка.
- Signature
Это четырёхбайтовое поле содержит сигнатуру, а именно значение 50 45 00 00 (или «PE\x00\x00»). Эта сигнатура указывает на то, что перед нами действительно PE-файл . - FileHeader
Это обязательный подзаголовок PE-заголовка. Он хранит в себе базовые характеристики исполняемого файла.- Machine; // Архитектура процессора
- NumberOfSections; // Кол-во секций
- TimeDateStamp; // Дата и время создания программы
- PointerToSymbolTable; // Указатель на таблицу символов
- NumberOfSymbols; // Число символов в таблицу
- SizeOfOptionalHeader; // Размер дополнительного заголовка
- Characteristics; // Характеристика
- OptionalHeader — это ещё один обязательный подзаголовок PE-файла. В нём хранится необходимая информация для загрузки PE-файла.
Давайте рассмотрим FileHeader более детально:
- Machine — двухбайтовое поле содержит информацию о характеристике процессора, на котором может быть выполнена данная программа.
- (0x014c —> 4c 01) — означает, что программа может выполняться на x32.
- (0x0200 —> 00 02) — означает, что программа может выполняться на процессорах Intel Itanium (Intel x64).
- (0x8664 —> 64 86) — означает, что программа может выполняться на процессорах AMD64 (x64).
- NumberOfSections — двухбайтовое поле содержит в себе число секций в PE-файле.
- TimeDateStamp — четырехбайтовое поле.
- PointerToSymbolTable — четырехбайтовое поле.
- NumberOfSymbols — четырехбайтовое поле.
- SizeOfOptionalHeader — двухбайтовое поле содержащее размер дополнительного заголовка, который идёт сразу за файловым заголовком.
- Characteristics— данное двухбайтовое поле содержит характеристики PE-файла. Например, является ли это exe-файлом, или dll. Также, тут описано, является ли данная программа x64-битной или x86-битной.

Третий заголовок OptionalHeader — это ещё один обязательный подзаголовок PE-файла. В нём хранится необходимая информация для загрузки PE-файла. Он имеет всего два формата PE32+ (для 64-битных программ) и PE32 (для 32-битных).
- Magic; Это двухбайтовое поле отвечает за битность программы (x32/x64). Оно может принимать следующие значения:
- (0B 01) — означает, что это x32 (x86) исполняемый образ.
- (0B 02) — означает, что это x64 исполняемый образ.
- (07 01) — означает, что это ROM образ.
- MajorLinkerVersion;
- MinorLinkerVersion;
- SizeOfCode; — четырёхбайтовое поле (смещение 4 байта), обычно помещается в раздел «.text«. Если имеется несколько разделов кода, это сумма всех разделов кода. Должен быть целым числом, кратным FileAlignment, то есть размеру файла.
- SizeOfInitializedData;
- SizeOfUninitializedData;
- AddressOfEntryPoint; — четырёхбайтовое поле (смещение 16 байт) содержит адрес начала кода, точка откуда программа начинает выполняться.
- BaseOfCode;
- BaseOfData;
- ImageBase; — это четырёхбайтовое поле (смещение 28/24 байт) содержит предпочтительный адрес загрузки программы в память.
- SectionAlignment; — это четырёхбайтовое поле (смещение 32 байта) содержит размер выравнивания блока в памяти. Если блок занимает меньше памяти , то в блок добавиться нулевые байты( 0x00 ) до размера выравнивания.
- FileAlignment; — это четырёхбайтовое поле (смещение 36 байт) содержит размер выравнивания блока в файле. Да блоки в файле тоже выравниваются на целые числа.
- MajorOperatingSystemVersion; — в этом двухбайтовом поле содержится необходимая версия Windows.
- MinorOperatingSystemVersion; — в этом двухбайтовом поле содержится необходимая версия Windows.
- MajorImageVersion;
- MinorImageVersion;
- MajorSubsystemVersion;
- MinorSubsystemVersion;
- Win32VersionValue;
- SizeOfImage; — это четырёхбайтовое поле (смещение 56 байт) содержит размер (в байтах) загруженного исполняемого файла в памяти.
- SizeOfHeaders; — четырёхбайтовое поле (смещение 60 байт) содержит размер всех заголовков + таблица разделов.
- CheckSum;
- Subsystem; — двухбайтовое поле содержащее тип подсистемы (GUI, CLI, Driver, …).
- DllCharacteristics;
- SizeOfStackReserve;
- SizeOfStackCommit;
- SizeOfHeapReserve;
- SizeOfHeapCommit;
- LoaderFlags;
- NumberOfRvaAndSizes; — данное четырёхбайтовое поле содержит число каталогов в массиве каталогов. По умолчанию равна 16.
- DataDirectory — это поле на самом деле массив, которая содержит информацию о каталогах. Их число определено в поле NumberOfRvaAndSizes .

Нажав клавишу F8 мы можем посмотреть основные данные заголовка.

Таблица секций.
Перейдём к последнему заголовку. Этот заголовок — таблица, которая содержит различную информацию о секциях. Мы уже знаем, что их количество определено в файловом заголовке в поле NumberOfSections. Проще говоря, это массив с NumberOfSections элементов. Этот массив содержит элементы типа IMAGE_SECTION_HEADER.
Давайте подробно рассмотрим основные поля.
- Name
Это поле, размером в 8 байт, содержит имя секции, в ASCII кодировке. - VirtualSize
Это четырёхбайтовое поле содержит размер (в байтах) секции в виртуальной памяти. - VirtualAddress
А это четырёхбайтовое поле уже содержит относительный адрес секции в виртуальной памяти. - SizeOfRawData
Данное четырёхбайтовое поле содержит размер секции в файле. - PointerToRawData
А указатель на эти самые данные, содержаться в этом четырёхбайтовом поле. - Characteristics
Это четырёхбайтовое поле содержит атрибуты секции. Например, права чтения, записи и исполнения (Read Write Execute) (RWE).
По сути, в таблице секций просто зафиксирована информация о секциях.
Вот и всё, мы закончили изучать заголовки. Теперь мы приступаем к изучению секций. По сути, секции являются простыми последовательными блоками данных. Они следуют друг за другом и у них нет определенного формата, так как их характеристики описаны в таблице секций. А вот формат данных, в этих секциях, зависят от типа информации, которая в них хранится. Секции, как я уже сказал можно представить в виде коробок с информацией. Размер каждой секции зафиксирован в таблице секций, поэтому секции должны быть определённого размера, а для этого их дополняют NULL-байтами (00). Вот и всё, что касается секций.
Также, небольшая шпаргалка, для того, чтобы понимать какое назначение носит имя определенного заголовка секции в таблице:
- .text: Исполняемый код.
- .data: Инициализированные данные.
- .bss: Неинициализированные данные.
- .rdata: Константные (рид-онли) данные.
- .edata: Дескрипторы экспорта.
- .idata: Дескрипторы импорта.
- .reloc: Таблица релокации.
- .rsrc: Ресурсы.
- .tls: __declspec(thread) данные.
Также, секциями являются и различные каталоги.
Нажав F8 потом F6 мы сожем посмотреть секции в файле. Выбираем любую нажимаем Enter и попадаем на начало секции.

В статье использовались материалы из статьи : codeby.net/threads/0x01-issleduem-portable-executable-exe-fajl-format-pe-fajla.65415/