diff --git a/src/loader/module_loader.cpp b/src/loader/module_loader.cpp index 1d4b2bc..615c9e1 100644 --- a/src/loader/module_loader.cpp +++ b/src/loader/module_loader.cpp @@ -14,28 +14,46 @@ void module_loader::register_module(std::unique_ptr&& module_) modules_->push_back(std::move(module_)); } -void module_loader::post_start() +bool module_loader::post_start() { static auto handled = false; - if (handled || !modules_) return; + if (handled || !modules_) return true; handled = true; - for (const auto& module_ : *modules_) + try { - module_->post_start(); + for (const auto& module_ : *modules_) + { + module_->post_start(); + } } + catch(premature_shutdown_trigger&) + { + return false; + } + + return true; } -void module_loader::post_load() +bool module_loader::post_load() { static auto handled = false; - if (handled || !modules_) return; + if (handled || !modules_) return true; handled = true; - for (const auto& module_ : *modules_) + try { - module_->post_load(); + for (const auto& module_ : *modules_) + { + module_->post_load(); + } } + catch (premature_shutdown_trigger&) + { + return false; + } + + return true; } void module_loader::pre_destroy() @@ -59,3 +77,8 @@ void module_loader::destroy_modules() delete modules_; modules_ = nullptr; } + +void module_loader::trigger_premature_shutdown() +{ + throw premature_shutdown_trigger(); +} diff --git a/src/loader/module_loader.hpp b/src/loader/module_loader.hpp index 1ee9999..ae27493 100644 --- a/src/loader/module_loader.hpp +++ b/src/loader/module_loader.hpp @@ -4,6 +4,14 @@ class module_loader final { public: + class premature_shutdown_trigger final : public std::exception + { + const char* what() const noexcept override + { + return "Premature shutdown requested"; + } + }; + template class installer final { @@ -18,10 +26,12 @@ public: static void register_module(std::unique_ptr&& module); - static void post_start(); - static void post_load(); + static bool post_start(); + static bool post_load(); static void pre_destroy(); + static void trigger_premature_shutdown(); + private: static std::vector>* modules_; diff --git a/src/main.cpp b/src/main.cpp index 1a94d49..2b8290d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,56 +37,72 @@ void verify_tls() } } +FARPROC load_binary(const launcher::mode mode) +{ + loader loader(mode); + utils::nt::module self; + + loader.set_import_resolver([self](const std::string& module, const std::string& function) -> FARPROC + { + if (module == "steam_api.dll") + { + return self.get_proc(function); + } + else if (function == "ExitProcess") + { + return FARPROC(exit_hook); + } + + return nullptr; + }); + + return loader.load(self); +} + int main() { FARPROC entry_point; - try { -#ifdef GENERATE_DIFFS - binary_loader::create(); - return 0; -#endif - - verify_tls(); - module_loader::post_start(); - - launcher launcher; - utils::nt::module self; - - const auto mode = launcher.run(); - if (mode == launcher::mode::none) + bool premature_shutdown = true; + const auto _ = gsl::finally( [&premature_shutdown]() { - module_loader::pre_destroy(); - return 0; - } - - loader loader(mode); - loader.set_import_resolver([self](const std::string& module, const std::string& function) -> FARPROC - { - if (module == "steam_api.dll") + if(premature_shutdown) { - return self.get_proc(function); + module_loader::pre_destroy(); } - else if (function == "ExitProcess") - { - return FARPROC(exit_hook); - } - - return nullptr; }); - entry_point = loader.load(self); - if (!entry_point) throw std::runtime_error("Unable to load binary into memory"); + try + { +#ifdef GENERATE_DIFFS + binary_loader::create(); + return 0; +#endif - game::initialize(mode); - module_loader::post_load(); - } - catch (std::exception& e) - { - MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); - module_loader::pre_destroy(); - return 1; + verify_tls(); + if (!module_loader::post_start()) return 0; + + launcher launcher; + const auto mode = launcher.run(); + if (mode == launcher::mode::none) return 0; + + entry_point = load_binary(mode); + if (!entry_point) + { + throw std::runtime_error("Unable to load binary into memory"); + } + + game::initialize(mode); + if (!module_loader::post_load()) return 0; + + premature_shutdown = false; + } + catch (std::exception& e) + { + MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); + return 1; + } } return entry_point(); diff --git a/src/module/steam.cpp b/src/module/steam.cpp deleted file mode 100644 index 0c1da5a..0000000 --- a/src/module/steam.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include "loader/module_loader.hpp" -#include "game/game.hpp" - -class steam final : public module -{ -public: - void post_load() override - { - if (game::is_dedi()) return; - } - - void pre_destroy() override - { - - } -}; - -REGISTER_MODULE(steam) diff --git a/src/module/steam_proxy.cpp b/src/module/steam_proxy.cpp new file mode 100644 index 0000000..b010585 --- /dev/null +++ b/src/module/steam_proxy.cpp @@ -0,0 +1,79 @@ +#include +#include +#include "loader/module_loader.hpp" +#include "game/game.hpp" +#include "utils/nt.hpp" +#include "steam/steam.hpp" +#include "steam/interface.hpp" +#include "utils/string.hpp" + +class steam_proxy final : public module +{ +public: + void post_start() override + { + if (game::is_dedi()) return; + + this->run_mod(); + this->load_client(); + } + + void pre_destroy() override + { + + } + +private: + utils::nt::module steam_client_module_; + + void run_mod() + { + const char* command = "-proc "; + const char* parent_proc = strstr(GetCommandLineA(), command); + + if (parent_proc) + { + const int pid = atoi(parent_proc + strlen(command)); + + const HANDLE process_handle = OpenProcess(SYNCHRONIZE, FALSE, pid); + if (process_handle && process_handle != INVALID_HANDLE_VALUE) + { + WaitForSingleObject(process_handle, INFINITE); + CloseHandle(process_handle); + } + + module_loader::trigger_premature_shutdown(); + } + } + + void* load_client_engine() + { + if (!this->steam_client_module_.is_valid()) return nullptr; + + for(int i = 1;; ++i) + { + std::string name = utils::string::va("CLIENTENGINE_INTERFACE_VERSION%03i", i); + void* client_engine = this->steam_client_module_.invoke("CreateInterface", name.data(), nullptr); + if (client_engine) return client_engine; + } + } + + void load_client() + { + const auto steam_path = ::steam::get_steam_install_directory(); + if (steam_path.empty()) return; + + SetDllDirectoryA(steam_path.data()); + + this->steam_client_module_ = utils::nt::module::load("steamclient.dll"); + if (!this->steam_client_module_.is_valid()) return; + + steam::interface steam_client = this->steam_client_module_.invoke("CreateInterface", "SteamClient008", nullptr); + steam::interface client_engine = load_client_engine(); + + const auto pipe = steam_client.invoke(0); // CreateSteamPipe + const auto gobal_user = steam_client.invoke(2, pipe); // ConnectToGlobalUser + } +}; + +//REGISTER_MODULE(steam_proxy) diff --git a/src/steam/interface.cpp b/src/steam/interface.cpp new file mode 100644 index 0000000..78cdc61 --- /dev/null +++ b/src/steam/interface.cpp @@ -0,0 +1,95 @@ +#include +#include "interface.hpp" +#include "utils/memory.hpp" + +namespace steam +{ + interface::interface(void* interface_ptr) : interface_ptr_(static_cast(interface_ptr)) + { + + } + + interface::method interface::find_method(const std::string& name) + { + const auto method_entry = this->methods_.find(name); + if(method_entry != this->methods_.end()) + { + return method_entry->second; + } + + return this->search_method(name); + } + + interface::method interface::search_method(const std::string& name) + { + if (!utils::memory::is_bad_read_ptr(this->interface_ptr_)) + { + auto vftbl = *this->interface_ptr_; + + while (!utils::memory::is_bad_read_ptr(vftbl) && !utils::memory::is_bad_code_ptr(*vftbl)) + { + const interface::method_result result = this->analyze_method(*vftbl); + if (!result.valid) break; + + const interface::method method_result{ *vftbl, result.param_size }; + this->methods_[result.name] = method_result; + + if(result.name == name) + { + return method_result; + } + + ++vftbl; + } + } + + return {}; + } + + interface::method_result interface::analyze_method(const void* method_ptr) + { + interface::method_result result; + if (utils::memory::is_bad_code_ptr(method_ptr)) return result; + + ud_t ud; + ud_init(&ud); + ud_set_mode(&ud, 32); + ud_set_pc(&ud, uint64_t(method_ptr)); + ud_set_input_buffer(&ud, static_cast(method_ptr), INT32_MAX); + + while (true) + { + ud_disassemble(&ud); + + if (ud_insn_mnemonic(&ud) == UD_Iret) + { + const ud_operand* operand = ud_insn_opr(&ud, 0); + if (!operand) break; + + if (operand->type == UD_OP_IMM && operand->size == 16) + { + result.param_size = operand->lval.uword; + result.valid = !result.name.empty(); + return result; + } + + break; + } + + if (ud_insn_mnemonic(&ud) == UD_Ipush && result.name.empty()) + { + const auto operand = ud_insn_opr(&ud, 0); + if (operand->type == UD_OP_IMM && operand->size == 32) + { + char* operand_ptr = reinterpret_cast(operand->lval.udword); + if (!utils::memory::is_bad_read_ptr(operand_ptr)) + { + result.name = operand_ptr; + } + } + } + } + + return result; + } +} diff --git a/src/steam/interface.hpp b/src/steam/interface.hpp new file mode 100644 index 0000000..f275be4 --- /dev/null +++ b/src/steam/interface.hpp @@ -0,0 +1,66 @@ +#pragma once + +#ifdef interface +#undef interface +#endif + +namespace steam +{ + class interface final + { + public: + class method final + { + public: + void* pointer = nullptr; + size_t param_size = 0; + }; + + class method_result final + { + public: + bool valid = false; + std::string name; + size_t param_size = 0; + }; + + interface(void* interface_ptr); + + template + T invoke(const std::string& method_name, Args... args) + { + if(!this->interface_ptr_) + { + throw std::runtime_error("Invalid interface pointer"); + } + + const auto method_result = this->find_method(method_name); + if(!method_result.pointer) + { + throw std::runtime_error("Unable to find desired method"); + } + + return reinterpret_cast(method_result.pointer)(this->interface_ptr_, args...); + } + + template + T invoke(size_t table_entry, Args... args) + { + if (!this->interface_ptr_) + { + throw std::runtime_error("Invalid interface pointer"); + } + + return reinterpret_cast((*this->interface_ptr_)[table_entry])(this->interface_ptr_, args...); + } + + private: + void*** interface_ptr_; + std::unordered_map methods_; + + method find_method(const std::string& name); + method search_method(const std::string& name); + + method_result analyze_method(const void* method_ptr); + }; +} diff --git a/src/steam/steam.cpp b/src/steam/steam.cpp index 175bba1..d027751 100644 --- a/src/steam/steam.cpp +++ b/src/steam/steam.cpp @@ -77,6 +77,30 @@ namespace steam results_.clear(); } + std::string get_steam_install_directory() + { + HKEY reg_key; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", 0, KEY_QUERY_VALUE, ®_key) == + ERROR_SUCCESS) + { + char path[MAX_PATH] = { 0 }; + DWORD length = sizeof(path); + RegQueryValueExA(reg_key, "InstallPath", nullptr, nullptr, reinterpret_cast(path), + &length); + RegCloseKey(reg_key); + + std::string steam_path = path; + if (steam_path.back() != '\\' && steam_path.back() != '/') + { + steam_path.push_back('\\'); + } + + return steam_path; + } + + return {}; + } + extern "C" { bool SteamAPI_RestartAppIfNecessary() { @@ -89,24 +113,10 @@ namespace steam if (!overlay) { - HKEY reg_key; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", 0, KEY_QUERY_VALUE, ®_key) == - ERROR_SUCCESS) + const auto steam_path = get_steam_install_directory(); + if(!steam_path.empty()) { - char steam_path[MAX_PATH] = {0}; - DWORD length = sizeof(steam_path); - RegQueryValueExA(reg_key, "InstallPath", nullptr, nullptr, reinterpret_cast(steam_path), - &length); - RegCloseKey(reg_key); - - std::string overlay_path = steam_path; - if (overlay_path.back() != '\\' && overlay_path.back() != '/') - { - overlay_path.push_back('\\'); - } - - overlay_path.append("gameoverlayrenderer.dll"); - overlay = ::utils::nt::module::load(overlay_path); + overlay = ::utils::nt::module::load(steam_path + "gameoverlayrenderer.dll"); } } diff --git a/src/steam/steam.hpp b/src/steam/steam.hpp index ddf15a2..113372e 100644 --- a/src/steam/steam.hpp +++ b/src/steam/steam.hpp @@ -101,5 +101,7 @@ namespace steam STEAM_EXPORT apps* SteamApps(); STEAM_EXPORT user_stats* SteamUserStats(); + std::string get_steam_install_directory(); + extern ::utils::nt::module overlay; }