Remove minhook

This commit is contained in:
6arelyFuture 2022-05-17 17:05:43 +02:00
parent 5f754be07a
commit 7f5643dcae
Signed by: Future
GPG Key ID: FA77F074E98D98A5
12 changed files with 278 additions and 282 deletions

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "deps/minhook"]
path = deps/minhook
url = https://github.com/TsudaKageyu/minhook.git
[submodule "deps/GSL"] [submodule "deps/GSL"]
path = deps/GSL path = deps/GSL
url = https://github.com/microsoft/GSL.git url = https://github.com/microsoft/GSL.git

1
deps/minhook vendored

@ -1 +0,0 @@
Subproject commit 4a455528f61b5a375b1f9d44e7d296d47f18bb18

View File

@ -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)

View File

@ -61,10 +61,10 @@ public:
cl_EnableCheats = game::Dvar_RegisterBool( cl_EnableCheats = game::Dvar_RegisterBool(
"cl_EnableCheats", false, game::DVAR_NONE, "Enable FoF wallhack"); "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::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(); add_cheat_commands();
} }

View File

@ -14,7 +14,9 @@ void dvar_set_from_string_by_name_stub(const char* dvar_name,
class component final : public component_interface { class component final : public component_interface {
public: public:
void post_unpack() override { 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 } // namespace dvar_patches

View File

@ -57,8 +57,8 @@ public:
add_exploit_commands(); add_exploit_commands();
add_key_hooks(); add_key_hooks();
utils::hook::call(0x420B76, write_message_sequence); utils::hook(0x420B76, write_message_sequence, HOOK_CALL).install()->quick();
utils::hook::call(0x420B86, write_command_sequence); utils::hook(0x420B86, write_command_sequence, HOOK_CALL).install()->quick();
} }
private: private:

View File

@ -6,8 +6,6 @@
#include "key_catcher.hpp" #include "key_catcher.hpp"
namespace key_catcher { namespace key_catcher {
utils::hook::detour cl_key_event_hook;
namespace { namespace {
std::unordered_map<std::string, key_catcher::callback>& get_key_callbacks() { std::unordered_map<std::string, key_catcher::callback>& get_key_callbacks() {
static std::unordered_map<std::string, key_catcher::callback> key_callbacks{}; static std::unordered_map<std::string, key_catcher::callback> key_callbacks{};
@ -37,16 +35,14 @@ void cl_key_event_stub(game::LocalClientNum_t local_client, int key_id,
int a3) { int a3) {
handle_key_event(local_client, key_id); handle_key_event(local_client, key_id);
cl_key_event_hook.invoke<void>(local_client, key_id, a3); utils::hook::invoke<void>(0x4CD840, local_client, key_id, a3);
} }
class component final : public component_interface { class component final : public component_interface {
public: public:
void post_unpack() override { 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 } // namespace key_catcher

View File

@ -53,7 +53,9 @@ public:
void post_unpack() override { void post_unpack() override {
add_network_commands(); add_network_commands();
utils::hook::call(0x5B27E1, packet_interception_handler); utils::hook(0x5B27E1, packet_interception_handler, HOOK_CALL)
.install()
->quick();
} }
private: private:

View File

@ -45,8 +45,12 @@ public:
void post_start() override { remove_tekno_hooks(); } void post_start() override { remove_tekno_hooks(); }
void post_unpack() override { void post_unpack() override {
utils::hook::call(0x4E3D42, msg_read_bits_compress_check_sv); utils::hook(0x4E3D42, msg_read_bits_compress_check_sv, HOOK_CALL)
utils::hook::call(0x4A9F56, msg_read_bits_compress_check_cl); .install()
->quick();
utils::hook(0x4A9F56, msg_read_bits_compress_check_cl, HOOK_CALL)
.install()
->quick();
} }
private: private:

View File

@ -144,9 +144,9 @@ public:
} }
}); });
utils::hook::call(0x4E4A0D, cl_frame_stub); utils::hook(0x4E4A0D, cl_frame_stub, HOOK_CALL).install()->quick();
utils::hook::call(0x5B54D2, r_end_frame_stub); utils::hook(0x5B54D2, r_end_frame_stub, HOOK_CALL).install()->quick();
utils::hook::call(0x543B0E, main_frame_stub); utils::hook(0x543B0E, main_frame_stub, HOOK_CALL).install()->quick();
} }
void pre_destroy() override { void pre_destroy() override {

View File

@ -1,156 +1,166 @@
#include "hook.hpp" #include "hook.hpp"
#include "string.hpp"
#include <MinHook.h> namespace utils {
void hook::signature::process() {
namespace utils::hook { if (this->signatures_.empty())
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<void*>(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<void*>(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<void*>(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<void*>(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<uint8_t>(patch_pointer, 0xE8);
set<int32_t>(patch_pointer + 1,
int32_t(size_t(data) - (size_t(pointer) + 5)));
}
void call(const size_t pointer, void* data) {
return call(reinterpret_cast<void*>(pointer), data);
}
void call(const size_t pointer, const size_t data) {
return call(pointer, reinterpret_cast<void*>(data));
}
void set(std::uintptr_t address, std::vector<std::uint8_t>&& bytes) {
DWORD oldProtect = 0;
auto* place = reinterpret_cast<void*>(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<void*>(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)
return; return;
std::uint8_t* bytes = new std::uint8_t[5]; const auto start = static_cast<char*>(this->start_);
*bytes = 0xE9;
*reinterpret_cast<std::uint32_t*>(bytes + 1) =
CalculateRelativeJMPAddress(address, destination);
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;
}
} }
void redirect_jump(void* pointer, void* data) { if (j == strlen(container->mask)) {
char* operand_ptr = static_cast<char*>(pointer) + 2; container->callback(address);
int new_operand = }
reinterpret_cast<int>(data) - (reinterpret_cast<int>(pointer) + 6); }
set<int>(operand_ptr, new_operand); }
} }
void redirect_jump(size_t pointer, void* data) { void hook::signature::add(const container& container) {
redirect_jump(reinterpret_cast<void*>(pointer), data); signatures_.push_back(container);
} }
} // namespace utils::hook
hook::~hook() {
if (this->initialized_) {
this->uninstall();
}
}
hook* hook::initialize(const DWORD place, void (*stub)(), const bool use_jump) {
return this->initialize(place, reinterpret_cast<void*>(stub), use_jump);
}
hook* hook::initialize(const DWORD place, void* stub, const bool use_jump) {
return this->initialize(reinterpret_cast<void*>(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<char*>(this->place_) + 5 +
*reinterpret_cast<DWORD*>((static_cast<char*>(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<char*>(this->place_);
*code = static_cast<char>(this->use_jump_ ? 0xE9 : 0xE8);
*reinterpret_cast<size_t*>(code + 1) =
reinterpret_cast<size_t>(this->stub_) -
(reinterpret_cast<size_t>(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<void*>(place), length);
}
} // namespace utils

View File

@ -1,81 +1,84 @@
#pragma once #pragma once
#include "signature.hpp" #include <mutex>
#include "nt.hpp"
#define CalculateRelativeJMPAddress(X, Y) \ #define HOOK_JUMP true
(((std::uintptr_t)Y - (std::uintptr_t)X) - 5) #define HOOK_CALL false
namespace utils::hook { namespace utils {
class detour { class hook final {
public: public:
detour() = default; class signature final {
detour(void* place, void* target); public:
detour(size_t place, void* target); struct container final {
~detour(); const char* signature;
const char* mask;
detour(detour&& other) noexcept { this->operator=(std::move(other)); } std::function<void(char*)> callback;
detour& operator=(detour&& other) noexcept {
if (this != &other) {
this->~detour();
this->place_ = other.place_;
this->original_ = other.original_;
other.place_ = nullptr;
other.original_ = nullptr;
}
return *this;
}
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 <typename T> T* get() const {
return static_cast<T*>(this->get_original());
}
template <typename T, typename... Args> T invoke(Args... args) {
return static_cast<T (*)(Args...)>(this->get_original())(args...);
}
[[nodiscard]] void* get_original() const;
private:
void* place_{};
void* original_{};
}; };
void nop(void* place, size_t length); signature(void* start, const size_t length)
void nop(size_t place, size_t length); : start_(start), length_(length) {}
void copy(void* place, const void* data, size_t length); signature(const DWORD start, const size_t length)
void copy(size_t place, const void* data, size_t length); : signature(reinterpret_cast<void*>(start), length) {}
bool is_relatively_far(const void* pointer, const void* data, int offset = 5); signature() : signature(0x400000, 0x800000) {}
void call(void* pointer, void* data); void process();
void call(size_t pointer, void* data); void add(const container& container);
void call(size_t pointer, size_t data);
void jump(std::uintptr_t address, void* destination); private:
void* start_;
size_t length_;
std::vector<container> signatures_;
};
void redirect_jump(void* pointer, void* data); hook()
void redirect_jump(size_t pointer, void* data); : initialized_(false), installed_(false), place_(nullptr), stub_(nullptr),
original_(nullptr), use_jump_(false), protection_(0) {
template <typename T> T extract(void* address) { ZeroMemory(this->buffer_, sizeof(this->buffer_));
const auto data = static_cast<uint8_t*>(address);
const auto offset = *reinterpret_cast<int32_t*>(data);
return reinterpret_cast<T>(data + offset + 4);
} }
hook(void* place, void* stub, const bool use_jump = true) : hook() {
this->initialize(place, stub, use_jump);
}
hook(void* place, void (*stub)(), const bool use_jump = true)
: hook(place, reinterpret_cast<void*>(stub), use_jump) {}
hook(const DWORD place, void* stub, const bool use_jump = true)
: hook(reinterpret_cast<void*>(place), stub, use_jump) {}
hook(const DWORD place, const DWORD stub, const bool use_jump = true)
: hook(reinterpret_cast<void*>(place), reinterpret_cast<void*>(stub),
use_jump) {}
hook(const DWORD place, void (*stub)(), const bool use_jump = true)
: hook(reinterpret_cast<void*>(place), reinterpret_cast<void*>(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 <typename T> static void set(void* place, T value) { template <typename T> static void set(void* place, T value) {
DWORD old_protect; DWORD old_protect;
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect); VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
@ -86,7 +89,7 @@ template <typename T> static void set(void* place, T value) {
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T)); FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
} }
template <typename T> static void set(const size_t place, T value) { template <typename T> static void set(const DWORD place, T value) {
return set<T>(reinterpret_cast<void*>(place), value); return set<T>(reinterpret_cast<void*>(place), value);
} }
@ -99,4 +102,19 @@ template <typename T, typename... Args>
static T invoke(void* func, Args... args) { static T invoke(void* func, Args... args) {
return static_cast<T (*)(Args...)>(func)(args...); return static_cast<T (*)(Args...)>(func)(args...);
} }
} // namespace utils::hook
private:
bool initialized_;
bool installed_;
void* place_;
void* stub_;
void* original_;
char buffer_[5]{};
bool use_jump_;
DWORD protection_;
std::mutex state_mutex_;
};
} // namespace utils