Initial commit

This commit is contained in:
acidicoala
2022-05-09 02:28:43 +03:00
commit c3e1cc7601
146 changed files with 2958 additions and 0 deletions

View File

@@ -0,0 +1,270 @@
#include <steam_functions/steam_functions.hpp>
#include <build_config.h>
#include <koalabox/hook.hpp>
#include <koalabox/win_util.hpp>
#include <polyhook2/Misc.hpp>
namespace steam_functions {
typedef Map<String, Map<int, int>> FunctionOrdinalMap;
FunctionOrdinalMap steam_client_ordinal_map = { // NOLINT(cert-err58-cpp)
{"ISteamClient_GetISteamApps",
{
{6, 16},
{7, 18},
{8, 15},
{9, 16},
{12, 15},
}
},
{"ISteamClient_GetISteamUser",
{
{6, 6},
{7, 5},
}
},
{"ISteamClient_GetISteamUtils",
{
{6, 12},
{7, 9},
}
},
{"ISteamClient_GetISteamGenericInterface",
{
{7, 14},
{8, 13},
{12, 12},
}
},
{"ISteamClient_GetISteamInventory",
{
{17, 34},
{18, 35},
}
}
};
FunctionOrdinalMap steam_apps_ordinal_map = { // NOLINT(cert-err58-cpp)
{"ISteamApps_BIsSubscribedApp", {{2, 6}}},
{"ISteamApps_BIsDlcInstalled", {{2, 7}}},
{"ISteamApps_GetDLCCount", {{2, 10}}},
{"ISteamApps_BGetDLCDataByIndex", {{2, 11}}},
};
FunctionOrdinalMap steam_user_ordinal_map = { // NOLINT(cert-err58-cpp)
{"ISteamUser_UserHasLicenseForApp", {
{12, 15},
{13, 16},
{15, 17},
}}
};
FunctionOrdinalMap steam_utils_ordinal_map = { // NOLINT(cert-err58-cpp)
{"ISteamUtils_GetAppID", {{2, 9}}}
};
FunctionOrdinalMap steam_inventory_ordinal_map = { // NOLINT(cert-err58-cpp)
{"ISteamInventory_GetResultStatus", {{1, 0}}},
{"ISteamInventory_GetResultItems", {{1, 1}}},
{"ISteamInventory_GetResultItemProperty", {{2, 2}}},
{"ISteamInventory_CheckResultSteamID", {{1, 3}, {2, 4}}},
{"ISteamInventory_GetAllItems", {{1, 5}, {2, 6}}},
{"ISteamInventory_GetItemsByID", {{1, 6}, {2, 7}}},
{"ISteamInventory_SerializeResult", {{1, 7}, {2, 8}}},
{"ISteamInventory_GetItemDefinitionIDs", {{1, 20}, {2, 21}}},
{"ISteamInventory_GetItemDefinitionProperty", {{1, 21}, {2, 22}}},
};
int extract_version_number(
const String& version_string,
const String& prefix,
int min_version,
int max_version
) {
logger->debug("Hooking interface '{}'", version_string);
try {
const auto version_number = stoi(version_string.substr(prefix.length()));
if (version_number < min_version) {
util::panic("Unsupported old version of {}: {}", version_string, version_number);
}
if (version_number > max_version) {
logger->warn(
"Unsupported new version of {}: {}. Fallback version {} will be used",
version_string, version_number, max_version
);
}
return version_number;
} catch (const std::exception& ex) {
util::panic("Failed to extract version number from: '{}'", version_string);
}
}
int get_ordinal(const FunctionOrdinalMap& ordinal_map, const String& function_name, int interface_version) {
const auto& map = ordinal_map.at(function_name);
for (auto it = map.rbegin(); it != map.rend(); it++) {
const auto [version, ordinal] = *it;
if (interface_version >= version) {
return ordinal;
}
}
util::panic("Invalid interface version ({}) for function {}", interface_version, function_name);
}
#define HOOK(MAP, FUNC) \
hook::swap_virtual_func( \
interface, \
#FUNC, \
get_ordinal(MAP, #FUNC, version_number), \
(FunctionAddress) (FUNC) \
);
#define HOOK_STEAM_CLIENT(FUNC) HOOK(steam_client_ordinal_map, FUNC)
#define HOOK_STEAM_APPS(FUNC) HOOK(steam_apps_ordinal_map, FUNC)
#define HOOK_STEAM_USER(FUNC) HOOK(steam_user_ordinal_map, FUNC)
#define HOOK_STEAM_INVENTORY(FUNC) HOOK(steam_inventory_ordinal_map, FUNC)
void hook_virtuals(void* interface, const String& version_string) {
if (interface == nullptr) {
// Game has tried to use an interface before initializing steam api
return;
}
static Set<void*> hooked_interfaces;
if (hooked_interfaces.contains(interface)) {
// This interface is already hooked. Skipping it.
return;
}
static std::mutex section;
std::lock_guard<std::mutex> guard(section);
if (version_string.starts_with(STEAM_CLIENT)) {
const auto version_number = extract_version_number(version_string, STEAM_CLIENT, 6, 20);
HOOK_STEAM_CLIENT(ISteamClient_GetISteamApps)
HOOK_STEAM_CLIENT(ISteamClient_GetISteamUser)
if (version_number >= 7) {
HOOK_STEAM_CLIENT(ISteamClient_GetISteamGenericInterface)
}
if (version_number >= 17) {
HOOK_STEAM_CLIENT(ISteamClient_GetISteamInventory)
}
} else if (version_string.starts_with(STEAM_APPS)) {
const auto version_number = extract_version_number(version_string, STEAM_APPS, 2, 8);
HOOK_STEAM_APPS(ISteamApps_BIsSubscribedApp)
if (version_number >= 3) {
HOOK_STEAM_APPS(ISteamApps_BIsDlcInstalled)
}
if (version_number >= 4) {
HOOK_STEAM_APPS(ISteamApps_GetDLCCount)
HOOK_STEAM_APPS(ISteamApps_BGetDLCDataByIndex)
}
} else if (version_string.starts_with(STEAM_USER)) {
const auto version_number = extract_version_number(version_string, STEAM_USER, 9, 21);
if (version_number >= 12) {
HOOK_STEAM_USER(ISteamUser_UserHasLicenseForApp)
}
} else if (version_string.starts_with(STEAM_INVENTORY)) {
const auto version_number = extract_version_number(version_string, STEAM_INVENTORY, 1, 3);
HOOK_STEAM_INVENTORY(ISteamInventory_GetResultStatus)
HOOK_STEAM_INVENTORY(ISteamInventory_GetResultItems)
HOOK_STEAM_INVENTORY(ISteamInventory_CheckResultSteamID)
HOOK_STEAM_INVENTORY(ISteamInventory_GetAllItems)
HOOK_STEAM_INVENTORY(ISteamInventory_GetItemsByID)
HOOK_STEAM_INVENTORY(ISteamInventory_SerializeResult)
HOOK_STEAM_INVENTORY(ISteamInventory_GetItemDefinitionIDs)
HOOK_STEAM_INVENTORY(ISteamInventory_GetItemDefinitionProperty)
if (version_number >= 2) {
HOOK_STEAM_INVENTORY(ISteamInventory_GetResultItemProperty)
}
} else {
return;
}
hooked_interfaces.insert(interface);
}
HSteamPipe get_steam_pipe_or_throw() {
const auto& steam_api_module = win_util::get_module_handle_or_throw(ORIGINAL_DLL);
void* GetHSteamPipe_address;
try {
GetHSteamPipe_address = (void*) win_util::get_proc_address_or_throw(
steam_api_module, "SteamAPI_GetHSteamPipe"
);
} catch (const Exception& ex) {
GetHSteamPipe_address = (void*) win_util::get_proc_address_or_throw(
steam_api_module, "GetHSteamPipe"
);
}
typedef HSteamPipe (__cdecl* GetHSteamPipe_t)();
const auto GetHSteamPipe_o = (GetHSteamPipe_t) GetHSteamPipe_address;
return GetHSteamPipe_o();
}
template<typename F>
F get_virtual_function(void* interface, int ordinal) {
auto* v_table = (void***) interface;
return (F) (*v_table)[ordinal];
}
template<typename F, typename... Args>
auto call_virtual_function(void* interface, F function, Args... args) {
#ifdef _WIN64
void* RCX = interface;
#else
void* ECX = interface;
void* EDX = nullptr;
#endif
return function(ARGS(args...));
}
uint32_t get_app_id_or_throw() {
// Get CreateInterface
const auto& steam_client_module = win_util::get_module_handle_or_throw(STEAMCLIENT_DLL);
auto* CreateInterface_address = (void*) win_util::get_proc_address_or_throw(
steam_client_module, "CreateInterface"
);
auto* CreateInterface_o = PLH::FnCast(CreateInterface_address, CreateInterface);
// Get ISteamClient
int result;
auto* i_steam_client = CreateInterface_o("SteamClient006", &result);
if (i_steam_client == nullptr) {
throw util::exception("Failed to obtain SteamClient006 interface. Result: {}", result);
}
// Get GetISteamUtils
typedef void*** (__fastcall* GetISteamUtils_t)(PARAMS(HSteamPipe hSteamPipe, const char* version));
const auto steam_utils_ordinal = steam_client_ordinal_map["ISteamClient_GetISteamUtils"][6];
const auto GetISteamUtils_o = get_virtual_function<GetISteamUtils_t>(i_steam_client, steam_utils_ordinal);
// Get ISteamUtils
const auto steam_pipe = get_steam_pipe_or_throw();
auto* i_steam_utils = call_virtual_function(i_steam_client, GetISteamUtils_o, steam_pipe, "SteamUtils002");
// Get GetAppID
typedef uint32_t (__fastcall* GetAppID_t)(PARAMS());
const auto get_app_id_ordinal = steam_utils_ordinal_map["ISteamUtils_GetAppID"][2];
const auto GetAppID_o = get_virtual_function<GetAppID_t>(i_steam_utils, get_app_id_ordinal);
return call_virtual_function(i_steam_utils, GetAppID_o);
}
}

View File

@@ -0,0 +1,135 @@
#pragma once
#include <koalabox/koalabox.hpp>
#include <steam_types/steam_types.hpp>
/**
* By default, virtual functions are declared with __thiscall
* convention, which is normal since they are class members.
* But it presents an issue for us, since we cannot pass *this
* pointer as a function argument. This is because *this
* pointer is passed via register ECX in __thiscall
* convention. Hence, to resolve this issue we declare our
* hooked functions with __fastcall convention, to trick
* the compiler into reading ECX & EDX registers as 1st
* and 2nd function arguments respectively. Similarly, __fastcall
* makes the compiler push the first argument into the ECX register,
* which mimics the __thiscall calling convention. Register EDX
* is not used anywhere in this case, but we still pass it along
* to conform to the __fastcall convention. This all applies
* to the x86 architecture.
*
* In x86-64 however, there is only one calling convention,
* so __fastcall is simply ignored. However, RDX in this case
* will store the 1st actual argument to the function, so we
* have to omit it from the function signature.
*
* The macros below implement the above-mentioned considerations.
*/
#ifdef _WIN64
#define PARAMS(...) void* RCX, ##__VA_ARGS__
#define ARGS(...) RCX, ##__VA_ARGS__
#define THIS RCX
#else
#define PARAMS(...) void* ECX, void* EDX, ##__VA_ARGS__
#define ARGS(...) ECX, EDX, ##__VA_ARGS__
#define THIS ECX
#endif
#define DLL_EXPORT(TYPE) extern "C" __declspec( dllexport ) TYPE __cdecl
#define VIRTUAL(TYPE) __declspec(noinline) TYPE __fastcall
class ISteamClient;
class ISteamApps;
class ISteamUser;
class ISteamInventory;
typedef __int32 HSteamPipe;
typedef __int32 HSteamUser;
typedef uint32_t AppId_t;
typedef uint64_t CSteamID;
typedef uint32_t HCoroutine;
// ISteamClient
VIRTUAL(void*) ISteamClient_GetISteamApps(PARAMS(HSteamUser, HSteamPipe, const char*));
VIRTUAL(void*) ISteamClient_GetISteamUser(PARAMS(HSteamUser, HSteamPipe, const char*));
VIRTUAL(void*) ISteamClient_GetISteamGenericInterface(PARAMS(HSteamUser, HSteamPipe, const char*));
VIRTUAL(void*) ISteamClient_GetISteamInventory(PARAMS(HSteamUser, HSteamPipe, const char*));
// ISteamApps
VIRTUAL(bool) ISteamApps_BIsSubscribedApp(PARAMS(AppId_t));
VIRTUAL(bool) ISteamApps_BIsDlcInstalled(PARAMS(AppId_t));
VIRTUAL(int) ISteamApps_GetDLCCount(PARAMS());
VIRTUAL(bool) ISteamApps_BGetDLCDataByIndex(PARAMS(int, AppId_t*, bool*, char*, int));
// ISteamUser
VIRTUAL(EUserHasLicenseForAppResult) ISteamUser_UserHasLicenseForApp(PARAMS(CSteamID, AppId_t));
// ISteamInventory
VIRTUAL(EResult) ISteamInventory_GetResultStatus(PARAMS(uint32_t));
VIRTUAL(bool) ISteamInventory_GetResultItems(PARAMS(SteamInventoryResult_t, SteamItemDetails_t*, uint32_t*));
VIRTUAL(bool) ISteamInventory_GetResultItemProperty(
PARAMS(SteamInventoryResult_t, uint32_t, const char*, char*, const uint32_t*)
);
VIRTUAL(bool) ISteamInventory_GetAllItems(PARAMS(SteamInventoryResult_t*));
VIRTUAL(bool) ISteamInventory_GetItemsByID(PARAMS(SteamInventoryResult_t*, const SteamItemInstanceID_t*, uint32_t));
VIRTUAL(bool) ISteamInventory_SerializeResult(PARAMS(SteamInventoryResult_t, void*, uint32_t*));
VIRTUAL(bool) ISteamInventory_GetItemDefinitionIDs(PARAMS(SteamItemDef_t*, uint32_t*));
VIRTUAL(bool) ISteamInventory_GetItemDefinitionProperty(PARAMS(SteamItemDef_t, const char*, char*, uint32_t*));
VIRTUAL(bool) ISteamInventory_CheckResultSteamID(PARAMS(SteamInventoryResult_t, CSteamID));
// API
DLL_EXPORT(void*) CreateInterface(const char*, int*);
DLL_EXPORT(void*) SteamInternal_FindOrCreateUserInterface(HSteamUser, const char*);
DLL_EXPORT(void*) SteamInternal_CreateInterface(const char*);
DLL_EXPORT(void*) SteamApps();
DLL_EXPORT(void*) SteamClient();
DLL_EXPORT(void*) SteamUser();
// Flat interfaces
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsSubscribedApp(ISteamApps*, AppId_t);
DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsDlcInstalled(ISteamApps*, AppId_t);
DLL_EXPORT(int) SteamAPI_ISteamApps_GetDLCCount(ISteamApps*);
DLL_EXPORT(bool) SteamAPI_ISteamApps_BGetDLCDataByIndex(ISteamApps*, int, AppId_t*, bool*, char*, int);
DLL_EXPORT(EUserHasLicenseForAppResult) SteamAPI_ISteamUser_UserHasLicenseForApp(ISteamUser*, CSteamID, AppId_t);
DLL_EXPORT(void*) SteamAPI_ISteamClient_GetISteamGenericInterface(ISteamClient*, HSteamUser, HSteamPipe, const char*);
DLL_EXPORT(EResult) SteamAPI_ISteamInventory_GetResultStatus(ISteamInventory*, SteamInventoryResult_t);
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItems(
ISteamInventory*, SteamInventoryResult_t, SteamItemDetails_t*, uint32_t*
);
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItemProperty(
ISteamInventory*, SteamInventoryResult_t, uint32_t, const char*, char*, const uint32_t*
);
DLL_EXPORT(bool) SteamAPI_ISteamInventory_CheckResultSteamID(ISteamInventory*, SteamInventoryResult_t, CSteamID);
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetAllItems(ISteamInventory*, SteamInventoryResult_t*);
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemsByID(
ISteamInventory*, SteamInventoryResult_t*, const SteamItemInstanceID_t*, uint32_t
);
DLL_EXPORT(bool) SteamAPI_ISteamInventory_SerializeResult(ISteamInventory*, SteamInventoryResult_t, void*, uint32_t*);
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionIDs(ISteamInventory*, SteamItemDef_t*, uint32_t*);
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionProperty(
ISteamInventory*, SteamItemDef_t, const char*, char*, uint32_t*
);
// vstdlib
DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, struct CoroutineData* data);
namespace steam_functions {
using namespace koalabox;
const String STEAM_APPS = "STEAMAPPS_INTERFACE_VERSION"; // NOLINT(cert-err58-cpp)
const String STEAM_CLIENT = "SteamClient"; // NOLINT(cert-err58-cpp)
const String STEAM_USER = "SteamUser"; // NOLINT(cert-err58-cpp)
const String STEAM_INVENTORY = "STEAMINVENTORY_INTERFACE_V"; // NOLINT(cert-err58-cpp)
void hook_virtuals(void* interface, const String& version_string);
uint32_t get_app_id_or_throw();
}