#pragma once #include "signature.hpp" #include "../../asmjit/core/jitruntime.h" #include "../../asmjit/x86/x86assembler.h" #pragma comment(lib, "minhook.lib") using namespace asmjit::x86; namespace utils::hook { namespace detail { template std::vector get_iota_functions() { if constexpr (Entries == 0) { std::vector functions; return functions; } else { auto functions = get_iota_functions(); functions.emplace_back([]() { return Entries - 1; }); return functions; } } } // Gets the pointer to the entry in the v-table. // It seems otherwise impossible to get this. // This is ugly as fuck and only safely works on x64 // Example: // ID3D11Device* device = ... // auto entry = get_vtable_entry(device, &ID3D11Device::CreateTexture2D); template void** get_vtable_entry(Class* obj, T(Class::* entry)(Args ...)) { union { decltype(entry) func; void* pointer; }; func = entry; auto iota_functions = detail::get_iota_functions(); auto* object = iota_functions.data(); using fake_func = size_t(__thiscall*)(void* self); auto index = static_cast(pointer)(&object); void** obj_v_table = *reinterpret_cast(obj); return &obj_v_table[index]; } class detour { public: detour(); detour(void* place, void* target); detour(size_t place, void* target); ~detour(); detour(detour&& other) noexcept { this->operator=(std::move(other)); } detour& operator=(detour&& other) noexcept { if (this != &other) { this->clear(); this->place_ = other.place_; this->original_ = other.original_; this->moved_data_ = other.moved_data_; other.place_ = nullptr; other.original_ = nullptr; other.moved_data_ = {}; } return *this; } detour(const detour&) = delete; detour& operator=(const detour&) = delete; void enable(); void disable(); void create(void* place, void* target); void create(size_t place, void* target); void clear(); void move(); void* get_place() const; template T* get() const { return static_cast(this->get_original()); } template T invoke(Args ... args) { return static_cast(this->get_original())(args...); } [[nodiscard]] void* get_original() const; private: std::vector moved_data_{}; void* place_{}; void* original_{}; void un_move(); }; std::optional> iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub); void nop(void* place, size_t length); void nop(size_t place, size_t length); void copy(void* place, const void* data, size_t length); void copy(size_t place, const void* data, size_t length); void copy_string(void* place, const char* str); void copy_string(size_t place, const char* str); bool is_relatively_far(const void* pointer, const void* data, int offset = 5); void call(void* pointer, void* data); void call(size_t pointer, void* data); void call(size_t pointer, size_t data); void jump(void* pointer, void* data, bool use_far = false, bool use_safe = false); void jump(size_t pointer, void* data, bool use_far = false, bool use_safe = false); void jump(size_t pointer, size_t data, bool use_far = false, bool use_safe = false); void inject(void* pointer, const void* data); void inject(size_t pointer, const void* data); std::vector move_hook(void* pointer); std::vector move_hook(size_t pointer); template T extract(void* address) { auto* const data = static_cast(address); const auto offset = *reinterpret_cast(data); return reinterpret_cast(data + offset + 4); } void* follow_branch(void* address); template static void set(void* place, T value = false) { copy(place, &value, sizeof(value)); } template static void set(const size_t place, T value = false) { return set(reinterpret_cast(place), value); } template static T invoke(size_t func, Args ... args) { return reinterpret_cast(func)(args...); } template static T invoke(void* func, Args ... args) { return static_cast(func)(args...); } std::vector query_original_data(const void* data, size_t length); }