"Виртуальная библиотека Delphi" - читать интересную книгу автора

Описание формата ресурсов в MS PE COFF.

Я делаю сокращенное изложение фрагмента документации PE COFF. Я полагаю, этого более-менее достаточно, чтобы разобраться, при желании, с текстом примера Delphi. Файл PE.TXT (author Micheal J. O'Leary) взят из документации Microsoft C. Он же входит в MS Software Developers Kit (SDK) и в комплект поставки большинства компиляторов C для Win32. Если Вам интересно положение корневого каталога ресурсов в заголовке PE COFF или более подробный формат заголовка – можно смотреть исходные тексты проекта проекта RSEXPLOR или, разумеется, сам первоисточник — PE.TXT

Ресурсы индексированы как многоуровневое двоичное дерево. Технологически возможно 2**31 уровней, но в Windows стандартно используются только три: первый — TYPE (тип), далее — NAME (имя), далее — LANGUAGE (язык). Ресурсы должны быть отсортированы по определенным правилам – для ускорения поиска.

Типичное расположение ресурсов в файле: сначала лежит `RESOURCE DIRECTORY` (каталог/каталоги ресурсов), затем – `RESOURCE DATA` (собственно данные ресурсов).

Каталог ресурсов довольно похож, по структуре, на каталоги дисков. Он содержит записи (`DIR ENTRIES` – см. далее), которые указывают либо на ресурсы, либо на другие каталоги (точнее – подкаталоги) ресурсов. В отличие от дисков, сами данные не разносятся по кластерам, а наоборот – их стараются плотнее прижать друг к другу, поскольку никто не собирается вставлять туда дополнительные данные после сборки (линковки) исполняемого файла.

Каталог ресурсов начинается с заголовка (четыре 32-битных слова):

DD RESOURCE FLAGS

DD TIME/DATE STAMP

DW MAJOR VERSION, DW MINOR VERSION

DW # NAME ENTRY,  DW # ID ENTRY


декларация в RXTypes.Pas:

IMAGE_RESOURCE_DIRECTORY = packed record

  Characteristics : DWORD;

  TimeDateStamp   : DWORD;

  MajorVersion    : WORD;

  MinorVersion    : WORD;

  NumberOfNamedEntries : WORD;

  NumberOfIdEntries : WORD;

end;

Здесь важны два поля: `# NAME ENTRY` — число точек входа, имеющих имена, и `# ID ENTRY` — число точек входа, имеющих вместо имен целочисленные идентификаторы.

За заголовком следует массив из записей `RESOURCE DIR ENTRIES` (точек входа каталога). Там лежат `# NAME ENTRY`+ `# ID ENTRY` записей типа `DIR ENTRY`. Формат записи `DIR ENTRY` — два 32-битных слова:

DD NAME RVA       | INTEGER ID

DD DATA ENTRY RVA | SUBDIR RVA


декларация в RXTypes.Pas:

IMAGE_RESOURCE_DIRECTORY_ENTRY = packed record

  Name: DWORD; // Or ID: Word (Union)

  OffsetToData: DWORD;

end;

Первое поле содержит либо `NAME RVA` — адрес строки (UNICODE) с именем, либо — `INTEGER ID` – целочисленный идентификатор. `INTEGER ID` может быть, например, одним из стандартных кодов типа ресурса или заданным пользователем кодом строки в таблице строк.

Самый старший бит второго поля (31-й бит) называется `Escape-флагом`. Если он установлен в `1`, считается что данная `DIR ENTRY` — ссылка на другой подкаталог ресурсов. Если сброшен в `0` — данная запись ссылка на данные ресурса. Понятно, при вычислении адреса этот бит всегда должен считаться `0`.

Строка, на которую указывает `NAME RVA`, очень похожа на паскалевскую short-string, только вместо байтов она состоит из 16-битные слов. Самое первое слово – длина строки, за ним лежат 16-битные символы UNICODE. Физически линкер кладет эти строки переменной длиины между каталогами и собственно данными ресурсов.

Понятно, что `SUBDIR RVA` указывает на совершенно аналогичную таблицу подкаталога.

`DATA ENTRY RVA` указывает на запись `RESOURCE DATA ENTRY` такого вида:

DD DATA RVA

DD SIZE

DD CODEPAGE

DD RESERVED


декларация в RXTypes.Pas:

IMAGE_RESOURCE_DATA_ENTRY = packed record

  OffsetToData    : DWORD;

  Size            : DWORD;

  CodePage        : DWORD;

  Reserved        : DWORD;

end;

`DATA RVA` — адрес бинарных данных, `SIZE` — их размер. `CODEPAGE` (кодовая страницa) обычно имеет снысл только для строковых ресурсов. Оговаривается, что в Win32 это должна быть одна из стандартных страниц UNICODE. Сами бинарные данные могут жить либо прямо за полем `RESERVED`, либо где-то в другом месте — смотря куда линкер их положит.