diff --git a/src/loader/loader.cpp b/src/loader/loader.cpp index 392db92..421955e 100644 --- a/src/loader/loader.cpp +++ b/src/loader/loader.cpp @@ -5,10 +5,6 @@ loader::loader(const launcher::mode mode) : mode_(mode) { } -void loader::patch() -{ -} - FARPROC loader::load(const utils::nt::module& module) const { const auto buffer = this->load_binary(); diff --git a/src/loader/loader.hpp b/src/loader/loader.hpp index 159d4ce..6c4a4e0 100644 --- a/src/loader/loader.hpp +++ b/src/loader/loader.hpp @@ -7,7 +7,6 @@ class loader final public: explicit loader(launcher::mode mode); - void patch(); FARPROC load(const utils::nt::module& module) const; void set_import_resolver(const std::function& resolver); diff --git a/src/loader/module.hpp b/src/loader/module.hpp index 2f6a3b6..6784585 100644 --- a/src/loader/module.hpp +++ b/src/loader/module.hpp @@ -1,10 +1,10 @@ #pragma once +#include "launcher/launcher.hpp" class module { public: virtual ~module() {}; - virtual void pre_load() {} virtual void post_load() {} virtual void pre_destroy() {} }; diff --git a/src/loader/module_loader.cpp b/src/loader/module_loader.cpp index 56e40bb..62c323b 100644 --- a/src/loader/module_loader.cpp +++ b/src/loader/module_loader.cpp @@ -1,11 +1,12 @@ #include #include "module_loader.hpp" +launcher::mode module_loader::mode_ = launcher::mode::NONE; std::vector>* module_loader::modules_ = nullptr; void module_loader::register_module(std::unique_ptr&& module_) { - if(!module_loader::modules_) + if (!module_loader::modules_) { module_loader::modules_ = new std::vector>(); atexit(module_loader::destroy_modules); @@ -14,9 +15,45 @@ void module_loader::register_module(std::unique_ptr&& module_) module_loader::modules_->push_back(std::move(module_)); } +void module_loader::post_load() +{ + static auto handled = false; + if (handled || !module_loader::modules_) return; + handled = true; + + for (const auto& module_ : *module_loader::modules_) + { + module_->post_load(); + } +} + +void module_loader::pre_destroy() +{ + static auto handled = false; + if (handled || !module_loader::modules_) return; + handled = true; + + for (const auto& module_ : *module_loader::modules_) + { + module_->pre_destroy(); + } +} + +launcher::mode module_loader::get_mode() +{ + return module_loader::mode_; +} + +void module_loader::set_mode(const launcher::mode mode) +{ + module_loader::mode_ = mode; +} + void module_loader::destroy_modules() { - if(!module_loader::modules_) return; + module_loader::pre_destroy(); + + if (!module_loader::modules_) return; delete module_loader::modules_; module_loader::modules_ = nullptr; diff --git a/src/loader/module_loader.hpp b/src/loader/module_loader.hpp index 50dff89..b0a9899 100644 --- a/src/loader/module_loader.hpp +++ b/src/loader/module_loader.hpp @@ -18,7 +18,14 @@ public: static void register_module(std::unique_ptr&& module); + static void post_load(); + static void pre_destroy(); + + static launcher::mode get_mode(); + static void set_mode(launcher::mode mode); + private: + static launcher::mode mode_; static std::vector>* modules_; static void destroy_modules(); diff --git a/src/main.cpp b/src/main.cpp index 8baee2a..422eb97 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,13 @@ #include #include "launcher/launcher.hpp" #include "loader/loader.hpp" +#include "loader/module_loader.hpp" + +void exit_hook(const int code) +{ + module_loader::pre_destroy(); + exit(code); +} int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/) { @@ -10,6 +17,8 @@ int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR launcher launcher; const auto mode = launcher.run(); + module_loader::set_mode(mode); + if (mode == launcher::mode::NONE) return 0; loader loader(mode); @@ -21,7 +30,7 @@ int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR } else if (function == "ExitProcess") { - return FARPROC(exit); + return FARPROC(exit_hook); } return nullptr; @@ -30,7 +39,7 @@ int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR entry_point = loader.load({}); if (!entry_point) return 1; - loader.patch(); + module_loader::post_load(); } return entry_point(); diff --git a/src/module/ceg.cpp b/src/module/ceg.cpp index 6522fdb..6cc86ba 100644 --- a/src/module/ceg.cpp +++ b/src/module/ceg.cpp @@ -1,17 +1,71 @@ #include #include "loader/module_loader.hpp" +#include "utils/hook.hpp" class ceg final : public module { public: - ceg() + void post_load() override { - OutputDebugStringA("+ CEG\n"); - } + // Only SP has CEG + // CEG in MP has accidentally been removed due to CVE-2018-10718 + if(module_loader::get_mode() != launcher::mode::SINGLEPLAYER) return; - ~ceg() - { - OutputDebugStringA("- CEG\n"); + utils::hook::signature signature; + + signature.add({ "\x56\xE8\x00\x00\x00\x00\x8B\xF0\xE8\x00\x00\x00\x00\x50\x56\xE8", "xx????xxx????xxx", [](char* address) + { + utils::hook::set(address, 0xC301B0); + } }); + + // Generic killer caller. + signature.add({ "\x55\x8B\xEC\x80\x7D\x08\x00\x75\x55", "xxxxxx?xx", []( char* address) + { + utils::hook::set(address, 0xC301B0); + } }); + + // CEG initialization. + signature.add({ "\x55\x8B\xEC\x83\xEC\x18\x53\x56\x57\xE8\x00\x00\x00\x00", "xxxxxxxxxx????", [](char* address) + { + utils::hook::set(address, 0xC3); + } }); + + // Some odd trap. + signature.add({ "\x55\x8B\xEC\x81\xEC\x00\x00\x00\x00\x53\x56\x57\x8B\x3D", "xxxxx??xxxxxxx", [](char* address) + { + utils::hook::set(address, 0xC301B0); + } }); + + // Custom shit + signature.add({ "\x55\x8B\xEC\x68\x00\x00\x00\x00\x68\x00\x00\x00\x00\x64\xFF\x35\x00\x00\x00\x00\x64\x89\x25\x00\x00\x00\x00\xE8", "xxxx????x????xxx????xxx????x", [](char* address) + { + utils::hook::set(address, 0xC3); + } }); + + // hkcr guid check + signature.add({ "\x55\x8B\xEC\xB8\x00\x00\x00\x00\xE8\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x84\xC0\x75\x06", "xxxx????x????x????xxxx", [](char* address) + { + utils::hook::nop(address + 0xD, 5); // Call + utils::hook::nop(address + 0x14, 2); // Jump + } }); + + // hkcr guid check 2 + signature.add({ "\x55\x8B\xEC\x81\xEC\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x84\xC0\x75\x06", "xxxxx????x????xxxx", [](char* address) + { + utils::hook::nop(address + 0x9, 5); // Call + utils::hook::nop(address + 0x10, 2); // Jump + } }); + + signature.process(); + + // Function fixup + utils::hook(0x4CA310, 0x48A8E0, HOOK_JUMP).install()->quick(); // DB_LoadXAssets + + // Some value obfuscation + utils::hook(0x493B81, 0x493BFC, HOOK_JUMP).install()->quick(); + + // Ceg uninit + utils::hook::set(0x527110, 0xC3); } }; diff --git a/src/utils/hook.cpp b/src/utils/hook.cpp new file mode 100644 index 0000000..a96b390 --- /dev/null +++ b/src/utils/hook.cpp @@ -0,0 +1,154 @@ +#include +#include "hook.hpp" + +namespace utils +{ + void hook::signature::process() + { + if (this->signatures_.empty()) return; + + const auto start = reinterpret_cast(this->start_); + + const unsigned int sig_count = this->signatures_.size(); + const auto containers = this->signatures_.data(); + + 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 hook::signature::add(const hook::signature::container& container) + { + hook::signature::signatures_.push_back(container); + } + + 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(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 (hook::installed_) + { + hook::installed_ = false; + } + } + + 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::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); + } +} diff --git a/src/utils/hook.hpp b/src/utils/hook.hpp new file mode 100644 index 0000000..0c0bae1 --- /dev/null +++ b/src/utils/hook.hpp @@ -0,0 +1,87 @@ +#pragma once + +#define HOOK_JUMP true +#define HOOK_CALL false + +namespace utils +{ + class hook final + { + public: + class signature final + { + public: + struct container final + { + const char* signature; + const char* mask; + std::function callback; + }; + + signature(void* start, const size_t length) : start_(start), length_(length) {} + signature(const DWORD start, const size_t length) : signature(reinterpret_cast(start), length) {} + signature() : signature(0x400000, 0x800000) {} + + void process(); + void add(const container& container); + + 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_)); } + + 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(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(); + + 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 quick(); + + 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)); + } + + template static void set(const DWORD place, T value) + { + return set(reinterpret_cast(place), value); + } + + private: + bool initialized_; + bool installed_; + + void* place_; + void* stub_; + void* original_; + char buffer_[5]{}; + bool use_jump_; + + DWORD protection_; + + std::mutex state_mutex_; + }; +}