Assembler для Windows

           

Часть IV. Отладка, анализ кода программ, драйверы

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

Глава 1. Структура исполняемых модулей

Исполняемым форматом в Windows является формат PE. Сокращение PE означает Portable Executable, т.е. переносимый исполняемый формат. Этот формат имеют как ЕХЕ-файлы, так и динамические библиотеки. Важно, что сейчас фирма Microsoft ввела "новый" формат и для объектных модулей - это COFF-формат (COFF - Common Object File Format), который, однако, на поверку оказался, в сущности, все тем же PE-форматом. Заметим в этой связи, что фирма Borland по-прежнему работает с объектными файлами, имеющими структуру OMF (Object Module Format). Старый NE-формат (NE - New Executable), используемый старой операционной системой Windows и рассчитанный на сегментную структуру памяти, ушел в небытие. Кроме


того, есть еще формат VXD-драйверов - LE-формат (Linear Executable, т.е. линейный исполняемый). Этого формата мы коснемся, когда будем говорить о драйверах в операционной системе Windows. Таким образом, данная глава будет посвящена разбору структуры исполняемых РЕ-модулей. Поскольку в состав исполняемого РЕ-модуля входит и DOS-программа (STUB), мы начнем наше рассмотрение со структуры DOS-программ. Наше рассмотрение будет кратким, и мы воспользуемся таблицей из книги [1].

Структура ЕХЕ-программ для MS DOS

Смещение Длина Название Комментарий
+0 2 MZ подпись, признак ЕХЕ-программы
+2 2 PartPag длина неполной последней страницы
+4 2 PageCnt длина в страницах (512 б), включая заголовок и последнюю страницу
+6 2 ReloCnt число элементов в таблице перемещения
+8 2 HdrSize длина заголовка в параграфах
+0AН 2 MinMem минимум требуемой памяти за концом программы
+0CH 2 MaxMem максимум требуемой памяти за концом программы
+0EН 2 ReloSS сегментный адрес стека
+10H 2 EXESp значение регистра SP
+12Н 2 ChkSum контрольная сумма
+14Н 2 ExeIP значение регистра IP
+16H 2 ReloCS сегментный адрес кодового сегмента
+18H 2 TablOff смещение в файле первого элемента таблицы перемещения
+1АН 2 Overlay номер оверлея, 0 для главного модуля
* Конец форматированной порции заголовка **
+1СН
** Начало таблицы перемещения (возможно с 1СН) **
+? 4*? смещ. сегмент ... смещ. сегмент

Рис. 4.1.1. Структура ЕХЕ-программы для MS DOS.

Более подробно разбор заголовка DOS-программы можно найти в [1]. Добавлю только, что сразу за таблицей перемещения начинается исполняемая часть модуля. Таблица же перемещения используется для того, чтобы при загрузке настроить адреса. Но это лишь в том случае, если в программе используются адреса сегментов. В противном случае таблица перемещения не содержит элементов, и исполняемый код начинается сразу за форматированной частью заголовка. Перейдем теперь к общей структуре РЕ-модуля.

I

Общая структура РЕ-модуля. Начало заголовка ЕХЕ-файлов в WIN32 представляет собой небольшую DOS-программу, основное предназначение которой заключается в том, чтобы при запуске в операционной системе MS DOS сделать сообщение о том, что данный модуль не предназначен для работы в MS DOS. Программа LINK.EXE (TLINK32.EXE) устанавливает свой вариант DOS-программы. Однако при желании Вы всегда можете поставить свою программу-заглушку (stub в переводе заглушка).

Рассмотрим общую структуру РЕ-заголовка.

