#c #portable-executable
#c #переносимый-исполняемый
Вопрос:
Я пытался проанализировать / отобразить информацию в таблице адресов импорта (IAT) процесса после его загрузки и запуска. Я понимаю, что вызовы API в программах переходят к соответствующей точке в IAT, которая затем переходит к фактической функции в загруженных DLL.
Правильно ли, что IAT можно найти, прочитав заголовок PE и следуя OptionalHeader.DataDirectory[1] указатель на массив IMAGE_IMPORT_DESCRIPTORs. Затем следуем указателям FirstThunk. В то время как приведенные здесь указатели на OriginalFirstThunk дадут вам исходную таблицу импорта (IT)?
Я также попытался следовать OptionalHeader.Указатель DataDirectory[12] в заголовке PE, но это было еще менее успешным.
Я тестировал это, пытаясь проанализировать эту структуру для notepad.exe (32 бит), используя ReadProcessMemory из другого процесса.
Вот приблизительный C-псевдокод для того, что я делаю:
char buf[128];
// get first import descriptor
readMemory(amp;import, procImgBase DataDirectory[1].VirtualAddress, sizeof(IMAGE_IMPORT_DESCRIPTOR));
// get dll name
readMemory(buf, import.Name procImgBase, 127);
printf("libname: %sn", buf);
// get first function name
DWORD iltAddress = 0;
readMemory(amp;iltAddress, import.FirstThunk procImgBase, 4);
readMemory(buf, iltAddress procImgBase, 127);
printf("fname: %sn", libName 2); // <-- the 2 for the 2byte 'hint' of import lookup table entries
Если в строке с 3-й по последнюю я заменяю ее на import .OriginalFirstThunk вместо FirstThunk, он напечатает все, как ожидалось. Должно быть, я что-то упускаю концептуально, и поэтому мне было интересно, может ли кто-нибудь пояснить, что это такое, для меня?
Большое спасибо!
Ответ №1:
Похоже, вы движетесь в правильном направлении. Некоторые примечания:
- DataDirectory выдает вам смещение к массиву IMAGE_IMPORT_DESCRIPTOR, который завершается вводом всех нулей. Для каждой импортируемой библиотеки DLL будет один IMAGE_IMPORT_DESCRIPTOR
- IMAGE_IMPORT_DESCRIPTOR имеет смещения в 2 массива IMAGE_THUNK_DATA, один из которых поддерживает смещения в именах импортированных функций (OriginalFirstThunk), а другой, который теперь содержит фактические адреса функций (FirstThunk)
Поскольку ваш исполняемый файл запущен, IAT должен содержать фактический адрес функции, а не RVA для записи имени.
Вместо этого вы могли бы сделать что-то вроде этого:
DWORD rva_to_name_of_function = 0;
DWORD address_of_function = 0;
// get the RVA of the IMAGE_IMPORT_BY_NAME entry
readMemory(amp;rva_to_name, import.OriginalFirstThunk procImgBase, 4);
// copy the name of the import
readMemory(buf, rva_to_name procImgBase 2, 127);
// get the actual address that was filled in by the loader
readMemory(amp;address_of_function, import.FirstThunk procImgBase, 4);
printf("fname: %s address: %X", buf, address_of_function);
Взгляните на эту статью для получения некоторых полезных деталей:
http://msdn.microsoft.com/en-us/magazine/cc301808.aspx
Комментарии:
1. Спасибо! теперь это имеет смысл. Я думаю, я должен был понять, когда значения, которые я получал обратно, выглядели как действительные адреса, а не RVA.
2. @Eric Rahm, ты уверен насчет своего первого пункта? В
winnt.h
необязательные структуры заголовка определяютIMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
, неIMAGE_IMPORT_DESCRIPTOR DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
3. Вот где я сейчас застрял… Я готов получить каталоги данных, но как загрузить их в правильные структуры, чтобы получить имена DLL из IAT, ILT или INT, в настоящее время блокирует меня.
Ответ №2:
Эрик дал хороший ответ, вот несколько дополнительных разъяснений:
Я понимаю, что вызовы API в программах переходят к соответствующей точке в IAT, которая затем переходит к фактической функции в загруженных DLL.
Программа использует ВЫЗОВ PTR DS:[IAT-ADDRESS], который считывает данные с адреса в IAT, чтобы определить, где находится программа во время выполнения.
Принимая во внимание, что указатели OriginalFirstThunk, приведенные здесь, дадут вам исходную таблицу импорта (IT)?
Указатели OriginalFirstThunk указывают на таблицу поиска импорта (ILT). Если вы откроете двоичный файл на диске, ILT и IAT будут идентичны; оба содержат строки RVA для имен функций. После загрузки программы записи IAT (в памяти) перезаписываются адресами импортированных функций.
По моему опыту, лучшим источником информации о таблице импорта и всех сопутствующих структурах данных является сама спецификация PE. Если вы терпеливо прочтете раздел об импорте, все станет ясно.
Комментарии:
1. Спасибо за разъяснение этого. Я читал и перечитывал документацию MSDN, но мой мозг, похоже, устроен иначе, чем у авторов — я просто не мог понять эту ее часть. То, как вы формулируете вещи, понятнее 🙂
2. Этот pecoff docx меня действительно раздражает. В ней содержится тонна информации, но в ней есть некоторые действительно обидные пробелы… Тоже дезинформация. Их предложение о наличии «практически всех исполняемых файлов»
.idata
не так уж и всеобъемлюще…