typedef int (WINAPI* MessageBoxS)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);// Прототип функции
MessageBoxS box = (MessageBoxS)0x7FFFB8B88120;// Адресс взят напрямую чтоб не заморачиваться
void hookfunc()//функция которая будет выполняться вместо оригинала
{
box(0, 0, 0, 0);
}
3. Напишем функцию которая будет устанавливать хук
LPVOID Hook(LPVOID pClass, LPVOID pHookFunc, int dwOffset)// Функция которая устанавливает хук в виртуальной таблице методов (VMT)
{
LPVOID pVTable = *(LPVOID*)pClass;//Получаем адресс vmt таблица всегда находится по нулевому смещению
LPVOID pAdressInTable = (LPVOID)((DWORD64)pVTable + dwOffset * 8);//Вычисляем адресс функции в таблице по ее порядковому номеру
DWORD dwOldProtection;
VirtualProtect((LPVOID)pAdressInTable, sizeof(pAdressInTable), PAGE_READWRITE, &dwOldProtection);// меняем параметры страницы установка флага чтение запись
memcpy(pAdressInTable, pHookFunc, 8);//Записываем наш адресс хука
VirtualProtect((LPVOID)pAdressInTable, sizeof(pAdressInTable), dwOldProtection, &dwOldProtection);// Восстановили параметры страницы
return (LPVOID)pAdressInTable;// Вернули адресс из таблицы где установили хук
}
4. Пишем функцию для детекта хука
bool DetectVMT(LPVOID pClass, int MaxCount)// Простая функция для детекта хука. Проверяет таблицу VMT на изменения адрессов функций
{
DWORD64 pVMT = *(DWORD64*)pClass;//Получаем адресс vmt таблица всегда находится по нулевому смещению
MODULEINFO mInfo;// Структура которая будет получать информацию о модуле (конкретно наша программа)
GetModuleInformation(GetCurrentProcess(), GetModuleHandleA(0), &mInfo, sizeof(mInfo));// Можете в гугл заглянуть что делает эта функция
printf("Base adress %llp\n", mInfo.lpBaseOfDll);
DWORD64 EndFile = (DWORD64)mInfo.lpBaseOfDll + mInfo.SizeOfImage;// Вычисляем конец нашей программы базовый адресс + размер. Нужен для сравнения адрессов. Так как в основном адресса которые в таблице VMT не могут быть за пределами пространства программы
for (int i = 0; i < MaxCount; i++)// Перебор таблицы методов
{
printf("VMT check %llp\n", *(DWORD64*)pVMT);
if(*(DWORD64*)pVMT > EndFile)return 1;// Если адресс функции в таблице больше адресного пространства нашей программы то детект(То есть адресс ссылается на внешний код за пределами проги)
if (*(DWORD64*)pVMT < (DWORD64)mInfo.lpBaseOfDll)return 1;// Если адресс функции в таблице меньше (для тех кто выделил память ниже нашей проги)
pVMT += 8;//К первому адрессу в таблице прибавляем 8 байт это размер адрессов в х64 системе. Тем самым переходим на след функцию в таблице
}
return 0;// 0 если все адресса находятся в рамках пространства программы
}
5. Точка входа программы и использование функций
int main()
{
TestVMT* pTestVMT = new TestVMT;
printf("class adr %llp\n", pTestVMT);
pTestVMT->Func4();// Проверка функции до хука
if(DetectVMT(pTestVMT, 5))printf("VMT Detect hook\n");//Проверка таблицы перед хуком
else
{
printf("VMT UNDetect hook\n");
}
system("pause");
LPVOID vAdress = VirtualAlloc(0, 100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);// Выделяем память для хука (Имитируем что мы заинжектили чит)
memcpy(vAdress, (LPVOID)hookfunc, 100);// Запись кода hookfunc
printf("vAdress %llp\n", vAdress);
system("pause");
LPVOID adresshook = Hook(pTestVMT, &vAdress, 3);// Установка хука в VMT (1 класс 2 адресс хука 3 номер функции в таблице)
printf("adresshook %llp\n", adresshook);
system("pause");
pTestVMT->Func4();// Повторно вызывает функцию после хука для проверки
if (DetectVMT(pTestVMT, 5))printf("VMT Detect hook\n");// Повторно проверяем наше таблицу VMT класса
else
{
printf("VMT UNDetect hook\n");
}
system("pause");
return 1;
}
typedef int (WINAPI* MessageBoxS)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);// Прототип функции
MessageBoxS box = (MessageBoxS)0x7FFFB8B88120;// Адресс взят напрямую чтоб не заморачиваться
void hookfunc()//функция которая будет выполняться вместо оригинала
{
box(0, 0, 0, 0);
}
LPVOID Hook(LPVOID pClass, LPVOID pHookFunc, int dwOffset)// Функция которая устанавливает хук в виртуальной таблице методов (VMT)
{
LPVOID pVTable = *(LPVOID*)pClass;//Получаем адресс vmt таблица всегда находится по нулевому смещению
LPVOID pAdressInTable = (LPVOID)((DWORD64)pVTable + dwOffset * 8);//Вычисляем адресс функции в таблице по ее порядковому номеру
DWORD dwOldProtection;
VirtualProtect((LPVOID)pAdressInTable, sizeof(pAdressInTable), PAGE_READWRITE, &dwOldProtection);// меняем параметры страницы установка флага чтение запись
memcpy(pAdressInTable, pHookFunc, 8);//Записываем наш адресс хука
return (LPVOID)pAdressInTable;// Вернули адресс из таблицы где установили хук
}
bool DetectVMT(LPVOID pClass, int MaxCount)// Простая функция для детекта хука. Проверяет таблицу VMT на изменения адрессов функций
{
DWORD64 pVMT = *(DWORD64*)pClass;//Получаем адресс vmt таблица всегда находится по нулевому смещению
MODULEINFO mInfo;// Структура которая будет получать информацию о модуле (конкретно наша программа)
GetModuleInformation(GetCurrentProcess(), GetModuleHandleA(0), &mInfo, sizeof(mInfo));// Можете в гугл заглянуть что делает эта функция
printf("Base adress %llp\n", mInfo.lpBaseOfDll);
DWORD64 EndFile = (DWORD64)mInfo.lpBaseOfDll + mInfo.SizeOfImage;// Вычисляем конец нашей программы базовый адресс + размер. Нужен для сравнения адрессов. Так как в основном адресса которые в таблице VMT не могут быть за пределами пространства программы
for (int i = 0; i < MaxCount; i++)// Перебор таблицы методов
{
printf("VMT check %llp\n", *(DWORD64*)pVMT);
if(*(DWORD64*)pVMT > EndFile)return 1;// Если адресс функции в таблице больше адресного пространства нашей программы то детект(Тоесть адресс ссылается на внешний код за пределами проги)
if (*(DWORD64*)pVMT < (DWORD64)mInfo.lpBaseOfDll)return 1;// Если адресс функции в таблице меньше (для тех кто выделил память ниже нашей проги)
pVMT += 8;//К первому адрессу в таблице прибавляем 8 байт это размер адрессов в х64 системе. Тем самым переходим на след функцию в таблице
}
return 0;// 0 если все адресса находятся в рамках пространства программы
}
int main()
{
TestVMT* pTestVMT = new TestVMT;
printf("class adr %llp\n", pTestVMT);
pTestVMT->Func4();// Проверка функции до хука
if(DetectVMT(pTestVMT, 5))printf("VMT Detect hook\n");//Проверка таблицы перед хуком
else
{
printf("VMT UNDetect hook\n");
}
system("pause");
LPVOID vAdress = VirtualAlloc(0, 100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);// Выделяем память для хука (Имитируем что мы заинжектили чит)
memcpy(vAdress, (LPVOID)hookfunc, 100);// Запись кода hookfunc
printf("vAdress %llp\n", vAdress);
system("pause");
LPVOID adresshook = Hook(pTestVMT, &vAdress, 3);// Установка хука в VMT (1 класс 2 адресс хука 3 номер функции в таблице)
printf("adresshook %llp\n", adresshook);
system("pause");
pTestVMT->Func4();// Повторно вызывает функцию после хука для проверки
if (DetectVMT(pTestVMT, 5))printf("VMT Detect hook\n");// Повторно проверяем наше таблицу VMT класса
comment_2664Установка и обнаружение VMT Хука (Виртуальная таблица методов класса)
В примере видно как легко детектится такой хук. Как обойти такую простую проверку!?
1. Для хука заходить во внутрь функции и установить хук в самой функции а не в vmt.
2. Записать код хука в пустом месте адресного пространства программы места там хватает чтоб записать хотябы редирект
тем самым адресс хука в vmt будет находится в адресном пространстве программы и условный античит пропустить такой код
1. Для теста создаем консольный проект на C++ и пропишем простой класс с VMT
class TestVMT//Простой класс с использованием VMT { public: virtual void Func1(); virtual void Func2(); virtual void Func3(); virtual void Func4(); virtual void Func5(); DWORD64 ad = 0; }; void TestVMT::Func1() { MessageBoxA(0, "Func1", 0, 0); } void TestVMT::Func2() { MessageBoxA(0, "Func2", 0, 0); } void TestVMT::Func3() { MessageBoxA(0, "Func3", 0, 0); } void TestVMT::Func4() { MessageBoxA(0, "Func4", 0, 0); } void TestVMT::Func5() { MessageBoxA(0, "Func5", 0, 0); }2. Напишем функцию которая будет в роле хука
typedef int (WINAPI* MessageBoxS)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);// Прототип функции MessageBoxS box = (MessageBoxS)0x7FFFB8B88120;// Адресс взят напрямую чтоб не заморачиваться void hookfunc()//функция которая будет выполняться вместо оригинала { box(0, 0, 0, 0); }3. Напишем функцию которая будет устанавливать хук
LPVOID Hook(LPVOID pClass, LPVOID pHookFunc, int dwOffset)// Функция которая устанавливает хук в виртуальной таблице методов (VMT) { LPVOID pVTable = *(LPVOID*)pClass;//Получаем адресс vmt таблица всегда находится по нулевому смещению LPVOID pAdressInTable = (LPVOID)((DWORD64)pVTable + dwOffset * 8);//Вычисляем адресс функции в таблице по ее порядковому номеру DWORD dwOldProtection; VirtualProtect((LPVOID)pAdressInTable, sizeof(pAdressInTable), PAGE_READWRITE, &dwOldProtection);// меняем параметры страницы установка флага чтение запись memcpy(pAdressInTable, pHookFunc, 8);//Записываем наш адресс хука VirtualProtect((LPVOID)pAdressInTable, sizeof(pAdressInTable), dwOldProtection, &dwOldProtection);// Восстановили параметры страницы return (LPVOID)pAdressInTable;// Вернули адресс из таблицы где установили хук }4. Пишем функцию для детекта хука
bool DetectVMT(LPVOID pClass, int MaxCount)// Простая функция для детекта хука. Проверяет таблицу VMT на изменения адрессов функций { DWORD64 pVMT = *(DWORD64*)pClass;//Получаем адресс vmt таблица всегда находится по нулевому смещению MODULEINFO mInfo;// Структура которая будет получать информацию о модуле (конкретно наша программа) GetModuleInformation(GetCurrentProcess(), GetModuleHandleA(0), &mInfo, sizeof(mInfo));// Можете в гугл заглянуть что делает эта функция printf("Base adress %llp\n", mInfo.lpBaseOfDll); DWORD64 EndFile = (DWORD64)mInfo.lpBaseOfDll + mInfo.SizeOfImage;// Вычисляем конец нашей программы базовый адресс + размер. Нужен для сравнения адрессов. Так как в основном адресса которые в таблице VMT не могут быть за пределами пространства программы for (int i = 0; i < MaxCount; i++)// Перебор таблицы методов { printf("VMT check %llp\n", *(DWORD64*)pVMT); if(*(DWORD64*)pVMT > EndFile)return 1;// Если адресс функции в таблице больше адресного пространства нашей программы то детект(То есть адресс ссылается на внешний код за пределами проги) if (*(DWORD64*)pVMT < (DWORD64)mInfo.lpBaseOfDll)return 1;// Если адресс функции в таблице меньше (для тех кто выделил память ниже нашей проги) pVMT += 8;//К первому адрессу в таблице прибавляем 8 байт это размер адрессов в х64 системе. Тем самым переходим на след функцию в таблице } return 0;// 0 если все адресса находятся в рамках пространства программы }5. Точка входа программы и использование функций
int main() { TestVMT* pTestVMT = new TestVMT; printf("class adr %llp\n", pTestVMT); pTestVMT->Func4();// Проверка функции до хука if(DetectVMT(pTestVMT, 5))printf("VMT Detect hook\n");//Проверка таблицы перед хуком else { printf("VMT UNDetect hook\n"); } system("pause"); LPVOID vAdress = VirtualAlloc(0, 100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);// Выделяем память для хука (Имитируем что мы заинжектили чит) memcpy(vAdress, (LPVOID)hookfunc, 100);// Запись кода hookfunc printf("vAdress %llp\n", vAdress); system("pause"); LPVOID adresshook = Hook(pTestVMT, &vAdress, 3);// Установка хука в VMT (1 класс 2 адресс хука 3 номер функции в таблице) printf("adresshook %llp\n", adresshook); system("pause"); pTestVMT->Func4();// Повторно вызывает функцию после хука для проверки if (DetectVMT(pTestVMT, 5))printf("VMT Detect hook\n");// Повторно проверяем наше таблицу VMT класса else { printf("VMT UNDetect hook\n"); } system("pause"); return 1; }Дополнение:
- Как наглядно выглядит таблица (VMT) по нулевому смещению в классе https://prnt.sc/U72mdnLfdQGj
- После установки хука но номеру функции https://prnt.sc/qTUdUi3d7JYs
- Номер функции это порядковый номер https://prnt.sc/c4QilwUNh-Ys
Исходник: