Автор: Пользователь скрыл имя, 03 Октября 2011 в 21:21, реферат
В практической деятельности большинства программистов рано или поздно возникают проблемы, для решения которых необходимо знать форматы исполняемых файлов. Если ассемблер является отражением архитектуры компьютера, то формат исполняемого файла является отражением архитектуры операционной системы.
Технике импорта функций в PE-файле следует уделить особое внимание, так как именно эти исполняемые файлы работают на платформе Win32. Данная платформа, как известно, обеспечивает работу 32-разрядных версий операционной системы Windows. Особенность работы приложений в этих операционных системах заключается в том, что они вынуждены, помимо своей внутренней работы, связанной с обработкой некоторых данных, постоянно обращаться к операционной системе за различными сервисами (подробнее этот вопрос рассмотрен на уроке 18). Физически такое обращение осуществляется в форме вызова функций, которые разбросаны по различным системным dll-библиотекам. В принципе, программист может разработать и свои dll-библиотеки. Информация о том, какие функции и из каких библиотек используются в данном файле, содержится в секции .idata. Эту секцию называют также таблицей импорта. С точки зрения структуры секция .idata состоит из трех частей:
m массив с описанием используемых dll-библиотек — ArrayDllDef;
m два массива с адресами импортируемых функций — ArrayAdressImpFunc;
m массив с описанием имен импортируемых функций — ArrayNameImpFunc.
Эти четыре массива расположены в секции .idata последовательно друг за другом. Массив с описанием используемых dll-библиотек ArrayDllDef предназначен для указания местоположения в секции .idata имен dll-библиотек, массивов с адресами импортируемых функций ArrayAdressImpFunc для каждой из используемых dll-библиотек. Массив ArrayDllDef состоит из 20-байтовых элементов, структура которых показана в табл. 32.
Таблица 32. Элементы массива ArrayDllDef
Смещение Размер, Назначение
байт
+00h 4 Значение
смещения от начала секции .idata к началу мас-
сива с адресами импортируемых функций ArrayAdressImpFunc
(первая копия)
+04h 4 0
+08h 4 0
+0ch 4 Значение
смещения от начала секции .idata до начала
ASCIIZ строки с именем dll-библиотеки
+10h 4 Значение
смещения от начала секции .idata к началу мас-
сива с адресами импортируемых функций ArrayAdressImpFunc
(вторая копия)
Количество элементов массива ArrayDllDef равно количеству dll-библиотек, функции которых импортируются данным приложением. Конец массива ArrayDllDef обозначается элементом с нулевыми полями (его общий размер 20 байт). За этим массивом следуют массивы с адресами импортируемых функций ArrayAdressImp- Func для каждой из dll-библиотек. Количество этих массивов равно количеству dll-библиотек. Для удобства обсуждения нужно рассматривать совокупность этих массивов, как единое целое. Тогда можно говорить, что секция .idata PE-файла содержит две копии этой совокупности массивов ArrayAdressImpFunc. Смещение относительно начала секции .idata к началу каждого массива в первой копии совокупности массивов ArrayAdressImpFunc для конкретной dll-библиотеки содержится в поле со смещением +00h в соответствующем элементе массива ArrayDllDef. Смещение к началу каждого массива ArrayAdressImpFunc во второй копии находится в поле со смещением +10h в элементе массива ArrayDllDef для соответствующей dll-бибилиотеки. Элемент массива ArrayAdressImpFunc представляет собой двойное слово, содержимое которого соответствует импортируемой функции и зависит от того, в какой момент времени оно рассматривается и в какой копии совокупности массивов ArrayAdressImpFunc оно содержится. Ответы на эти вопросы следуют из ответа на вопрос: а зачем вообще нужны две копии совокупности массивов ArrayAdressImpFunc?
В полной мере необходимость в двух копиях массивов ArrayAdressImpFunc проявляется лишь после того, как файл загружен в память для выполнения. До этого, пока PE-файл находится на диске, содержимое этих двух копий абсолютно одинаково, что вы можете наблюдать, открыв файл для просмотра в шестнадцатеричном виде. Каждый элемент массива ArrayAdressImpFunc в виде двойного слова соответствует одной импортируемой функции и является смещением относительно начала секции .idata к элементу массива ArrayNameImpFunc. В массиве ArrayName-ImpFunc содержатся имена импортируемых функций, их номера экспорта (см. далее) из соответствующих dll-библиотек. Конец каждого массива ArrayAdressImpFunc обозначается нулевым двойным словом. Массивы ArrayAdressImpFunc в первой копии не изменяются загрузчиком при загрузке в память файла для исполнения. Массивы ArrayAdressImpFunc во второй копии изменяются в процессе загрузки следующим образом. Загрузчик последовательно просматривает элементы массива ArrayAdressImpFunc, по содержащимся в них смещениям относительно начала секции .idata находит адреса (или экспортные номера) функций в соответствующих dll-библиотеках и замещает содержимое двойных слов на истинные значения адресов импортируемых функций в памяти. Таким образом, массивы Array-AdressImpFunc в первой копии не являются, строго говоря, массивами адресов, и вообще, будьте готовы к тому, что компиляторы фирм, отличных от Microsoft, могут создавать PE-файлы с одной копией массивов ArrayAdressImpFunc. Судя по вышесказанному, необходимость в наличии двух копий представляется сомнительной, если только за этим не скрывается нечто большее.
Как уже было отмечено выше, в массиве ArrayNameImpFunc хранятся имена и экспортные номера функций, используемых в данном исполняемом файле. Элемент этого массива имеет формат, показанный в табл. 33.
Таблица 33. Структура элемента массива ArrayNameImpFunc
Смещение Размер, Назначение
байт
+00h 2 Номер
экспорта из dll-библиотеки для данной импортируе-
мой функции
+02h ? ASCIIZ-строка с именем импортируемой функции
Для чего нужна такая схема формирования адресов импортируемых функций? Дело в том, что вызов импортируемых функций в исполняемом файле осуществляется командами jmp или call. В качестве адресов перехода в этих командах используются адреса элементов во второй копии массивов ArrayAdressImpFunc, соответствующих импортируемым функциям. Как мы уже отметили ранее, в элементах этого массива загрузчиком были сформированы истинные адреса этих функций в памяти. То есть элементы массива ArrayAdressImpFunc играют роль переходников между воображаемыми и истинными адресами импортируемых функций.
Файл PE-формата может содержать секцию .edata, в которую помещается информация об экспортируемых данным файлом функциях. Обычные приложения редко содержат подобную секцию. Ее наличие характерно для dll-библиотек, в которых экспорт функций является основной задачей. Поэтому для ознакомления с данным материалом откройте в шестнадцатеричном виде любой файл с расширением .dll и найдите в нем секцию .edata.
Секция .edata состоит из заголовка и трех массивов, указатели на которые содержатся в заголовке секции. Структура оглавления показана в табл. 34.
Таблица 34. Структура оглавления секции .edata
Смещение Размер, Назначение
байт
+00h 4 0
+04h 4 Время и дата создания файла или 0
+08h 4 0
Таблица 34 (продолжение)
Смещение Размер, Назначение
байт
+0ch 4 Смещение
относительно начала секции .edata к ASCIIZ-
строке с именем данной dll-бибилиотеки
+10h 4 Начальное
значение, с которого начинаются экспортные
номера функций данной dll-библиотеки
+14h 4 Количество
элементов в массиве адресов экспортируемых
функций ArrAdrExpFunc
+18h 4 Количество
элементов в массиве имен экспортируемых
функций ArrNameExpFunc
+1ch 4 Смещение
относительно начала секции .edata к массиву
ArrAdrExpFunc. Этот массив содержит
смещения относи-
тельно начала PE-файла точек входа для
каждой экспор-
тируемой функции
+20h 4 Смещение
относительно начала секции .edata к массиву
ArrNameExpFunc, который содержит указатели
на строки
с именами экспортируемых функций
+24h 4 Смещение
относительно начала секции .edata к массиву
ArrNumExpFunc, который содержит слова
с номерами экспорти-
руемых функций. Для получения истинного
экспортного
номера функции эти номера необходимо
складывать со
значением в поле +10h оглавления секции .edata
Центральное место в секции .edata занимает массив двойных слов ArrAdrExpFunc, содержащий адреса экспортируемых функций в PE-файле. Записи в этом массиве упорядочены в соответствии с экспортными номерами функций в dll-библиотеке. Фактически, номер функции является индексом в массиве ArrAdrExpFunc. И если есть разрывы в нумерации функций, то пропущенным номерам будут соответствовать нулевые элементы в таблице ArrAdrExpFunc.
За массивом ArrAdrExpFunc по адресу, находящемуся в оглавлении секции (поле со смещением +10h), расположен массив двойных слов ArrNameExpFunc, который содержит смещения относительно начала секции .edata к ASCIIZ-строкам с именами экспортируемых функций. Этот массив следует рассматривать совместно с массивом слов ArrNumExpFunc (его адрес в поле со смещением +24h), в котором содержатся номера экспортируемых функций. Как уже было отмечено выше, нумерация экспортируемых функций не обязательно должна быть линейной. Значения слов в массиве ArrNumExpFunc являются индексами в массиве ArrAdrExpFunc. Что же касается самих массивов ArrNameExpFunc и ArrNumExpFunc, то между ними существует взаимно однозначное соответствие, так как они содержат одинаковое количество элементов. Первый элемент массива ArrNumExpFunc является номером функции с именем (если оно есть), доступ к которому можно получить, используя смещение, содержащееся в первом элементе массива ArrNameExpFunc. При этом не забывайте, что для получения истинного номера экспортируемой функции к значениям, содержащимся в элементах массива ArrNumExpFunc, необходимо прибавлять значение в поле со смещением +10h заголовка секции .edata. Такая схема организации секции .edata позволяет экспортировать функции по их именам и номерам.
На этом мы закончим описание форматов исполняемых файлов. Нам не удалось обсудить все тонкости, но это и не являлось нашей конечной целью. Главное, чего мы достигли, — это еще большей уверенности в своих силах. По сути, компьютер — это более или менее «большой кусок (хотя и высокотехнологичный) железа». Магического в нем на самом деле очень мало. Его поведение (по крайней мере на сегодняшнем этапе технологического развития) полностью зависит от управляющей им программы. Настоящий урок показал нам возможные внутренние форматы этой программы. Разобравшись с ними, вы, может быть, даже почувствовали в себе прилив творческих сил и захотели написать нечто похожее на собственный дизассемблер или, на худой конец, утилиту, исследующую и корректирующую внутренности исполняемых файлов. Только большая просьба к вам: не нужно увлекаться написанием вирусов. Займитесь лучше изучением материала следующих уроков — они достойны вашего внимания.
ю Работая с операционными системами фирмы Microsoft, пользователь может иметь дело с четырьмя типами исполняемых файлов. Изучая их, мы фактически можем проследить, как эволюционировали операционные системы этой фирмы.
ю Самый простой формат исполняемого файла — файл com-формата. Данный тип исполняемого файла достался по наследству от 8-разрядной операционной системы CP/M. Ему свойственны жесткие требования к объему занимаемой памяти и оформлению исходного текста программы.
ю По мере развития аппаратных возможностей компьютера на базе микропроцессоров Intel и для удовлетворения возросших потребностей программ, был разработан формат исполняемых файлов MS-DOS. Этот формат учитывает требования по сегментации памяти и при соблюдении этих требований дает возможность разрабатывать достаточно большие программы.
ю Появление операционной системы Windows потребовало внесения изменений в формат исполняемого файла. Основных причин было несколько. Например, появились новые объекты (ресурсы), поддержка которых была необходима на уровне операционной системы. Также со стороны микропроцессора появились новые возможности: дополнительные режимы работы, аппаратная поддержка многозадачности, организация защиты на основе привилегий и т. д. Старый формат исполняемого файла для операционной системы MS-DOS даже приблизительно не мог обеспечить все эти возможности.
ю Windows 3.1 работает с исполняемыми файлами NE-формата (New Executable — новый исполняемый файл). Этот формат полностью обеспечивает потребности 16-разрядной платформы — Win16, хотя и имеет ряд недостатков.