Русский / Russian English / Английский

Сейчас на форуме: (+4 невидимых)
 · Начало · Статистика · Регистрация · Поиск · ПРАВИЛА ФОРУМА · Язык · RSS ·

 eXeL@B —› Программирование —› Заполнение таблицы импорта образа, загруженного по нестандартному адресу
Посл.ответ Сообщение

Ранг: 9.0 (гость)
Статус: Участник

Создано: 19 июля 2017 12:49 · Поправил: Aoizora New!
Цитата · Личное сообщение · #1

Я пишу функцию обработки импорта. Здесь нет некоторых нужных проверок: например, импорта по ординалу. Наверняка нет еще некоторых нужных вещей. Но моя цель сейчас - сделать минимальный работающий код, который заполнит таблицу импорта.

Важная информация: образ загружается по произвольному адресу, то есть память для него выделяется VirtualAlloc'ом, затем в этой памяти проставляются атрибуты секций, заполняется импорт и т.д.

Кодес такой:

Code:
  1. PIMAGE_THUNK_DATA GetOriginalFirstThunk(void* image_base, PIMAGE_IMPORT_DESCRIPTOR import_desk)
  2. {
  3.          return PIMAGE_THUNK_DATA((DWORD)image_base + import_desk->OriginalFirstThunk);
  4. }
  5.  
  6. PIMAGE_THUNK_DATA GetFirstThunk(void* image_base, PIMAGE_IMPORT_DESCRIPTOR import_desk)
  7. {
  8.          return PIMAGE_THUNK_DATA((DWORD)image_base + import_desk->FirstThunk);
  9. }


