Finish steam mod integration
This commit is contained in:
parent
83c070f13b
commit
9d23999da7
@ -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,14 +19,35 @@ 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 ";
|
||||
@ -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<void*>("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<void*>("CreateInterface", "SteamClient008", nullptr);
|
||||
steam::interface client_engine = load_client_engine();
|
||||
this->steam_client_ = this->steam_client_module_.invoke<void*>("CreateInterface", "SteamClient008", nullptr);
|
||||
this->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
|
||||
if (!this->client_engine_) return;
|
||||
|
||||
this->steam_pipe_ = this->steam_client_.invoke<void*>(0); // CreateSteamPipe
|
||||
this->global_user_ = this->steam_client_.invoke<void*>(2, this->steam_pipe_); // ConnectToGlobalUser
|
||||
this->client_user_ = this->client_engine_.invoke<void*>(8, this->steam_pipe_, this->global_user_,
|
||||
"CLIENTUSER_INTERFACE_VERSION001"); // GetIClientUser
|
||||
this->client_utils_ = this->client_engine_.invoke<void*>(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<void>("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<const unsigned int*>(mod_id) | 0x80000000;
|
||||
|
||||
this->client_user_.invoke<bool>("SpawnProcess", self.get_path().data(), cmdline.data(), 0, our_directory,
|
||||
game_id.bits, title.data(), app_id, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,14 +1,25 @@
|
||||
#include <std_include.hpp>
|
||||
#include "interface.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/nt.hpp"
|
||||
#include <minwinbase.h>
|
||||
|
||||
namespace steam
|
||||
{
|
||||
interface::interface() : interface(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
interface::interface(void* interface_ptr) : interface_ptr_(static_cast<void***>(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,15 +40,16 @@ 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 };
|
||||
if (result.param_size_found && result.name_found)
|
||||
{
|
||||
const interface::method method_result { *vftbl, result.param_size };
|
||||
this->methods_[result.name] = method_result;
|
||||
|
||||
if(result.name == name)
|
||||
if (result.name == name)
|
||||
{
|
||||
return method_result;
|
||||
}
|
||||
}
|
||||
|
||||
++vftbl;
|
||||
}
|
||||
@ -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<char*>(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<unsigned char*>(ud.pc) == 0xCC) break; // int 3
|
||||
if (result.param_size_found && result.name_found) break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,17 @@
|
||||
|
||||
namespace steam
|
||||
{
|
||||
template <size_t ...>
|
||||
struct argument_size_calculator final : std::integral_constant<size_t, 0>
|
||||
{
|
||||
};
|
||||
|
||||
template <size_t X, size_t ... Xs>
|
||||
struct argument_size_calculator<X, Xs...> final : std::integral_constant<
|
||||
size_t, X + ((argument_size_calculator<Xs...>::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<typename T, typename... Args>
|
||||
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<T(__thiscall*)(void*, Args ...)>(method_result.pointer)(this->interface_ptr_, args...);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T invoke(size_t table_entry, Args... args)
|
||||
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");
|
||||
}
|
||||
|
||||
return reinterpret_cast<T(__thiscall*)(void*, Args ...)>((*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<sizeof(Args)...>::value;
|
||||
if (passed_argc != method_result.param_size)
|
||||
{
|
||||
throw std::runtime_error("Invalid argument count");
|
||||
}
|
||||
|
||||
return reinterpret_cast<T(__thiscall*)(void*, Args ...)>(method_result.pointer)(
|
||||
this->interface_ptr_, args...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke(const 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:
|
||||
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user