В игре Source SDK
-
Автор темы
- #1
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Приветствую. В данной статье расскажу
Virtual Method Table Hooks или просто VMT хуки.
В движке игры CSGO есть виртуальные классы - интерфейсы,
в которых находятся различные методы.
Пример:
Virtual Method Table Hooks или просто VMT хуки.
В движке игры CSGO есть виртуальные классы - интерфейсы,
в которых находятся различные методы.
Пример:
Код:
class CEngine {
public:
CEngine() {}
virtual void Func1() { printf("CEngine::Func1\n"); }
virtual void Func2() { printf("CEngine::Func2\n"); }
virtual ~CEngine() {}
};
Класс, в котором есть хоть одна виртуальная функция, имеет виртуальную таблицу.
Указатель на которую можно получить так
Указатель на которую можно получить так
Код:
DWORD * VMT = *(DWORD**)Instance;
Виртуальная таблица по сути своей - это массив 4 байтных указатель для x32 и
8 байтных для x64 соответственно.
Пример вызова виртуальной функции через
каст указателя на неё к тайпдефу.
Код:
class CEngine {
public:
CEngine() {}
virtual void Func1() { printf("CEngine::Func1\n"); }
virtual void Func2() { printf("CEngine::Func2\n"); }
virtual ~CEngine() {}
};
int main() {
CEngine * g_pEngine = new CEngine();
typedef void(__fastcall * fn)(void);
fn ofn;
DWORD * VMT = *(DWORD**)g_pEngine;
ofn = (fn)VMT[0];
if (ofn != nullptr)
ofn();
delete g_pEngine;
return 0L;
}
Создаём тайпдеф данной функции. Используем __fastcall
т.к. в нём параметры передаются через регистры
ECX, EDX , как раз указатель на класс передаётся через
регистр ECX соответственно для соглашения __thiscall.
Далее к объекту тайпдефа кастим указатель на нулевую
виртуальную функцию в виртуальной таблице, проверяем на валидность и вызываем.
Вы уже могли начать догадываться о том, что указатель внутри
виртуальной таблицы можно подменить на свою функцию тем самым
установив хук (как и делают VMT hooks библиотеки).
Пример:
т.к. в нём параметры передаются через регистры
ECX, EDX , как раз указатель на класс передаётся через
регистр ECX соответственно для соглашения __thiscall.
Далее к объекту тайпдефа кастим указатель на нулевую
виртуальную функцию в виртуальной таблице, проверяем на валидность и вызываем.
Вы уже могли начать догадываться о том, что указатель внутри
виртуальной таблицы можно подменить на свою функцию тем самым
установив хук (как и делают VMT hooks библиотеки).
Пример:
Код:
class CEngine {
public:
CEngine() {}
virtual void Func1() { printf("CEngine::Func1\n"); }
virtual void Func2() { printf("CEngine::Func2\n"); }
virtual ~CEngine() {}
};
void __fastcall hkFunc1() { printf("Hook was called\n"); }
int main() {
CEngine * g_pEngine = new CEngine();
DWORD * VMT = *(DWORD**)g_pEngine;
g_pEngine->Func1(); // call orig method
DWORD oldProt;
VirtualProtect(VMT, SizeOfVMT(VMT) * sizeof(DWORD), PAGE_EXECUTE_READWRITE, &oldProt);
VMT[0] = (DWORD)hkFunc1; // "hook"
VirtualProtect(VMT, SizeOfVMT(VMT) * sizeof(DWORD), oldProt, &oldProt);
g_pEngine->Func1(); // call hook method
delete g_pEngine;
return 0L;
}
Получаем указатель на начало виртуальной таблицы.
Ставим флаг на память, чтобы разрешить нам писать туда.
Подменяем указатель на оригинальный метод указателем на нашу
hook - функцию, которая имеет прототип оригинальной функции с соглашением
__fatscall (объяснял выше).
Также нужно делать трамполайн - возврат оригинального адреса, а то
будет краш. Делается это также как и ставится хук.
Разбирайтесь)
Ставим флаг на память, чтобы разрешить нам писать туда.
Подменяем указатель на оригинальный метод указателем на нашу
hook - функцию, которая имеет прототип оригинальной функции с соглашением
__fatscall (объяснял выше).
Также нужно делать трамполайн - возврат оригинального адреса, а то
будет краш. Делается это также как и ставится хук.
Разбирайтесь)