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

  • Автор темы Автор темы P45H3
  • Дата начала Дата начала
В игре Source SDK
Забаненный
Забаненный
Статус
Оффлайн
Регистрация
10 Янв 2017
Сообщения
2,188
Реакции
806
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Приветствую. В данной статье расскажу
Virtual Method Table Hooks или просто VMT хуки.


В движке игры CSGO есть виртуальные классы - интерфейсы,
в которых находятся различные методы.
Пример:​
Код:
Expand Collapse Copy
class CEngine {
public:

    CEngine() {}

    virtual void Func1() { printf("CEngine::Func1\n"); }
    virtual void Func2() { printf("CEngine::Func2\n"); }

    virtual ~CEngine() {}
};

Класс, в котором есть хоть одна виртуальная функция, имеет виртуальную таблицу.
Указатель на которую можно получить так​
Код:
Expand Collapse Copy
DWORD * VMT = *(DWORD**)Instance;

Виртуальная таблица по сути своей - это массив 4 байтных указатель для x32 и
8 байтных для x64 соответственно.

Пример вызова виртуальной функции через
каст указателя на неё к тайпдефу.
Код:
Expand Collapse Copy
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 библиотеки).

Пример:​
Код:
Expand Collapse Copy
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 (объяснял выше).

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

Разбирайтесь)​
 
Полезный гайд, расскажи как правильно делать возврат оригинальной функции, думаю лишним не будет.
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Полезный гайд, расскажи как правильно делать возврат оригинальной функции, думаю лишним не будет.
Для этого тебе нужно описать функцию
Код:
Expand Collapse Copy
    template <typename _TY>
    _TY GetOriginal(int Index) {
        return (_TY)m_OrigVMT[Index];
    }

где m_OrigVMT - буффер содержащий в себе оригинальную виртуальную таблицу.
Далее внутри функции - хука вызываешь у инстанца вмт менджера для этой функции функцию GetOriginal с нужным индексом.
Пример
Код:
Expand Collapse Copy
void __fastcall hk(void* thisptr, void* edx) {
    printf("THook\n");
    auto Trampoline = hook.GetOriginal<test>(0);
    Trampoline(thisptr, edx);
}
 
Назад
Сверху Снизу