Как прочитать имена функций экспорта (родной) DLL в C #? - PullRequest
4 голосов
/ 28 октября 2010

Я знаю, что могу прочитать спецификацию PE , чтобы написать код, который делает это. Однако, поскольку у меня не так много времени, я надеялся, что у некоторых из вас уже может быть готов такой код для отправки.

Важное примечание: Есть ли разница между 32-битным и 64-битным?

Спасибо за ваше время!

1 Ответ

4 голосов
/ 28 октября 2010

Экспорт файлов 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), и все они параллельны друг другу.Чтобы найти всю информацию о четвертой функции, вам нужно найти четвертый элемент в каждом массиве.

alt text

Рисунок 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

...