❓ Помните ли вы времена, когда Win95 только вышла, и все работало на процессорах i386 с тактовой частотой около 40 МГц? Разработчики PE-формата тогда придумали три способа, как справиться с импортом API из системных dll:
1. Статический импорт "Bound": Компилятор прописывает имена библиотек и адреса API в программу. Это экономит время на загрузку, но может привести к краху приложения, если версии библиотек изменятся.
2. Динамический импорт: Универсальный подход, где адреса функций вычисляются загрузчиком после загрузки программы в память. Да, это медленно, но зато надёжно.
3. Отложенный импорт "Delay": Загружаем функции только тогда, когда они реально нужны. Это сокращает время первоначальной загрузки, но может замедлить выполнение программы.
🕯 Структура и иерархия импорта
Система импорта в PE-файлах - это настоящая иерархия. Начало этой иерархии лежит в каталоге
DATA_DIRECTORY. В центре внимания всегда находится таблица адресов IMPORT_ADDRESS_TABLE (IAT), независимо от выбранной схемы импорта. Интересный момент: поле TimeDataStamp играет важную роль в процессе импорта.🗓 Основная таблица "Image Import Table"
Каталог начинается с нуля, а сам каталог является частью опционального заголовка. Здесь содержится массив дескрипторов, каждый из которых описывает импорт из одной dll. Поле
OriginalFirstThunk указывает на начало IMPORT_LOOKUP_TABLE, где лежат 32-битные значения для каждой функции API. Интересный момент: если старший бит взведён, то это ординал (номер) функции в экспорте dll, иначе это RVA-адрес записи с именем функции.📎 Статический импорт и его тонкости
Старый добрый статический импорт использует поле
TimeDataStamp для проверки валидности связывания. Если всё хорошо, загрузчик продолжает работу, если нет - переходит к динамическому импорту. В более новой схеме Bound, информация о связывании хранится в таблице, на которую указывает запись каталога Bound. Важно отметить, что поле TimeStamp в дескрипторе импорта указывает на тип связывания: 0 - динамический импорт, -1 - новая схема Bound, и все остальные значения - старая схема.💡 РЕ32+ без импорта
Заглянем в схемы импорта «Bound» и узнаем, как можно создавать приложения без секции импорта вообще. Представьте, никакого импорта — чистый хардкор! 🥇
Это возможно благодаря тому, что ОС проецирует Kernel32 и Ntdll.dll во все пользовательские процессы. Главная задача - найти базу Kernel32, пробраться к её экспорту и вытащить оттуда адреса всех необходимых API.
➡️ Читать подробнее