Code:
  1. void LoadPE_ResolveImport(LoadPE_CONTEXT* ctx)
  2. {
  3.     PIMAGE_IMPORT_DESCRIPTOR import_desc =
  4.         (PIMAGE_IMPORT_DESCRIPTOR) ((DWORD)ctx->real_base_addr
  5.                                     + ctx->pe_hdr_ptr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
  6.     for (; import_desc->Characteristics; ++import_desc)
  7.     {
  8.         char* dll_name = (char*)((DWORD)ctx->real_base_addr + import_desc->Name);
  9.         std::cout << dll_name << std::endl;
  10.  
  11.                  HMODULE hModule = LoadLibraryA(dll_name);
  12.  
  13.         PIMAGE_THUNK_DATA ThunkData = GetOriginalFirstThunk(ctx->real_base_addr, import_desc);
  14.                  for (PIMAGE_THUNK_DATA iat = GetFirstThunk(ctx->real_base_addr, import_desc); ThunkData->u1.Function; ++ThunkData, ++iat)
  15.                  {
  16.                         PIMAGE_IMPORT_BY_NAME symbol = PIMAGE_IMPORT_BY_NAME(DWORD(ctx->real_base_addr) + ThunkData->u1.Function);
  17.                         std::cout << (char*)symbol->Name << std::endl;
  18.  
  19.                         FARPROC function = GetProcAddress(hModule, symbol->Name);
  20.                         std::cout << std::hex << DWORD(function) << std::endl;
  21.  
  22.                         *(DWORD_PTR *)(DWORD(ctx->real_base_addr) + iat->u1.Function) = DWORD_PTR(function);
  23.                  }
  24.     }
  25.  


Названия библиотек и функций выводятся на консоль, но выполнение строчки

Code:
  1. *(DWORD_PTR *)(DWORD(ctx->real_base_addr) + iat->u1.Function) = DWORD_PTR(function);


не приводит к ожидаемому результату. В отладчике видно, что call'ы обращаются по адресам [402XXX]. Проблема в том, что релоки надо было обработать до заполнения импорта?

Есть ли замечания по логике функции ResolveImport?


Ранг: 793.4 (! !)
Статус: Участник
Шаман

Создано: 20 июля 2017 21:15 New!
Цитата · Личное сообщение · #2

Если ты имеешь ввиду, что у тебя в коде есть такие вызовы:

call dword ptr [402XXX]

то это к импорту вообще не относится. Так могут вызываться и внутренние функции программы. Нужно обработать релоки и получишь счастье.

Ранг: 156.3 (ветеран)
Статус: Участник

Создано: 20 июля 2017 22:10 · Поправил: VOLKOFF New!
Цитата · Личное сообщение · #3

Aoizora пишет:
релоки надо было обработать до заполнения импорта?

Обычно привычный порядок такой - выделяешь память, грузишь хидер (если нужно), правишь релоки, заполняешь импорт, выставляешь секциям флаги доступа, инициируешь, профит. Ну а там смотри по обстоятельствам

Ранг: 94.3 (постоянный)
Статус: Участник

Создано: 21 июля 2017 05:07 New!
Цитата · Личное сообщение · #4

Aoizora
Возможно пригодится:
https://exelab.ru/faq/%D0%A2%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0_%D0%B8%D0%BC%D0%BF%D0%BE%D1%80%D1%82%D0%B0_PE_%D1%84%D0%B0%D0%B9%D0%BB%D0%B0
https://kaimi.io/2011/09/pe-format-import

Ранг: 9.0 (гость)
Статус: Участник

Создано: 21 июля 2017 11:42 New!
Цитата · Личное сообщение · #5

Можно ли как-то исхитриться и переместить файл без релоков?

Ранг: 44.8 (посетитель)
Статус: Участник

Создано: 21 июля 2017 12:03 New!
Цитата · Личное сообщение · #6

Aoizora пишет:
Можно ли как-то исхитриться и переместить файл без релоков?


это надо понимать: "переместить загружаемый образ, не обрабатывая релоков?"

Блин, а прочитать-то и понять-то элементарные вещи, что релоки как раз и предназначены для корректировки адресов в загружаемом файле при его перемещении?

Абсолютное непонимание механизмов работы!
VOLKOFF ясно прописал, что нужно делать, дальше дали ссылки, что изучать.
Прочитать-то и разобраться сложно?..


Ранг: 556.6 (!)
Статус: Участник
оптимист

Создано: 21 июля 2017 14:13 New!
Цитата · Личное сообщение · #7

PE_Kill какие люди

Ранг: 9.0 (гость)
Статус: Участник

Создано: 21 июля 2017 15:30 · Поправил: Aoizora New!
Цитата · Личное сообщение · #8

>Блин, а прочитать-то и понять-то элементарные вещи, что релоки как раз и предназначены для корректировки адресов в загружаемом файле при его перемещении?

А если загрузить имидж два раза по разным адресам и найти различия в значениях двордов?

PS. Релоки обработал, вроде. Код проходит по всем фиксапам и попадает в самый центр поломанных инструкций! Но с импортом какая-то проблема: адреса функций записываются куда-то не туда.

У меня сомнения насчет типа указателей на адрес для исправления.

Code:
  1. DWORD_PTR* Address = (DWORD_PTR *)(ctx->real_base_addr + Reloc->VirtualAddress + Fixup[i].Offset);
  2. *Address += Delta;


DWORD* или DWORD_PTR* использовать? Последний, как я понимаю, это memsize тип и может содержать в себе дворд, но не наоборот. Однако в WinNT.h они определены одинаково.


Ранг: 793.4 (! !)
Статус: Участник
Шаман

Создано: 21 июля 2017 17:32 New!
Цитата · Личное сообщение · #9

Если это весь код обработки релоков, то это полная херня. Релоки могут быть разных типов, и даже попадаться такие, которые не надо обрабатывать.
Вот что ли посмотри чьи то сорсы, там вроде доступно http://hackedme.narod.ru/Articles/Articles11.html

А вообще такое под дебагером отлаживают, а не тычут пальцем в небо.

| Сообщение посчитали полезным: sendersu


Ранг: 9.0 (гость)
Статус: Участник

Создано: 21 июля 2017 17:33 New!
Цитата · Личное сообщение · #10

Не получается вкурить теорию, написано запутанно.

Code:
  1. Таблица просмотра импорта (Import LookUp Table), как я уже написал выше, представляет из себя массив, в котором последовательно хранятся имена функций какой-либо DLL.
  2.  
  3. При запуске программы загрузчик ОС получает адреса используемых в программе функций и записывает их в массив адресов имен функций, на который ссылаются поля FirstThunk, и в котором до этого были RVA, ссылающиеся на имена соответствующих функций. После того, как этот массив заполнен адресами функций, он называется Таблицей Адресов Импорта (Import Address Table, сокращенно IAT). 


До этого у Iczelion'а я читал, что массивы LookupTable и IAT дублируют друг друга, в них записаны одни и те же адреса имен функций. При этом LookupTable необязательная таблица, и имена можно доставать из IAT. Здесь же (на нашей вики) написано, что просматривать импорт обязательно надо по LookupTable.

Я получаю адрес функции из DLL при помощи GetProcessAddress или используя свою функцию для разбора таблицы экспорта. Как мне записать этот адрес в IAT? Я получаю указатель PIMAGE_THUNK_DATA на FirstThunk (не original) и записываю адрес функции в PIMAGE_THUNK_DATA::u1.Function?


Ранг: 793.4 (! !)
Статус: Участник
Шаман

Создано: 21 июля 2017 18:17 New!
Цитата · Личное сообщение · #11

Хз даже что тебе ответить. Я уже даже сорсы привел, там можно было посмотреть:
// если есть IAT то сохраним в неё найденный адрес
if (ImportTable->AddresTableRVA)
{
*(ULONG*)(ImportTable->AddresTableRVA + filebase + IAT_Index) = addr;
}
else // иначе сохраним туда откуда брали
{
*(ULONG*)RVA = addr;
}

Ну да ладно, я вообще мимо проходил, развлекайтесь...

| Сообщение посчитали полезным: Rio


Ранг: 9.0 (гость)
Статус: Участник

Создано: 21 июля 2017 20:04 New!
Цитата · Личное сообщение · #12

Все нормально, мой минималистичный лоадер заработал. Буду дальше его улучшать, добавлю проверки, потестирую на разных файлах и буду дальше изучать PE64. Статьи Volodya об упаковщиках хватит для написания PE64-лоадера?

Ранг: 94.3 (постоянный)
Статус: Участник

Создано: 22 июля 2017 16:43 New!
Цитата · Личное сообщение · #13

Aoizora пишет:
Статьи Volodya об упаковщиках хватит для написания PE64-лоадера?

https://habrahabr.ru/post/266831/
не причём x64

Ранг: 9.0 (гость)
Статус: Участник

Создано: 23 июля 2017 12:14 New!
Цитата · Личное сообщение · #14

Еще такой вопрос. Допустим, PE-файл не содержит секции релоков, тогда его надо загрузить по ожидаемому адресу загрузки в OptionalHeader.ImageBase, пусть это будет 400000. Но PE-загрузчик тоже загружен по адресу 400000. Что делать в таком случае? Как в загрузчике gr8'та анпамить память по адресу 400000 и загружать туда образ? Как тогда загрузчик сможет продолжить свою работу после анмапинга его памяти?

Ранг: 44.8 (посетитель)
Статус: Участник

Создано: 24 июля 2017 10:12 New!
Цитата · Личное сообщение · #15

Aoizora пишет:
Допустим, PE-файл не содержит секции релоков, тогда его надо загрузить по ожидаемому адресу загрузки


2 варианта:
1. Релоки в этом файле не нужны, он может быть загружен по любому адресу.
2. Всё настроено на абсолютные адреса. В этом случае лучше считайте, что работаете с "кривым" ехе-шником, который "иногда" работает правильно. Вряд ли потенции хватит сделать его перемещаемым, забейте, это выше Ваших возможностей!!!

Ранг: 248.6 (наставник)
Статус: Участник

Создано: 24 июля 2017 11:19 · Поправил: cppasm New!
Цитата · Личное сообщение · #16

Его и не надо никуда перемещать.
Перемещать надо загрузчик чтобы освободить необходимый регион начиная с ImageBase.
Как - это уже второй вопрос. Либо собрать загрузчик с релоками и их обрабатывать, либо сделать его позиционно независимым (что как по мне проще).
Ничего кривого в exe без релоков нету - они там особо не нужны в принципе, разве что для ASLR.

Aoizora пишет:
Как тогда загрузчик сможет продолжить свою работу после анмапинга его памяти?

Никак.
Его предварительно надо скопировать в другую область памяти и передать туда управление.
А потом уже анмапить память и т.д.

| Сообщение посчитали полезным: Rio


Ранг: 9.0 (гость)
Статус: Участник

Создано: 24 июля 2017 11:32 New!
Цитата · Личное сообщение · #17

Попробую разобраться в статье про реконструкцию релоков: http://www.cs.columbia.edu/~vpappas/papers/reloc.raid14.pdf

>Его предварительно надо скопировать в другую область памяти и передать туда управление. А потом уже анмапить память и т.д.

Тогда придется сделать его в виде шеллкода. Это не так сложно, я думаю. Напишу позиционно-независимый код, упакую в отдельную секцию и сдамплю эту секцию.
 eXeL@B —› Программирование —› Заполнение таблицы импорта образа, загруженного по нестандартному адресу

Видеокурс ВЗЛОМ