From 9d23999da71ec3eb42332388f5f857d32eb560fc Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 7 Jan 2019 20:28:34 +0100 Subject: [PATCH] Finish steam mod integration --- src/module/steam_proxy.cpp | 98 ++++++++++++++++++++++++++++++-------- src/steam/interface.cpp | 94 +++++++++++++++++++++++++----------- src/steam/interface.hpp | 65 +++++++++++++++++-------- src/steam/steam.hpp | 15 ++++++ 4 files changed, 204 insertions(+), 68 deletions(-) diff --git a/src/module/steam_proxy.cpp b/src/module/steam_proxy.cpp index b010585..fa6f2cc 100644 --- a/src/module/steam_proxy.cpp +++ b/src/module/steam_proxy.cpp @@ -6,6 +6,7 @@ #include "steam/steam.hpp" #include "steam/interface.hpp" #include "utils/string.hpp" +#include "utils/io.hpp" class steam_proxy final : public module { @@ -18,31 +19,52 @@ public: this->load_client(); } + void post_load() override + { + if (game::is_dedi()) return; + + try + { + this->start_mod(game::is_mp() ? "LUUUUUL" : "LEEEEL", 480); + } + catch (std::exception& e) + { + printf("Steam: %s\n", e.what()); + } + } + void pre_destroy() override { - } private: utils::nt::module steam_client_module_; + steam::interface steam_client_; + steam::interface client_engine_; + steam::interface client_user_; + steam::interface client_utils_; + + void* steam_pipe_ = nullptr; + void* global_user_ = nullptr; + void run_mod() { - const char* command = "-proc "; - const char* parent_proc = strstr(GetCommandLineA(), command); - - if (parent_proc) + 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(); + 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(); } } @@ -50,12 +72,14 @@ private: { if (!this->steam_client_module_.is_valid()) return nullptr; - for(int i = 1;; ++i) + for (int i = 1; i > 0; ++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; } + + return nullptr; } void load_client() @@ -63,16 +87,48 @@ private: const auto steam_path = ::steam::get_steam_install_directory(); if (steam_path.empty()) return; - SetDllDirectoryA(steam_path.data()); + utils::nt::module::load(steam_path + "tier0_s.dll"); + utils::nt::module::load(steam_path + "vstdlib_s.dll"); + this->steam_client_module_ = utils::nt::module::load(steam_path + "steamclient.dll"); - this->steam_client_module_ = utils::nt::module::load("steamclient.dll"); - if (!this->steam_client_module_.is_valid()) return; + if (!this->steam_client_module_) return; - steam::interface steam_client = this->steam_client_module_.invoke("CreateInterface", "SteamClient008", nullptr); - steam::interface client_engine = load_client_engine(); + this->steam_client_ = this->steam_client_module_.invoke("CreateInterface", "SteamClient008", nullptr); + this->client_engine_ = load_client_engine(); - const auto pipe = steam_client.invoke(0); // CreateSteamPipe - const auto gobal_user = steam_client.invoke(2, pipe); // ConnectToGlobalUser + if (!this->client_engine_) return; + + this->steam_pipe_ = this->steam_client_.invoke(0); // CreateSteamPipe + this->global_user_ = this->steam_client_.invoke(2, this->steam_pipe_); // ConnectToGlobalUser + this->client_user_ = this->client_engine_.invoke(8, this->steam_pipe_, this->global_user_, + "CLIENTUSER_INTERFACE_VERSION001"); // GetIClientUser + this->client_utils_ = this->client_engine_.invoke(13, this->steam_pipe_, + "CLIENTUTILS_INTERFACE_VERSION001"); // GetIClientUtils + } + + void start_mod(const std::string& title, const size_t app_id) + { + if (!this->client_utils_ || !this->client_user_) return; + + this->client_utils_.invoke("SetAppIDForCurrentPipe", app_id, false); + + utils::nt::module self; + std::string path = self.get_path(); + + char our_directory[MAX_PATH] = {0}; + GetCurrentDirectoryA(sizeof(our_directory), our_directory); + + std::string cmdline = utils::string::va("\"%s\" -proc %d", path.data(), GetCurrentProcessId()); + + game_id game_id; + game_id.raw.type = 1; // k_EGameIDTypeGameMod + game_id.raw.app_id = app_id & 0xFFFFFF; + + const char* mod_id = "OIW5"; + game_id.raw.mod_id = *reinterpret_cast(mod_id) | 0x80000000; + + this->client_user_.invoke("SpawnProcess", self.get_path().data(), cmdline.data(), 0, our_directory, + game_id.bits, title.data(), app_id, 0, 0); } }; diff --git a/src/steam/interface.cpp b/src/steam/interface.cpp index 78cdc61..c0fa3ec 100644 --- a/src/steam/interface.cpp +++ b/src/steam/interface.cpp @@ -1,14 +1,25 @@ -#include -#include "interface.hpp" -#include "utils/memory.hpp" - +#include +#include "interface.hpp" +#include "utils/memory.hpp" +#include "utils/nt.hpp" +#include + namespace steam { + interface::interface() : interface(nullptr) + { + } + interface::interface(void* interface_ptr) : interface_ptr_(static_cast(interface_ptr)) { } + interface::operator bool() const + { + return this->interface_ptr_ != nullptr; + } + interface::method interface::find_method(const std::string& name) { const auto method_entry = this->methods_.find(name); @@ -29,14 +40,15 @@ namespace steam 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) + if (result.param_size_found && result.name_found) { - return method_result; + const interface::method method_result { *vftbl, result.param_size }; + this->methods_[result.name] = method_result; + + if (result.name == name) + { + return method_result; + } } ++vftbl; @@ -46,11 +58,11 @@ namespace steam 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; - + 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); @@ -61,35 +73,63 @@ namespace steam { ud_disassemble(&ud); - if (ud_insn_mnemonic(&ud) == UD_Iret) + if (ud_insn_mnemonic(&ud) == UD_Iret && !result.param_size_found) { const ud_operand* operand = ud_insn_opr(&ud, 0); - if (!operand) break; - - if (operand->type == UD_OP_IMM && operand->size == 16) + if (operand && operand->type == UD_OP_IMM && operand->size == 16) { result.param_size = operand->lval.uword; - result.valid = !result.name.empty(); - return result; + } + else + { + result.param_size = 0; } - break; + result.param_size_found = true; } - if (ud_insn_mnemonic(&ud) == UD_Ipush && result.name.empty()) + if (ud_insn_mnemonic(&ud) == UD_Ipush && !result.name_found) { 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)) + if (!utils::memory::is_bad_read_ptr(operand_ptr) && this->is_rdata(operand_ptr)) { result.name = operand_ptr; + result.name_found = true; } } } + + if(*reinterpret_cast(ud.pc) == 0xCC) break; // int 3 + if (result.param_size_found && result.name_found) break; } - return result; - } + return result; + } + + bool interface::is_rdata(void* pointer) + { + const auto pointer_lib = utils::nt::module::get_by_address(pointer); + + for(const auto& section : pointer_lib.get_section_headers()) + { + const auto size = sizeof(section->Name); + char name[size + 1]; + name[size] = 0; + std::memcpy(name, section->Name, size); + + if(name == ".rdata"s) + { + const auto target = size_t(pointer); + const size_t source_start = size_t(pointer_lib.get_ptr()) + section->PointerToRawData; + const size_t source_end = source_start + section->SizeOfRawData; + + return target >= source_start && target <= source_end; + } + } + + return false; + } } diff --git a/src/steam/interface.hpp b/src/steam/interface.hpp index f275be4..6d9903e 100644 --- a/src/steam/interface.hpp +++ b/src/steam/interface.hpp @@ -6,6 +6,17 @@ namespace steam { + template + struct argument_size_calculator final : std::integral_constant + { + }; + + template + struct argument_size_calculator final : std::integral_constant< + size_t, X + ((argument_size_calculator::value + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1))> + { + }; + class interface final { public: @@ -19,39 +30,51 @@ namespace steam class method_result final { public: - bool valid = false; std::string name; size_t param_size = 0; + bool name_found = false; + bool param_size_found = false; }; + interface(); 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"); - } + operator bool() const; - 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) + template + T invoke(const std::string& method_name, 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...); + const auto method_result = this->find_method(method_name); + if (!method_result.pointer) + { + throw std::runtime_error("Unable to find desired method"); + } + + constexpr size_t passed_argc = argument_size_calculator::value; + if (passed_argc != method_result.param_size) + { + throw std::runtime_error("Invalid argument count"); + } + + return reinterpret_cast(method_result.pointer)( + this->interface_ptr_, args...); + } + + template + T invoke(const 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: @@ -62,5 +85,7 @@ namespace steam method search_method(const std::string& name); method_result analyze_method(const void* method_ptr); + + bool is_rdata(void* pointer); }; } diff --git a/src/steam/steam.hpp b/src/steam/steam.hpp index 113372e..80dab72 100644 --- a/src/steam/steam.hpp +++ b/src/steam/steam.hpp @@ -17,6 +17,21 @@ typedef union unsigned long long bits; } steam_id; +#pragma pack( push, 1 ) +struct raw_game_id final +{ + unsigned int app_id : 24; + unsigned int type : 8; + unsigned int mod_id : 32; +}; + +typedef union +{ + raw_game_id raw; + unsigned long long bits; +} game_id; +#pragma pack( pop ) + #include "interfaces/apps.hpp" #include "interfaces/user.hpp" #include "interfaces/utils.hpp"