СмещениеКомментарий
00HОбычный DOS-заголовок.
1CHЧетыре байта до выравнивания до 20Н байт, т.е. выравнивание до двух параграфов.
20HИнформация о программе, обычно отсутствующая.
3CHСмещение 32-битного РЕ-заголовка.
40HТаблица перемещения для программы-заглушки. У стандартных заглушек таблица, разумеется, пуста. Не смотря на это TablOff должен показывать именно сюда.
40H+??Здесь начинается тело самой заглушки, которая начинается за таблицей перемещения. Естественно, в стандартных заглушках нет ничего, кроме сообщения о невозможности запуска программы. Однако заглушка может иметь и весьма разрушительные свойства.
??Здесь начинается собственно PE-заголовок. Сюда показывает содержимое четырех байт по адресу 3CH. Начало должно быть выровнено по 8-байтной границе.
??Таблица описаний секций файлов (Object Table).
??Остальная информация: coff-символы, отладочная информация, таблица импорта и таблица экспорта, ресурсы и т.д. Данный раздел называется Image Pages, т.е. страницы образов.

Рис. 4.1.2. Общая структура РЕ-заголовка.

00000: 4D 5A 0A 00 02 00 00 00 | 04 00 0F 00 FF FF 00 00 MZ
00010: C0 00 00 00 00 00 00 00 | 40 00 00 00 00 00 00 00
00020: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00030: 00 00 00 00 00 00 00 00 | 00 00 00 00 80 00 00 00
00040: B4 09 BA 10 00 OE IF CD | 21 B8 01 4C CD 21 90 90
00050: 54 68 69 73 20 69 73 20 | 61 20 57 69 6E 33 32 20
00060: 70 72 6F 67 72 61 6D 2E | 0D OA 24 00 00 00 00 00
00070: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00080: 50 45 00 00 4C 01 03 00 | 39 30 00 00 00 00 00 00 PE
00090: 00 00 00 00 E0 00 OE 03 | 0B 01 02 34 00 30 00 00
000A0: 00 10 00 00 00 60 00 00 | 30 96 00 00 00 70 00 00
000B0: 00 A0 00 00 00 00 40 00 | 00 10 00 00 00 02 00 00

Рис. 4.1.3. Фрагмент РЕ-заголовка.

На рис. 4.1.3 показан фрагмент PE-заголовка. Обратите внимание, что по смещению 3CH действительно находится адрес начала основного заголовка (символы PE).

II

В таблице, представленной ниже мы даем описание заголовка РЕ.

