WYOS - Выпуск №13
PE - продолжение
commrade, Среда, 30 Июнь 2004, 23:18
Напиши свою ОС! #13Здравствуйте, уважаемые подписчики!
Итак продолжаем разговор о PE-файлах.
Раздел секцииВслед за заголовком следует основная часть PE-файла - раздел секций. Необходимо отметить, что компиляторы фирм Microsoft и Bоrland (Inprise), создающие файлы PE-формата, используют несколько отличающихся по составу и наименованию секций. Для однозначности далее будут обсуждаться секции, создаваемые компиляторами фирмы Microsoft. Но если вы столкнетесь с необходимостью работать с PE-файлами, создаваемыми компиляторами фирмы Bоrland (Inprise), то по названиям и содержанию секций вы относительно легко сможете провести аналогию и правильную интерпретацию находящихся в них данных. Типичное приложение Windows реально содержит около девяти секций:
<table border='1' bgcolor='#FFFFB3'>
<caption align='right'><b>Секция </b></caption>
<tr>
<td>Таблица </td>
</tr>
<tr>
<td>Адресная </td>
</tr>
<tr>
<td>Таблица указателей на </td>
</tr>
<tr>
<td>Таблица </td>
</tr>
<tr>
<td>Таблица самих </td>
</tr>
</table>
Таблица экспорта (Export Directory Table)Export Directory Table содержит адресную информацию используемую при развязке настраиваемых ссылок в внешние точки входа внутри программы.
<table border='1' bgcolor='#FFFFB3'>
<caption align='right'><b>Таблица экспортируемых </b></caption>
<tr>
<td><b>Смещение </b></td>
<td><b>Размер или тип </b></td>
<td><b>Имя записи </b></td>
<td><b>Описание </b></td>
</tr>
<tr>
<td>00h </td>
<td>DWord </td>
<td>Flags </td>
<td>зарезервированона будущее = 0 </td>
</tr>
<tr>
<td>04h </td>
<td>DWord </td>
<td>Time/Date Stamp </td>
<td>время и дата создания экспортных данных </td>
</tr>
<tr>
<td>08h </td>
<td>Word </td>
<td>Major Version </td>
<td>опять для нас, блин, старший номер версии таблицы экспорта
как хочешь, так и используй </td>
</tr>
<tr>
<td>0Ah </td>
<td>DWord </td>
<td>Minor Version </td>
<td>аналогично, младший </td>
</tr>
<tr>
<td>0Ch </td>
<td>DWord </td>
<td>Name RVA </td>
<td>RVA строки указывающей на имя нашей библиотеки </td>
</tr>
<tr>
<td>10h </td>
<td>DWord </td>
<td>Ordinal Base </td>
<td>начальный номер экспорта, для функций нашей библиотеки, обычно установлено в 1,
но не факт </td>
</tr>
<tr>
<td>14h </td>
<td>DWord </td>
<td>Num of Functions </td>
<td>количество функций экспортируемых нашим модулем, является числом элементов
массива Address Table см.ниже </td>
</tr>
<tr>
<td>18h</td>
<td>DWord </td>
<td>Num of Name Pointers </td>
<td>число указателей на имена, обычно равно числу функций, но это не так,
если у нас есть функции экспортируемые только по номеру </td>
</tr>
<tr>
<td>1Ch </td>
<td>DWord </td>
<td>Address Table RVA</td>
<td>указатель на таблицу адресов (RVA) экспорта </td>
</tr>
<tr>
<td>20h </td>
<td>DWord </td>
<td>Name Pointers RVA </td>
<td>указатель на таблицу указателей на имена экспорта </td>
</tr>
<tr>
<td>24h </td>
<td>DWord </td>
<td>Ordinal Table RVA </td>
<td>указатель на таблицу ординалов экспорта, данный массив по индексам параллелен
Name Pointers, элементами являются слова </td>
</tr>
</table>
Таким образом подный размер таблицы экспорта 28h. Для обработки запросов на связывание загрузчик системы ищет: импорт по имени: имя в массиве имен, по его индексу ординал, ординал корректируется на базу и этим индексом вычитывается адрес функции из поля массива Address Table импорт по ординалу: ординал корректируется на базу и далее, как описано выше. Надо отметить, что таблица экспорта может содержать пропуски, которые отображаются нулевыми значениями адресов экспорта. Импортировать по ординалам очень нежелательно ибо в разных версиях Windows'95 ординалы функций в модулях различаются, не говоря уже об NT и проч.
Таблица адресов экспорта (Address Table)Данная структура данных содержит адреса экспортируемых функций (их точки входа), портируемых данных и т.п. в формате DWord VAR (по 4 байта на элемент). Для доступа к данным используется ординал функции с коррекцией на базу ординалов (Ordinal Base).
Таблица указателей на имена (Name Table Pointers)Данная структура содержит указатели на имена экспортируемых функций, указатели отсортированы в лексическом порядке для обеспечения возможности бинарного поиска. Каждый указатель занимает 4 байта. Имена функций обычно лежат в секции экспорта, но я опять сказал обычно
Вы их можете помещать не туда. Еще раз повторюсь, секции - нечто эфемерное, обеспечивающее упаковку программы в файле и защиту участков кода, но не больше.
Таблица ординалов (Ordinal Table)Данная структура совместно с Name Table Pointers формирует 2 параллельных массива, разделенных для облегчения к ним доступа индексированиемна родные для процессора данные (слова, двойные слова, но не сложные структуры). Данный массив содержит ординалы экспорта, которые в общем случае являются индексами в Address Table экспорта (за вычетом базы Ordinal Base). Элементами данного массива являются слова (2 байта).
Таблица имен экспорта (Export Name Table)Эта таблица содержит необязательные (по мнению Microsoft) имена экспортируемых функций. Данный массив используется для совместно с Name Table Pointers и Ordinal Table для обеспечения связывания загрузчиком импорта/экспорта по имени. Механизм описывался выше. Каждый элемент являет собой ASCII строку с именем экспортируемой функции. Никто не говорит, что они должны в файле идти друг за другом последовательно хотя так и построено большинство файлов. Надо отметить, что имена экспорта чувствительны к регистру. Отметим особенность загрузчика - при связывании, если адрес функции находится в секции экспорта, то на самом деле по указанному адресу лежит строка переадресующая к другой библиотеке экспортирующей данную функцию (с указанием библиотеки и самой функции), это называется передача экспорта.
Импорт (.idata)По сравнению со старыми 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
Каталог импорта (Import Directory Table)Информация импорта начинается с Import Directory Table, которая описывает остальную информацию об импорте. Import Directory Table содержит адресную информацию используемую для разрешения ссылок на точки входа внутри образа библиотеки. Таблица импорта состоит из отдельных входов, как минимум по одному на каждую импортируемую библиотеку. Последний вход, указывающий на конец таблицы является пустым (заполнен нулями). В "" файлах вся информация об импорте предворяется записью Import Directory Table (но физически вы можете разместить эту таблицу где угодно). Дело обстоит примерно так:
<table border='1' bgcolor='#FFFFB3'>
<caption align='right'><b>Стуктура секции импорта </b></caption>
<tr>
<td>Каталог импорта </td>
<td>Import Directory Table </td>
</tr>
<tr>
<td>Таблица ссылок на имена </td>
<td>LookUp Table </td>
</tr>
<tr>
<td>Таблица имен </td>
<td>Hint-Name Table </td>
</tr>
<tr>
<td>Таблица адресов импорта </td>
<td>Import Address Table </td>
</tr>
</table>
Это приблизительная последовательноть расположения в файле различных частей секции импорта, создаваемой существующими компоновщиками. В реальных файлах нет никаких ограничений на порядок следования участков секции импорта, а у загрузчика Windows'95 и на расположение последних. Формат одного входа каталога импорта приведен в таблице чуть ниже.
<table border='1' bgcolor='#FFFFB3'>
<caption align='right'><b>Стуктура входа каталога </b></caption>
<tr>
<td></td>
<td>Размер или </td>
<td>Имя </td>
<td></td>
</tr>
<tr>
<td>00h</td>
<td>DWord </td>
<td>Import LookUp </td>
<td>Содержит ссылку на табличку RVA указывающих на соответствующие Hint-Name's или непосредственноординал ипортируемого </td>
</tr>
<tr>
<td>04h</td>
<td>DWord</td>
<td>Time/Date Stamp</td>
<td>Отметка о времени создания, часто содержит 0</td>
</tr>
<tr>
<td>08h </td>
<td>DWord </td>
<td>Forward Chain</td>
<td>связано с возможностью передачи экспорта в другие библиотеки. Обычно равно 0FFFFFFFFh</td>
</tr>
<tr>
<td>0Ch </td>
<td>DWord </td>
<td>Name RVA </td>
<td>Ссылка на библиотеку с которой нам необходимо поиметь вызовы представлена в виде ASCIZ.</td>
</tr>
<tr>
<td>10h</td>
<td>DWord </td>
<td>Addres Table RVA </td>
<td>Ссылка на табличку адресов импорта, заполняется системой при свя</td>
</tr>
</table>
Общий размер таблички импорта 14h.
Таблица просмотра импорта (Import LookUp Table)Имена сервисов библиотеки содержатся в Hint-Name's Table. Ее формат довольно прост:
Hint-Name Entry
Word
---------------------------------
Hint Размер произвольный
---------------------------------
ASCIZ Service Name Byte
---------------------------------
PAD Строка закрывается нулевым байтом, и при необходимости ее длинна выравнивается до четной границы еще одним 0. На имена сервисов и ссылаются RVA из таблицы Import LookUp. В случае импорта по ординалам старший бит значения из таблицы Import LookUp установлен в единицу. Конец таблицы находится по нулевому элементу. При попытке связывания по имени системный загрузчик использует вначале значение Hint (укороченный идентификатор точки входа) и только при неудачной попытке его использования производит в своих системных таблицах поиск требуемой точки входа. Имя сервиса чувствительно к регистру. Имя библиотеки - нет.
Таблица адресов импорта (Import Address Table)Данная таблица принимает в себя информацию после связывания загрузчиком импорта из внешних библиотек, она завершается нулевым элементом. Очень интересным фактом является то, самой Windows'95. Подобный факт заставляет предполагать, что загрузчик может не выполнять утомительной процедуры настройки во многих случаях. Ему будет необходимо лишь загрузить файл в ОЗУ и передать туда управление... Да здравствует вечно живой COM формат! Да, еще, для перехвата функции из библиотеки можно поменять адрес в этой таблице - довольно простой и общий метод перехвата управления внутри отдельного процесса. И еще пара плюшек на заметку, данная таблица (как и многие другие таблицы импорта) может находиться на своем старом месте, а вот Import Directory Table имеет смысл изменить, перенести в требуемое место и там оставить. Файл корректируется минимально, а проблемы возникающие при этом весьма незначительны и я их описывал.
Ресурсы (.rsrc)Ресурсы представляют собой многоуровневое двоично-отсортированноедерево. Их спроектированна структура позволяет содержать до 2^31 уровней, однако, реально используется только 3: самый верхний есть Type, затем Name, и затем Language (тип, имя, язык). Типичное представление ресурсного участка в файлах:
<table border='1' bgcolor='#FFFFB3'>
<caption align='right'><b>Таблица </b></caption>
<tr>
<td>Каталог </td>
<td>Resources Directory Table</td>
</tr>
<tr>
<td>Данные </td>
<td>Resources Data</td>
</tr>
</table>
Каталог ресурсов (Resource Directory Table)Структуру каталога ресурсов рассмотрим ниже.
<table border='1' bgcolor='#FFFFB3'>
<caption align='right'><b>Каталог </b></caption>
<tr>
<td></td>
<td>Размер или </td>
<td>Имя </td>
<td></td>
</tr>
<tr>
<td>00h</td>
<td>DWord</td>
<td>Flags</td>
<td>Пока не используются, должны быть сброшены в 0</td>
</tr>
<tr>
<td>04h</td>
<td>DWord</td>
<td>Time/Date Stamp</td>
<td>Дата и время подключения ресурсов от ресурсного компиля</td>
</tr>
<tr>
<td>08h</td>
<td>Word</td>
<td>Major Version</td>
<td>номер версии, старший по </td>
</tr>
<tr>
<td>0Ah</td>
<td>Word</td>
<td>Minor Version</td>
<td>номер версии, младший по </td>
</tr>
<tr>
<td>0Ch</td>
<td>Word</td>
<td>Name Entry</td>
<td>Количество входов в таблицу имен ресурсов, таблица располагается в самом
начале массива входов и содержит строковые имена ассоциируемые с </td>
</tr>
<tr>
<td>0Eh</td>
<td>Word</td>
<td>ID_Num Entry</td>
<td>Количество 32-битовых идентификаторов</td>
</tr>
</table>
Общий размер структуры 10h. За каталогом ресурсов сразу следует массив переменной длинны, содержащий ресурсные входы. Name Entry содержит число ресурсных входов имеющих имена (связанные с каждым входом). Имена нечувствительнык регистру и расположены в порядке возрастания. ID_Num Entry определяет число входов имеющих в качестве имени 32-битовый идентификатор. Эти входы так же отсортированы по возрастанию. Данная структура позволяет получать быстрый доступ к ресурсам по имени или по идентификатору, но для отдельно взятого ресурса только одна из форм поиска поддерживается, не обе! Это согласуется с синтаксисом .RC и .RES файлов. Каждый вход в таблице ресурсов имеет следующий формат:
<table border='1' bgcolor='#FFFFB3'>
<caption align='right'><b>Структура ресурсного </b></caption>
<tr>
<td></td>
<td>Размер или </td>
<td>Имя </td>
<td></td>
</tr>
<tr>
<td>00h</td>
<td>DWord</td>
<td>Name RVA or Res ID</td>
<td>Поле содержит либо идентификатор ресурса, либо указатель на его
имя в таблице имен </td>
</tr>
<tr>
<td>04h</td>
<td>DWord</td>
<td>Data Entry RVA or SubDirectory RVA</td>
<td>Указывает либо на данные, либо на еще одну таблицу входов ресурсов,
31-бит сброшенный в 0 указывает на то, что это ссылка на данные и </td>
</tr>
</table>
Размер ресурсного входа 08h. Строки каталога ресурсов имеют следующий формат:
File Name:
----------------------------------------
File Type: Length
----------------------------------------
Word Unicode String
----------------------------------------
unpredictable длина строки должна быть кратна 2 (это очевидно)
Все такие строковые объекты часто хранят вместе
Строки каталога ресурсов размещают после последнего Resource Directory Entry но до первого Resource Data Item, это позволяет более компактно разместить информацию. Каждый пункт данных имеет следующий формат:
<table border='1' bgcolor='#FFFFB3'>
<caption align='right'><b>Указатель на данные </b></caption>
<tr>
<td></td>
<td>Размер или </td>
<td>Имя </td>
<td></td>
</tr>
<tr>
<td>00h </td>
<td>DWord </td>
<td>Data RVA </td>
<td>Указатель на реально расположенные данные относительно Image Base </td>
</tr>
<tr>
<td>04h </td>
<td>DWord </td>
<td>Size </td>
<td>Размер ресурсных данных </td>
</tr>
<tr>
<td>08h </td>
<td>DWord </td>
<td>CodePage </td>
<td>Кодовая страница </td>
</tr>
<tr>
<td>0Ch </td>
<td>DWord </td>
<td>Reserved </td>
<td>Не используется и устанавливается в 0 </td>
</tr>
</table>
Размер указателя данные ресурса 10h. Каждый вход в таблице ресурсов описывает узел в дереве ресурсов. Он содержит адрес относительно Image Base, поле Size указывает на число байов данных находящихся по этому адресу, а кодовая страница используется для расшифровки ключевых значений внутри ресурсных данных. Обычно новые приложения содержат значение соответствующее Unicode кодовой таблице. (хотя, хм. Обычно?)
Отладочная информация (Debug Information)Отладочная информация размещается для использования отладчиком и создается в пределах формата линкером. Единственная определенная структура - Таблица отладочной информации (Debug Directory Table). PE файлы также поддерживают COFF информацию для отладчика (соответствующие ссылки есть в заголовке). Здесь будет дано очень сокращенное общее описание отладочной информации в PE файлах.
Отладочный каталог (.debug)В каталоге отладки хранятся ссылки на прочую отладочную информацию, формат его следующий:
<table border='1' bgcolor='#FFFFB3'>
<caption align='right'><b>Каталог </b></caption>
<tr>
<td></td>
<td>Размер или </td>
<td>Имя </td>
<td></td>
</tr>
<tr>
<td>00h</td>
<td>DWord</td>
<td>Debug Flags</td>
<td>Не используются и установлены в нулевое </td>
</tr>
<tr>
<td>04h</td>
<td>DWord</td>
<td>Time/Date Stamp</td>
<td>Дата и время создания отладочной </td>
</tr>
<tr>
<td>08h</td>
<td>Word</td>
<td>Major Version</td>
<td>Старший номер версии отладочной </td>
</tr>
<tr>
<td>0Ah</td>
<td>Word</td>
<td>Minor Version</td>
<td>Младший номер версии --//--</td>
</tr>
<tr>
<td>0Ch</td>
<td>DWord</td>
<td>Debug Type</td>
<td>Тип информации для </td>
</tr>
<tr>
<td>10h</td>
<td>DWord</td>
<td>Data Size</td>
<td>Размер в байтах данных для отладки без размера </td>
</tr>
<tr>
<td>14h</td>
<td>DWord</td>
<td>Data RVA</td>
<td>Адрес расположения отладочных данных в </td>
</tr>
<tr>
<td>18h</td>
<td>DWord</td>
<td>Data Seek</td>
<td>Смещение к отладочным данным в </td>
</tr>
</table>
<p>Размер элемента каталога отладки 1Ch. Тип отладочной информации:</p>
<ul>
<li>0000h - UNKNOWN/BORLAND (всегда они в стороне держатся, Inprise однако)</li>
<li>0001h - COFF таблица символов.</li>
<li>0002h - CodeView Таблица символов.</li>
<li>0003h - FPO таблица символов.</li>
<li>0004h - MISC \</li>
<li>0005h - EXCEPTION > Эти три флага мною не проверялись!</li>
<li>0006h - FIXUP /</li>
</ul>
<p>Если в программе содержится более одного типа отладочной информации,
то следующая запись в каталоге отладки будет следовать сразу за первой и
иметь не нулевое значение. </p>
Список литературы и источноков информации<ol>
<li>ФОРМАТ ИСПОЛНЯЕМЫХ ФАЙЛОВ PortableExecutables (PE)by Hard Wisdo, перевод некого Roland / [RD]</li>
<li>Portable Executable Formats, Tool Interface Standards (TIS), Formats Specification for Windows, Version 1.0</li>
</ol>
это контент от Центр информации по операционным системам
( http://www.osrc.info/plugins/content/content.php?content.17 )