Перейти к содержанию
Опубликовано
comment_446
Приветствую. В данной статье расскажу 

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 библиотеки).

 

Пример:



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 (объяснял выше).

 

Также нужно делать трамполайн - возврат оригинального адреса, а то

будет краш. Делается это также как и ставится хук.

 

Разбирайтесь) 

With love by P45H3 =)

02a1fd3cc9ed.png

 

 

Рекомендуемые сообщения

Гость
Эта тема закрыта для публикации ответов.