3. PE-файлы: заголовок PE

3.1. Структура заголовка

Заголовок PE (IMAGE_NT_HEADERS) всегда выравнен на границу 8 байт и состоит из трех частей:

Сигнатура
Заголовок файла
Необязательный заголовок

или

struct IMAGE_NT_HEADERS {
  DWORD Signature;
  IMAGE_FILE_HEADER FileHeader;
  IMAGE_OPTIONAL_HEADER OptionalHeader;
};

Как уже было сказано, заголовок PE всегда начинается с 4-байтовой сигнатуры "PE\0\0" (IMAGE_NT_SIGNATURE). За ней следуют два заголовка: заголовок файла (IMAGE_FILE_HEADER) и необязательный заголовок (IMAGE_OPTIONAL_HEADER). Несмотря на свое название, IMAGE_OPTIONAL_HEADER присутствует в PE-файле всегда (необязательным он является с точки зрения общего формата COFF, поскольку не используется в объектных и библиотечных файлах).

3.2. Заголовок файла

Заголовок файла состоит из 0x14 байтов (определение IMAGE_SIZEOF_FILE_HEADER), размещается сразу после сигнатуры и содержит общее описание файла. Он состоит из следующих полей:

Смещение (hex) Размер Тип Название Описание
00 2 WORD Machine Обозначение процессора.
02 2 WORD NumberOfSections Количество секций в файле.
04 4 DWORD TimeDateStamp Дата и время создания файла.
08 4 DWORD PointerToSymbolTable Смещение до таблицы символов или 0.
0C 4 DWORD NumberOfSymbols Количество элементов в таблице символов.
10 2 WORD SizeOfOptionalHeader Размер необязательного заголовка.
12 2 WORD Characteristics Атрибуты файла.

Опишем назначения полей.

Поле Machine

16-битовое число, которое задает архитектуру процессора, на которой может выполняться данная программа. Мне известны следующие коды процессоров (большинство из которых так никогда и не были поддержаны в Windows):

Название Значение Процессор
IMAGE_FILE_MACHINE_UNKNOWN 0 неизвестный процессор
IMAGE_FILE_MACHINE_I386 0x014C Intel 80386 или выше
0x014D Intel 80486 или выше
0x014E Intel Pentium или выше
0x0160 MIPS R3000, big-endian
IMAGE_FILE_MACHINE_R3000 0x0162 MIPS R3000, little-endian
IMAGE_FILE_MACHINE_R4000 0x0166 MIPS R4000
IMAGE_FILE_MACHINE_R10000 0x0168 MIPS R10000
IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 MIPS WCE v2
IMAGE_FILE_MACHINE_ALPHA 0x0184 DEC/Compaq Alpha AXP
IMAGE_FILE_MACHINE_SH3 0x01A2 Hitachi SH3
IMAGE_FILE_MACHINE_SH3DSP 0x01A3 Hitachi SH3 DSP
IMAGE_FILE_MACHINE_SH3E 0x01A4 Hitachi SH3E
IMAGE_FILE_MACHINE_SH4 0x01A6 Hitachi SH4
IMAGE_FILE_MACHINE_SH5 0x01A8 Hitachi SH5
IMAGE_FILE_MACHINE_ARM 0x01C0 ARM
IMAGE_FILE_MACHINE_THUMB 0x01C2 ARM Thumb
IMAGE_FILE_MACHINE_AM33 0x01D3 Panasonic AM33
IMAGE_FILE_MACHINE_POWERPC 0x01F0 IBM PowerPC
IMAGE_FILE_MACHINE_POWERPCFP 0x01F1 IBM PowerPC FP
IMAGE_FILE_MACHINE_IA64 0x0200 Intel IA-64 (Itanium)
IMAGE_FILE_MACHINE_MIPS16 0x0266 MIPS16
0x0268 Motorola 68000
IMAGE_FILE_MACHINE_ALPHA64 0x0284 DEC/Compaq Alpha AXP 64-bit
0x0290 HP PA-RISC
IMAGE_FILE_MACHINE_MIPSFPU 0x0366 MIPS with FPU
IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 MIPS16 with FPU
IMAGE_FILE_MACHINE_TRICORE 0x0520 Infineon TriCore
IMAGE_FILE_MACHINE_CEF 0x0CEF ???
IMAGE_FILE_MACHINE_EBC 0x0EBC EFI Byte Code
IMAGE_FILE_MACHINE_AMD64 0x8664 AMD 64 (K8)
IMAGE_FILE_MACHINE_M32R 0x9041 Renesas M32R
IMAGE_FILE_MACHINE_CEE 0xC0EE ???

Поле TimeDateStamp

32-битовое число, которое содержит дату и время создания данного файла. Формат этого поля недокументирован, однако сборщики Microsoft заносят сюда время как количество секунд от полуночи 01.01.1970 в UTC (т. е. используют юниксовский формат времени, возвращаемого функцией time языка C). Между прочим, это означает, что текущее состояние формата PE действительно только до 18.01.2038 г.

Поскольку формат этого поля недокументирован, некоторые сборщики третьих фирм сохраняют его значение в других форматах. Это может оказаться важным, т. к. данное поле используется при динамическом связывании импорта из DLL.

Поля PointerToSymbolTable и NumberOfSymbols

Согласно стандарту COFF, эти 4-байтовые поля должны обеспечивать доступ к отладочной информации в файле. Однако все известные мне сборщики заносят в них нули, а для доступа к отладочной информации используется иной способ (см. каталог отладочной информации).

Поле SizeOfOptionalHeader

16-битовое число, задающее размер необязательного заголовка. Оно равно 0xE0 для файлов PE32 (IMAGE_SIZEOF_NT_OPTIONAL32_HEADER) и 0xF0 для файлов PE32+ (IMAGE_SIZEOF_NT_OPTIONAL64_HEADER).

Поле Characteristics

16-битовое поле флагов, содержащее COFF-атрибуты файла. Перечислим только те флаги, которые относятся к исполняемым файлам.

Название Значение Описание
IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 Файл является исполняемым, т. е. не содержит не разрешенных сборщиком внешних ссылок. Всегда установлен, кроме тех случаев, когда сборщик не смог собрать исполняемый файл, но создал его для последущей быстрой пересборки (incremental linking).
IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 Рекомендация операционной системе агрессивно выталкивать виртуальную память данного процесса в файл подкачки. Этот флаг устанавливается для сервисов, которые "просыпаются" очень редко.
IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 Программа может работать с адресами, большими 2 Гб. Учитывается в серверных платформах Windows, в которых включена опция /3GB, выделяющая пользовательским программам 3, а не 2 Гб виртуальной памяти.
IMAGE_FILE_BYTES_REVERSED_LO 0x0080 Эти флаги устанавливаются, если порядок байтов (endianess) в файле отличен от ожидаемого ОС. Первый флаг говорит, что порядок байтов в памяти little-endian, второй – что big-endian. По-видимому, для исполняемых файлов эти флаги не применяются, т. к. загрузчик предполагает, что порядок байтов в файле правильный.
IMAGE_FILE_BYTES_REVERSED_HI 0x8000
IMAGE_FILE_32BIT_MACHINE 0x0100 Ожидается 32-разрядный процессор. В настоящее время всегда установлен.
IMAGE_FILE_DEBUG_STRIPPED 0x0200 Отладочная информация вынесена в отдельный файл .DBG. Применимость этого флага к исполняемым файлам мне неизвестна.
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 Указание операционной системе, что при загрузке с дискеты или компакт-диска этот файл нужно предварительно скопировать в файл подкачки.
IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 Указание операционной системе, что при загрузке из сети этот файл нужно предварительно скопировать в файл подкачки.
IMAGE_FILE_SYSTEM 0x1000 Файл является системным. Применимость этого флага к исполняемым файлам мне неизвестна.
IMAGE_FILE_DLL 0x2000 Файл является динамической библиотекой.
IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 Файл может исполняться только на однопроцессорной машине.

3.3. Необязательный заголовок

Необязательный заголовок имеет два различных формата: для PE32 и для PE32+. Соответствующие структуры называются IMAGE_OPTIONAL_HEADER32 и IMAGE_OPTIONAL_HEADER64. Их поля сведены в следующей таблице:

Смещение (hex, PE32/PE32+) Размер (PE32/PE32+) Тип (PE32/PE32+) Название Описание
00 2 WORD Magic Сигнатура заголовка.
02 1 BYTE MajorLinkerVersion Старшая цифра номера версии сборщика. Загрузчиком не используется.
03 1 BYTE MinorLinkerVersion Младшая цифра номера версии сборщика. Загрузчиком не используется.
04 4 DWORD SizeOfCode Сумма размеров всех секций, содержащих програмный код.
08 4 DWORD SizeOfInitializedData Сумма размеров всех секций, содержащих инициализированные данные.
0C 4 DWORD SizeOfUninitializedData Сумма размеров всех секций, содержащих неинициализированные данные.
10 4 DWORD AddressOfEntryPoint RVA точки запуска программы. Для драйвера – это адрес DriverEntry, для DLL – адрес DllMain или нуль.
14 4 DWORD BaseOfCode RVA начала кода программы.
18/– 4/0 DWORD BaseOfData RVA начала данных программы. Ненадежное поле, загрузчиком не используется. В PE32+ отсутствует!
1С/18 4/8 DWORD/ULONGLONG ImageBase Предпочтительный базовый адрес программы в памяти, кратный 64 Кб. По умолчанию равен 0x00400000 для EXE-файлов в Windows 9x/NT, 0x00010000 для EXE-файлов в Windows CE и 0x10000000 для DLL. Загрузка программы с этого адреса позволяет обойтись без настройки адресов.
20 4 DWORD SectionAlignment Выравнивание в байтах для секций при загрузке в память, большее или равное FileAlignment. По умолчанию равно размеру страницы виртуальной памяти для данного процессора.
24 4 DWORD FileAlignment Выравнивание в байтах для секций внутри файла. Должно быть степенью 2 от 512 до 64 Кб включительно. По умолчанию равно 512. Если SectionAlignment меньше размера страницы виртуальной памяти, то FileAlignment должно с ним совпадать.
28 2 WORD MajorOperatingSystemVersion Старшая цифра номера версии операционной системы. Загрузчиком не используется.
2A 2 WORD MinorOperatingSystemVersion Младшая цифра номера версии операционной системы. Загрузчиком не используется.
2C 2 WORD MajorImageVersion Старшая цифра номера версии данного файла. Загрузчиком не используется.
2E 2 WORD MinorImageVersion Младшая цифра номера версии данного файла. Загрузчиком не используется.
30 2 WORD MajorSubsystemVersion Старшая цифра номера версии подсистемы.
32 2 WORD MinorSubsystemVersion Младшая цифра номера версии подсистемы.
34 4 DWORD Win32VersionValue Зарезервировано, всегда равно 0.
38 4 DWORD SizeOfImage Размер файла в памяти, включая все заголовки. Должен быть кратен SectionAlignment.
3C 4 DWORD SizeOfHeaders Суммарный размер заголовка и заглушки DOS, заголовка PE и заголовков секций, выравненный на границу FileAlignment. Задает смещение от начала файла до данных первой секции.
40 4 DWORD CheckSum Контрольная сумма файла.
44 2 WORD Subsystem Исполняющая подсистема Windows для данного файла.
46 2 WORD DllCharacteristics Дополнительные атрибуты файла.
48 4/8 DWORD/ULONGLONG SizeOfStackReserve Размер стека стартового потока программы в байтах виртуальной памяти. При загрузке в физическую память отображается только SizeOfStackCommit байт, в дальнейшем отображается по одной странице виртуальной памяти. По умолчанию равен 1 Мб.
4C/50 4/8 DWORD/ULONGLONG SizeOfStackCommit Начальный размер стека программы в байтах. По умолчанию равен 4 Кб.
50/58 4/8 DWORD/ULONGLONG SizeOfHeapReserve Размер кучи программы в байтах. При загрузке в физическую память отображается только SizeOfHeapCommit байт, в дальнейшем отображается по одной странице виртуальной памяти. По умолчанию равен 1 Мб. Во всех 32-разрядных версиях Windows куча ограничена только размером виртуальной памяти и это поле, по-видимому, игнорируется.
54/60 4/8 DWORD/ULONGLONG SizeOfHeapCommit Начальный размер кучи программы в байтах. По умолчанию равен 4 Кб.
58/68 4 DWORD LoaderFlags Устаревшее поле, не используется.
5C/6C 4 DWORD NumberOfRvaAndSizes Количество описателей каталогов данных. На текущий момент всегда равно 16.
60/70 128 IMAGE_DATA_DIRECTORY[16] DataDirectory Описатели каталогов данных.

Поясним некоторые поля этой структуры.

Поле Magic

16-битовое поле, содержащее сигнатуру заголовка. Может принимать значения:

Название Значение Описание
IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x0107 Заголовок прошивки в ПЗУ ?
IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x010B Заголовок PE32.
IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x020B Заголовок PE32+.

Поля MajorSubsystemVersion и MinorSubsystemVersion

16-битовые числа, указывающее ожидаемую версию Windows. В отличие от всех остальных полей с номерами версий это поле анализировалось при загрузке программ в NT 4.0 и 95. Если программа была графическим приложением и это поле не содержало версии 4.0, то считалось, что программа разработана для NT 3.51 и моделировалось поведение этой ОС (в частности, отсутствие трехмерных стилей диалогов и пр.). В настоящее время не используется и практически всегда равно 4.0.

Поле CheckSum

32-битовая контрольная сумма файла. Проверяется только в Windows NT при загрузке драйверов ядра и нескольких системных DLL. Алгоритм вычисления контрольной суммы недокументирован, но для ее вычисления можно использовать системную функцию CheckSumMappedFile из библиотеки IMAGEHLP.DLL.

Поле Subsystem

16-битовое число, указывающее в какой подсистеме Windows API должен исполняться данный файл. Его возможные значения:

Название Значение Подсистема
IMAGE_SUBSYSTEM_UNKNOWN 0 Неизвестная подсистема.
IMAGE_SUBSYSTEM_NATIVE 1 Подсистема не требуется, используется драйверами и "родными" приложениями NT.
IMAGE_SUBSYSTEM_WINDOWS_GUI 2 Графическая подсистема Windows.
IMAGE_SUBSYSTEM_WINDOWS_CUI 3 Консольная подсистема Windows.
IMAGE_SUBSYSTEM_OS2_CUI 5 Консольная подсистема OS/2.
IMAGE_SUBSYSTEM_POSIX_CUI 7 Консольная подсистема POSIX.
IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 "Родной" драйвер Win 9x.
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 Графическая подсистема Windows CE.
IMAGE_SUBSYSTEM_EFI_APPLICATION 10 Программа EFI.
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 Драйвер EFI, обеспечивающий загрузочный сервис.
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 Драйвер EFI времени выполнения.
IMAGE_SUBSYSTEM_EFI_ROM 13 Прошивка ПЗУ для EFI.
IMAGE_SUBSYSTEM_XBOX 14 Подсистема Xbox.

Поле DLLCharacteristics

16-битовое поле флагов, задающие дополнительные атрибуты файла. Возможные значения флагов:

Название Значение Описание
IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 Файл не использует структурную обработку исключений.
IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 Запрещает статическое связывание этого файла.
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 Файл является WDM-драйвером.
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 Если этот флаг не установлен, то при загрузке программы терминальным сервером будет загружена вспомогательная DLL, обеспечивающая совместимость кода.

Поля NumberOfRvaAndSizes и DataDirectory

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

struct IMAGE_DATA_DIRECTORY {
  DWORD VirtualAddress;
  DWORD Size;
};

В настоящее время поле NumberOfRvaAndSizes всегда содержит число 16 (символическое имя IMAGE_NUMBEROF_DIRECTORY_ENTRIES). Соответственно массив DataDirectory состоит также из 16 описателей. Каждый описатель содержит RVA и размер для одного каталога данных. Если какого-либо каталога в файле нет, то оба поля его описателя равны нулю.

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

© 2005 Юрий Лукач