Создание резолвера функций

Тема в разделе "WASM.BEGINNERS", создана пользователем Payton111, 2 апр 2024.

  1. Payton111

    Payton111 New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2021
    Сообщения:
    12
    Здравствуйте всем, подскажите пожалуйста, если можно то пример создания резолвера функций mvscrt и Api. То есть в проекте visual studio мы убираем зависимости от Kernel32 например и C Runtime, а потом используя библиотеку lazy importer получаем динамически функцию в процессе выполнения, но нужно помимо этого сделать так чтобы везде не прописывать LI_FIND, LI_GET используя библиотеку lazy importer, а обычно вызывать эти функции, но при этом их динамически получать, пример нашёл в интернете, но что-то не получается пока. Библиотеку импорта создал через dumpbin и последующий def файл пропустил через lib для создания lib файла. Как правильно сделать, уважаемые гуру. Вот пример
    Код (C++):
    1. // resolver.h
    2. void crt_init();
    3. void* ___memcpy(void* dst, const void* src, size_t size);
    4.  
    5. // resolver.cpp
    6. uintptr_t msvcrtLib = 0;
    7. #define _VCRTFunc(fn) LI_GET(msvcrtLib,fn)
    8. void crt_init()
    9. {
    10.     msvcrtLib = reinterpret_cast<uintptr_t>(LI_FIND(LoadLibraryA)(_S("msvcrt.dll")));
    11. }
    12. // Dynamic memcpy
    13. void* ___memcpy(void* dst, const void* src, size_t size)
    14. {
    15.     return _VCRTFunc(memcpy)(dst, src, size);
    16. }
    17. // resolver_export.cpp
    18. #include "resolver.h"
    19. #define RESOLVER extern "C"
    20. RESOLVER void* __cdecl memcpy(void* dst, const void* src, size_t size)
    21. {
    22.     return ___memcpy(dst, src, size);
    23. }
     
  2. k3rnl

    k3rnl Member

    Публикаций:
    0
    Регистрация:
    28 янв 2021
    Сообщения:
    68
    Зачем?

    Дёргаете PEB библиотеки ntdll.dll
    Код (C++):
    1. unsigned __int64 __readgsqword(unsigned __int32 Offset);
    2. #pragma intrinsic(__readgsqword)
    3. PEB *__ptr64 peb = (PEB *__ptr64)__readgsqword(0x60); // mov rax, gs:[0x60] (x64)
    4.  
    5. void *__ptr64 NtDllBase = (void *__ptr64)0;
    6. LIST_ENTRY *__ptr64 pList = peb->Ldr->InLoadOrderModuleList.Flink;
    7. for (unsigned __int32 i = 0; i < 2; i++) {
    8.   LDR_DATA_TABLE_ENTRY *__ptr64 pDataTable = (LDR_DATA_TABLE_ENTRY *__ptr64)(pList);
    9.   if (i == 1) {
    10.   NtDllBase = pDataTable->DllBase;
    11.   break;
    12.   }
    13.   else pList = pList->Flink;
    14. }
    затем ищите в ней функцию LdrLoadDll
    Код (C++):
    1. IMAGE_DOS_HEADER *__ptr64 pDos = (IMAGE_DOS_HEADER *__ptr64)NtDllBase;
    2. IMAGE_NT_HEADERS64 *__ptr64 pNt = (IMAGE_NT_HEADERS64 *__ptr64)((unsigned __int64)NtDllBase + pDos->e_lfanew);
    3. IMAGE_EXPORT_DIRECTORY *__ptr64 pExpDir = (IMAGE_EXPORT_DIRECTORY *__ptr64)((unsigned __int64)NtDllBase + pNt->OptionalHeader.DataDirectory[0].VirtualAddress);
    4.  
    5. unsigned __int32 *__ptr64 AddrFunc = (unsigned __int32 *__ptr64)((unsigned __int64)NtDllBase + pExpDir->AddressOfFunctions);
    6.  
    7. // номер функции ===LdrLoadDll=== в таблице экспорта ntdll.dll = 138
    8. unsigned __int32 *__ptr64 fn_LdrLoadDll = (unsigned __int32 *__ptr64)0;
    9. for (; 138 < pExpDir->NumberOfNames;) {
    10.   fn_LdrLoadDll = (unsigned __int32 *__ptr64)((unsigned __int64)NtDllBase + AddrFunc[138]);
    11.   break;
    12. }
    прототип функции LdrLoadDll
    Код (C++):
    1. typedef __int32(__stdcall *__ptr64 _LdrLoadDll)(
    2.   unsigned short *__ptr64 PathToFile,
    3.   unsigned __int32 Flags,
    4.   UNICODE_STRING *__ptr64 ModuleFileName,
    5.   void *__ptr64 *__ptr64 ModuleHandle
    6.   );
    7. _LdrLoadDll LdrLoadDll;
    8. LdrLoadDll = (_LdrLoadDll)fn_LdrLoadDll;
    и прототип функции LdrGetProcedureAddress
    Код (C++):
    1. typedef __int32(__stdcall *__ptr64 _LdrGetProcedureAddress)(
    2.   void *__ptr64 ModuleHandle,
    3.   STRING *__ptr64 FunctionName,
    4.   unsigned __int16 Oridinal,
    5.   void *__ptr64 *__ptr64 FunctionAddress
    6.   );
    7. _LdrGetProcedureAddress LdrGetProcedureAddress;
    8.  
    9. // номер функции ===LdrGetProcedureAddress=== в таблице экспорта ntdll.dll = 129
    10. unsigned __int32 *__ptr64 fn_LdrGetProcedureAddress = (unsigned __int32 *__ptr64)0;
    11. for (; 129 < pExpDir->NumberOfNames;) {
    12.   fn_LdrGetProcedureAddress = (unsigned __int32 *__ptr64)((unsigned __int64)NtDllBase + AddrFunc[129]);
    13.   break;
    14. }
    15. LdrGetProcedureAddress = (_LdrGetProcedureAddress)fn_LdrGetProcedureAddress;
    В VS:
    1. Открываем свойства проекта, идём в C\C++ -> Создание кода -> Основные проверки времени выполнения (ставим "По умолчанию"), Проверка безопасности ("Отключить проверку безопасности (/GS-)")
    2. C\C++ -> Дополнительно -> Пропускать имена стандартных библиотек (ставим "Да (/ZI)")
    3. Переходим Компоновщик -> Ввод -> Игнорировать все стандартные библиотеки (ставим "Да (/NODEFAULTLIB)")
    4. Определяем точку входа в коде программы как
    Код (C++):
    1. void mainCRTStartup(void) { }
    Всё, теперь имеется загрузчик бибилиотек и функций без kernel32.dll
     
    CaptainObvious нравится это.
  3. Payton111

    Payton111 New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2021
    Сообщения:
    12
    Спасибо, код понятен, загружаем с более низкого уровня получается, но допустим я вызываю функцию memcpy в программе, а она вызывается динамически, как переопределить её стандартный вызов.
    --- Сообщение объединено, 2 апр 2024 ---
    k3rnl, а как переопределить функции, чтобы вызывать их как обычно, но получать динамически их, спасибо, просто понять не могу.
     
  4. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    530
    Код (C++):
    1.     char dst[128];
    2.     const char* src = "this is just a source stringos";
    3.     LI_FN(memcpy)(dst, src, sizeof(dst)-1);
    4.  
     
  5. Payton111

    Payton111 New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2021
    Сообщения:
    12
    alex_dz, это понятно, как вызвать просто memcpy, чтобы везде макросы не писать, где требуется множество вызовов этих функций
     
  6. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    530
    а сколько у вас исползований memcpy?
     
  7. Payton111

    Payton111 New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2021
    Сообщения:
    12
    Очень много используется в библиотеках сторонних, поэтому я хотел узнать, как правильно написать резолвер, чтобы вызывая к примеру memcpy или другую функцию можно было вызвать её как будто она присутствует статически при загрузке естественно, но передавая параметры в другую функцию которая уже динамически её найдёт, в сообщении указал примерный код посмотрите, искал в интернете, нужно создать библиотеку импорта из dll, это я сделал, у меня ошибка в определении функции, я не знаю как оформить правильно, просто пока опыта мало)
     
  8. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.486
    Адрес:
    Россия, Нижний Новгород
    Примерно то, что ты хочешь, msvc умеет делать сам через отложенные импорты: /DELAYLOAD (Delay Load Import) | Microsoft Learn
    Для каждой функции из библиотеки компилятор генерирует заглушку, при первом вызове которой делается загрузка библиотеки, если та ещё не загружена, и поиск нужной функции.
    Но если тебе надо вручную сделать поиск (например, чтобы не светить названиями библиотек и функций) - то простых способов нет: придётся как-то говорить линкеру, чтобы он использовал твои функции.
    Например, пройтись по всем экспортам нужных тебе библиотек, на основе этого списка скриптом сделать статическую либу, которая для каждого экспорта сгенерирует заглушку с Lazy Importer'ом, и подложить эту либу линкеру вместо оригинальной либы.
     
  9. Payton111

    Payton111 New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2021
    Сообщения:
    12
    Вы имеете ввиду библиотеку импорта сделать правильно? Но я это сделал, из msvcrt.dll создал библиотеку импорта с именами функций экспорта, потом включил её в каталог дополнительных библиотек в компоновщике
     
  10. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.486
    Адрес:
    Россия, Нижний Новгород
    А дефолтные библиотеки убрал из линковки, как выше писал k3rnl в п.2 и п.3?
     
  11. Payton111

    Payton111 New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2021
    Сообщения:
    12
    В общем решил вопрос, в силу малого пока опыта понимания процессов на с++ и использования visual studio пришлось много сопутствующих вещей изучать. В общем суть резолвера проста как апельсин и заключается в создании статической библиотеки lib (не библиотеки импорта) из переходников функций, которые перенаправляют вызов уже на поиск реальной функции через библиотеку lazy import, которую включил в эту статическую библиотеку, в основном коде вызываем исходные функции как есть по оригинальным именам, а библиотека уже делает свою работу, естественно как отметили выше нужно отключить все лишние зависимости, а также убрать исключения из проекта, это касается не только основного кода, где используется библиотека, но и в самой библиотеке lib (тоже убрать зависимости). В основной программе определить точку входа как void mainCRTStartup(void). Всем спасибо.