Skip to content

Instantly share code, notes, and snippets.

@Midi12
Last active October 29, 2018 10:09
Show Gist options
  • Save Midi12/734b9e6e2cc5ed02f0ffa95458d27ba3 to your computer and use it in GitHub Desktop.
Save Midi12/734b9e6e2cc5ed02f0ffa95458d27ba3 to your computer and use it in GitHub Desktop.
Quick x64 virtual function hook class
#include <iostream>
#include <string>
#include "VMTHook.h"
class Test {
public:
virtual int Add(int a, int b);
};
int Test::Add(int a, int b) {
return a + b;
}
int hook(int a, int b) {
std::cout << "hook called !! ";
return 42;
}
int main() {
Test *test = new Test();
Detour::VMTHook *hk = new Detour::VMTHook(reinterpret_cast<std::uintptr_t **>(test), 0, reinterpret_cast<std::uintptr_t>(&hook));
std::cout << "orig result " << std::to_string(test->Add(12, 9)) << std::endl;
hk->Apply();
std::cout << "hooked result " << std::to_string(test->Add(12, 9)) << std::endl;
std::cout << "orig result (call original) " << std::to_string(hk->CallOriginal<int>(12, 9)) << std::endl;
hk->Restore();
std::cout << "restored result " << std::to_string(test->Add(12, 9)) << std::endl;
}
#include "VMTHook.h"
namespace Detour {
/*
* Constructor
*/
VMTHook::VMTHook(std::uintptr_t** vtable, const std::uint16_t index, std::uintptr_t hook)
: _vtable(vtable), _index(index), _ptr(hook), _orig(0) {
assert(vtable != nullptr);
assert(index >= 0);
assert(hook != 0);
}
/*
* Apply hook in memory if not already applied
*/
bool VMTHook::Apply(void) {
if (!IsApplied()) {
DWORD old;
_orig = (*_vtable)[_index];
VirtualProtect(&((*_vtable)[_index]), sizeof(std::uintptr_t), PAGE_READWRITE, &old);
(*_vtable)[_index] = _ptr;
VirtualProtect(&((*_vtable)[_index]), sizeof(std::uintptr_t), old, &old);
return true;
}
return false;
}
/*
* Restore original function pointer in memory if applied
*/
bool VMTHook::Restore(void) {
if (IsApplied()) {
DWORD old;
VirtualProtect(&((*_vtable)[_index]), sizeof(std::uintptr_t), PAGE_READWRITE, &old);
(*_vtable)[_index] = _orig;
VirtualProtect(&((*_vtable)[_index]), sizeof(std::uintptr_t), old, &old);
return true;
}
return false;
}
/*
* Returns true if the hook is applied in memory
*/
bool VMTHook::IsApplied(void) {
return IsValid() && (*_vtable)[_index] == _ptr;
}
/*
* Returns true if hook data are valid
*/
bool VMTHook::IsValid(void) {
return _vtable != nullptr && (*_vtable) != nullptr && _index >= 0;
}
}
#pragma once
#include <cstdint>
#include <cassert>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
namespace Detour {
/*
* Virtual function table hooking class
* Only designed to works on x64 (passing `this` as first argument)
*/
class VMTHook {
public:
VMTHook() = delete;
VMTHook(std::uintptr_t** vtable, const std::uint16_t index, std::uintptr_t hook);
VMTHook(const VMTHook& other) = delete;
VMTHook& operator=(const VMTHook& other) = delete;
VMTHook(VMTHook&& other) = default;
VMTHook& operator=(VMTHook&& other) = default;
~VMTHook() = default;
bool Apply(void);
bool Restore(void);
bool IsApplied(void);
bool IsValid(void);
/*
* Call the original function pointer with parameters
* Automatically pass `this` pointer as first argument
*/
template <typename ReturnType, typename ...ArgsType>
ReturnType CallOriginal(ArgsType&&... args) const {
return reinterpret_cast<ReturnType(*)(std::uintptr_t **, ArgsType ...)>(_orig)(_vtable, std::forward<ArgsType>(args)...);
}
private:
std::uintptr_t _ptr;
std::uintptr_t _orig;
std::uintptr_t **_vtable;
std::uint16_t _index;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment