diff --git a/.gitmodules b/.gitmodules index 8d3a7ca..265ded9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "deps/minhook"] - path = deps/minhook - url = https://github.com/TsudaKageyu/minhook.git [submodule "deps/GSL"] path = deps/GSL url = https://github.com/microsoft/GSL.git diff --git a/deps/minhook b/deps/minhook deleted file mode 160000 index 4a45552..0000000 --- a/deps/minhook +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4a455528f61b5a375b1f9d44e7d296d47f18bb18 diff --git a/deps/premake/minhook.lua b/deps/premake/minhook.lua deleted file mode 100644 index b69d149..0000000 --- a/deps/premake/minhook.lua +++ /dev/null @@ -1,32 +0,0 @@ -minhook = { - source = path.join(dependencies.basePath, "minhook") -} - -function minhook.import() - links { "minhook" } - minhook.includes() -end - -function minhook.includes() - includedirs { - path.join(minhook.source, "include") - } -end - -function minhook.project() - project "minhook" - language "C" - - minhook.includes() - - files - { - path.join(minhook.source, "src/**.h"), - path.join(minhook.source, "src/**.c") - } - - warnings "Off" - kind "StaticLib" -end - -table.insert(dependencies, minhook) diff --git a/src/client/component/cheats.cpp b/src/client/component/cheats.cpp index 1033a55..6e94754 100644 --- a/src/client/component/cheats.cpp +++ b/src/client/component/cheats.cpp @@ -61,10 +61,10 @@ public: cl_EnableCheats = game::Dvar_RegisterBool( "cl_EnableCheats", false, game::DVAR_NONE, "Enable FoF wallhack"); - utils::hook::jump(0x430561, draw_red_box_stub); + utils::hook(0x430561, draw_red_box_stub, HOOK_JUMP).install()->quick(); utils::hook::nop(0x430566, 2); - utils::hook::jump(0x5AA524, blind_eye_check_stub); + utils::hook(0x5AA524, blind_eye_check_stub, HOOK_JUMP).install()->quick(); add_cheat_commands(); } diff --git a/src/client/component/dvar_patches.cpp b/src/client/component/dvar_patches.cpp index c838480..4c1306c 100644 --- a/src/client/component/dvar_patches.cpp +++ b/src/client/component/dvar_patches.cpp @@ -14,7 +14,9 @@ void dvar_set_from_string_by_name_stub(const char* dvar_name, class component final : public component_interface { public: void post_unpack() override { - utils::hook::call(0x59C0EF, dvar_set_from_string_by_name_stub); + utils::hook(0x59C0EF, dvar_set_from_string_by_name_stub, HOOK_CALL) + .install() + ->quick(); } }; } // namespace dvar_patches diff --git a/src/client/component/exploit.cpp b/src/client/component/exploit.cpp index 8364832..0a0bbd1 100644 --- a/src/client/component/exploit.cpp +++ b/src/client/component/exploit.cpp @@ -57,8 +57,8 @@ public: add_exploit_commands(); add_key_hooks(); - utils::hook::call(0x420B76, write_message_sequence); - utils::hook::call(0x420B86, write_command_sequence); + utils::hook(0x420B76, write_message_sequence, HOOK_CALL).install()->quick(); + utils::hook(0x420B86, write_command_sequence, HOOK_CALL).install()->quick(); } private: diff --git a/src/client/component/key_catcher.cpp b/src/client/component/key_catcher.cpp index 8a5feae..dd9199f 100644 --- a/src/client/component/key_catcher.cpp +++ b/src/client/component/key_catcher.cpp @@ -6,8 +6,6 @@ #include "key_catcher.hpp" namespace key_catcher { -utils::hook::detour cl_key_event_hook; - namespace { std::unordered_map& get_key_callbacks() { static std::unordered_map key_callbacks{}; @@ -37,16 +35,14 @@ void cl_key_event_stub(game::LocalClientNum_t local_client, int key_id, int a3) { handle_key_event(local_client, key_id); - cl_key_event_hook.invoke(local_client, key_id, a3); + utils::hook::invoke(0x4CD840, local_client, key_id, a3); } class component final : public component_interface { public: void post_unpack() override { - cl_key_event_hook.create(0x4CD840, &cl_key_event_stub); + utils::hook(0x53CC70, cl_key_event_stub, HOOK_CALL).install()->quick(); } - - void pre_destroy() override { cl_key_event_hook.clear(); } }; } // namespace key_catcher diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index e3f70a9..5c21f0f 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -53,7 +53,9 @@ public: void post_unpack() override { add_network_commands(); - utils::hook::call(0x5B27E1, packet_interception_handler); + utils::hook(0x5B27E1, packet_interception_handler, HOOK_CALL) + .install() + ->quick(); } private: diff --git a/src/client/component/remove_hooks.cpp b/src/client/component/remove_hooks.cpp index cf56e01..02d054f 100644 --- a/src/client/component/remove_hooks.cpp +++ b/src/client/component/remove_hooks.cpp @@ -45,8 +45,12 @@ public: void post_start() override { remove_tekno_hooks(); } void post_unpack() override { - utils::hook::call(0x4E3D42, msg_read_bits_compress_check_sv); - utils::hook::call(0x4A9F56, msg_read_bits_compress_check_cl); + utils::hook(0x4E3D42, msg_read_bits_compress_check_sv, HOOK_CALL) + .install() + ->quick(); + utils::hook(0x4A9F56, msg_read_bits_compress_check_cl, HOOK_CALL) + .install() + ->quick(); } private: diff --git a/src/client/component/scheduler.cpp b/src/client/component/scheduler.cpp index eec544f..d364b02 100644 --- a/src/client/component/scheduler.cpp +++ b/src/client/component/scheduler.cpp @@ -144,9 +144,9 @@ public: } }); - utils::hook::call(0x4E4A0D, cl_frame_stub); - utils::hook::call(0x5B54D2, r_end_frame_stub); - utils::hook::call(0x543B0E, main_frame_stub); + utils::hook(0x4E4A0D, cl_frame_stub, HOOK_CALL).install()->quick(); + utils::hook(0x5B54D2, r_end_frame_stub, HOOK_CALL).install()->quick(); + utils::hook(0x543B0E, main_frame_stub, HOOK_CALL).install()->quick(); } void pre_destroy() override { diff --git a/src/common/utils/hook.cpp b/src/common/utils/hook.cpp index fffaeeb..5f633bc 100644 --- a/src/common/utils/hook.cpp +++ b/src/common/utils/hook.cpp @@ -1,156 +1,166 @@ #include "hook.hpp" -#include "string.hpp" -#include - -namespace utils::hook { -namespace { -[[maybe_unused]] class _ { -public: - _() { - if (MH_Initialize() != MH_OK) { - throw std::runtime_error("Failed to initialize MinHook"); - } - } - - ~_() { MH_Uninitialize(); } -} __; -} // namespace - -detour::detour(const size_t place, void* target) - : detour(reinterpret_cast(place), target) {} - -detour::detour(void* place, void* target) { this->create(place, target); } - -detour::~detour() { this->clear(); } - -void detour::enable() const { MH_EnableHook(this->place_); } - -void detour::disable() const { MH_DisableHook(this->place_); } - -void detour::create(void* place, void* target) { - this->clear(); - this->place_ = place; - - if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK) { - throw std::runtime_error( - string::va("Unable to create hook at location: %p", this->place_)); - } - - this->enable(); -} - -void detour::create(const size_t place, void* target) { - this->create(reinterpret_cast(place), target); -} - -void detour::clear() { - if (this->place_) { - MH_RemoveHook(this->place_); - } - - this->place_ = nullptr; - this->original_ = nullptr; -} - -void* detour::get_original() const { return this->original_; } - -void nop(void* place, const size_t length) { - DWORD old_protect{}; - VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect); - - std::memset(place, 0x90, length); - - VirtualProtect(place, length, old_protect, &old_protect); - FlushInstructionCache(GetCurrentProcess(), place, length); -} - -void nop(const size_t place, const size_t length) { - nop(reinterpret_cast(place), length); -} - -void copy(void* place, const void* data, const size_t length) { - DWORD old_protect{}; - VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect); - - std::memmove(place, data, length); - - VirtualProtect(place, length, old_protect, &old_protect); - FlushInstructionCache(GetCurrentProcess(), place, length); -} - -void copy(const size_t place, const void* data, const size_t length) { - copy(reinterpret_cast(place), data, length); -} - -bool is_relatively_far(const void* pointer, const void* data, - const int offset) { - const int64_t diff = size_t(data) - (size_t(pointer) + offset); - const auto small_diff = int32_t(diff); - return diff != int64_t(small_diff); -} - -void call(void* pointer, void* data) { - if (is_relatively_far(pointer, data)) { - throw std::runtime_error("Too far away to create 32bit relative branch"); - } - - auto* patch_pointer = PBYTE(pointer); - set(patch_pointer, 0xE8); - set(patch_pointer + 1, - int32_t(size_t(data) - (size_t(pointer) + 5))); -} - -void call(const size_t pointer, void* data) { - return call(reinterpret_cast(pointer), data); -} - -void call(const size_t pointer, const size_t data) { - return call(pointer, reinterpret_cast(data)); -} - -void set(std::uintptr_t address, std::vector&& bytes) { - DWORD oldProtect = 0; - - auto* place = reinterpret_cast(address); - VirtualProtect(place, bytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect); - memcpy(place, bytes.data(), bytes.size()); - VirtualProtect(place, bytes.size(), oldProtect, &oldProtect); - FlushInstructionCache(GetCurrentProcess(), place, bytes.size()); -} - -void set(std::uintptr_t address, void* buffer, size_t size) { - DWORD oldProtect = 0; - - auto* place = reinterpret_cast(address); - VirtualProtect(place, size, PAGE_EXECUTE_READWRITE, &oldProtect); - memcpy(place, buffer, size); - VirtualProtect(place, size, oldProtect, &oldProtect); - FlushInstructionCache(GetCurrentProcess(), place, size); -} - -void jump(std::uintptr_t address, void* destination) { - if (!address) +namespace utils { +void hook::signature::process() { + if (this->signatures_.empty()) return; - std::uint8_t* bytes = new std::uint8_t[5]; - *bytes = 0xE9; - *reinterpret_cast(bytes + 1) = - CalculateRelativeJMPAddress(address, destination); + const auto start = static_cast(this->start_); - set(address, bytes, 5); + const unsigned int sig_count = this->signatures_.size(); + const auto containers = this->signatures_.data(); - delete[] bytes; + for (size_t i = 0; i < this->length_; ++i) { + const auto address = start + i; + + for (unsigned int k = 0; k < sig_count; ++k) { + const auto container = &containers[k]; + + unsigned int j; + for (j = 0; j < strlen(container->mask); ++j) { + if (container->mask[j] != '?' && + container->signature[j] != address[j]) { + break; + } + } + + if (j == strlen(container->mask)) { + container->callback(address); + } + } + } } -void redirect_jump(void* pointer, void* data) { - char* operand_ptr = static_cast(pointer) + 2; - int new_operand = - reinterpret_cast(data) - (reinterpret_cast(pointer) + 6); - set(operand_ptr, new_operand); +void hook::signature::add(const container& container) { + signatures_.push_back(container); } -void redirect_jump(size_t pointer, void* data) { - redirect_jump(reinterpret_cast(pointer), data); +hook::~hook() { + if (this->initialized_) { + this->uninstall(); + } } -} // namespace utils::hook + +hook* hook::initialize(const DWORD place, void (*stub)(), const bool use_jump) { + return this->initialize(place, reinterpret_cast(stub), use_jump); +} + +hook* hook::initialize(const DWORD place, void* stub, const bool use_jump) { + return this->initialize(reinterpret_cast(place), stub, use_jump); +} + +hook* hook::initialize(void* place, void* stub, const bool use_jump) { + if (this->initialized_) + return this; + this->initialized_ = true; + + this->use_jump_ = use_jump; + this->place_ = place; + this->stub_ = stub; + + this->original_ = + static_cast(this->place_) + 5 + + *reinterpret_cast((static_cast(this->place_) + 1)); + + return this; +} + +hook* hook::install(const bool unprotect, const bool keep_unprotected) { + std::lock_guard _(this->state_mutex_); + + if (!this->initialized_ || this->installed_) { + return this; + } + + this->installed_ = true; + + if (unprotect) + VirtualProtect(this->place_, sizeof(this->buffer_), PAGE_EXECUTE_READWRITE, + &this->protection_); + std::memcpy(this->buffer_, this->place_, sizeof(this->buffer_)); + + const auto code = static_cast(this->place_); + + *code = static_cast(this->use_jump_ ? 0xE9 : 0xE8); + + *reinterpret_cast(code + 1) = + reinterpret_cast(this->stub_) - + (reinterpret_cast(this->place_) + 5); + + if (unprotect && !keep_unprotected) + VirtualProtect(this->place_, sizeof(this->buffer_), this->protection_, + &this->protection_); + + FlushInstructionCache(GetCurrentProcess(), this->place_, + sizeof(this->buffer_)); + + return this; +} + +void hook::quick() { + if (this->installed_) { + this->installed_ = false; + } +} + +bool hook::iat(const nt::library module, const std::string& target_module, + const std::string& process, void* stub) { + if (!module.is_valid()) + return false; + + auto ptr = module.get_iat_entry(target_module, process); + if (!ptr) + return false; + + DWORD protect; + VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect); + + *ptr = stub; + + VirtualProtect(ptr, sizeof(*ptr), protect, &protect); + return true; +} + +hook* hook::uninstall(const bool unprotect) { + std::lock_guard _(this->state_mutex_); + + if (!this->initialized_ || !this->installed_) { + return this; + } + + this->installed_ = false; + + if (unprotect) + VirtualProtect(this->place_, sizeof(this->buffer_), PAGE_EXECUTE_READWRITE, + &this->protection_); + + std::memcpy(this->place_, this->buffer_, sizeof(this->buffer_)); + + if (unprotect) + VirtualProtect(this->place_, sizeof(this->buffer_), this->protection_, + &this->protection_); + + FlushInstructionCache(GetCurrentProcess(), this->place_, + sizeof(this->buffer_)); + + return this; +} + +void* hook::get_address() const { return this->place_; } + +void* hook::get_original() const { return this->original_; } + +void hook::nop(void* place, const size_t length) { + DWORD old_protect; + VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect); + + memset(place, 0x90, length); + + VirtualProtect(place, length, old_protect, &old_protect); + FlushInstructionCache(GetCurrentProcess(), place, length); +} + +void hook::nop(const DWORD place, const size_t length) { + nop(reinterpret_cast(place), length); +} +} // namespace utils diff --git a/src/common/utils/hook.hpp b/src/common/utils/hook.hpp index 6cae5d3..39ccc9b 100644 --- a/src/common/utils/hook.hpp +++ b/src/common/utils/hook.hpp @@ -1,102 +1,120 @@ #pragma once -#include "signature.hpp" +#include +#include "nt.hpp" -#define CalculateRelativeJMPAddress(X, Y) \ - (((std::uintptr_t)Y - (std::uintptr_t)X) - 5) +#define HOOK_JUMP true +#define HOOK_CALL false -namespace utils::hook { -class detour { +namespace utils { +class hook final { public: - detour() = default; - detour(void* place, void* target); - detour(size_t place, void* target); - ~detour(); + class signature final { + public: + struct container final { + const char* signature; + const char* mask; + std::function callback; + }; - detour(detour&& other) noexcept { this->operator=(std::move(other)); } + signature(void* start, const size_t length) + : start_(start), length_(length) {} - detour& operator=(detour&& other) noexcept { - if (this != &other) { - this->~detour(); + signature(const DWORD start, const size_t length) + : signature(reinterpret_cast(start), length) {} - this->place_ = other.place_; - this->original_ = other.original_; + signature() : signature(0x400000, 0x800000) {} - other.place_ = nullptr; - other.original_ = nullptr; - } + void process(); + void add(const container& container); - return *this; + private: + void* start_; + size_t length_; + std::vector signatures_; + }; + + hook() + : initialized_(false), installed_(false), place_(nullptr), stub_(nullptr), + original_(nullptr), use_jump_(false), protection_(0) { + ZeroMemory(this->buffer_, sizeof(this->buffer_)); } - detour(const detour&) = delete; - detour& operator=(const detour&) = delete; - - void enable() const; - void disable() const; - - void create(void* place, void* target); - void create(size_t place, void* target); - void clear(); - - template T* get() const { - return static_cast(this->get_original()); + hook(void* place, void* stub, const bool use_jump = true) : hook() { + this->initialize(place, stub, use_jump); } - template T invoke(Args... args) { - return static_cast(this->get_original())(args...); + hook(void* place, void (*stub)(), const bool use_jump = true) + : hook(place, reinterpret_cast(stub), use_jump) {} + + hook(const DWORD place, void* stub, const bool use_jump = true) + : hook(reinterpret_cast(place), stub, use_jump) {} + + hook(const DWORD place, const DWORD stub, const bool use_jump = true) + : hook(reinterpret_cast(place), reinterpret_cast(stub), + use_jump) {} + + hook(const DWORD place, void (*stub)(), const bool use_jump = true) + : hook(reinterpret_cast(place), reinterpret_cast(stub), + use_jump) {} + + hook(const hook&) = delete; + hook(const hook&&) = delete; + + ~hook(); + + hook* initialize(void* place, void* stub, bool use_jump = true); + hook* initialize(DWORD place, void* stub, bool use_jump = true); + hook* initialize(DWORD place, void (*stub)(), + bool use_jump = true); // For lambdas + hook* install(bool unprotect = true, bool keep_unprotected = false); + hook* uninstall(bool unprotect = true); + + void* get_address() const; + void* get_original() const; + void quick(); + + static bool iat(nt::library module, const std::string& target_module, + const std::string& process, void* stub); + + static void nop(void* place, size_t length); + static void nop(DWORD place, size_t length); + + template static void set(void* place, T value) { + DWORD old_protect; + VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect); + + *static_cast(place) = value; + + VirtualProtect(place, sizeof(T), old_protect, &old_protect); + FlushInstructionCache(GetCurrentProcess(), place, sizeof(T)); } - [[nodiscard]] void* get_original() const; + template static void set(const DWORD place, T value) { + 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...); + } private: - void* place_{}; - void* original_{}; + bool initialized_; + bool installed_; + + void* place_; + void* stub_; + void* original_; + char buffer_[5]{}; + bool use_jump_; + + DWORD protection_; + + std::mutex state_mutex_; }; - -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); - -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(std::uintptr_t address, void* destination); - -void redirect_jump(void* pointer, void* data); -void redirect_jump(size_t pointer, void* data); - -template T extract(void* address) { - const auto data = static_cast(address); - const auto offset = *reinterpret_cast(data); - return reinterpret_cast(data + offset + 4); -} - -template static void set(void* place, T value) { - DWORD old_protect; - VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect); - - *static_cast(place) = value; - - VirtualProtect(place, sizeof(T), old_protect, &old_protect); - FlushInstructionCache(GetCurrentProcess(), place, sizeof(T)); -} - -template static void set(const size_t place, T value) { - 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...); -} -} // namespace utils::hook +} // namespace utils