Prepare steam proxy
This commit is contained in:
parent
7b2698561b
commit
83c070f13b
@ -14,28 +14,46 @@ void module_loader::register_module(std::unique_ptr<module>&& module_)
|
|||||||
modules_->push_back(std::move(module_));
|
modules_->push_back(std::move(module_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void module_loader::post_start()
|
bool module_loader::post_start()
|
||||||
{
|
{
|
||||||
static auto handled = false;
|
static auto handled = false;
|
||||||
if (handled || !modules_) return;
|
if (handled || !modules_) return true;
|
||||||
handled = 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;
|
static auto handled = false;
|
||||||
if (handled || !modules_) return;
|
if (handled || !modules_) return true;
|
||||||
handled = 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()
|
void module_loader::pre_destroy()
|
||||||
@ -59,3 +77,8 @@ void module_loader::destroy_modules()
|
|||||||
delete modules_;
|
delete modules_;
|
||||||
modules_ = nullptr;
|
modules_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void module_loader::trigger_premature_shutdown()
|
||||||
|
{
|
||||||
|
throw premature_shutdown_trigger();
|
||||||
|
}
|
||||||
|
@ -4,6 +4,14 @@
|
|||||||
class module_loader final
|
class module_loader final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
class premature_shutdown_trigger final : public std::exception
|
||||||
|
{
|
||||||
|
const char* what() const noexcept override
|
||||||
|
{
|
||||||
|
return "Premature shutdown requested";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class installer final
|
class installer final
|
||||||
{
|
{
|
||||||
@ -18,10 +26,12 @@ public:
|
|||||||
|
|
||||||
static void register_module(std::unique_ptr<module>&& module);
|
static void register_module(std::unique_ptr<module>&& module);
|
||||||
|
|
||||||
static void post_start();
|
static bool post_start();
|
||||||
static void post_load();
|
static bool post_load();
|
||||||
static void pre_destroy();
|
static void pre_destroy();
|
||||||
|
|
||||||
|
static void trigger_premature_shutdown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::vector<std::unique_ptr<module>>* modules_;
|
static std::vector<std::unique_ptr<module>>* modules_;
|
||||||
|
|
||||||
|
94
src/main.cpp
94
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<FARPROC>(function);
|
||||||
|
}
|
||||||
|
else if (function == "ExitProcess")
|
||||||
|
{
|
||||||
|
return FARPROC(exit_hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
return loader.load(self);
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
FARPROC entry_point;
|
FARPROC entry_point;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
#ifdef GENERATE_DIFFS
|
bool premature_shutdown = true;
|
||||||
binary_loader::create();
|
const auto _ = gsl::finally( [&premature_shutdown]()
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
module_loader::pre_destroy();
|
if(premature_shutdown)
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
loader loader(mode);
|
|
||||||
loader.set_import_resolver([self](const std::string& module, const std::string& function) -> FARPROC
|
|
||||||
{
|
|
||||||
if (module == "steam_api.dll")
|
|
||||||
{
|
{
|
||||||
return self.get_proc<FARPROC>(function);
|
module_loader::pre_destroy();
|
||||||
}
|
}
|
||||||
else if (function == "ExitProcess")
|
|
||||||
{
|
|
||||||
return FARPROC(exit_hook);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
entry_point = loader.load(self);
|
try
|
||||||
if (!entry_point) throw std::runtime_error("Unable to load binary into memory");
|
{
|
||||||
|
#ifdef GENERATE_DIFFS
|
||||||
|
binary_loader::create();
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
game::initialize(mode);
|
verify_tls();
|
||||||
module_loader::post_load();
|
if (!module_loader::post_start()) return 0;
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
launcher launcher;
|
||||||
{
|
const auto mode = launcher.run();
|
||||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
if (mode == launcher::mode::none) return 0;
|
||||||
module_loader::pre_destroy();
|
|
||||||
return 1;
|
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();
|
return entry_point();
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include <discord_rpc.h>
|
|
||||||
#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)
|
|
79
src/module/steam_proxy.cpp
Normal file
79
src/module/steam_proxy.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include <discord_rpc.h>
|
||||||
|
#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<void*>("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<void*>("CreateInterface", "SteamClient008", nullptr);
|
||||||
|
steam::interface client_engine = load_client_engine();
|
||||||
|
|
||||||
|
const auto pipe = steam_client.invoke<void*>(0); // CreateSteamPipe
|
||||||
|
const auto gobal_user = steam_client.invoke<void*>(2, pipe); // ConnectToGlobalUser
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//REGISTER_MODULE(steam_proxy)
|
95
src/steam/interface.cpp
Normal file
95
src/steam/interface.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "interface.hpp"
|
||||||
|
#include "utils/memory.hpp"
|
||||||
|
|
||||||
|
namespace steam
|
||||||
|
{
|
||||||
|
interface::interface(void* interface_ptr) : interface_ptr_(static_cast<void***>(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<const uint8_t*>(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<char*>(operand->lval.udword);
|
||||||
|
if (!utils::memory::is_bad_read_ptr(operand_ptr))
|
||||||
|
{
|
||||||
|
result.name = operand_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
66
src/steam/interface.hpp
Normal file
66
src/steam/interface.hpp
Normal file
@ -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<typename T, typename... Args>
|
||||||
|
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<T(__thiscall*)(void*, Args ...)>(method_result.pointer)(this->interface_ptr_, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
T invoke(size_t table_entry, Args... args)
|
||||||
|
{
|
||||||
|
if (!this->interface_ptr_)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid interface pointer");
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<T(__thiscall*)(void*, Args ...)>((*this->interface_ptr_)[table_entry])(this->interface_ptr_, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void*** interface_ptr_;
|
||||||
|
std::unordered_map<std::string, method> methods_;
|
||||||
|
|
||||||
|
method find_method(const std::string& name);
|
||||||
|
method search_method(const std::string& name);
|
||||||
|
|
||||||
|
method_result analyze_method(const void* method_ptr);
|
||||||
|
};
|
||||||
|
}
|
@ -77,6 +77,30 @@ namespace steam
|
|||||||
results_.clear();
|
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<BYTE*>(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" {
|
extern "C" {
|
||||||
bool SteamAPI_RestartAppIfNecessary()
|
bool SteamAPI_RestartAppIfNecessary()
|
||||||
{
|
{
|
||||||
@ -89,24 +113,10 @@ namespace steam
|
|||||||
|
|
||||||
if (!overlay)
|
if (!overlay)
|
||||||
{
|
{
|
||||||
HKEY reg_key;
|
const auto steam_path = get_steam_install_directory();
|
||||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", 0, KEY_QUERY_VALUE, ®_key) ==
|
if(!steam_path.empty())
|
||||||
ERROR_SUCCESS)
|
|
||||||
{
|
{
|
||||||
char steam_path[MAX_PATH] = {0};
|
overlay = ::utils::nt::module::load(steam_path + "gameoverlayrenderer.dll");
|
||||||
DWORD length = sizeof(steam_path);
|
|
||||||
RegQueryValueExA(reg_key, "InstallPath", nullptr, nullptr, reinterpret_cast<BYTE*>(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,5 +101,7 @@ namespace steam
|
|||||||
STEAM_EXPORT apps* SteamApps();
|
STEAM_EXPORT apps* SteamApps();
|
||||||
STEAM_EXPORT user_stats* SteamUserStats();
|
STEAM_EXPORT user_stats* SteamUserStats();
|
||||||
|
|
||||||
|
std::string get_steam_install_directory();
|
||||||
|
|
||||||
extern ::utils::nt::module overlay;
|
extern ::utils::nt::module overlay;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user