Гайд Хуки в CSGO (VMT hooks)

В игре Source SDK
Забаненный
Статус
Оффлайн
Регистрация
10 Янв 2017
Сообщения
2,148
Реакции[?]
806
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Приветствую. В данной статье расскажу
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 (объяснял выше).

Также нужно делать трамполайн - возврат оригинального адреса, а то
будет краш. Делается это также как и ставится хук.

Разбирайтесь)​
 
push me to the edge
Олдфаг
Статус
Оффлайн
Регистрация
22 Мар 2017
Сообщения
2,253
Реакции[?]
1,204
Поинты[?]
1K
Полезный гайд, расскажи как правильно делать возврат оригинальной функции, думаю лишним не будет.
 
В игре Source SDK
Забаненный
Статус
Оффлайн
Регистрация
10 Янв 2017
Сообщения
2,148
Реакции[?]
806
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Полезный гайд, расскажи как правильно делать возврат оригинальной функции, думаю лишним не будет.
Для этого тебе нужно описать функцию
Код:
    template <typename _TY>
    _TY GetOriginal(int Index) {
        return (_TY)m_OrigVMT[Index];
    }
где m_OrigVMT - буффер содержащий в себе оригинальную виртуальную таблицу.
Далее внутри функции - хука вызываешь у инстанца вмт менджера для этой функции функцию GetOriginal с нужным индексом.
Пример
Код:
void __fastcall hk(void* thisptr, void* edx) {
    printf("THook\n");
    auto Trampoline = hook.GetOriginal<test>(0);
    Trampoline(thisptr, edx);
}
 
Сверху Снизу