Исследуем формат EXE-файла

Целью работы компилятора является получение EXE-файла. Поэтому, исследуем его структуру.

EXE-файлы появились ещё в DOS и потом они с небольшими изменениями перекочевали в Windows. Формат EXE-файла под Windows называется PE-файлом. Он организован в виде линейного потока данных.

Формат PE-файла
Заголовок MS-DOS
Программа-заглушка
Заголовок PE
Доп. заголовок PE
Массив DataDir
Заголовки сегментов
Тела сегментов
Остальные области данных

Заголовок MS-DOS не нов, он используется начиная с MS-DOS версии 2. Если вы пытаетесь запустить Windows-программу под DOS-ом, то программа-заглушка, которая размещена ниже, сообщит о невозможности этого сделать. Если бы заголовок MS-DOS и программа-заглушка не были бы включены в PE-файл, то скорее всего бы это бы привело к сбою.

Залоговок MS-DOS (размер 40H байт)
АдресТипИмяОписание
00hwordMagicМагическая сигнатура DOS-файла - два символа "MZ", явно от MZ-club :)
02hwordLastByteCountКоличество байт на последней странице файла
04hwordPageCountКоличество страниц в файле
06hwordRelocCountКоличество релокейшенов
08hwordHeaderSizeРазмер заголовка в параграфах
0AhwordMinAllocМин. выделение памяти в параграфах
0ChwordMaxAllocМакс. выделение памяти в параграфах
0EhwordInitSSНачальное (относительное) значение регистра SS
10hwordInitSPНачальное значение регистра SP
12hwordCheckSumКонтрольная сумма
14hwordInitIPНачальное значение регистра IP
16hwordInitCSНачальное (относительное) значение регистра CS
18hwordRelocAddrАдрес на релокейшены и программу-заглушку
1AhwordOverlayCountКоличество оверлеев
1ChwordRes1[4]Зарезервировано
24hwordOEMIdentifierДля OEMInfo
26hwordOEMInfoИнформация о программе
28hwordRes1[10]Зарезервировано
3ChdwordPEHeaderAddrАдрес в файле заголовка PE

Для Windows-программы заголовок MS-DOS не содержит релокейшины (пока даже и не знаю что это такое), то есть Relocations = 0, поэтому RelocAddr указывает сразу на программу-заглушку. Но нам важен заголовок PE, его адрес находиться в PEHeaderAddr.

Залоговок PE (размер 18H байт)
АдресТипИмяОписание
00hdwordMagicМагическая сигнатура PE-файла 4550H или "PE", 0H, 0H
04hwordCPUTypeТип процессора
06hwordSectionCountКоличество сегментов
08hdwordDateTimeДата/время создания/модификации линкером
0СhdwordSymbolTableAddrАдрес местонахождения таблицы символов
10hdwordSymbolTableSizeРазмер таблицы символов
14hwordOptionalHeaderSizeРазмер доп. заголовка PE
16hwordFlagsПредназначение программы

Сразу за основным заголовком идёт дополнительный заголовок PE.

Доп. залоговок PE (размер 18H - 77H байт)
АдресТипИмяОписание
18hwordMagicВсегда 10Bh
1AhbyteMajorLinkVerВерсия линкера, создавшего данный файл
1BhbyteMinorLinkVer-
1ChdwordCodeSizeРазмер исполнительного кода
20hdwordInitDataSizeРазмер инициализированных данных
24hdwordUnInitDataSizeРазмер неинициализированных данных
28hdwordEntryPointAddrАдрес, относительно ImageBase, по которому передаётся управление при запуске программы или адрес инициализации/завершения библиотеки
2ChdwordCodeBaseОтносительное смещение сегмента кода
30dwordDataBaseОтносительное смещение сегмента неинициализированных данных
34hdwordImageBaseПредподчтительный адрес для загрузки исполнимого файла (по умолчанию 400000H)
38hdwordSectionAlignВыравнивание программных секций (по умолчанию 1000H)
3ChdwordFileAlignМинимальная гранулярность сегментов, то есть размер сегментов должен быть кратен FileAlign, должен быть равен значению степени 2 между 200H и 10000H (по умолчанию 200H)
40hwordMajorOSVerСтарший номер версии OS, необходимый для запуска программы
42hwordMinorOSVerМладший номер версии OS
44hwordMajorImageVerПользовательский старший номер версии, задается пользователем при линковке программы и им же и используется
46hwordMinorImageVerПользовательский младший номер версии, задается пользователем при линковке программы и им же и используется
48hwordMajorSubSysVerСтарший номер версии Win32
4AhwordMinorSubSysVerМладший номер версии Win32
4ChdwordRes1-
50hdwordImageSizeВиртуальный размер в байтах всего загружаемого образа, вместе с заголовками, кратен ObjectAlign
54hdwordHeaderSizeОбщий размер всех заголовков: MS-DOS, PE, доп PE и всех сегментов
58hdwordCheckSumКонтрольная сумма (не используется и равна 0)
5ChwordSubSystemПодсистема, необходимая для запуска данного файла //(0 - неизвестная подсистема, 1 - не требует подсистему, 2 - Windows GUI, 3 - Windows консоль...)
5EhwordDllFlagsСпециальные флаги при загрузке, начиная с NT 3.5 не используются
60hdwordStackReserveSizeПамять, требуемая для стека приложения, память резервируется, но выделяется только StackCommitSize байтов, следующая страница является охранной. Когда приложение достигает этой страницы, то страница становится доступной, а следующая страница - охранной, и так до достижения нижней границы, после чего Windows убивает программу с сообщением о конце стека
64hdwordStackCommitSizeОбъем памяти, отводимый в стеке немедленно после загрузки
68hdwordHeapReserveSizeМаксимальный возможный размер локального хипа
6ChdwordHeapComitSizeОтводимый при загрузке хип
70hdwordLoaderFlagsДанный параметр устарел
74hdwordDataDirSizeУказывает размер массива DataDir, расположенный ниже (по умолчанию 10h)

Далее идёт массив DataDir, 8-байтные элементы которого состоят из двух 4-х байтных: адрес и размер.

Массив DataDir (размер 78H - F8H байт)
АдресТипИмяОписание
78hqwordExportDirКаталог экспортируемых объектов
80hqwordImportDirКаталог импортируемых объектов
88hqwordResourceDirКаталог ресурсов
90hqwordExceptionDirКаталог исключений
98hqwordSecurityDirКаталог безопастности
A0hqwordBaseRelocDirКаталог переадресаций
A8hqwordDebugDirОтладочный каталог
B0hqwordCopyrightDirКаталог описаний
B8hqwordCpuSpecDirКаталог значений, специфичных для процессора
C0hqwordTLSDirКаталог TLS (Thread local storage - локальная память потоков)
C8hqwordConfigDirКаталог конфигураций загрузки
D0hqwordResDir11-
D8hqwordResDir12-
E0hqwordResDir13-
E8hqwordResDir14-
F0hqwordResDir15-

Элемент массива DataDir (размер 8 байт)
АдресТипИмяОписание
00hdwordAddrАдрес каталога
04hdwordSizeРазмер каталога

Далее идёт подряд несколько сегментов, количество указано в SectionCount.

Заголовок сегмента (размер 2Ch байт)
АдресТипИмяОписание
00hchar[8]SectNameИмя секции, если имя < 8, то остаток заполнен нулями
08hdwordVirtualSizeВиртуальный размер секции, именно столько памяти будет отведено под секцию. Если VirtualSize превышает PhysicalSize, то разница заполняется нулями, так определяются секции неинициализированных данных (Physical Size = 0)
0ChdwordVirtualAddrВиртуальный адрес секции относительно ImageBase
10hdwordPhysicalSizeРазмер секции (её инициализированной части) в файле, кратно полю FileAlign, должно быть <= VirtualSize
14hdwordPhysicalAddrФизическое смещение тела сегмента относительно начала EXE файла
18h0ChRes1Зарезервировано для OBJ файла, в экзешниках смысла не имеет
28hdwordSectFlagsБитовые флаги секции

Практически любая программа под Windows работает с такими её DLL-ками: kernel32.dll, user32.dll, gdi32.dll и т.д.. Поэтому, EXE-шник должен уметь импортировать функции данных библиотек, то есть работать с каталогом импорта ImportDir. Каталог импорта сразу же начинается с таблицы импорта ImportDirTable, которая описывает остальную информацию об импорте. Такая таблица состоит из элементов ImportDirTableItem, указывающих, как минимум, на каждую импортируемую библиотеку. Последний элемент, указывающий на конец таблицы, заполнен нулями.

Элемент таблицы каталога импортируемых объектов ImportDirTableItem (размер 14h байт)
АдресТипИмяОписание
00hdwordFuncNameListСписок имён импортируемых функций
04hdwordRes1-
08hdwordRes2-
0ChdwordLibNameИмя библиотеки
10hdwordFuncAddrListСписок адресов импортируемых функций

Параметр LibName указывает на имя библиотеки, которое должно заканчиваться нулём. FuncNameList указывает на список адресов (0-ой адрес - конец списка), по которым находится сначала Hint - (укороченный идентификатор точки входа), а затем имя функции, заканчивающееся нулём. Параметр FuncAddrList указывает на точно такой же список адресов, находящийся (по моим наблюдениям) перед ImportDirTable.

Формат EXE-файла здесь описан не полностью. Остальное будет описано позже. Однако, этого уже достаточно для создания компилятора.

Для глубокого изучения EXE-шника, написана специальная программа "EXE-исследователь". Последнюю версию данной программы можно скачать на страничке Download

Hosted by uCoz