Экспорт файлов PE
Противоположностью импорта функции является экспорт функции для использования EXE-файлами или другими DLL-библиотеками.Файл PE хранит информацию о своих экспортируемых функциях в разделе .edata.Как правило, PE EXE-файлы, созданные Microsoft linker, ничего не экспортируют, поэтому у них нет раздела .edata.TLINK32 от Borland всегда экспортирует хотя бы один символ из EXE-файла.Большинство библиотек DLL выполняют функции экспорта и имеют раздел .edata.Основными компонентами раздела .edata (он же таблица экспорта) являются таблицы имен функций, адресов точек входа и порядковых значений экспорта.В NE-файле эквивалентами таблицы экспорта являются таблица записей, таблица имен резидентов и таблица имен нерезидентов.Эти таблицы хранятся как часть заголовка сетевого элемента, а не в отдельных сегментах или ресурсах.
В начале раздела .edata находится структура IMAGE_EXPORT_DIRECTORY (см. Таблицу 10).За этой структурой сразу следуют данные, на которые указывают поля в структуре.
Таблица 10. IMAGE_EXPORT_DIRECTORY Формат
DWORD Characteristics
Это поле не используется и всегдаустановить на 0.
DWORD TimeDateStamp
Отметка времени / даты, указывающая, когда был создан этот файл.
WORD MajorVersion
WORD MinorVersion
Эти поля не используются и имеют значение 0.
DWORD Name
RVA строки ASCIIZ с именем этой DLL.
DWORD Base
Начальный порядковый номер для экспортируемых функций.Например, если файл экспортирует функции с порядковыми значениями 10, 11 и 12, это поле содержит 10. Чтобы получить экспортированный порядковый номер для функции, необходимо добавить это значение в соответствующий элемент массива AddressOfNameOrdinals.
DWORD NumberOfFunctions
Количество элементов в массиве AddressOfFunctions.Это значение также является числом функций, экспортируемых этим модулем.Теоретически это значение может отличаться от поля NumberOfNames (далее), но на самом деле они всегда одинаковы.
DWORD NumberOfNames
Количество элементов в массиве AddressOfNames.Кажется, что это значение всегда идентично полю NumberOfFunctions, как и количество экспортируемых функций.
PDWORD *AddressOfFunctions
Это поле является RVA и указывает на массив адресов функций.Адреса функций - это точки входа (RVA) для каждой экспортируемой функции в этом модуле.
PDWORD *AddressOfNames
Это поле является RVA и указывает на массив строковых указателей.Строки - это имена экспортируемых функций в этом модуле.
PWORD *AddressOfNameOrdinals
Это поле является RVA и указывает на массив WORD.СЛОВА - это порядковые номера экспорта всех экспортируемых функций в этом модуле.Однако не забудьте добавить начальный порядковый номер, указанный в поле «База».
Структура таблицы экспорта выглядит несколько странно (см. Рисунок 4 и Таблицу 10).Как я упоминал ранее, требования для экспорта функции - это имя, адрес и порядковый номер экспорта.Можно подумать, что разработчики формата PE поместили бы все эти три элемента в структуру, а затем получили бы массив этих структур.Вместо этого каждый компонент экспортируемой записи является элементом в массиве.Существует три из этих массивов (AddressOfFunctions, AddressOfNames, AddressOfNameOrdinals), и все они параллельны друг другу.Чтобы найти всю информацию о четвертой функции, вам нужно найти четвертый элемент в каждом массиве.
Рисунок 4. Экспорт макета таблицы
Таблица 11. Типичная таблица экспорта из EXE-файла
Name: KERNEL32.dll
Characteristics: 00000000
TimeDateStamp: 2C4857D3
Version: 0.00
Ordinal base: 00000001
# of functions: 0000021F
# of Names: 0000021F
Entry Pt Ordn Name
00005090 1 AddAtomA
00005100 2 AddAtomW
00025540 3 AddConsoleAliasA
00025500 4 AddConsoleAliasW
00026AC0 5 AllocConsole
00001000 6 BackupRead
00001E90 7 BackupSeek
00002100 8 BackupWrite
0002520C 9 BaseAttachCompleteThunk
00024C50 10 BasepDebugDump
// Rest of table omitted...
Кстати, если вы выгрузите экспорт из системных библиотек Windows NT (например, KERNEL32.DLL и USER32.DLL), вы заметите, что во многих случаях есть две функции, которые отличаются только на один символ в конец имени, например CreateWindowExA и CreateWindowExW. Вот как поддержка UNICODE реализована прозрачно. Функции, оканчивающиеся на A, являются совместимыми с ASCII (или ANSI) функциями, а функции, оканчивающиеся на W, являются UNICODE-версией функции. В вашем коде вы явно не указываете, какую функцию вызывать. Вместо этого соответствующая функция выбирается в WINDOWS.H через препроцессор #ifdefs. Этот отрывок из Windows NT WINDOWS.H показывает пример того, как это работает:
Копирование
#ifdef UNICODE
#define DefWindowProc DefWindowProcW
#else
#define DefWindowProc DefWindowProcA
#endif // !UNICODE
//
// Export Format
//
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
РЕДАКТИРОВАТЬ: В таблице экспорта в формате PE не дифференцируется, адрес функций является RVA только 64-битный адрес.
Источник: http://msdn.microsoft.com/en-us/library/ms809762.aspx