PE Format - IAT Вопросы - PullRequest
       5

PE Format - IAT Вопросы

5 голосов
/ 06 октября 2011

Я пытаюсь написать упаковщик EXE для Windows.У меня есть кое-что проработанное до сих пор.Часть, которая мне нужна, - это чтение «Таблицы каталогов BOUND IMPORT» (или раздела .idata?), В основном, раздела PE-файла, который содержит список библиотек DLL, которые необходимо импортировать загрузчику.

Мне интересно, как лучше всего:

[A] выяснить, где находится IAT (потому что запуск PEView для нескольких разных .exe-файлов, кажется, показывает, что этот список может содержаться в нескольких разных местах), а затем прочитайте список

ИЛИ

[B] Просто найдите способ непосредственно прочитать список библиотек DLL, которые требуется импортировать exe.

Есть ли способделать это?Есть ли какие-нибудь дополнительные читатели, которые могут порекомендовать, где должен быть IAT и как его читать?

Ответы [ 2 ]

8 голосов
/ 05 ноября 2012

Кроме того, вот pdf , который может помочь вам понять, как структуры названы и организованы. Некоторые полезные программы: CFF Explorer и хороший hex редактор .

Мой ответ отличается от приведенного выше тем, что в нем описан способ ручного выполнения описанного выше в исполняемом файле, все еще находящемся на диске.

Чтобы получить Относительный виртуальный адрес (адрес во время выполнения, или RVA) IAT:

  1. Начните с базового адреса двоичного файла. Это структура IMAGE_DOS_HEADER.
  2. Следуйте полю e_lfanew, чтобы перейти к структуре IMAGE_NT_HEADERS. Со смещения 0, прыгайте 0x3c вниз и разыменовывайте, чтобы получить начало IMAGE_NT_HEADERS.
  3. Следуйте OptionalHeader (содержится в IMAGE_NT_HEADERS и, следовательно, является смежным с ним). Чтобы добраться до структуры IMAGE_OPTIONAL_HEADER (несмотря на ее название, она больше не является обязательной), знайте, что это третья структура в IMAGE_NT_HEADER. Чтобы попасть в OptionalHeader, добавьте 0x18 к значению, которое вы разыменовали ранее
  4. Из OptionalHeader разыменуйте в DataDirectory. DataDirectory - это массив в OptionalHeader, который находится в IMAGE_NT_HEADERS. Следуйте за 24-й (если 0 является первым, как в 0, 1, 2 ...) записи в массиве DataDirectory к IMAGE_DIRECTORY_ENTRY_IAT. Добавьте 0xc0 к адресу, по которому вы сейчас находитесь, чтобы получить каталог таблицы адресов импорта

Если вы хотите просмотреть список библиотек DLL и их функциональных адресов, есть некоторая предыстория:

  • 3 важных поля в структуре IMAGE_IMPORT_DESCRIPTOR: OriginalFirstThunk (RVA таблицы поиска импорта), Имя (RVA имя ASCII Dll с нулевым символом в конце) и FirstThunk (RVA IAT / array линейных адресов, созданных загрузчиком).
  • Требуются два массива потому что один - это массив имен импортируемых подпрограмм (ILT), а Другой - это массив импортированных стандартных адресов (IAT). Подпрограммы импортированные из DLL могут быть импортированы по имени или по порядковому номеру число. Чтобы определить, является ли процедура порядковым импортом, проверьте флаг, установленный в поле Ordinal структуры IMAGE_THUNK_DATA в массиве ILT.
  • Каждая функция, импортированная модулем во время загрузки, будет представлена ​​структурой IMAGE_THUNK_DATA. И первый, и первый разделы указывают на массив IMAGE_THUNK_DATA. Однако структура IMAGE_THUNK_DATA является объединением, которое содержит другую структуру, IMAGE_IMPORT_BY_NAME. Это важно знать, поскольку оригинальный первый блок использует структуру IMAGE_IMPORT_BY_NAME, а первый блок использует поле Function во внутреннем объединении структуры IMAGE_THUNK_DATA.
  • RVA, указанный на диске, не позволит вам просмотреть файл, поскольку RVA представляет адрес, когда двоичный файл загружен в память; чтобы пройти двоичный файл на диске, вам нужно будет преобразовать значения RVA в правильную форму. Формула проста; HMODULE + RVA = линейный адрес элемента PE. HMODULE также известен как базовый адрес. Но получение базового адреса на самом деле требует алгоритма, который несколько длинен и зависит от того, какое значение RVA у вас есть на самом деле. Чтобы получить значение базового адреса для данного RVA для вычисления линейного адреса элемента PE на диске:

    1. Получить раздел Заголовок; Для этого просмотрите список разделов (таких как .data, .text, ect), пока не найдете раздел, в котором RVA вопроса находится в пределах currentSection.VirtualAddress и currentSection.VirtualAddress + currentSection.size.

      1.1) Сначала найдите количество разделов в FileHeader в структуре NT_HEADERS. Это 2 байта после 2-байтового номера машины в FileHeader.* Чтобы сделать это вручную: добавьте 0x6 к значению, разыменованному из e_lfanew; поэтому перейдите 0x3c со смещения 0, разыменуйте значение и 0x6 к этому. Затем прочитайте два байта и интерпретируйте как целое число.

      1.2) Найти местоположение первого раздела; это смежно с OptionalHeader. Помните, что в OptionalHeader есть массив DataDirectories. OptionalHeader имеет длину 216 байт плюс два слова в конце, обозначающие его окончание; поэтому возьмите 224 в шестнадцатеричном (0xe0) и добавьте его к значению, разыменованному в 0x3c с начала, чтобы получить первое местоположение раздела.

      1.3) Чтобы найти заголовок раздела, в котором находится ваш RVA, постоянно выполняйте этот тест относительно текущего раздела, в котором вы находитесь. Если тест не пройден, перейдите к следующему разделу. Если вы перебираете все разделы и обнаруживаете, что достигли конечных пустых слов, файл должен быть поврежден или вы допустили ошибку. Тест состоит в следующем: сравните RVA, который вы хотите преобразовать, в полезный указатель с виртуальным адресом раздела; RVA должно быть> = для виртуального адреса раздела и <сумма виртуального адреса раздела и виртуального размера. Виртуальный адрес раздела можно узнать, добавив 12 к адресу раздела. А виртуальный размер раздела можно найти по 8 на адрес раздела. Подведем итог: передайте if - (section.virtualAddress + section.virtualSize)> RVA> = section.virtualAddress. * Чтобы перейти к следующему разделу, длина описания раздела составляет 0x28; Вы можете просто добавить 0x28 к указателю текущего раздела, чтобы перейти к следующему. Последний раздел является нулевым байтом для обозначения конца.

    2. Из полученного заголовка раздела выполните следующее: (baseAddress + RVA) - (sectionHeader.virtualAddress - sectionhHeader.PointerToRawData). * Виртуальный адрес sectionHeader находится в 12 байтах от самого sectionHeader, как рассчитано выше. PointerToRawData находится в 20 от заголовка раздела.

    3. Полученное значение представляет фактический указатель на данные, требуемые / представленные RVA. Вы можете использовать его для поиска фактических данных в нужном месте.

