┌──┌─┐┌──
──┘├─┘──┘ Presents
┐ ┌┐┐┌─┤ VMag, Issue 3, 1 January 1999
└─┘┘ ┘└─┘ ─────────────────────────────
╔════════════════════════════╗
║ Version: 1.1.0 ║
║ Release: 30-Oct-1998y ║
║ Created by: Hard Wisdom ║
╚════════════════════════════╝
─[27-Dec-1997y]─[v1.0.0]───────────────────────────────────────────────────────
Первоначально написанный файл
─[30-Oct-1998y]─[v1.1.0]───────────────────────────────────────────────────────
Внесены накопленные изменения, + корректировки от Shadow Dragon
───────────────────────────────────────────────────────────────────────────────
ФОРМАТ ИСПОЛНЯЕМЫХ ФАЙЛОВ PortableExecutables (PE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(даже не руководство системного программиста)
[] Содержание Стр
~~~~~~~~~~~~~ ~~~
[0] Вступление.............................................................___
[1] Обзор..................................................................___
[2] Заголовок PE файла (PE Header).........................................___
[3] Таблица объектов (секций) файла (Object Table).........................___
[4] Страницы образов секций (Image Pages)..................................___
[5] Экспорт................................................................___
[5.1] Таблица экспорта (Export Directory Table)...........................___
[5.2] Таблица адресов экспорта (Address Table)............................___
[5.3] Таблица указателей на имена (Name Table Pointers)...................___
[5.4] Таблица ординалов (Ordinal Table)...................................___
[5.5] Таблица имен экспорта (Export Name Table)...........................___
[6] Импорт.................................................................___
[6.1] Каталог импорта (Import Directory Table)............................___
[6.2] Таблица просмотра импорта (Import LookUp Table).....................___
[6.3] Таблица адресов импорта (Import Address Table)......................___
[7] Локальная область данных цепочек (Thread Local Storage)................___
[7.1] Таблица разделов цепочек (TLS Directory table)......................___
[7.2] Таблица обратных вызовов цепочки (TLS CallBack Table)...............___
[8] Ресурсы................................................................___
[8.1] Каталог ресурсов (Resource Directory Table).........................___
[8.2] Пример структуры размещения ресурсов................................___
[9] Таблица настроек адресов (FixUp Table).................................___
[9.1] Блок настроек перемещений (FixUp Block).............................___
[10] Отладочная информация (Debug Information).............................___
[10.1] Отладочный каталог (Debug Directory)..............................___
[11] Вопросы не рассмотренные в данном описании............................___
[12] P.S...................................................................___
[0] Вступление
~~~~~~~~~~~~~~
Итак, в этом документе излагается формат PE файла с комментариями. Что
побудило меня к такой работе? Отчасти пинания Shadow Dragon'a, а отчасти одна
законченная шутка для Windows'95. В основе лежит официальное описание фирмы
Микрософт (весьма и весьма глюкавое), книга Мэтта Питрека "Секреты системного
программирования в Windows'95" (ну очень тяжела для прочтения, ну очень много
воды. . .), книга Эндрю Шульмана "Неофициальная Windows'95" (тоже очень
мокрая), книга Джеффри Рихтера "Программирование в Win32 API для Windows NT
3.5 и Windows'95", а так же готовые экзешники Windows'95. Общие соглашения в
написанном тексте: все именования полей и ключевых структур - *Английские* и
только, остальной текст может быть произвольным ;-), тект изложения
перемежается с вольными комментариями и лирическими отступлениями. Знаком "?"
отмечены места, назначение которых не ясно, либо вызывает сомнения.
[1] Обзор
~~~~~~~~~
PE формат файлов существует очень давно, Windows 3.11 при установленном
Win32s может запускать такие файлы (более того, это заложено в архитектуру
системы). Но только недавно он получил довольно широкое распространение, а
именно с расширением использования Windows'95. Формат файла чрезвычайно прост,
но тем не менее в нем есть очень много спотычек, через которые падают
некоторые программы, да и сами Windows'95 (как программа). Следует отметить,
что формат файла слизан с аналогичного в юних системах, кроме того,
мелкомягкие опять поехали на изменении форматов и теперь вы можете так же
лицезреть COFF формат OBJ файлов, который весьма похож на PE (собственно
говоря это почти одно и то же ;-). Добавлю, что с новым форматом объектных
модулей работает только Microsoft (вполне естественно, учитывая, что они его
предложили), но найти данный компилятор ассемблера мне не удалось, впрочем как
и линковщик. Borland по прежнему работает со старым форматом OBJ файлов, а
именно - Intel OMF (справедливо для TASM v5.0 старше я не нашел). Ну а теперь о
самом файловом формате.
Микрософт решила оставить у запускаемых файлов расширение EXE, а тобы не
было проблем начальным заголовком сделать запускаемый файл MS-DOS. У них это
получилось, вот краткая схема названного:
Summary File Structure
~~~~~~~~~~~~~~~~~~~~~~
╒═════════════════════════════════════════════════════════════════════════════╕
│ У таблицы нет определенного формата и заголовков │
╞═════╤══════════════╤════════════════════════════════════════════════════════╡
│ 00h │ DOS 2 Header │ Совместимый заголовок (форматированная часть), │
│ │ │ будем считать, что его формат всем известен │
├─────┼──────────────┴────────────────────────────────────────────────────────┤
│ 1Ch │ 4 байта, выравнивающие форматированную │
│ │ область заголовка с 1Ch до 20h, никто не мешает │
│ │ им там не присутствовать ;-) но у Микрософта они описаны │
│ │ Это позволяет заголовку файла иметь красивый размер в 2 параграфа... │
├─────┼────────────────┬──────────────────────────────────────────────────────┤
│ 20h │ OEM Identifier │ Другими словами, информация о программе, │
│ │ OEM Info │ практически никогда не присутствует, однако │
│ │ │ место должно быть зарезервировано. │
│ │ │ Я встречал файлы с заполненным полем, наверное их │
│ │ │ делали люди буквально соблюдающие требования доку- │
│ │ │ ментации от Microsoft │
├─────┼──────────────┬─┴──────────────────────────────────────────────────────┤
│ 3Ch │ Offset to PE │ Смещение реального PE заголовка в файле, │
│ │ Header │ DWord, присутствует именно здесь, ? заголовок выравни- │
│ │ │ вается на 8 байтовую границу относительно начала файла.│
├─────┼──────────────┴────────────────────────────────────────────────────────┤
│ min │ релокейшены программы-заглушки, у стандартного STUB'а их нет │
│ 40h │ На это поле указывает ReloOfs заголовка DOS 2 Header, соответственно │
│ │ его значение должно быть >=40h иначе такой файл как кандидат в PE │
│ │ рассматриваться вообще не будет. ;-) Но на самом деле загрузчику без- │
│ │ различно фактическое их положение. │
├─────┼───────────────────────────────────────────────────────────────────────┤
│ min │ собственно говоря, тело DOS программы, иначе говоря STUB'a │
│ 40h │ чаще всего говорит о невозможности запуска, но может содержать │
│ + │ в себе очень разрушительные вещи, как то поиск в PATH и запуск │
│ XXh │ файла WIN.COM с указанием имени данного файла, причем без │
│ │ предупреждений, что конечно-же весьма неприятно. 40h есть │
│ │ нижняя граница данного поля, может, собственно говоря, │
│ │ находиться сколь угодно выше, зависит от размера заголовка │
├─────┼───────────┬───────────────────────────────────────────────────────────┤
│ XXh │ PE Header │ Туточки находится заголовок PE файла и, другими словами, │
│ │ │ начинается сама 32-битная программа, по идее он должен │
│ │ │ быть выровнен на 8-байтовую границу, пусть так и будет │
├─────┼───────────┴──┬────────────────────────────────────────────────────────┤
│ XXh │ Object Table │ табличка описаний секций файла, подробнее далее │
├─────┼──────────────┴───┬────────────────────────────────────────────────────┤
│ XXh │ Image Pages │ Остальная часть запускаемого файла. . . │
│ │ import info │ │
│ │ export info │ │
│ │ fixup info │ │
│ │ resource info │ │
│ │ debug info │ │
│ │ etc. . . │ │
└─────┴──────────────────┴────────────────────────────────────────────────────┘
Для проверки на возможность файла быть в формате PE необходимо, чтобы он
был во первых EXE (байты по смещению 0h равны 5A4Dh), во вторых, слово по
смещению 18h должно быть >=40h, тогда и только тогда DWord поле по смещению
3Ch имеет смысл. Загрузчик маздая файлы с заголовком 'ZM' - 4D5Ah не считает
валидными 32-битовыми программами. При запуске из дос-окна выполняется ДОС
часть программы, а при запуске с помощью CreateProcessA (консольная утилита
START.EXE вызывает данный сервис) выдается сообщение о невозможности запуска
программы, т.к. это не валидное 32-битовое приложение. Так же интересен
следующий момент: возможен запуск файлов с расширением COM и структурой PE, но
невозможен запуск файлов с расширением COM и структурой NE. Загрузчик
32-битового приложения проверяет на принадлежность файла в разряду 16-битовых
приложений и передает управление в 16-битовый Kernel, в свою очередь тот
отбраковывает переданное ему приложение и выполняет STUB.
Так же следует заметить, что фактическое значение поля ReloOffs заголовка
DOS 2 Header для загрузчика PE файлов безразлично (он его не проверяет),
соответственно это можно использовать в своих целях. (Зато HIEW это проверяет
и отказывается работать с такими файлами, чтение документаций - вредная штука).
[2] Заголовок PE файла (PE Header)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Заметки:
o VA есть виртуальный адрес, который уже базирован на смещение Image Base,
прочитанное из PE Header'а. RVA есть относительный адрес ссылающийся на
Image Base.
o RVA в PE Header имеющий нулевое значение указывает на то, что соответ-
ствующее поле не используется (что многие не проверяют IMHO).
o Все используемые страницы (Image pages) выравнены дополнением нулями до
границы File Align (опять же, выравнивать можно чем угодно, и не только
нулями :-). Базы всех прочих таблиц и структур должны быть выравнены на
DWord (4 байта) грвницу. Таким образом, все VA и RVA должны находиться
на 32-битной границе [имеется ввиду на границе размещения двойных слов,
начиная от 0 в файле, так что читайте смело DWord'ами ;-) в жизни не
все так просто - прим. мое]. Все таблицы и структурные поля должны быть
выравнены на их "родную" границу, за возможным исключением Debug Info.
А теперь поехали. . .
PE Header
~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ DWord │ Signature Bytes │ No │ Сигнатурка того, что этот файл │
│ │ │ │ │ собственно говоря является PE │
│ │ │ │ │ должна быть 4550h, иначе - │
│ │ │ │ │ 'PE',0h,0h; два последних бай- │
│ │ │ │ │ та под что-то-там Микрософт │
│ │ │ │ │ зарезервировала (и проверяет │
│ │ │ │ │ их равенству на 0!). │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 04h │ Word │ CPU Type │ Yes │ это поле указывает на предпоч- │
│ │ │ │ │ тительный ;-) тип процессора, │
│ │ │ │ │ на котором желательно запус- │
│ │ │ │ │ кать данную программу, вы ред- │
│ │ │ │ │ ко увидите что-либо отличное │
│ │ │ │ │ от 14Ch -> i386 │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 06h │ Word │ Num of Objects │ No │ это поле указывает на число │
│ │ │ │ │ реальных входов в Object Table │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 08h │ DWord │ Time/Date Stamp │ No │ используется для хранения даты │
│ │ │ │ │ и времени создания/модификации │
│ │ │ │ │ линкером │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 0Ch │ DWord │ Pointer to COFF table │ No │ дополнительный указатель опре- │
│ │ │ │ │ деляющий местонахождение отла- │
│ │ │ │ │ дочной COFF таблицы в файлах, │
│ │ │ │ │ отладочную информацию лучше │
│ │ │ │ │ всего искать по другому │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 10h │ DWord │ COFF table size │ No │ кол-во символов в COFF таблице │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 14h │ Word │ NT Header Size │ No │ размер заголовка PE файла на- │
│ │ │ │ │ чиная с поля Magic, название │
│ │ │ │ │ взято у программы Hiew, таким │
│ │ │ │ │ образом, общий размер заголовка │
│ │ │ │ │ PE файла составляет NT Header │
│ │ │ │ │ Size + 18h │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 16h │ Word │ Flags │ Yes │ указывает на предназначение │
│ │ │ │ │ программы, конкретное значение │
│ │ │ │ │ флагов см.ниже │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 18h │ Word │ Magic │ Yes │ поле указывает на основное │
│ │ │ │ │ предназначение программы │
│ │ │ │ │ абсолютно всем наплевать в него │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 1Ah │ Byte │ Link Major │ No │ старший номер версии использо- │
│ │ │ │ │ вавшегося при создании линкера │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 1Bh │ Byte │ Link Minor │ No │ младший номер версии использо- │
│ │ │ │ │ вавшегося при создании линкера │
│ │ │ │ │ (эти 2 поля загрузчик пока │
│ │ │ │ │ игнорирует) │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 1Ch │ DWord │ Size of Code │ No │ размер именно программного ко- │
│ │ │ │ │ да в файле, KERNEL использует │
│ │ │ │ │ это значение для фактического │
│ │ │ │ │ отведения памяти под загружае- │
│ │ │ │ │ мую программу, установка этого │
│ │ │ │ │ значения слишком маленьким │
│ │ │ │ │ приведет к выдаче идиотского │
│ │ │ │ │ сообщения о нехватке памяти, │
│ │ │ │ │ хотя ее может быть валом │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 20h │ DWord │ Size of Init Data │ No │ размер секции инициализирован- │
│ │ │ │ │ ных данных, очевидно не исполь- │
│ │ │ │ │ зуется в Windows'95 но исполь- │
│ │ │ │ │ зуется в NT, назначение анало- │
│ │ │ │ │ гично приведенному выше │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 24h │ DWord │ Size of UnInit Data │ No │ размер секции неинициализиро- │
│ │ │ │ │ ванных данных, сложно сказать, │
│ │ │ │ │ как эти 3 поля корреспондируют │
│ │ │ │ │ между собой, но лучше с ними │
│ │ │ │ │ по честному ;-) явно видно, что │
│ │ │ │ │ формат разрабатывали одни, а │
│ │ │ │ │ реализовывали его другие. Ре- │
│ │ │ │ │ комендую изучить регионы па- │
│ │ │ │ │ мяти и VirtualXXX функции │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 28h │ DWord │ Entry point RVA │ No │ адрес, относительно Image Base │
│ │ │ │ │ по которому передается управ- │
│ │ │ │ │ ление при запуске программы │
│ │ │ │ │ или адрес инициализации/завер- │
│ │ │ │ │ шения библиотеки │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 2Ch │ DWord │ Base of Code │ No │ RVA секции, которая содержит │
│ │ │ │ │ программный код (как будто бы │
│ │ │ │ │ она одна единственная ;-) ) │
│ │ │ │ │ судя по всему никем не исполь- │
│ │ │ │ │ зуется (но установлено верно) │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 30h │ DWord │ Base of Data │ No │ RVA секции содержащей якобы │
│ │ │ │ │ данные, в реальных экзешниках │
│ │ │ │ │ указывает и на .data и на .bss │
│ │ │ │ │ и еще бог знает куда, вряд ли │
│ │ │ │ │ кем-нибудь используется │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 34h │ DWord │ Image Base │ No │ виртуальный начальный адрес │
│ │ │ │ │ загрузки программы (ее первого │
│ │ │ │ │ байта). Должен быть на границе │
│ │ │ │ │ 64 Кб (связано с системой па- │
│ │ │ │ │ мяти Windows'95) │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 38h │ DWord │ Object Align │ No │ выравнивание программных сек- │
│ │ │ │ │ ций, должен быть степенью 2 │
│ │ │ │ │ между 512 и 256М включительно, │
│ │ │ │ │ так же связано с системой па- │
│ │ │ │ │ мяти. При использовании других │
│ │ │ │ │ значений программа не загру- │
│ │ │ │ │ зится. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 3Ch │ DWord │ File Align │ No │ фактор используемый для вырав- │
│ │ │ │ │ нивания секций в программном │
│ │ │ │ │ файле. В байтовом значении │
│ │ │ │ │ указывает на границу на кото- │
│ │ │ │ │ рую секции дополняются 0 при │
│ │ │ │ │ размещении в файле. Большое │
│ │ │ │ │ значение приводит к нерациона- │
│ │ │ │ │ льному использованию дискового │
│ │ │ │ │ пространства, маленькое увели- │
│ │ │ │ │ чивает компактность, но и сни- │
│ │ │ │ │ жает скорость загрузки. Должен │
│ │ │ │ │ быть степенью 2 в диапазоне от │
│ │ │ │ │ 512 до 64К включительно. Про- │
│ │ │ │ │ чие значения вызовут ошибку │
│ │ │ │ │ загрузки файла. Я так думаю, │
│ │ │ │ │ что размер файла штука более │
│ │ │ │ │ важная. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 40h │ Word │ OS Major │ No │ старший номер версии операци- │
│ │ │ │ │ онки необходимый для запуска │
│ │ │ │ │ программы. (нулевое значение │
│ │ │ │ │ не позволяет запустить прог- │
│ │ │ │ │ рамму, остальные игнорируются │
│ │ │ │ │ проверялось на OSR2) │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 42h │ Word │ OS Minor │ No │ младший номер версии операц. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 44h │ Word │ USER Major │ No │ пользовательский номер версии, │
│ │ │ │ │ задается пользователем при │
│ │ │ │ │ линковке программы и им же и │
│ │ │ │ │ используется │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 46h │ Word │ USER Minor │ No │ аналогично, младший номер │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 48h │ Word │ SubSys Major │ No │ старший номер версии подсисте- │
│ │ │ │ │ мы, черт его знает как он │
│ │ │ │ │ использается, по моему всяких │
│ │ │ │ │ версий уже через край │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 4Ah │ Word │ SubSys Minor │ No │ аналогично, младший номер │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 4Ch │ DWord │ Reserved │ No │ судя по всему так оно и есть │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 50h │ DWord │ Image Size │ No │ виртуальный размер в байтах │
│ │ │ │ │ всего загружаемого образа, │
│ │ │ │ │ вместе с заголовками, кратен │
│ │ │ │ │ Object Align │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 54h │ DWord │ Header Size │ No │ общий размер всех заголовков: │
│ │ │ │ │ DOS Stub + PE Header + │
│ │ │ │ │ + Object Table │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 58h │ DWord │ File CheckSum │ No │ контрольная сумма всего файла, │
│ │ │ │ │ опять же как и в DOS'е ее ник- │
│ │ │ │ │ то не контролирует, а линкер │
│ │ │ │ │ ее ставит в 0 при линковке │
│ │ │ │ │ Предполагалось ее рассчитывать │
│ │ │ │ │ как инверсию суммы всех байтов │
│ │ │ │ │ файла. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 5Ch │ Word │ SubSytem │ Yes │ операционная подсистема необ- │
│ │ │ │ │ ходимая для запуска данного │
│ │ │ │ │ файла (GUI, консоль...) │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 5Eh │ Word │ DLL Flags │ Yes │ указывает на специальные пот- │
│ │ │ │ │ ребности при загрузке, начиная │
│ │ │ │ │ с NT 3.5 устарел и не использ. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 60h │ DWord │ Stack Reserve Size │ No │ память требуемая для стека │
│ │ │ │ │ приложения, память резервиру- │
│ │ │ │ │ ется, но выделяется только │
│ │ │ │ │ Stack Commit Size байтов, сле- │
│ │ │ │ │ дующая страница является охра- │
│ │ │ │ │ нной. Когда приложение дости- │
│ │ │ │ │ гает этой страницы, то стра- │
│ │ │ │ │ ница становится доступной, а │
│ │ │ │ │ следующая страница - охранной, │
│ │ │ │ │ и так до достижения нижней │
│ │ │ │ │ границы, после чего Windows'95 │
│ │ │ │ │ убивает программу с воплями об │
│ │ │ │ │ исключении у нее в стеке │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 64h │ DWord │ Stack Commit Size │ No │ объем памяти отводимой в стеке │
│ │ │ │ │ немедленно после загрузки │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 68h │ DWord │ Heap Reserve Size │ No │ максимальный возможный размер │
│ │ │ │ │ локального хипа │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 6Ch │ DWord │ Heap Comit Size │ No │ отводимый при загрузке хип │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 70h │ DWord │ Loader Flags │ No │ ? начиная с NT 3.5 объявлено │
│ │ │ │ │ неиспользуемым, назначение │
│ │ │ │ │ неясно, но в целом связано с │
│ │ │ │ │ поддержкой отладки │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 74h │ DWord │ Num of RVA and Sizes │ No │ указывает размер массива │
│ │ │ │ │ VA/Size который следует ниже, │
│ │ │ │ │ данная фича зарезервирована │
│ │ │ │ │ под будущие расширения формата │
│ │ │ │ │ В данный момент его значение │
│ │ │ │ │ всегда равно 10h │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 78h │ DWord │ Export Table RVA │ No │ RVA адрес таблицы экспорта │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 7Ch │ DWord │ Export Data Size │ No │ размер таблицы экспорта │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 80h │ DWord │ Import Table RVA │ No │ RVA адрес таблицы импорта │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 84h │ DWord │ Import Data Size │ No │ размер таблицы импорта │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 88h │ DWord │ Resource Table RVA │ No │ RVA адрес таблицы ресурсов │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 8Ch │ DWord │ Resource Data Size │ No │ размер таблицы ресурсов │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 90h │ DWord │ Exception Table RVA │ No │ RVA адрес таблицы исключений │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 94h │ DWord │ Exception Data Size │ No │ размер таблицы исключений │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 98h │ DWord │ Security Table RVA │ No │ ? адрес таблицы безопасности │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 9Ch │ DWord │ Security Data Size │ No │ ? размер таблицы безопасности │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ A0h │ DWord │ Fix Up's Table RVA │ No │ RVA адрес таблицы настроек │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ A4h │ DWord │ Fix Up's Data Size │ No │ размер таблицы настроек │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ A8h │ DWord │ Debug Table RVA │ No │ RVA адрес таблицы отладочной инф│
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ ACh │ DWord │ Debug Data Size │ No │ размер таблицы отладоч. инф. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ B0h │ DWord │ Image Description RVA │ No │ RVA адрес строки описани модуля │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ B4h │ DWord │ Description Data Size │ No │ размер строки описания модуля │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ B8h │ DWord │ Machine Specific RVA │ No │ ? адрес таблицы значений специ- │
│ │ │ │ │ фичных для микропроцессора │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ BCh │ DWord │ Machnine Data Size │ No │ ? размер таблицы значений специ-│
│ │ │ │ │ фичных для микропроцессора │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ C0h │ DWord │ TLS RVA │ No │ указатель на локальную область │
│ │ │ │ │ данных цепочек │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ C4h │ DWord │ TLS Data Size │ No │ размер области данных цепочек │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ C8h │ DWord │ Load Config RVA │ No │ ? │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ CCh │ DWord │ Load Config Data Size │ No │ ? │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ D0h │ 08h │ Reserved │ No │ ? │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ D8h │ DWord │ IAT RVA │ No │ ? мною это поле обнаружено │
│ │ │ │ │ только в мультимедийных файлах │
│ │ │ │ │ системы Windows'95, это │
│ │ │ │ │ SNDREC32, CDPLAYER, MPLAYER │
│ │ │ │ │ оно указывает на таблицу │
│ │ │ │ │ адресов импорта в файле (поми- │
│ │ │ │ │ мо структуры импорта) писал │
│ │ │ │ │ эти программы один человек и │
│ │ │ │ │ чего он хотел. . . │
│ │ │ │ │ используется в NT, в Windows'95 │
│ │ │ │ │ судя по всему нет │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ DCh │ DWord │ IAT Data Size │ No │ ? размер описанного поля │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ E0h │ 08h │ Reserved │ No │ ? │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ E8h │ 08h │ Reserved │ No │ ? │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ F0h │ 08h │ Reserved │ No │ ? │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ F8h │ Общий размер заголовка │
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
CPU Type имеет следующие значения:
~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
o 0000h - а черт его знает
o 014Ch - i386
o 014Dh - i486
o 014Eh - i586
o 0162h - MIPS Mark I (R2000, R3000)
o 0163h - MIPS Mark II (R6000)
o 0166h - MIPS Mark III (R4000)
Flags имеет следующие битовые значения:
~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
o 0000h - это программа
o 0001h - файл не содержит перемещений и таблицы перемещаемых элементов
o 0002h - образ в файле можно запускать
Если этот бит не установлен, то это обычно указывает на ошибку обнару-
женную на этапе линковки, или же на то, что код был инкрементально
отлинкован и, следовательно, не может быть запущен. [инкрементальная
линковка - частичная линковка кода при изменении участка программы, а
не тотальная перекомпиляция проекта, что подразумевается здесь -
сказать очень трудно, скажем так - ОШИБКА!]
o 0200h - грузить фиксированно
Указывает на то, что программу можно грузить только по адресу записан-
ному в Image Base, если это невозможно, то такой файл лучше вообще не
запускать.
o 2000h - это библиотека
Magic имеет следующие значения:
~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
o 0107h - программа должна выполняться в ПЗУ (что за черт?)
o 010Bh - нормальная программа для ОЗУ
(на самом деле можно ставить любое значение, программа грузится нормально)
SubSystem имеет следующие значения:
~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
o 0000h - а черт его знает
o 0001h - Native, пошли вы все к такой-то матери, никто не нужен
o 0002h - Windows GUI, т.е. окошечная
o 0003h - Windows Character (консольное приложение)
o 0005h - OS/2 Character
o 0007h - Posix Character (формат PE с юниховского передран, вот и. . .)
DLL Flags имеет следующие битовые значения:
~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
o 0001h - инициализация библиотеки на процесс
o 0002h - завершение библиотеки на процесс
o 0004h - инициализация библиотеки на нить (цепочку)
o 0008h - завершение библиотеки на нить (цепочку)
Все прочие биты зарезервированы и желательно их установить в 0 значение, но
можно этого и не делать ;-)
[3] Таблица объектов (секций) файла (Object Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Число входов в таблице объектов (секций) определяется полем Num of Objects
заголовка PE Header. Входы в таблице объектов нумеруются начиная с 1. Сама
таблица располагается непосредственно за PE Header. Последовательность секций
кода и данных в памяти выбирается линкером. Виртуальные адреса объектам должны
быть присвоены линкером в возрастающем порядке и являются кратными Object
Align в заголовке PE Header. Стоит заметить, что текущая реализация загрузчика
Windows'95 не различает порядка объектов (секций) в таблице, поэтому можно
смело располагать их в произвольном порядке. Каждая секция (объект)
располагает именем, которое никого ни к чему не обязывает, имя может быть
произвольным, но вообще-то смысл содержания секции и ее наименования как
правило совпадают.
Object Entry
~~~~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ 08h │ Object Name │ Yes │ Имя объекта, остаток заполнен │
│ │ │ │ │ нулями, если имя объекта имеет │
│ │ │ │ │ длину 8 символов, то заключи- │
│ │ │ │ │ тельного 0 нет. Некоторые PE │
│ │ │ │ │ дамперы падают на этом факте. │
│ │ │ │ │ Имя - штука отфонарная и никого │
│ │ │ │ │ ни к чему не обязывает. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 08h │ DWord │ Virtual Size │ No │ виртуальный размер секции, │
│ │ │ │ │ именно столько памяти будет от- │
│ │ │ │ │ ведено под секцию. Если Virtual │
│ │ │ │ │ Size превышает Physical Size, │
│ │ │ │ │ то разница заполняется нулями, │
│ │ │ │ │ так определяются секции неини- │
│ │ │ │ │ циализированных данных (Physical│
│ │ │ │ │ Size = 0) │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 0Ch │ DWord │ Section RVA │ No │ размещение секции в памяти, │
│ │ │ │ │ виртуальный ее адрес относитель-│
│ │ │ │ │ но Image Base. Позиция каждой │
│ │ │ │ │ секции выравнена на границу │
│ │ │ │ │ Object Align (степень 2 от 512 │
│ │ │ │ │ до 256М включительно, по умол- │
│ │ │ │ │ чанию 64К) и секции упакованы │
│ │ │ │ │ впритык друг к другу, впрочем, │
│ │ │ │ │ можно это не соблюдать. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 10h │ DWord │ Physical Size │ No │ размер секции (ее инициализиро- │
│ │ │ │ │ ванной части) в файле, кратно │
│ │ │ │ │ полю File Align в заголовке PE │
│ │ │ │ │ Header, должно быть меньше или │
│ │ │ │ │ равно Virtual Size. Играя с │
│ │ │ │ │ этим полем можно добиться неко- │
│ │ │ │ │ торых результатов ;-) загрузчик │
│ │ │ │ │ по идее хлопает всю секцию в │
│ │ │ │ │ отведенное ОЗУ │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 14h │ DWord │ Physical Offset │ No │ физическое смещение относитель- │
│ │ │ │ │ но начала EXE файла, выровнено │
│ │ │ │ │ на границу File Align поля за- │
│ │ │ │ │ головка PE Header. Смещение │
│ │ │ │ │ используется загрузчиком как │
│ │ │ │ │ seek значение. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 18h │ 0Ch │ Reserved │ No │ зарезервировано для OBJ файла, │
│ │ │ │ │ в экзешниках смысла не имеет │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 28h │ DWord │ Object Flags │ Yes │ битовые флаги секции, см.ниже │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ 2Ch │ Общий размер описателя секции │
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
Object Name несколько примеров из жизни:
~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.text - сюда Микрософт бросает выполнимый код
CODE - а Борланд любит это делать здесь
.icode - переходники импорта старых версий TLINK32
.data - Микрософт швыряет данные сюда
DATA - а Борланд сюда
.bss - неинициализированные данные (равна 0 в файле)
.CRT - инициализированные данные C/C++ от Борланда
.rsrc - ресурсы
.idata - секция импорта
.edata - секция экспорта
.reloc - таблица настроек
.tls - данные на базе которых Windows запускает цепочки
.rdata - отладочная информация
_FREQASM - посмотрите в KERNEL32, я думаю, и так понятно
PROTECTED - это взято из Хасповского сервера, вот так
. . . . . - и так далее
Как можно заметить - имя секции значит некоторые вещи, но вообще-то не
определяет ничего ;-) Следует еще раз предупредить! Экзешник может содержать
одну единственную секцию, в которую можно натолкать все. И это будет работать!
Object Flags имеет следующие значения:
~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
o 00000004h - используется для кода с 16 битными смещениями
o 00000020h - секция кода
o 00000040h - секция инициализированных данных
o 00000080h - секция неинициализированных данных
o 00000200h - комментарии или любой другой тип информации
o 00000400h - оверлейная секция
o 00000800h - не будет являться частью образа программы
o 00001000h - общие данные
o 00500000h - выравнивание по умолчанию, если не указано иное
o 02000000h - может быть выгружен из памяти
o 04000000h - не кэшируется
o 08000000h - не подвергается страничному преобразованию
o 10000000h - разделяемый
o 20000000h - выполнимый
o 40000000h - можно читать
o 80000000h - можно писать
Все прочие значения зарезервированы и должны быть установлены в 0. Большинство
значений в Windows'95 не используется, наверняка не все используются даже в NT.
[4] Страницы образов секций (Image Pages)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Раздел образов содержит все инициализированные данные для всех объектов
(секций). Значения позиционирования для начала страницы каждого объекта
указаны в таблице объектов (Object Table) и выравнены на границу File Align в
заголовке PE Header. Объекты отсортированы в порядке их RVA и выравнены на
Object Align, это используется для оптимизации загрузки, но можно данное
правило не соблюдать, загрузчик Windows'95 это не использует.
Следует отметить одну важную особенность, если где-то указан RVA, то не
подразумевая его расположение надо просканировать таблицу секций для
определения его реального места в файле !!! Программы типа борландовской
TDUMP, PEDUMP (Мэтта Питрека) этого не делают и получают свое GPF пенальти при
попытке обработать такие файлы в которых RVA несколько отличается от
предполагаемых, тем не менее Windows'95 эти файлы прекрасно грузит и
обрабатывает. Это не ошибка! Я видел гораздо позднее файлы с подобной
структурой созданные компилятором Borland C++ (связано с импортом, это будет
обсуждено далее, но вовсе от импорта не зависит).
Говоря об импорте нужно указать на одну возможность, не рассмотренную
авторами Bizatch, впрочем об этом так-же ниже.
[5] Экспорт
~~~~~~~~~~~
обычно секция экспорта приблизительно выглядит следующим образом:
Typical Export Layout
~~~~~~~~~~~~~~~~~~~~~
╒══════════════════════════════════════╤══════════════════════════════════════╕
│ Таблица собственно экспорта │ Export Directory Table │
╞══════════════════════════════════════╪══════════════════════════════════════╡
│ Адресная таблица │ Export Address Table │
├──────────────────────────────────────┼──────────────────────────────────────┤
│ Таблица указателей на имена │ Export Name Table Pointers │
├──────────────────────────────────────┼──────────────────────────────────────┤
│ Таблица ординалов │ Export Ordinal Table │
├──────────────────────────────────────┼──────────────────────────────────────┤
│ Таблица самих имен │ Export Name Table │
└──────────────────────────────────────┴──────────────────────────────────────┘
я сказал обычно, но я не сказал, что так должно быть, чуть ниже мы сейчас
рассмотрим данные экспорта и механизм экспорта, сейчас замечу, что, как и с
именами секций, вышеописанную структуру вы найдете в нормальных исполнимых
файлах (без наворотов). За исключением Export Directory Table все остальное
структурами можно назвать лишь с натяжкой.
[5.1] Таблица экспорта (Export Directory Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Информация экспорта начинается с Export Directory Table, которая описывает
требуемую экспортную информацию. Export Directory Table содержит адресную
информацию используемую при развязке настраиваемых ссылок в внешние точки
входа внутри программы.
Export Directory Table
~~~~~~~~~~~~~~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ DWord │ Flags │ No │ зарезервировано на будущее =0 │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 04h │ DWord │ Time/Date Stamp │ No │ время и дата создания экспорт- │
│ │ │ │ │ ных данных │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 08h │ Word │ Major Version │ No │ опять для нас, блин, старший │
│ │ │ │ │ номер версии таблицы экспорта │
│ │ │ │ │ как хочешь, так и используй │
├─────┼───────┼───────────────────────┼─────┤ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ 0Ah │ Word │ Minor Version │ No │ аналогично, младший │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 0Ch │ DWord │ Name RVA │ No │ RVA строки указывающей на имя │
│ │ │ │ │ нашей библиотеки │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 10h │ DWord │ Ordinal Base │ No │ начальный номер экспорта, для │
│ │ │ │ │ функций нашей библиотеки, обычно│
│ │ │ │ │ установлено в 1, но не факт │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 14h │ DWord │ Num of Functions │ No │ количество функций экспортиру- │
│ │ │ │ │ емых нашим модулем, является │
│ │ │ │ │ числом элементов массива │
│ │ │ │ │ Address Table см.ниже │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 18h │ DWord │ Num of Name Pointers │ No │ число указателей на имена, обыч-│
│ │ │ │ │ но равно числу функций, но это │
│ │ │ │ │ не так если у нас есть функции │
│ │ │ │ │ экспортируемые только по номеру │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 1Ch │ DWord │ Address Table RVA │ Yes │ указатель на таблицу адресов │
│ │ │ │ │ (RVA) экспорта │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 20h │ DWord │ Name Pointers RVA │ Yes │ указатель на таблицу указателей │
│ │ │ │ │ на имена экспорта │
├─────┼───────┼───────────────────────┼─────┤ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ 24h │ DWord │ Ordinal Table RVA │ Yes │ указатель на таблицу ординалов │
│ │ │ │ │ экспорта, данный массив по │
│ │ │ │ │ индексам параллелен Name │
│ │ │ │ │ Pointers, элементами являются │
│ │ │ │ │ слова │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ 28h │ Общий размер таблички экспорта │
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
Для обработки запросов на связывание загрузчик системы ищет:
- импорт по имени: имя в массиве имен, по его индексу ординал, ординал
корректируется на базу и этим индексом вычитывается адрес функции
из поля массива Address Table
- импорт по ординалу: ординал корректируется на базу и далее, как описано выше
Надо отметить, что таблица экспорта может содержать пропуски, которые
отображаются нулевыми значениями адресов экспорта. Импортировать по ординалам
очень нежелательно ибо в разных версиях Windows'95 ординалы функций в модулях
различаются, не говоря уже об NT и проч.
[5.2] Таблица адресов экспорта (Address Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Данная структура данных содержит адреса экспортируемых функций (их точки
входа) экпортируемых данных и т.п. в формате DWord RVA (по 4 байта на
элемент). Для доступа к данным используется ординал функции с коррекцией на
базу ординалов (Ordinal Base).
[5.3] Таблица указателей на имена (Name Table Pointers)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Данная структура содержит указатели на имена экспортируемых функций,
указатели отсортированы в лексическом порядке для обеспечения возможности
бинарного поиска. Каждый указатель занимает 4 байта. Имена функций обычно
лежат в секции экспорта, но я опять сказал обычно ;-) Вы их можете помещать не
туда. Еще раз повторюсь, секции - нечто эфемерное, обеспечивающее упаковку
программы в файле и защиту участков кода, но не больше.
[5.4] Таблица ординалов (Ordinal Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Данная структура совместно с Name Table Pointers формирует 2 параллельных
массива, разделенных для облегчения к ним доступа индексированием на родные
для процессора данные (слова, двойные слова, но не сложные структуры). Данный
массив содержит ординалы экспорта, которые в общем случае являются индексами в
Address Table экспорта (за вычетом базы Ordinal Base). Элементами данного
массива являются слова (2 байта).
[5.5] Таблица имен экспорта (Export Name Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Эта таблица содержит необязательные (по мнению Microsoft, ничего себе ;-)
имена экспортируемых функций. Данный массив используется для совместно с Name
Table Pointers и Ordinal Table для обеспечения связывания загрузчиком
импорта/экспорта по имени. Механизм описывался выше. Каждый элемент являет
собой ASCIZ строку с именем экспортируемой функции. Никто не говорит, что они
должны в файле идти друг за другом последовательно, хотя так и построено
большинство файлов. Надо отметить, что имена экспорта чувствительны к регистру.
Отметим особенность загрузчика - при связывании, если адрес функции
находится в секции экспорта, то на самом деле по указанному адресу лежит
строка переадресующая к другой библиотеке экспортирующей данную функцию (с
указанием библиотеки и самой функции), это называется передача экспорта (если
верить Мэту Питреку, я данную фичу еще не проверял).
[6] Импорт
~~~~~~~~~~
Мы подходим к самому интересному разделу данного файла и любого PE
исполнимого файла. По сравнению со старыми 16 битными приложениями все
значительно упростилось - мы говорим системе о том, что мы хотим вызвать и
откуда, а система в нужное место нашей программы предоставляет адрес перехода
(внутри нашей FLAT памяти виртуальной машины). Все. Далее адрес уже используют
по разному. Borland строит самостоятельно в секции кода (точнее линкер)
переходники вида
SomeThunkGate: Jmp D,[0XXXXXXXXh]
и все ссылки в программе оформляются:
Call SomeThunkGate
При этом задача организации импорта возлагается на линковщик (напомним,
Borland использует старый OMF формат). Прародители метода пошли другим путем.
Переходники содержатся в библиотеке импорта и являются частью библиотеки.
Линкер просто компонует ее в программу, причем с помощью одной хитрой
особенности: имена секций содержащие в себе знак '$' могут объединяться с
отсечением оставшейся части имени (секции упорядочиваются перед слиянием по
оставшейся части имени). Линкеру остается лишь из чего-то вроде .idata$1
.idata$2 .idata$3 составить одну удобоваримую секцию .idata Следует еще
добавить, Microsoft в своих программах часто организует вызовы внешних функций
несколько иным образом: вместо Near вызова переходников используется
непосредственно требуемый адрес, примерно так
Call DWord Ptr [SomeServiceAddressVariable]
или так
Mov ESi,SomeServiceAddressValue
Call ESi
...
Call ESi
создавшие формат да создадут удобный компилятор ;-)
[6.1] Каталог импорта (Import Directory Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Информация импорта начинается с Import Directory Table, которая описывает
остальную информацию об импорте. Import Directory Table содержит адресную
информацию используемую для разрешения ссылок на точки входа внутри образа
библиотеки. Таблица импорта состоит из отдельных входов, как минимум по одному
на каждую импортируемую библиотеку. Последний вход, указывающий на конец
таблицы является пустым (заполнен нулями).
В "нормальных" файлах вся информация об импорте предворяется записью Import
Directory Table (но физически вы можете разместить эту таблицу где угодно).
Дело обстоит примерно так:
Typical Import Layout
~~~~~~~~~~~~~~~~~~~~~
╒══════════════════════════════════════╤══════════════════════════════════════╕
│ Каталог импорта │ Import Directory Table │
╞══════════════════════════════════════╪══════════════════════════════════════╡
│ Таблица ссылок на имена │ LookUp Table │
├──────────────────────────────────────┼──────────────────────────────────────┤
│ Таблица имен │ Hint-Name Table │
├──────────────────────────────────────┼──────────────────────────────────────┤
│ Таблица адресов импорта │ Import Address Table │
└──────────────────────────────────────┴──────────────────────────────────────┘
это приблизительная последовательность расположения в файле различных частей
секции импорта, создаваемой существующими компоновщиками. В реальных файлах
нет никаких ограничений на порядок следования участков секции импорта, а у
загрузчика Windows'95 и на расположение последних. Формат одного входа
каталога импорта приведен в таблице чуть ниже.
Import Directory Entry
~~~~~~~~~~~~~~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ DWord │ Import LookUp │ Yes │ Содержит ссылку на табличку RVA │
│ │ │ │ │ указывающих на соответствующие │
│ │ │ │ │ Hint-Name's или непосредственно │
│ │ │ │ │ ординал ипортируемого входа │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 04h │ DWord │ Time/Date Stamp │ No │ Отметка о времени создания, │
│ │ │ │ │ часто содержит 0 (У-уф) │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 08h │ DWord │ Forward Chain │ No │ ? связано с возможностью пере- │
│ │ │ │ │ дачи экспорта в другие библио- │
│ │ │ │ │ теки. Обычно равно 0FFFFFFFFh │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 0Ch │ DWord │ Name RVA │ No │ Ссылка на библиотеку с которой │
│ │ │ │ │ нам необходимо поиметь вызовы │
│ │ │ │ │ представлена в виде ASCIZ. │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 10h │ DWord │ Addres Table RVA │ Yes │ Ссылка на табличку адресов │
│ │ │ │ │ импорта, заполняется системой │
│ │ │ │ │ при связывании │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ 14h │ Общий размер таблички импорта │
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
[6.2] Таблица просмотра импорта (Import LookUp Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Имена сервисов библиотеки содержатся в Hint-Name's Table. Ее формат
довольно прост:
Hint-Name Entry
~~~~~~~~~~~~~~~
╒═══════╤═════════════════════╤═══════╤═══════════════════════════════════════╕
│ Word │ Размер произвольный │ Byte │ Строка закрывается нулевым байтом, и │
├ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ┤ при необходимости ее длинна выравни- │
│ Hint │ ASCIZ Service Name │ PAD │ вается до четной границы еще одним 0 │
└───────┴─────────────────────┴───────┴───────────────────────────────────────┘
На имена сервисов и ссылаются RVA из таблицы Import LookUp. В случае
импорта по ординалам старший бит значения из таблицы Import LookUp установлен
в единицу. Конец таблицы находится по нулевому элементу. При попытке
связывания по имени системный загрузчик использует вначале значение Hint
(укороченный идентификатор точки входа) и только при неудачной попытке его
использования производит в своих системных таблицах поиск требуемой точки
входа. Имя сервиса чувствительно к регистру. Имя библиотеки - нет.
[6.3] Таблица адресов импорта (Import Address Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Данная таблица принимает в себя информацию после связывания загрузчиком
импорта из внешних библиотек, она завершается нулевым элементом. Очень
интересным фактом является то, что во многих программах она уже заполнена. Это
справедливо, по меньшей мере, для программ самой Windows'95. Подобный факт
заставляет предполагать, что загрузчик может не выполнять утомительной
процедуры настройки во многих случаях. Ему будет необходимо лишь загрузить
файл в ОЗУ и передать туда управление. . . Да здравствует вечно живой COM
формат!
Да, еще, для перехвата функции из библиотеки можно поменять адрес в этой
таблице - довольно простой и общий метод перехвата управления внутри
отдельного процесса. И еще пара плюшек на заметку, данная таблица (как и
многие другие таблицы импорта) может находиться на своем старом месте, а вот
Import Directory Table имеет смысл изменить, перенести в требуемое место и там
оставить. Файл корректируется минимально, а проблемы возникающие при этом
весьма незначительны и я их описывал.
[7] Локальная область данных цепочек (Thread Local Storage)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Локальная область данных цепочек, это специальный протяженный блок данных.
Каждая цепочка получит собственный блок при своем создании. Вот примерная
структура данной области:
Typical TLS Layout
~~~~~~~~~~~~~~~~~~
╒══════════════════════════════════════╤══════════════════════════════════════╕
│ Таблица разделов цепочек │ TLS Directory Table │
╞══════════════════════════════════════╪══════════════════════════════════════╡
│ Данные цепочек │ TLS Data │
├──────────────────────────────────────┼──────────────────────────────────────┤
│ Индексные переменные │ Index Variables │
├──────────────────────────────────────┼──────────────────────────────────────┤
│ Адреса обратных вызовов │ CallBack Addresses │
└──────────────────────────────────────┴──────────────────────────────────────┘
[7.1] Таблица разделов цепочек (TLS Directory table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TLS Directory Table содержит адресную информацию, которая используется при
описании остальной части TLS. Она имеет следующий формат:
TLS Directory Table
~~~~~~~~~~~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ DWord │ Start Data Block VA │ No │ Виртуальный адрес начала блока │
│ │ │ │ │ данных цепочки │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 04h │ DWord │ End Data Block VA │ No │ Виртуальный адрес конца блока │
│ │ │ │ │ данных цепочки │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 08h │ DWord │ Index VA │ No │ Виртуальный адрес индексной пе- │
│ │ │ │ │ ременной, используемой для дос- │
│ │ │ │ │ тупа к локальному блоку данных │
│ │ │ │ │ цепочки │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 0Ch │ DWord │ CallBack Table VA │ Yes │ Виртуальный адрес таблицы обрат-│
│ │ │ │ │ ных вызовов │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ 10h │ Общий размер таблички TLS │
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
[7.2] Таблица обратных вызовов цепочки (TLS CallBack Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Локальные обратные вызовы - массив виртуальных адресов функций, которые
будут вызваны загрузчиком после создания цепочки (нити) и после ее завершения.
Последний вход имеет нулевое значение и указыает на конец таблицы.
[8] Ресурсы
~~~~~~~~~~~
Ресурсы представляют собой многоуровневое двоично-отсортированное дерево.
Их спроектированная структура позволяет содержать до 2^31 уровней, однако,
реально используется только 3: самый верхний есть Type, затем Name, и затем
Language (тип, имя, язык). Типичное представление ресурсного участка в файлах:
Typical Resources Layout
~~~~~~~~~~~~~~~~~~~~~~~~
╒══════════════════════════════════════╤══════════════════════════════════════╕
│ Каталог ресурсов │ Resources Directory Table │
╞══════════════════════════════════════╪══════════════════════════════════════╡
│ Данные ресурсов │ Resources Data │
╘══════════════════════════════════════╧══════════════════════════════════════╛
Структуру каталога ресурсов рассмотрим ниже.
[8.1] Каталог ресурсов (Resource Directory Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Resource Directory Table
~~~~~~~~~~~~~~~~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ DWord │ Flags │ No │ Пока не используются, должны │
│ │ │ │ │ быть сброшены в 0 │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 04h │ DWord │ Time/Date Stamp │ No │ Дата и время подключения ресур- │
│ │ │ │ │ сов от ресурсного компилятора │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 08h │ Word │ Major Version │ No │ Уугу, опять для нас номер вер- │
│ │ │ │ │ сии, старший по счету │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 0Ah │ Word │ Minor Version │ No │ --//-- и младший │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 0Ch │ Word │ Name Entry │ No │ Количество входов в таблицу имен│
│ │ │ │ │ ресурсов, таблица располагается │
│ │ │ │ │ в самом начале массива входов │
│ │ │ │ │ и содержит строковые имена │
│ │ │ │ │ ассоциируемые с ресурсами │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 0Eh │ Word │ ID_Num Entry │ No │ Количество 32-битовых идентифи- │
│ │ │ │ │ каторов ресурсов │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ 10h │ Размер каталога ресурсов │
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
За каталогом ресурсов сразу следует массив переменной длинны, содержащий
ресурсные входы. Name Entry содержит число ресурсных входов имеющих имена
(связанные с каждым входом). Имена нечувствительны к регистру и расположены в
порядке возрастания. ID_Num Entry определяет число входов имеющих в качестве
имени 32-битовый идентификатор. Эти входы так же отсортированы по возрастанию.
Данная структура позволяет получать быстрый доступ к ресурсам по имени или по
идентификатору, но для отдельно взятого ресурса только одна из форм поиска
поддерживается, не обе! Это согласуется с синтаксисом .RC и .RES файлов.
Каждый вход в таблице ресурсов имеет следующий формат:
Resource Entry Item
~~~~~~~~~~~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ DWord │ Name RVA or Res ID │ No │ Поле содержит либо идентифика- │
│ │ │ │ │ тор ресурса, либо указатель на │
│ │ │ │ │ его имя в таблице имен ресурсов │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 04h │ DWord │ Data Entry RVA or │ No │ Указывает либо на данные, либо │
│ │ │ SubDirectory RVA │ │ на еще одну таблицу входов │
│ │ │ │ │ ресурсов, 31-бит сброшенный │
│ │ │ │ │ в 0 указывает на то, что это │
│ │ │ │ │ ссылка на данные и наоборот │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ 08h │ Размер ресурсного входа │
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
Строки каталога ресурсов имеют следующий формат:
Resource Directory String Entry
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
┌─────────────┬────────┬────────────────┬─────────────────────────────────────┐
│ Field Name: │ Length │ Unicode String │ длинна строки должна быть кратна 2 │
├─────────────┼────────┼────────────────┤ (это очевидно) Все такие строковые │
│ Field Type: │ Word │ unpredictable │ объекты часто хранят вместе │
└─────────────┴────────┴────────────────┴─────────────────────────────────────┘
Строки каталога ресурсов размещают после последнего Resource Directory
Entry но до первого Resource Data Item, это позволяет более компактно
разместить информацию. Каждый пункт данных имеет следующий формат:
Resource Data Entry
~~~~~~~~~~~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ DWord │ Data RVA │ No │ Указатель на реально располо- │
│ │ │ │ │ женные данные относительно │
│ │ │ │ │ Image Base │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 04h │ DWord │ Size │ No │ Размер ресурсных данных │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 08h │ DWord │ CodePage │ No │ Кодовая страница │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 0Ch │ DWord │ Reserved │ No │ Не используется и устанавлива- │
│ │ │ │ │ ется в 0 │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ 10h │ Размер указателя данные ресурса │
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
Каждый вход в таблице ресурсов описывает узел в дереве ресурсов. Он содержит
адрес относительно Image Base, поле Size указывает на число байов данных
находящихся по этому адресу, а кодовая страница используется для расшифровки
ключевых значений внутри ресурсных данных. Обычно новые приложения содержат
значение соответствующее Unicode кодовой таблице. (хотя, хм. Обычно?)
[8.2] Пример структуры размещения ресурсов
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Приведем пример приложения которое хочет использовать следующие данные в
качестве ресурсов:
Тип Имя Язык Данные ресурса
~~~~~~~~ ~~~~~~~~ ~ ~~~~~~~~
00000001 00000001 0 00010001
00000001 00000001 1 10010001
00000001 00000002 0 00010002
00000001 00000003 0 00010003
00000002 00000001 0 00020001
00000002 00000002 0 00020002
00000002 00000003 0 00020003
00000002 00000004 0 00020004
00000009 00000001 0 00090001
00000009 00000009 0 00090009
00000009 00000009 1 10090009
00000009 00000009 2 20090009
Тогда ресурсный каталог в PE файле будет выглядеть следующим образом:
Смещение Данные
~~~~~ ~~~~~~~~ ~~~~~~~~ ~~~~~~~~ ~~~~~~~~
0000: 00000000 00000000 00000000 00030000 (3 входа в этом каталоге)
0010: 00000001 80000028
(Тип #1, Подкаталог по смещению 0x28)
0018: 00000002 80000050 (Тип #2, Подкаталог по смещению 0x50)
0020: 00000009 80000080 (Тип #9, Подкаталог по смещению 0x80)
0028: 00000000 00000000 00000000 00030000 (3 входа в этом каталоге)
0038: 00000001 800000A0 (Имя #1, Подкаталог по смещению 0xA0)
0040: 00000002 00000108 (Имя #2, дескриптор данных по смещению 0x108)
0048: 00000003 00000118 (Имя #3, дескриптор данных по смещению 0x118)
0050: 00000000 00000000 0000000000040000 (4 входа в этом каталоге)
0060: 00000001 00000128 (Имя #1, дескриптор данных по смещению 0x128)
0068: 00000002 00000138 (Имя #2, дескриптор данных по смещению 0x138)
0070: 00000003 00000148 (Имя #3, дескриптор данных по смещению 0x148)
0078: 00000004 00000158 (Имя #4, дескриптор данных по смещению 0x158)
0080: 00000000 00000000 00000000 00020000 (2 входа в этом каталоге)
0090: 00000001 00000168 (Имя #1, дескриптор данных по смещению 0x168)
0098: 00000009 800000C0 (Имя #9, Подкаталог по смещению 0xC0)
00A0: 00000000 00000000 00000000 00020000 (2 входа в этом каталоге)
00B0: 00000000 000000E8 (Язык 0, дескриптор данных по смещению 0xE8
00B8: 00000001 000000F8 (Язык 1, дескриптор данных по смещению 0xF8
00C0: 00000000 00000000 00000000 00030000 (3 входа в этом каталоге)
00D0: 00000001 00000178 (Язык 0, дескриптор данных по смещению 0x178
00D8: 00000001 00000188 (Язык 1, дескриптор данных по смещению 0x188
00E0: 00000001 00000198 (Язык 2, дескриптор данных по смещению 0x198
00E8: 000001A8 (По смещению 0x1A8, для Тип #1, Имя #1, Язык #0
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
00F8: 000001AC (По смещению 0x1AC, для Тип #1, Имя #1, Язык #1
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
0108: 000001B0 (По смещению 0x1B0, для Тип #1, Имя #2,
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
0118: 000001B4 (По смещению 0x1B4, для Тип #1, Имя #3,
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
0128: 000001B8 (По смещению 0x1B8, для Тип #2, Имя #1,
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
0138: 000001BC (По смещению 0x1BC, для Тип #2, Имя #2,
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано) 0
148: 000001C0 (По смещению 0x1C0, для Тип #2, Имя #3,
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
0158: 000001C4 (По смещению 0x1C4, для Тип #2, Имя #4,
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
0168: 000001C8 (По смещению 0x1C8, для Тип #9, Имя #1,
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
0178: 000001CC (По смещению 0x1CC, для Тип #9, Имя #9, Язык #0
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
0188: 000001D0 (По смещению 0x1D0, для Тип #9, Имя #9, Язык #1
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
0198: 000001D4 (По смещению 0x1D4, для Тип #9, Имя #9, Язык #2
00000004 (4 байта данных)
00000000 (кодовая страница)
00000000 (зарезервировано)
Ну а данные для ресурсов будут таковыми:
01A8: 00010001
01AC: 10010001
01B0: 00010002
01B4: 00010003
01B8: 00020001
01BC: 00020002
01C0: 00020003
01C4: 00020004
01C8: 00090001
01CC: 00090009
01D0: 10090009
01D4: 20090009
[9] Таблица настроек адресов (FixUp Table)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Таблица настроек адресов содержит элементы для всех фиксированных адресов в
образе программы. Поле в заголовке PE файла с названием FixUp's Data Size
содержит общий размер в байтах данной таблицы. Сама таблица настроет адресов
разбита на блоки настроек (каждый блок представляет настройки для 4-х
килобайтовой страницы).
Связанные линкером адреса не нуждаются в дополнительной настройке
загрузчиком до тех пор, пока загрузчик в состоянии загрузить программу по
адресу, указанному в ее заголовке. При невыполнении этого условия загрузчику
прийдется корректировать адреса в программе. С учетом FLAT модели памяти и
виртуализации адресного пространства для каждого процесса загрузчику никогда
не прийдется изменять эти адреса, исключением могут являться библиотеки,
которые многое компиляторы привязывают к одному фиксированному адресу (Borland
например), либо случайные конфликты в адресах библиотек.
[9.1] Блок настроек перемещений (FixUp Block)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Блок настроек имеет следующий довольно простой формат:
FixUp Block
~~~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ DWord │ Page RVA │ No │ Указатель на страницу примене- │
│ │ │ │ │ ния настроек перемещений │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 04h │ DWord │ Block Size │ No │ Размер блока настроек (с заго- │
│ │ │ │ │ ловком) │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 08h │ Word │ TypeOffset Record │ Yes │ Массив записей настроек, их │
│ │ │ │ │ переменное количество │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ ... │ таблица имеет переменный размер │
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
Для наложения настройки необходимо вычислить Дельта-значение. 32-битное
Дельта есть разница между желаемой базой загрузки и действительной. Если образ
программы загружен в требуемое место, то Дельта равна нулю и никакой настройки
произойти не может. Каждый блок настроек должен начинаться на DWord границе
(не проверял), для выравнивания блока можно пользоваться нулями.
При настройке необходимую позицию в блоке вычисляют как сумму Page RVA и
Image Base загруженной программы. TypeOffset определен следующим образом:
TypeOffset Entry
~~~~~~~~~~~~~~~~
┌───────────┬──────────┬──────────────────────────────────────────────────────┐
│ 15 ... 12 │ 11 ... 0 │ Биты слова, Type указывает на тип настройки, а Offset│
├───────────┼──────────┤ на ее смещение внутри 4-килобайтового блока примени- │
│ Type │ Offset │ мости настроек. │
└───────────┴──────────┴──────────────────────────────────────────────────────┘
Поле Type имеет следующие значения:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
o 0h - адрес абсолютный и никаких изменений производить не требуется.
o 1h - добавить старшие 16 битов Дельты к 16 битовому полю находящемуся по
смещению Offset. 16 битовое поле представляет старшие биты 32-бито-
вого слова.
o 2h - добавить младшие 16 битов Дельты по смещению Offset. 16-битовое поле
представляет младшую половину 32-битового слова. Данная запись нас-
тройки присутствует только на RISC машине когда Object Align не
является по умолчанию 64К.
o 3h - прибавляет 32-битовое Дельта к 32-битовому значению.
o 4h - настройка требует полного 32-битового значения. Старшие 16-бит
берутся по адресу Offset, а младшие в следующем элементе TypeOffset
Все это объединяется в знаковую переменную, затем добавляется
32-битовое дельта и DWord 8000h. Старшие 16 бит получившегося зна-
чения сохраняются по адресу Offset в 16-битовом поле.
o 5h - ? что-то связанное с MIPS.
В реальной жизни я сталкивался только с типами 0 и 3, все остальные на интелах
очевидно не столь юзабельны, интересное поле для экспериментов. Все прочие
типы перемещений зарезервированы Microsoft.
[10] Отладочная информация (Debug Information)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Отладочная информация размещается для использования отладчиком и создается
в пределах формата линкером. Единственная определенная структура - Таблица
отладочной информации (Debug Directory Table). PE файлы также поддерживают
COFF информацию для отладчика (соответствующие ссылки есть в заголовке). Здесь
будет дано очень сокращенное общее описание отладочной информации в PE файлах.
[10.1] Отладочный каталог (Debug Directory)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
В каталоге отладки хранятся ссылки на прочую отладочную информацию, формат
его следующий:
Debug Directory Entry
~~~~~~~~~~~~~~~~~~~~~
╒═════╤═══════╤═══════════════════════╤═════╤═════════════════════════════════╕
│ │ Size │ │Com- │ │
│Base │ or │ Name Of field │ments│ Brief description │
│ │ Type │ │ │ │
╞═════╪═══════╪═══════════════════════╪═════╪═════════════════════════════════╡
│ 00h │ DWord │ Debug Flags │ No │ Не используются и установлены в │
│ │ │ │ │ нулевое значение │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 04h │ DWord │ Time/Date Stamp │ No │ Дата и время создания отладоч- │
│ │ │ │ │ ной информации │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 08h │ Word │ Major Version │ No │ Старший номер версии отладоч- │
│ │ │ │ │ ной информации │
├─────┼───────┼───────────────────────┼─────┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ 0Ah │ Word │ Minor Version │ No │ Младший номер версии --//-- │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 0Ch │ DWord │ Debug Type │ Yes │ Тип информации для отладчика │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 10h │ DWord │ Data Size │ No │ Размер в байтах данных для │
│ │ │ │ │ отладки без размера заголовка │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 14h │ DWord │ Data RVA │ No │ Адрес расположения отладочных │
│ │ │ │ │ данных в ОЗУ │
├─────┼───────┼───────────────────────┼─────┼─────────────────────────────────┤
│ 18h │ DWord │ Data Seek │ No │ Смещение к отладочным данным в │
│ │ │ │ │ файле │
╞═════╧═══════╧═══════════════════════╪═════╪═════════════════════════════════╡
│ Total Structure size │ 1Ch │ Размер элемента каталога отладки│
╘═════════════════════════════════════╧═════╧═════════════════════════════════╛
Тип отладочной информации:
~~~~~~~~~~~~~~~~~~~~~~~~~~
o 0001h - COFF таблица символов.
o 0001h - CodeView Таблица символов.
o 0001h - FPO таблица символов.
Если в программе содержится более одного типа отладочной информации, то
следующая запись в каталоге отладки будет следовать сразу за первой и иметь не
нулевое значение.
[11] Вопросы не рассмотренные в данном описании
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Несмотря на значительное упрощение формата файла PE по сравнению со старыми
NE файлами, он все еще обширен для того, чтобы влезть в это маленькое
руководство. Со временем описание будет расширяться и корректироваться. В
данный момент времени за бортом остались следующие вопросы:
- детальное рассмотрение отладочной информации
- некоторые нюансы в описании полей заголовков
- более полное рассмотрение реакции системы на загрузку файлов
- что-то еще. . .
[12] P.S.
~~~~~~~~~
Для дальнейшего пополнения и уточнения описания, а так же устранения
неоднозначностей был бы признателен за любые конструктивные комментарии и
дополнения. Прежде чем писать ругательства, рекомендую посмотреть на рис.1 Меня
можно найти следующим образом:
Home Page: www.geocities.com/SiliconValley/Hills/7827
E-Mail: [email protected]