• XSS.stack #1 – первый литературный журнал от юзеров форума

Импорт функций из DLL в PE файл

ymmfty0

HDD-drive
Пользователь
Регистрация
28.10.2022
Сообщения
29
Реакции
5
Читаю КРИС КАСПЕРСКИ Путь воина – внедрение в pe/coff-файлы. В главе про импорт говорится
Стандартный механизм импорта работает приблизительно так: специальная таблица (называемая таблицей импорта) перечисляет имена/ординалы всех импортируемых функций, указывая, в какое место страничного имиджа загрузчик должен записать эффективный адрес каждой из них. Грубо говоря, на каждую импортируемую функцию приходится один вызов GetProcAddress, фактически сводящийся к поэлементному перебору всей таблицы экспорта.
Не много не понял. Про таблицу импорта он имеет в виду саму секцию Import Table , то есть .idata секцию
Цитата из документации msdn
Import TableThe import table address and size. For more information, see The .idata Section.
Перечисляет он имена от куда? Из структуры _IMAGE_IMPORT_DESCRIPTOR ? А куда именно записывает этот эффективный адрес ? В IAT ? Помоги мне пожалуйста разобраться , я совсем уже запутался
 
Читаю КРИС КАСПЕРСКИ Путь воина – внедрение в pe/coff-файлы. В главе про импорт говорится

Не много не понял. Про таблицу импорта он имеет в виду саму секцию Import Table , то есть .idata секцию
Цитата из документации msdn

Перечисляет он имена от куда? Из структуры _IMAGE_IMPORT_DESCRIPTOR ? А куда именно записывает этот эффективный адрес ? В IAT ? Помоги мне пожалуйста разобраться , я совсем уже запутался

Таблица импорта не обязательно находистя в секции idata


Код:
// Directory Entries

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

Указатель на таблицу импорта получаешь из DataDirectory:

NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]


Вот целый код для обработки импортов, объяснять полностью как работает долго


Код:
bool fixIAT(PVOID modulePtr)
{
    IMAGE_DATA_DIRECTORY *importsDir = getPeDir(modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);

    if (importsDir == NULL) return false;

    size_t maxSize = importsDir->Size;
    size_t impAddr = importsDir->VirtualAddress;

    IMAGE_IMPORT_DESCRIPTOR* lib_desc = 0;
    size_t parsedSize = 0;

    for (; parsedSize < maxSize; parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR))
    {
        lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR)modulePtr);

        if (lib_desc->OriginalFirstThunk == 0 && lib_desc->FirstThunk == 0) break;

        LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->Name);

        size_t call_via = lib_desc->FirstThunk;
        size_t thunk_addr = lib_desc->OriginalFirstThunk;

        if (!thunk_addr) thunk_addr = call_via;

        size_t offsetField = 0;
        size_t offsetThunk = 0;

        while (true)
        {
            IMAGE_THUNK_DATA* fieldThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetField + call_via);
            IMAGE_THUNK_DATA* orginThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetThunk + thunk_addr);

            if (orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32 || orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64) // check if using ordinal (both x86 && x64)
            {
                size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), (char*)(orginThunk->u1.Ordinal & 0xFFFF));
                fieldThunk->u1.Function = addr;
            }
           
            if (fieldThunk->u1.Function == 0) break;

            if (fieldThunk->u1.Function == orginThunk->u1.Function)
            {
               
                PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)(size_t(modulePtr) + orginThunk->u1.AddressOfData);

                LPSTR func_name = (LPSTR)by_name->Name;
                size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), func_name);

                fieldThunk->u1.Function = addr;
            }

            offsetField += sizeof(IMAGE_THUNK_DATA);
            offsetThunk += sizeof(IMAGE_THUNK_DATA);
        }
    }
   
    return true;
}
 
Таблица импорта не обязательно находистя в секции idata


Код:
// Directory Entries

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

Указатель на таблицу импорта получаешь из DataDirectory:

NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]


Вот целый код для обработки импортов, объяснять полностью как работает долго