СмещениеДлина поляНазвание поляОписание поля
00h DWORD Signature Bytes Сигнатура. Первые два байта "PE" 4550Н. Еще два байта обязательно должны быть равны нулю.
04h WORD CPU Type Данное поле указывает на процессор, который следует предпочесть при запуске программы. Вот возможное значение этого поля:
0000h - неизвестный процессор.
014Ch - i386
014Dh - i486
014Eh - i586
0162h - MIPS Mark I (R2000, R3000)
0163h - MIPS Mark II (R6000)
0166h - MIPS Mark III (R4000)
Чаще всего данное поле указывает на процессор 386.
06h WORD Num of Objects Поле указывает на число реальных входов в Object Table (см. таб. ниже).
08h DWORD Time/Date Stamp Дата и время, которые устанавливаются при компоновке программы.
0Ch DWORD Pointer to COFF table Дополнительный указатель, определяющий местонахождение отладочной COFF-таблицы в файлах. Это поле используется только в OBJ-файлах и РЕ-файлах, содержащих отладочную COFF-информацию.
10h DWORD COFF table size Количество символов в COFF-таблице.
14h WORD NT Header Size Размер заголовка РЕ-файла, начиная с поля Magic - таким образом, общий размер заголовка РЕ-файла составляет NT Header Size + 18h.
16h WORD Flags Указывает на предназначение программы. Значение флагов:
0000h - это программа;
0001h - файл не содержит перемещений и таблицы перемещаемых элементов;
0002h - образ в файле можно запускать. Если этот бит не установлен, то это обычно указывает на ошибку, обнаруженную на этапе линковки, или же на то, что код был инкрементально отлинкован (инкрементальная линковка - частичная линковка кода при изменении участка программы, а не тотальная перекомпиляция проекта);
0200h - загружать в память фиксированно. Указывает на то, что программу можно грузить только по адресу, записанному в Image Base, если это невозможно, то такой файл лучше вообще не запускать.
2000h - это библиотека.
18h WORD Magic Слово-сигнатура, определяющее состояние отображенного файла. Определены следующие значения:
107Н - отображение ПЗУ.
10BH - нормально исполняемое отображение.
1Ah BYTE Link Major Старший номер версии использовавшегося при создании модуля компоновщика. Десятичный вид.
1Bh BYTE Link Minor Младший номер версии использовавшегося при создании модуля компоновщика. Десятичный вид.
1Ch DWORD Size of Code Размер именно программного кода в файле. KERNEL использует это значение для фактического отведения памяти под загружаемую программу. Установка этого значения слишком маленьким приведет к выдаче сообщения о нехватке памяти. Обычно большинство модулей имеют только одну программную секцию .text.
20h DWORD Size of Init Data Размер секции инициализированных данных, очевидно, не используется в Windows 95, но используется в Windows NT. Назначение аналогично приведенному выше.
24h DWORD Size of UnInit Data Размер секции неинициализированных данных. Неинициализированные данные обычно содержатся в секции .bss. Данная секция не занимает на диске никакого места, но при загрузке модуля в память загрузчик отводит под нее память.
28h DWORD Entry point RVA Адрес относительно Image Base, no которому передается управление при запуске программы или адрес инициализации/завершения библиотеки.
2Ch DWORD Base of Code Адрес секции относительно базового адреса (40000Н), содержащей программный код. Этот адрес обычно равен 1000Н для компоновщика Microsoft и 10000H для компоновщика Borland.
30h DWORD Base of Data Адрес относительно базового (40000H), с которого начинаются секции данных файла. Секции данных обычно идут последними в памяти, после заголовка РЕ и программных секций.
34h DWORD Image Base При создании компоновщик помещает сюда адрес, куда будет отображен исполняемый файл в памяти. Если загрузчик отобразит файл именно по этому адресу, то дополнительной настройки не потребуется.
38h DWORD Object align Выравнивание программных секций. После отображения в память каждая секция будет обязательно начинаться с виртуального адреса, кратного данной величине.
3Ch DWORD File align В случае РЕ-файла исходные данные, которые входят в состав каждой секции, будут обязательно начинаться с адреса, кратного данной величине. Значение по умолчанию составляет 200Н.
40h WORD OS Major Старший номер версии операционной системы, необходимый для запуска программы.
42h WORD OS Minor Младший номер версии операционной системы.
44h WORD USER Major Пользовательский номер версии, задается пользователем при линковке программы. Старшая часть.
46h WORD USER Minor Пользовательский номер версии, младшая часть.
48h WORD SubSys Major Старший номер версии подсистемы.
4Ah WORD SubSys Minor Младший номер версии подсистемы. Типичное значение версии 4.0, что означает Windows 95.
4Ch DWORD Reserved Зарезервировано.
50h DWORD Image Size Представляет общий размер всех частей отображения, находящихся под контролем загрузчика. Эта величина равна размеру области памяти, начиная с базового адреса отображения и заканчивая адресом конца последней секции. Адрес конца секции выровнен на ближайшую верхнюю границу секции.
54h DWORD Header Size Общий размер всех заголовков: DOS Stub + РЕ Header + Object Table
58h DWORD File CheckSum Контрольная сумма всего файла. Как и в операционной системе MS DOS, ее никто не контролирует, а программа редактирования связей устанавливает ее в 0. Предполагалось ее рассчитывать как инверсию суммы всех байтов файла.
5Ch WORD Subsystem Операционная подсистема, необходимая для запуска данного файла. Вот значения этого поля:
1 - подсистема не требуется (NATIVE).
2 - запускается в подсистеме Windows GUI.
3 - запускается в подсистеме Windows character (терминальное или консольное приложение).
5 - запускается в подсистеме OS/2.
7 - запускается в подсистеме Posix.
5Eh WORD DLL Flags Указывает на специальные потребности при загрузке, начиная с операционной системы NT 3.5. Устарел и не используется.
60h DWORD Stack Reserve Size Память, требуемая для стека приложения. Память резервируется, но выделяется только Stack Commit Size байтов. Следующая страница является охранной. Когда приложение достигает этой страницы, то она становится доступной, а следующая страница - охранной, и так до достижения нижней границы, после чего Windows 95 убивает программу.
64h DWORD Stack Commit Size Объем памяти, отводимой для стека немедленно после загрузки.
68h DWORD Heap Reserve Size Максимально возможный размер локальной кучи.
6Ch DWORD Heap Comit Size Отводимый размер кучи при загрузке.
70h DWORD Loader Flags Начиная с Windows NT 3.5 объявлено неиспользуемым, назначение неясно, но в целом связано с поддержкой отладки.
74h DWORD Num of RVA and Sizes Указывает размер массива VA/Size, который следует ниже, данное поле зарезервирована под будущие расширения формата. В данный момент его значение всегда равно 10h.
78h DWORD Export Table RVA Относительный адрес (относительно базового адреса) таблицы экспорта.
7Ch DWORD Export Data Size Размер таблицы экспорта.
80h DWORD Import Table RVA Относительный адрес (относительно базового адреса) таблицы импорта.
84h DWORD Import Data Size Размер таблицы импорта.
88h DWORD Resource Table RVA Относительный адрес (относительно базового адреса) таблицы ресурсов.
8Ch DWORD Resource Data Size Размер таблицы ресурсов.
90h DWORD Exception Table RVA Относительный адрес таблицы исключений.
94h DWORD Exception Data Size Размер таблицы исключений.
98h DWORD Security Table RVA Адрес таблицы безопасности. По-видимому, не используется.
9Ch DWORD Security Data Size Размер таблицы безопасности.
A0h DWORD Fix Up's Table RVA Относительный адрес таблицы настроек.
A4h DWORD Fix Up's Data Size Размер таблицы настроек.
A8h DWORD Debug Table RVA Относительный адрес таблицы отладочной информации.
ACh DWORD Debug Data Size Размер таблицы отладочной информации.
B0h DWORD Image Description RVA Относительный адрес строки описания модуля.
B4h DWORD Description Data Size Размер строки описания модуля.
B8h DWORD Machine Specific RVA Адрес таблицы значений, специфичных для микропроцессора.
BCh DWORD Machine Data Size Размер таблицы значений, специфичных для микропроцессора.
C0h DWORD TLS RVA Указатель на локальную область данных цепочек.
C4h DWORD TLS Data Size Размер области данных цепочек.
C8h DWORD Load Config RVA Предназначение неизвестно.
CCh DWORD Load Config Data Size Предназначение неизвестно.
D0h 08h Reserved Зарезервировано.
D8h DWORD IAT RVA Используется в NT. В Windows 95, судя по всему, нет.
DCh DWORD IAT Data Size Размер описанного поля.
E0h 08h Reserved Зарезервировано.
E8h 08h Reserved Зарезервировано.
F0h 08h Reserved Зарезервировано.

Между заголовком РЕ и данными для секций расположена таблица секций. Вот элемент этой таблицы.

Элемент таблицы секций содержит полную базу данных об одной секции.

Смещение Длина поля Название поля Описание поля
00h 08h Object Name Имя объекта, остаток заполнен нулями. Если имя объекта имеет длину 8 символов, то заключительного 0 нет. Вот несколько возможных имен:
.text - исполняемый код общего назначения.
CODE - исполняемый код, помещаемый компоновщиками фирмы BORLAND.
.icode - переходники (jump'ы), помещаемые сюда старой версией TLINK32.
.data - инициализированные данные, помещаются компоновщиком фирмы Microsoft.
DATA - инициализированные данные, помещаемые сюда компоновщиком TLINK32.
.bss - неинициализированные глобальные и статические переменные.
.CRT - еще одна секция для хранения инициализированных данных.
.rsrc - секция для хранения ресурсов.
.idata - секция импорта.
.edata - секция экспорта.
.reloc - секция настроек. Данная информация может понадобиться загрузчику, если он не сможет загрузить модуль по базовому адресу.
.tls - данные для запуска цепочек.
.rdata - данная секция в основном содержит отладочную информацию.
.debug$s и .debug$t - данные секции есть только в COFF-объектных файлах. Они содержат информацию о символах Code View и их типах.
.drective - в данной секции содержится текст программ для компоновки. Данная секция есть только в объектных файлах. Секции, содержащие символ $. Такие секции обрабатываются особым образом. Компоновщик объединяет все секции, имеющие одинаковые символы в имени до символа $. Именем получившейся секции считается то, что стоит перед указанным символом.
08h DWORD Virtual Size Виртуальный размер секции - именно столько памяти будет отведено под секцию. Если Virtual Size превышает Physical Size, то разница заполняется нулями, так определяются секции неинициализированных данных (Physical Size = 0).
0Ch DWORD Section RVA Размещение секции в памяти, ее виртуальный адрес относительно Image Base. Позиция каждой секции выровнена на границу Object align (степень 2 от 512 до 256М включительно, по умолчанию 64К), секции упакованы вплотную друг к другу, впрочем, можно это не соблюдать. Для объектных файлов поле не имеет смысла.
10h DWORD Physical Size Размер секции (ее инициализированной части) в файле кратно полю File align в заголовке РЕ Header, должно быть меньше или равно Virtual Size. Для объектных файлов это поле содержит точный размер секции, сгенерированный компилятором или ассемблером. Другими словами, для объектных файлов оно эквивалентно полю Virtual Size.
14h DWORD Physical Offset Физическое смещение относительно начала ЕХЕ-файла, выровнено на границу File align поля заголовка РЕ Header. Смещение используется загрузчиком для поиска.
18h DWORD Pointer to Linenumber Файловое смещение таблицы номеров строк. Используется для объектных файлов.
1Ch WORD Number of Relocations Количество перемещений в таблице поправок. Используется только для объектных файлов.
1Eh WORD Number of Linenumbers Количество номеров строк в таблице номеров строк для данной секции. Используется для объектных файлов.
20h 08h Reserved Зарезервировано для объектных файлов.
28h DWORD Object Flags Битовые флаги секции:
00000004h - используется для кода с 16-битными смещениями.
00000020h - секция кода.
00000040h - секция инициализированных данных.
00000080h - секция неинициализированных данных.
00000200h - комментарии или любой другой тип информации.
00000400h - оверлейная секция.
00000800h - не будет являться частью образа программы.
00001000h - общие данные.
00500000h - выравнивание по умолчанию, если не указано иное.
02000000h - может быть выгружен из памяти.
04000000h - не кэшируется.
08000000h - не подвергается страничному преобразованию.
10000000h - разделяемый.
20000000h - выполнимый.
40000000h - можно читать.
80000000h - можно писать.

Страницы образов секций. Здесь мы изучим некоторые секции.

Секция экспорта (.edata). Общая структура

1   Таблица собственно экспорта   Export Directory Table
2   Адресная таблица   Export Address Table
3   Таблица указателей на имена   Export Name Table Pointers
4   Таблица номеров   Export Ordinal Table
5   Таблица самих имен   Export Name Table

1. Таблица экспорта

Смещение Длина поля Название поля Описание поля
00h DWORD Flags Зарезервировано, должно быть равно нулю.
04h DWORD Time/Date Stamp Время и дата создания экспортных данных.
08h WORD Major Version Старший номер версии таблицы экспорта. Не используется.
0Ah DWORD Minor Version Младший номер версии таблицы экспорта, также не используется.
0Ch DWORD Name RVA Относительный адрес строки, указывающей на имя нашей библиотеки.
10h DWORD Ordinal Base Начальный номер экспорта, для функций, экспортируемых данным модулем.
14h DWORD Num of Functions Количество функций экспортируемых данным модулем, является числом элементов массива Address Table (см.ниже).
18h DWORD Num of Name Pointers Число указателей на имена, обычно равно числу функций, но это не так, если у нас есть функции, экспортируемые только по номеру.
1Ch DWORD Address Table RVA Указатель на таблицу относительных адресов экспортируемых функций.
20h DWORD Name Pointers RVA Указатель на таблицу указателей на имена экспортируемых функций данного модуля.
24h DWORD Ordinal Table RVA Указатель на таблицу номеров экспорта, данный массив по индексам параллелен Name Pointers, элементами являются слова.

2. Таблица адресов экспорта. Эта структура данных содержит адреса экспортируемых функций (их точки входа) в формате DWORD (по 4 байта на элемент). Для доступа к данным используется номер функции с коррекцией на базу номеров (Ordinal Base).

3. Таблица указателей на имена. Данная структура содержит указатели на имена экспортируемых функций, указатели отсортированы в лексическом порядке для обеспечения возможности бинарного поиска. Каждый указатель занимает 4 байта. Имена функций обычно лежат в секции экспорта.

4. Таблица номеров. Данная структура совместно с Name Table Pointers формирует два параллельных массива, разделенных для облегчения к ним доступа индексированием на родные для процессора данные (слова, двойные слова, но не сложные структуры). Данный массив содержит номера экспорта, которые в общем случае являются индексами в Address Table экспорта (за вычетом базы Ordinal Base). Элементами данного массива являются слова (2 байта).

5. Таблица имен экспорта. Эта таблица содержит необязательные (по мнению Microsoft) имена экспортируемых функций. Данный массив используется совместно с Name Table Pointers и Ordinal Table для обеспечения связывания загрузчиком импорта/экспорта по имени. Механизм описывался выше. Каждый элемент являет собой ASCIIZ строку с именем экспортируемой функции. Никто не говорит, что они должны в файле идти друг за другом последовательно, хотя так построено большинство файлов. Надо отметить, что имена экспорта чувствительны к регистру. Отметим особенность загрузчика - при связывании, если адрес функции находится в секции экспорта, на самом деле по указанному адресу лежит строка, переадресующая к другой библиотеке, экспортирующей данную функцию (с указанием библиотеки и самой функции). Это называется - передача экспорта.

Секция импорта (.idata)

Рис. 4.1.4. Вызов импортируемой функции.

Схема вызова импортируемых функций из РЕ-модуля изображена на Рис. 4.1.4, который с некоторыми изменениями взят из книги [2]. Смысл данного рисунка заключается в следующем. При компоновке все вызовы API-функций преобразуются к вызову типа CALL Адрес1. При этом адрес, также как и вызов, находится в секции кода (.text). По адресу же стоит команда JMP DWORD PTR [Адрес2]. [Адрес2] находится в секции .idata (импорта) и содержит двойное слово — адрес функции в динамической библиотеке. Современные компиляторы содержат директивы, позволяющие вместо двух вызовов (CALL и JMP) генерировать один CALL [Адрес2].

Таблица директория импорта

Каталог импорта   Import Directory Table
Таблица ссылок на имена   LookUp Table
Таблица имен   Hint-Name Table
Таблица адресов импорта   Import Address Table

Формат входа в каталог импорта

Смещение Длина поля Название поля Описание поля
00h DWORD Import LookUp Содержит ссылку на таблицу относительных адресов (относительно базового адреса), указывающих на соответствующие имена импортируемой функции, или непосредственно номер импортируемого входа.
04h DWORD Time/Date Stamp Отметка о времени создания, часто содержит ноль.
08h DWORD Forward Chain Связано с возможностью передачи экспорта в другие библиотеки. Обычно равно 0FFFFFFFFh.
0Ch DWORD Name RVA Ссылка на библиотеку импорта в виде ASCII строки с нулем на конце. Например, KERNEL32.DLL или USER32.DLL.
10h DWORD Addres Table RVA Ссылка на таблицу адресов импорта, заполняется системой при связывании.

Таблица просмотра импорта или таблица имен сервисов. Ссылка из поля Import LookUp на массив, содержащий ссылки на таблицу просмотра импорта. При импортировании по номеру старший бит элемента массива равен 1.

ТипСодержимое
Word   Номер функции
Hint   ASCII имя функции

Таблица адресов импорта. Данная таблица принимает в себя информацию после связывания загрузчиком импорта из внешних библиотек, она завершается нулевым элементом.

Локальная область данных цепочек (потоков)

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

Таблица разделов цепочек   TLS Directory Table
Данные цепочек   TLS Data
Индексные переменные   Index Variables
Адреса обратных вызовов   CallBack

Таблица разделов потоков

Смещение Длина поля Название поля Описание поля
00h DWORD Start Data Block VA Виртуальный адрес начала блока данных цепочки.
04h DWORD End Data Block VA Виртуальный адрес конца блока данных цепочки.
08h DWORD Index VA Виртуальный адрес индексной переменной, используемой для доступа к локальному блоку данных цепочки.
0Ch DWORD CallBack Table VA Виртуальный адрес таблицы обратных вызовов. Локальные обратные вызовы - массив виртуальных адресов функций, которые будут вызваны загрузчиком после создания цепочки (нити) и после ее завершения. Последний вход имеет нулевое значение и указывает на конец таблицы.

Секция ресурсов (.rdata)

Ресурсы представляют собой многоуровневое двоичное дерево. Их структура позволяет содержать до 2^31 уровней, однако реально используется только 3: самый верхний есть Type, затем Nam и затем Language (тип, имя, язык). Перемещения по иерархии каталогов ресурсов похожи на перемещения по каталогам жесткого диска. Типичное представление ресурсного участка в файлах:

Каталог ресурсов   Resources Directory Table
Данные ресурсов   Resources Data

Каталог ресурсов (Resource Directory Table)

Смещение Длина поля Название поля Описание поля
00h DWORD Flags Пока не используются, должны быть сброшены в ноль.
04h DWORD Time/Date Stamp Дата и время создания ресурсов от ресурсного компилятора.
08h WORD Major Version Старшая часть версии ресурсов. Обычно равно нулю.
0Ah WORD Minor Version Младшая часть версии ресурсов. Обычно равно нулю.
0Ch WORD Name Entry Количество входов в таблицу имен (элементов массива) ресурсов. Таблица располагается в самом начале массива входов и содержит строковые имена, ассоциируемые с ресурсами.
0Eh WORD ID_Num Entry Количество элементов массива, использующих целые ID.

За каталогом ресурсов сразу следует массив переменной длины, содержащий ресурсные входы. Name Entry содержит число ресурсных входов, имеющих имена (связанные с каждым входом). Имена нечувствительны к регистру и расположены в порядке возрастания. ID_Num Entry определяет число входов имеющих в качестве имени 32-битовый идентификатор. Эти входы так же отсортированы по возрастанию. Данная структура позволяет получать быстрый доступ к ресурсам по имени или по идентификатору, но для отдельно взятого ресурса поддерживается только одна из форм поиска. Что согласуется с синтаксисом .RC и .RES файлов. Каждый вход в таблицу ресурсов имеет следующий формат.

Вход в таблицу ресурсов (Resource Entry Item)

Смещение Длина поля Название поля Описание поля
00h DWORD Name RVA or Res ID Поле содержит либо идентификатор ресурса, либо указатель на его имя в таблице имен ресурсов.
04h DWORD Data Entry RVA or SubDirectory RVA Указывает либо на данные, либо на еще одну таблицу входов ресурсов. Старший бит поля, сброшенный в ноль, говорит, что поле указывает на данные.

Каждый пункт данных (Resource Entry Item) имеет следующий формат:

Смещение Длина поля Название поля Описание поля
00h DWORD Data RVA Указатель на реально расположенные данные относительно Image Base.
04h DWORD Size Размер ресурсных данных.
08h DWORD CodePage Кодовая страница.
0Ch DWORD Reserved He используется и устанавливается в ноль.

Таблица настроек адресов

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

Формирование данных базовых поправок выглядит следующим образом. Поправки упаковываются сериями смежных кусков различной длины. Каждый кусок описывает поправки для одной четырехбайтовой страницы отображения и начинается со следующей структуры:

Смещение Длина поля Название поля Описание поля
00h DWORD Page RVA Относительный адрес страницы применения.
04h DWORD Block Size Размер блока настроек (с заголовком). Эта величина используется для вычисления количества настроек.
08h WORD TypeOffset Record Массив записей настроек, их переменное количество.

Для наложения настройки необходимо вычислить 32-битную разницу ("Дельта") между желаемой базой загрузки и действительной. Если образ программы загружен в требуемое место, то эта разница равна нулю и никакой настройки не требуется. Каждый блок настроек должен начинаться на DWORD-границе, для выравнивания блока можно пользоваться нулями. При настройке необходимую позицию в блоке вычисляют как сумму относительного адреса страницы и базового адреса загруженной программы.

Структура элемента массива настроек

15...12
Type
11...0
Offset
Биты слова, Type указывает на тип настройки, a Offset на ее смещение внутри 4-килобайтной страницы.

Перечислим возможные типы поправок.

0h Адрес абсолютный и никаких изменений производить не требуется.
1h Добавить старшие 16 битов "Дельты" к 16-битовому полю, находящемуся по смещению Offset. 16-битовое поле представляет старшие биты 32-битового слова.
2h Добавить младшие 16 битов "Дельты" по смещению Offset. 16-битовое поле представляет младшую половину 32-битового слова. Данная запись настройки присутствует только на RISC машине, когда Object align не является по умолчанию 64К.
3h Прибавляет 32-битовое "Дельта" к 32-битовому значению.
4h Настройка требует полного 32-битового значения. Старшие 16-бит берутся по адресу Offset, а младшие в следующем элементе TypeOffset. Все это объединяется в знаковую переменную, затем добавляется 32-битовое "Дельта" и DWORD 8000h. Старшие 16 бит получившегося значения сохраняются по адресу Offset в 16-битовом поле.
5h ?

Отладочная информация (.debug$S, .debug$T)

Здесь помещается структура отладочного каталога, создаваемого любыми компоновщиками. Другая отладочная информация зависит от транслятора. Структуру COFF-отладочной информации можно посмотреть в книге [2].

Отладочный каталог

Смещение Длина поля Название поля Описание поля
00h DWORD Debug Flags Флаги, по-видимому, не используются и устанавливаются в нулевое значение.
04h DWORD Time/Date Stamp Дата и время создания отладочной информации.
08h WORD Major Version Старший номер версии отладочной информации.
0Ah WORD Minor Version Младший номер версии отладочной информации.
0Ch DWORD Debug Type Тип информации для отладчика. Вот эти типы:
0000h - UNKNOWN/BORLAND;
0001h - COFF таблица символов;
0002h - Code View таблица символов;
0003h - FPO таблица символов;
0004h - MISC;
0005h - EXCEPTION;
0006h - FIXUP.
10h DWORD Data Size Размер в байтах данных для отладки без размера заголовка.
14h DWORD Data RVA Относительный адрес расположения отладочных данных в памяти.
18h DWORD Data Seek Смещение к отладочным данным в файле.