Это был полный рот. Если вы хотите подвести итоги, вам следует прочитать страницы 257-60 главы 5 (Перехват таблиц вызовов) в Арсенале руткитов , но для более простого понимания графических изображений просмотрите pdf-ссылку openrce.org I дал около вершины.

Чтобы сделать это, начните с ...:

  1. Доберитесь до OptionalHeader как описано выше. OptionalHeader содержит массив элементов (DataDirectory) IMAGE_DATA_DIRECTORY в качестве последнего элемента. Вторым элементом в этом массиве является IMAGE_DIRECTORY_ENTRY_IMPORT, который определяет местонахождение IAT. Итак, чтобы уточнить, IMAGE_NT_HEADER содержит массив OptionalHeader, который содержит массив DataDirectory. Последняя запись в этом массиве будет обнулена.
  2. Из разыменования OptionalHeader в IMAGE_DIRECTORY_ENTRY_IMPORT. Следующим словом является размер каталога импорта. Начиная со смещения в файле, прыгайте на 0x68 вниз.
  3. Это значение представляет собой RVA каталога импорта, который представляет собой массив структур типа IMAGE_IMPORT_DESCRIPTOR (по одной для каждой библиотеки DLL, импортированной модулем), для последней из которой поля имеют нулевое значение. Третье слово в IMAGE_IMPORT_DESCRIPTOR содержит указатель FirstThunk на IMAGE_THUNK_DATA.
  4. Используя алгоритм, описанный выше, преобразуйте RVA каталога импорта в пригодный для использования указатель и используйте следующий алгоритм для перебора массива каталога импорта.

    4.1) Для importDescriptor преобразуйте поле имени RVA в указатель, чтобы получить имя. Может быть нулевым

    4.2) Чтобы получить имя и адрес каждой импортированной подпрограммы, получите записи OriginalFirstThunk и FirstThunk RVA дескриптора импорта. Каждый из OFT и FT может быть нулевым, что указывает на то, что он пустой, поэтому проверьте это.

    4.3) Конвертировать OFT RVA в указатели;OFT соответствует ILT, а FT соответствует IAT.И ILT, и IAT могут быть нулевыми, указывая, что они пусты.

    4.4) Получите имя функции, импортированной из указателя ILT, и адрес функции из указателя IAT.Чтобы перейти к следующей импортированной функции, помните, что ILT и IAT являются массивами;Следующий элемент находится на постоянном расстоянии.

    4.5) Убедитесь, что новые значения указателя ILT & IAT не равны нулю;если они не равны нулю, повторите.Если любой из них равен нулю, вы попали в конец списка функций, импортированных для этой библиотеки DLL;дескриптор импорта также является повторяющимся массивом, поэтому смещение к следующей импортируемой dll является постоянным.По сути, вы перебираете dll и для каждой dll перебираете функции, импортированные таким образом.19

4 голосов
/ 06 октября 2011

Да, вы можете найти IAT, просматривая заголовки исполняемого файла. Посмотрите в winnt.h объявления заголовков.

Отличную информацию о том, как найти информацию в заголовках, см. В статье Мэтта Пьетрека в MSDN Magazine , «Углубленный взгляд на формат исполняемых файлов Win32», части I и II .

Вы также можете получить актуальную спецификацию Microsoft PE по адресу здесь .

TL; DR: в основном последовательность поиска выглядит следующим образом:

  1. Начните с базового адреса двоичного файла. Это структура IMAGE_DOS_HEADER.
  2. Следуйте полю e_lfanew, чтобы перейти к структуре IMAGE_NT_HEADERS.
  3. Следуйте OptionalHeader, чтобы перейти к структуре IMAGE_OPTIONAL_HEADER (несмотря на ее название, она больше не является обязательной).
  4. Следуйте DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] к массиву IMAGE_IMPORT_DESCRIPTOR структур. В каждой импортированной DLL есть одна запись. Последняя запись в этом массиве будет обнулена.
  5. Поле Name в каждой записи представляет собой RVA, которое указывает на имя DLL. Поле FirstThunk - это RVA, которое указывает на IAT этой DLL, которая представляет собой массив структур IMAGE_THUNK_DATA.
...