Код:
bool fixIAT(PVOID modulePtr)
{
    IMAGE_DATA_DIRECTORY *importsDir = getPeDir(modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);

    if (importsDir == NULL) return false;

    size_t maxSize = importsDir->Size;
    size_t impAddr = importsDir->VirtualAddress;

    IMAGE_IMPORT_DESCRIPTOR* lib_desc = 0;
    size_t parsedSize = 0;

    for (; parsedSize < maxSize; parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR))
    {
        lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR)modulePtr);

        if (lib_desc->OriginalFirstThunk == 0 && lib_desc->FirstThunk == 0) break;

        LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->Name);

        size_t call_via = lib_desc->FirstThunk;
        size_t thunk_addr = lib_desc->OriginalFirstThunk;

        if (!thunk_addr) thunk_addr = call_via;

        size_t offsetField = 0;
        size_t offsetThunk = 0;

        while (true)
        {
            IMAGE_THUNK_DATA* fieldThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetField + call_via);
            IMAGE_THUNK_DATA* orginThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetThunk + thunk_addr);

            if (orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32 || orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64) // check if using ordinal (both x86 && x64)
            {
                size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), (char*)(orginThunk->u1.Ordinal & 0xFFFF));
                fieldThunk->u1.Function = addr;
            }
        
            if (fieldThunk->u1.Function == 0) break;

            if (fieldThunk->u1.Function == orginThunk->u1.Function)
            {
            
                PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)(size_t(modulePtr) + orginThunk->u1.AddressOfData);

                LPSTR func_name = (LPSTR)by_name->Name;
                size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), func_name);

                fieldThunk->u1.Function = addr;
            }

            offsetField += sizeof(IMAGE_THUNK_DATA);
            offsetThunk += sizeof(IMAGE_THUNK_DATA);
        }
    }
 
    return true;
}
Меня больше интересует, как работает стандартный механизм импорта в PE файле. Есть Bound Import и delay import . В этой статье , описывается работа стандартного метода импорта и не понял, это описание
Стандартный механизм импорта работает приблизительно так: специальная таблица (называемая таблицей импорта) перечисляет имена/ординалы всех импортируемых функций, указывая, в какое место страничного имиджа загрузчик должен записать эффективный адрес каждой из них. Грубо говоря, на каждую импортируемую функцию приходится один вызов GetProcAddress, фактически сводящийся к поэлементному перебору всей таблицы экспорта. "
. Что тут имеется в виду про таблицу импорта , это идет речь про структуру _IMAGE_IMPORT_DESCRIPTOR ? Имена берутся от туда ? От куда она берет адреса этих функций и куда оно записывает ? Когда , тут говорится про груборя говоря оно выполняет такой же как GetProcAddress , оно выполняет GetProcAddress фактически или просто получается адреса из Export Table ? К чему ты оставил этот пример кода ? Когда ты говоришь про обработку импортов, что ты имеешь в виду ?
 
Я просто хочу разобраться в стандартном методе импорта в PE файлах. Я взял информацию от сюда
КРИС КАСПЕРСКИ Путь воина – внедрение в pe/coff-файлы
И хотел бы понять, что он имеет в виду , вот объяснение стандартного метода
Стандартный механизм импорта работает приблизительно так: специальная таблица (называемая таблицей импорта) перечисляет имена/ординалы всех импортируемых функций, указывая, в какое место страничного имиджа загрузчик должен записать эффективный адрес каждой из них. Грубо говоря, на каждую импортируемую функцию приходится один вызов GetProcAddress, фактически сводящийся к поэлементному перебору всей таблицы экспорта. "
Мои вопросы, которые я получил прочитав это
Что тут имеется в виду про таблицу импорта , это идет речь про структуру _IMAGE_IMPORT_DESCRIPTOR ? Имена берутся от туда ? От куда она берет адреса этих функций и куда оно записывает ? Когда , тут говорится про груборя говоря оно выполняет такой же как GetProcAddress , оно выполняет GetProcAddress фактически или просто получается адреса из Export Table ?
Я бы хотел разобраться в этом методе , потомучто я запутался
 
Про таблицу импорта он имеет в виду саму секцию Import Table , то есть .idata секцию
Всё верно, только вместо "..перечисляет имена/ординалы" правильней было-бы сказать "..хранит имена/ординалы".
В секции .idata находятся несколько структур IMAGE_IMPORT_DESCRIPTOR, для каждой импортируемой dll своя. В первом поле OriginFirstThunk дескриптора лежит указатель на начало таблицы Lookup с именами импорт-функций, а в последнем поле FirstThunk указатель на IAT с адресами этих функций.

Теперь на ваш вопрос "А куда именно записывает этот эффективный адрес?" нужно сказать, что в дефолте таблица IAT располагается сразу за таблицей Lookup прямо в секции idata, хотя ничто не мешает переместить IAT и в любое/другое место "страничного имиджа", о чём и говорит Крис. Для этого достаточно внести поправки в поле FirstThunk дескриптора, обнулив при этом запись IAT в каталоге IMAGE_DIR.

Нулевое значение в каталоге позволяет размазать IAT буквально по всему пространству процесса, например эффективные адреса импорта из либы Kernel32 положить в секцию данных, а адреса из User32 отправить в секцию ресурсов. Главное чтобы в поле FirstThunk дескрипторов этих библиотек был прописан валидный указатель. Вот схема, на которой отображён весь возможный импорт в РЕ-файлах.

Import.png
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх