Руководство

Подробное руководство по эффективному использованию плагина, которое поможет вам максимально использовать его возможности и функциональность.

Динамическое перехватывание C/C++ функций на любом языке

Это плагин, который обеспечивает динамическое перехватывание C++ функций во время выполнения — включая как перехват таблицы виртуальных функций (vtable), так и перехват кода (detour). Он основан на мощной библиотеке polyhook2 и разработан для легкой интеграции в ваш плагин или скриптовую среду.

Это руководство охватывает:

  • Перехват detour-функций (по адресу функции)
  • Перехват виртуальных функций (по индексу или указателю на функцию)
  • Регистрацию пре- и пост-коллбэков
  • Проверку и изменение аргументов функций и возвращаемых значений
  • Снятие перехватов и управление коллбэками

Основные понятия

Что такое перехват (Hook)?

Перехват — это метод для перехвата вызовов функций. Вы можете:

  • Проверять/изменять аргументы функции перед вызовом (pre)
  • Наблюдать/изменять возвращаемое значение после вызова (post)
  • Переопределять или полностью пропускать исходную функцию (supercede)

Типы перехватов

  • HookDetour: Перехватывает отдельную или статическую функцию.
  • HookVirtual: Перехватывает метод класса через индекс vtable или указатель на функцию.

Типы данных

Используйте enum DataType для описания типов аргументов и возвращаемых значений:

enum class DataType : uint8_t {
  Void, Bool, Int8, UInt8, Int16, UInt16,
  Int32, UInt32, Int64, UInt64,
  Float, Double, Pointer, String
};

Используйте их последовательно при описании сигнатур функций для API перехватов.

Перехват функций

1. Detour Hook

Используйте HookDetour для перехвата глобальной/статической функции:

Callback* HookDetour(void* pFunc, DataType returnType, const plg::vector<DataType>& args, int varIndex);
  • pFunc: Указатель на функцию для перехвата.
  • returnType: Тип возвращаемого значения с использованием DataType.
  • args: Список типов аргументов.
  • varIndex: Используйте -1 для обычных вызовов; установите, если переменное количество аргументов.

Пример:

void* targetFunc = (void*) &DoSomething;
plg::vector<DataType> args = { DataType::Int32, DataType::Pointer };
Callback* cb = HookDetour(targetFunc, DataType::Void, args, -1);

2. Virtual Hook (по индексу)

Перехват метода из vtable объекта:

Callback* HookVirtual(void* pClass, int index, DataType returnType, const plg::vector<DataType>& args, int varIndex);

Пример:

Callback* cb = HookVirtual(someObj, 3, DataType::Bool, { DataType::Pointer }, -1);

Вы также можете получить индекс vtable из указателя на функцию:

int index = GetVTableIndex((void*) &MyClass::MyMethod);

3. Virtual Hook (по указателю на функцию)

Вместо использования индекса vtable:

Callback* HookVirtualByFunc(void* pClass, void* pFunc, DataType returnType, const vector<DataType>& args, int varIndex);

Снятие перехватов

  • UnhookDetour(void* pFunc)
  • UnhookVirtual(void* pClass, int index)
  • UnhookVirtualByFunc(void* pClass, void* pFunc)
  • UnhookAll() – удаляет все перехваты
  • UnhookAllVirtual(void* pClass) – удаляет все vtable-перехваты для класса

Система коллбэков

1. Регистрация коллбэка

bool AddCallback(Callback* cb, CallbackType type, CallbackHandler handler);
  • type: CallbackType::Pre или CallbackType::Post
  • handler: Указатель на функцию

Сигнатура обработчика:

ReturnAction myHandler(Callback* cb, const Parameters* params, int32_t count, const Return* ret, CallbackType type);

Используйте это для проверки или изменения аргументов и возвращаемых значений.

2. ReturnAction

enum class ReturnAction : int32_t {
  Ignored,   // Не вмешиваться
  Handled,   // Вмешаться, но вызвать реальную функцию
  Override,  // Вызвать реальную функцию, но изменить возвращаемое значение
  Supercede  // Полностью пропустить реальную функцию
};

Доступ к аргументам и возвращаемому значению

Получение аргументов:

int val = GetArgumentInt32(params, 0);
void* ptr = GetArgumentPointer(params, 1);

Установка аргументов:

SetArgumentInt32(params, 0, 42);
SetArgumentPointer(params, 1, myPtr);

Получение возвращаемого значения:

float retVal = GetReturnFloat(ret);

Переопределение возвращаемого значения:

SetReturnFloat(ret, 1.0f);
return ReturnAction::Supercede;

Полный пример

Перехват int Add(int a, int b) и изменение результата:

ReturnAction preAdd(Callback* cb, const Parameters* params, int32_t count, const Return* ret, CallbackType type) {
  int32_t a = GetArgumentInt32(params, 0);
  int32_t b = GetArgumentInt32(params, 1);
  SetArgumentInt32(params, 0, a + 1);
  SetArgumentInt32(params, 1, b + 1);
  return ReturnAction::Handled;
}

ReturnAction postAdd(Callback* cb, const Parameters* params, int32_t count, const Return* ret, CallbackType type) {
  int32_t result = GetReturnInt32(ret);
  SetReturnInt32(ret, result * 2);
  return ReturnAction::Override;
}

void setup() {
  auto cb = HookDetour((void*)&Add, DataType::Int32, { DataType::Int32, DataType::Int32 }, -1);
  AddCallback(cb, CallbackType::Pre, preAdd);
  AddCallback(cb, CallbackType::Post, postAdd);
}

Вспомогательные функции

  • FindDetour, FindVirtual, FindVirtualByFunc: Поиск перехватов
  • AreCallbacksRegistered, IsCallbackRegistered: Интроспекция
  • GetFunctionAddr: Получить указатель на функцию (возможно, перехваченную)
  • GetOriginalAddr: Получить trampoline/исходную функцию

Резюме

ДействиеФункция
Hook DetourHookDetour
Hook Virtual (Index)HookVirtual
Hook Virtual (Func)HookVirtualByFunc
Добавить коллбэкAddCallback
Доступ к аргументамGetArgumentX, SetArgumentX
Доступ к возвратуGetReturnX, SetReturnX
Снять перехватUnhookDetour, UnhookVirtual, и т.д.
Получить адресаGetFunctionAddr, GetOriginalAddr

Используйте это руководство в качестве справочника при разработке с вашим плагином на основе PolyHook. Всегда проверяйте аргументы-указатели и обеспечивайте типобезопасность при приведении типов аргументов или возвращаемых значений.