Сегодня столкнулся с тем, что мне нужно перехватить WinApi функции и изменить результат который они передают в программу.
Первый раз решил пойти сложным путем, методом изменения таблицы импортов, пытаясь поменять адрес в таблице импортов на нужный мне (перед этим создав темплейт функции).
Сделать у меня это не получилось, по этому я решил использовать библиотеку Detours.
Для работы будем использовать библиотеку detours, вам её нужно будет установить через NuGet.
Сначала создадим темплейты наших функций, я покажу это на 2х примерах: GetUserNameW и RegOpenKeyExW.
Далее, нам нужно создать "Прототип" функции которая будет заменятся на оригинальную.
Сначала я просто покажу код, потом более подробно расскажу что тут делается:
И внедряем их через DetourAttach, вот код main функции:
Я использовал это для взлома программы, который возможно в скором времени залью на форум, по этому такой cout вначале.
Далее расскажу как это всё работает.
Возьму для примера функцию GetUserNameW.
В самом низу, оригинальная функция (Для примера, что бы понять разницу), а слева функция которую мы хукаем.
DetourAttach изменяет 1 инструкцию в функции на jmp с адресом нашей "псевдо-функции", тем самым происходит переход.
С самого начала мы вызываем оригинальную функцию GetUserNameW по её адресу, что бы получить имя пользователя (это делать не обязательно, но я сделал).
Далее, я к имени MKII прибавляю случайное число, что бы рандомизировать имена.
И после этого, я передаю результат и return TRUE, что бы программа думала, что функция отработала корректно.
Теперь пару слов про функцию RegOpenKeyExW.
На этом всё, надеюсь эта статья хоть кому-то будет полезна.
Первый раз решил пойти сложным путем, методом изменения таблицы импортов, пытаясь поменять адрес в таблице импортов на нужный мне (перед этим создав темплейт функции).
Сделать у меня это не получилось, по этому я решил использовать библиотеку Detours.
Для работы будем использовать библиотеку detours, вам её нужно будет установить через NuGet.
Сначала создадим темплейты наших функций, я покажу это на 2х примерах: GetUserNameW и RegOpenKeyExW.
C++:
typedef BOOL(WINAPI* GETUSERNAMEW)(LPWSTR lpBuffer, LPDWORD pcbBuffer);
typedef LONG(WINAPI* REGOPENKEYEXW)(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
GETUSERNAMEW original_GetUserNameW;
REGOPENKEYEXW original_RegOpenKeyExW;
Сначала я просто покажу код, потом более подробно расскажу что тут делается:
C++:
BOOL WINAPI My_GetUserNameW(LPWSTR lpBuffer, LPDWORD pcbBuffer)
{
WCHAR originalName[256];
DWORD size = 256;
original_GetUserNameW(originalName, &size);
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<DWORD> dist(0, UINT32_MAX);
DWORD mask = dist(mt);
std::wstring username = L"MKII " + std::to_wstring(mask);
std::wcout << L"Original GetUserNameW: " << originalName << L", new GetUserNameW: " << username << std::endl;
std::wstring name = username;
wcscpy(lpBuffer, name.c_str());
*pcbBuffer = name.length();
return TRUE;
}
LONG WINAPI My_RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
{
std::wcout << L"RegOpenKeyExW called with subkey: " << lpSubKey << std::endl;
return original_RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
}
И внедряем их через DetourAttach, вот код main функции:
C++:
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
AllocConsole();
freopen("CONOUT$", "w", stdout);
std::cout << "Hooked:)\nCrack by MKII | https://ru-sfera.pw/" << std::endl;
// Получаем адреса оригинальных функций
original_GetUserNameW = (GETUSERNAMEW)GetProcAddress(GetModuleHandle(L"advapi32.dll"), "GetUserNameW");
original_RegOpenKeyExW = (REGOPENKEYEXW)GetProcAddress(GetModuleHandle(L"advapi32.dll"), "RegOpenKeyExW");
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)original_GetUserNameW, My_GetUserNameW);
DetourAttach(&(PVOID&)original_RegOpenKeyExW, My_RegOpenKeyExW);
DetourTransactionCommit();
break;
}
return TRUE;
}
Далее расскажу как это всё работает.
Возьму для примера функцию GetUserNameW.
В самом низу, оригинальная функция (Для примера, что бы понять разницу), а слева функция которую мы хукаем.
DetourAttach изменяет 1 инструкцию в функции на jmp с адресом нашей "псевдо-функции", тем самым происходит переход.
С самого начала мы вызываем оригинальную функцию GetUserNameW по её адресу, что бы получить имя пользователя (это делать не обязательно, но я сделал).
C++:
WCHAR originalName[256];
DWORD size = 256;
original_GetUserNameW(originalName, &size);
C++:
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<DWORD> dist(0, UINT32_MAX);
DWORD mask = dist(mt);
std::wstring username = L"MKII " + std::to_wstring(mask);
std::wcout << L"Original GetUserNameW: " << originalName << L", new GetUserNameW: " << username << std::endl;
C++:
std::wstring name = username;
wcscpy(lpBuffer, name.c_str());
*pcbBuffer = name.length();
return TRUE;
Теперь пару слов про функцию RegOpenKeyExW.
Наш прототип принимает параметры HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult (как оригинальная функция).
За имя ключа отвечает параметр lpSubKey, мы выводим его в консоль, тем самым, мы можем увидеть в какой раздел стучит программа:
После этого, вызываем оригинальную функцию по её адресу, что бы программа отработала корректно:
При желании, можно посмотреть вывод, добавив переменную типа Long и в неё загрузить результат original_RegOpenKeyExW.
Даже можно изменить данные которые выдаст original_RegOpenKeyExW, думаю труда сделать это вам не составит.
За имя ключа отвечает параметр lpSubKey, мы выводим его в консоль, тем самым, мы можем увидеть в какой раздел стучит программа:
C++:
std::wcout << L"RegOpenKeyExW called with subkey: " << lpSubKey << std::endl;
C++:
return original_RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
Даже можно изменить данные которые выдаст original_RegOpenKeyExW, думаю труда сделать это вам не составит.
Последнее редактирование: