Таблица импорта
В официальном описании формата PE нет конкретно понятия таблица импорта, есть понятие Import Directory Table, что можно перевести как каталог импорта. Обычно когда говорят Import Table (Таблица импорта, сокращенно IT) имеют в виду каталог импорта. Вообще полностью информация об импорте, т.е. об импортируемых функциях, складывается из трех структур:
- Каталог импорта (Import Directory Table)
- Таблица просмотра импорта (Import LookUp Table)
- Таблица адресов импорта (Import Address Table, сокращенно IAT)
Каталог импорта
Каталог импорта представляет из себя последовательность однотипных структур. Каждая такая структура называется Thunk и представляет из себя последовательность 4х байтных полей:
OriginalFirstThunk TimeDateStamp ForwarderChain Name FirstThunk
Поле OriginalFirstThunk хранит в себе RVA поля в массиве адресов имен функций. Адрес в этом поле указывает на первое поле (для соотвествующей DLL) в таблице просмотра импорта, в которой хранятся имена функций из какой либо DLL (каждый Thunk относится к отдельной DLL). TimeDateStamp хранит в себе отметку о времени создания Thunk'a (часто содержит 0 или 0FFFFFFFFh). ForwarderChain точно не известно для чего, но часто значение его равно
0 или 0FFFFFFFFh.
Name хранит в себе RVA имени DLL. Поле FirstThunk аналогично полю OriginalFirstThunk
Таблица просмотра импорта
Таблица просмотра импорта (Import LookUp Table), как я уже написал выше, представляет из себя массив, в котором последовательно хранятся имена функций какой-либо DLL.
При запуске программы загрузчик ОС получает адреса используемых в программе функций и записывает их в массив адресов имен функций, на который ссылаются поля FirstThunk, и в котором до этого были RVA, ссылающиеся на имена соответствующих функций. После того, как этот массив заполнен адресами функций, он называется Таблицей Адресов Импорта (Import Address Table, сокращенно IAT).
После того, как IAT заполнена адресами нужных функций, программа может начать работу. Хоть мы и видим в дизассемблере например call MessageBoxA, на самом деле этот call вызывает функцию по адресу, хранящемуся в таблице адресов импорта. Т.е. на самом деле этот call выглядит так: call dword ptr [xxxxxx], где по адресу xxxxxx (это адрес в IAT) хранится адрес функции MessageBoxA в системе.
Для полноты понимания можно уточнить, что каталог импорта и таблица просмотра импорта создаются при компиляции программы, а таблица адресов импорта, на основании данных в каталоге импорта и таблице просмотра импорта, заполняется загрузчиком перед запуском программы и до заполнения адресами функций содержит RVA имен этих функций в таблице просмотра импорта.
У вас может возникнуть вопрос: "А зачем нужны два поля OriginalFirstThunk и FirstThunk, которые фактически одинаковы, т.е. которые в итоге приводят к одинаковым именам функций?". Дело в том, что когда PE-файл загружается в память, и загрузчик ОС заполняет IAT (где до этого были RVA имен функций) адресами соответствующих функций, то если потребуется найти имена функций, то их можно будет найти с помощью полей OriginalFirstThunk, которые указывают на массив, в котором хранятся RVA этих имен функций. Т.е. OriginalFirstThunk, в отличие от FirstThunk, указывает на массив адресов имен функций, который остается нетронутым, исходным, оригинальным так сказать. Поэтому это поле и называется OriginalFirstThunk. Иногда поле OriginalFirstThunk содержит в себе 0. В этом случае найти имена функций уже будет невозможно, только если считывать их из файла на диске.
Поясняющую схему, которую я (MozgC) вырисовывал в PaintBrush'e =), можете посмотреть тут:
Надеюсь, что хоть что-то из этого вы поняли. Если нет - не расстраивайтесь. Все-таки это вопрос не для полных новичков. А добавил я его в этот FAQ по многочисленным просьбам. Если не понятно, то могу лишь порекомендовать еще раз все внимательно прочитать и проверить все на практике (значения в каталоге импорта можете посмотреть с помощью PE Tools: PE Editor -> Directories -> Import Directory).