Модератор форума
-
Автор темы
- #1
Название кликбейт, почти. Данный способ инжекта будет обходить лишь хук LdrLoadDll, длл все также будет находиться в списке модулей и т.д.
Ни для кого не секрет, что вызов LoadLibraryA (как и любой другой функции LoadLibrary) приведет в конце к вызову LdrLoadDll, следовательно, чтобы отследить простой инжект, нужно поставить хук на LdrLoadDll
В этом гайде мы обойдем данный хук
Взглянув на LdrLoadDll изнутри, можно увидеть вызовы недокументированных функций, которых нет и в экспортах. Нас интересует лишь этот блок кода:
А именно LdrpLoadDll. Данной функции нет в экспортах, соответственно через GetProcAddress ее адрес не получить
Есть несколько способов достать адрес этой функции:
Функция LdrpLoadDll имеет не 2 аргумента, как может показаться на первый взгляд, а 4
Если просто закинуть ntdll.dll в IDA и открыть LdrLoadDll, мы увидим:
Тогда с чего я взял, что аргумента 4?
Запустим x86 приложение, подключим к нему дебаггер IDA и перейдем в модуль ntdll.dll в функцию LdrLoadDll
Увидим это:
И убедимся, что аргументов и правда 4
С таким вызовом нам не нужно определять прототип функции, ее можно вызвать как на скриншоте выше, заменив ntdll_LdrpLoadDll адресом этой функции
Перейдем к коду
В данной части кода мы нашли адрес и размер ntdll.dll, а также адрес LdrpLoadDll
Здесь мы инициализировали UNICODE_STRING и вызвали функцию LdrLoadDllRebuild. Перейдем к ней
Некоторые части оригинального кода были убраны за ненадобностью. LdrpLoadDll загрузит наш модуль в процесс, при этом не вызвав LdrLoadDll, чтобы убедиться в этом, предлагаю поставить хук на LdrLoadDll и загрузить сначала 1 DLL с помощью LoadLibraryA, а затем следующую с помощью только что написанной функции. Для хука воспользуемся библиотекой MinHook
Определим прототип LdrLoadDll и напишем хук функцию:
При вызове LdrLoadDll нам выдаст сообщение в консоль с именем загружаемого модуля
Перейдем к настройке MinHook:
Запустим и увидим следующее:
В первом случае, при загрузке обычным LoadLibraryA, нам написало из LdrLoadDll о том, что модуль загружается в процесс и затем сам модуль вывел сообщение о своей загрузке. Во втором случае, мы не увидели сообщение из LdrLoadDll о загрузке модуля, только из самого модуля, что означает отсутствие вызова LdrLoadDll
Данный метод безопаснее простого LoadLibrary, так как обходит хук LdrLoadDll. Вряд-ли в том же VAC будет стоять хук LdrpLoadDll или функций внутри, так как они не документированы и, если загуглить LdrpLoadDll, практически на каждом сайте будет разная информация о прототипе, из чего следует, что на каждой версии Windows и ntdll.dll, ее поиск будет отличаться
Кредиты: Microsoft и их дебаг символы, IDA
Протестировано на Windows 10 x64 1903, на x86 приложении
Если в чем-то не прав, прошу поправить
Исходный код проекта -
Ни для кого не секрет, что вызов LoadLibraryA (как и любой другой функции LoadLibrary) приведет в конце к вызову LdrLoadDll, следовательно, чтобы отследить простой инжект, нужно поставить хук на LdrLoadDll
В этом гайде мы обойдем данный хук
Взглянув на LdrLoadDll изнутри, можно увидеть вызовы недокументированных функций, которых нет и в экспортах. Нас интересует лишь этот блок кода:
А именно LdrpLoadDll. Данной функции нет в экспортах, соответственно через GetProcAddress ее адрес не получить
Есть несколько способов достать адрес этой функции:
- Прибавить некоторое значение к адресу LdrLoadDll (получив его через GetProcAddress), это некоторое значение - смещение LdrpLoadDll относительно LdrLoadDll. Данный метод не совсем практичен на мой взгляд, поэтому воспользуемся следующим
- Найти сигнатуру LdrpLoadDll и найти адрес через скан. Это будет явно надежнее, чем прибавлять оффсет
Функция LdrpLoadDll имеет не 2 аргумента, как может показаться на первый взгляд, а 4
Если просто закинуть ntdll.dll в IDA и открыть LdrLoadDll, мы увидим:
Тогда с чего я взял, что аргумента 4?
Запустим x86 приложение, подключим к нему дебаггер IDA и перейдем в модуль ntdll.dll в функцию LdrLoadDll
Увидим это:
И убедимся, что аргументов и правда 4
С таким вызовом нам не нужно определять прототип функции, ее можно вызвать как на скриншоте выше, заменив ntdll_LdrpLoadDll адресом этой функции
Перейдем к коду
Создадим DLL, которая при присоединении будет писать в консоль, выводя адрес загрузки. Сделаем копию этой DLL
C++:
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
MODULEINFO NtDllInfo;
GetModuleInformation(GetCurrentProcess(), hNtdll, &NtDllInfo, sizeof(MODULEINFO));
printf("ntdll.dll module base is 0x%p\nntdll.dll module size is 0x%X\n", hNtdll, NtDllInfo.SizeOfImage);
oLdrpLoadDll = (LPVOID)FindPattern((UINT64)hNtdll, (UINT64)NtDllInfo.SizeOfImage, (BYTE*)"\x8B\xFF\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\x00\x00\x00\x00\xA1\x00\x00\x00\x00\x33\xC4\x89\x84\x24\x00\x00\x00\x00\x53\x8B\x5D\x0C\x56", (char*)"xxxxxxxxxx????x????xxxxx????xxxxx"); // LPVOID oLdrpLoadDll - определение вне main() функции
printf("LdrpLoadDll address is 0x%p\n", oLdrpLoadDll);
C++:
DWORD handle_out = 0;
UNICODE_STRING path;
RtlInitUnicodeString(&path, L"C:\\Users\\catahustle\\source\\repos\\ldrloaddll rebuild\\Release\\test_dll.dll");
LdrLoadDllRebuild(&path, &handle_out);
printf("LdrLoadDllRebuild is done\nHandle to module is 0x%X\n", handle_out);
C++:
NTSTATUS NTAPI LdrLoadDllRebuild(PUNICODE_STRING path, DWORD* handle)
{
signed int result;
int v7;
char v9;
if (*(WORD*)(__readfsdword(24) + 4042) & 0x2000)
{
result = -1073740004;
}
else
{
result = ((int(__fastcall*)(int, char*, int, int*))oLdrpLoadDll)((int)path, &v9, 0, &v7);
if (result >= 0)
{
*handle = *(DWORD*)(v7 + 24);
}
}
return result;
}
Определим прототип LdrLoadDll и напишем хук функцию:
C++:
typedef NTSTATUS(NTAPI* fnLdrLoadDll)(
IN PWCHAR PathToFile OPTIONAL,
IN ULONG Flags OPTIONAL,
IN PUNICODE_STRING ModuleFileName,
OUT PHANDLE ModuleHandle);
fnLdrLoadDll oLdrLoadDll = nullptr;
NTSTATUS NTAPI hkLdrLoadDll(IN PWCHAR PathToFile OPTIONAL,
IN ULONG Flags OPTIONAL,
IN PUNICODE_STRING ModuleFileName,
OUT PHANDLE ModuleHandle)
{
printf("\nLdrLoadDll has been called, DLL name: %ls\n", ModuleFileName->Buffer);
return oLdrLoadDll(PathToFile, Flags, ModuleFileName, ModuleHandle);
}
Перейдем к настройке MinHook:
C++:
if (MH_Initialize() != MH_OK) // инициализация MinHook
{
return 1;
}
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
LPVOID LdrLoadDll_addr = GetProcAddress(hNtdll, "LdrLoadDll");
printf("LdrLoadDll address is 0x%p\n", LdrLoadDll_addr);
//создание хука
if (MH_CreateHook(LdrLoadDll_addr, &hkLdrLoadDll, reinterpret_cast<LPVOID*>(&oLdrLoadDll)) != MH_OK)
{
printf("Failed to hook LdrLoadDll\nExiting");
system("pause > nul");
return 1;
}
printf("Original LdrLoadDll address is 0x%p\n", oLdrLoadDll);
//активация
if (MH_EnableHook(LdrLoadDll_addr) != MH_OK)
{
printf("Failed to enable LdrLoadDll hook\nExiting");
system("pause > nul");
return 1;
}
printf("Starting loading DLL with LoadLibrary\n");
LoadLibraryA("C:\\Users\\catahustle\\source\\repos\\ldrloaddll rebuild\\Release\\test_dll2.dll");
printf("\nStarting loading another DLL with LdrLoadDll rebuild\n");
DWORD handle_out = 0;
UNICODE_STRING path;
RtlInitUnicodeString(&path, L"C:\\Users\\catahustle\\source\\repos\\ldrloaddll rebuild\\Release\\test_dll.dll");
LdrLoadDllRebuild(&path, &handle_out);
printf("LdrLoadDllRebuild is done\nHandle to module is 0x%X\n", handle_out);
// деактивация
if (MH_DisableHook(LdrLoadDll_addr) != MH_OK)
{
printf("Failed to disable LdrLoadDll hook\nExiting");
system("pause > nul");
return 1;
}
// завершение работы MinHook
if (MH_Uninitialize() != MH_OK)
{
return 1;
}
В первом случае, при загрузке обычным LoadLibraryA, нам написало из LdrLoadDll о том, что модуль загружается в процесс и затем сам модуль вывел сообщение о своей загрузке. Во втором случае, мы не увидели сообщение из LdrLoadDll о загрузке модуля, только из самого модуля, что означает отсутствие вызова LdrLoadDll
Данный метод безопаснее простого LoadLibrary, так как обходит хук LdrLoadDll. Вряд-ли в том же VAC будет стоять хук LdrpLoadDll или функций внутри, так как они не документированы и, если загуглить LdrpLoadDll, практически на каждом сайте будет разная информация о прототипе, из чего следует, что на каждой версии Windows и ntdll.dll, ее поиск будет отличаться
Кредиты: Microsoft и их дебаг символы, IDA
Протестировано на Windows 10 x64 1903, на x86 приложении
Если в чем-то не прав, прошу поправить
Исходный код проекта -
Пожалуйста, авторизуйтесь для просмотра ссылки.
Последнее редактирование модератором: