mirror of
https://github.com/acidicoala/SmokeAPI.git
synced 2025-12-05 21:15:39 -05:00
Refactor
This commit is contained in:
1
.idea/codeStyles/Project.xml
generated
1
.idea/codeStyles/Project.xml
generated
@@ -30,6 +30,7 @@
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="ObjectiveC">
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="BLANK_LINES_BEFORE_IMPORTS" value="0" />
|
||||
<option name="BLANK_LINES_AFTER_IMPORTS" value="0" />
|
||||
<option name="BLANK_LINES_AROUND_METHOD_IN_INTERFACE" value="0" />
|
||||
|
||||
@@ -35,6 +35,7 @@ configure_build_config(extra_build_config)
|
||||
|
||||
set(
|
||||
SMOKE_API_SOURCES
|
||||
src/core/macros.hpp
|
||||
src/smoke_api/smoke_api.cpp
|
||||
src/smoke_api/smoke_api.hpp
|
||||
src/steam_api_exports/steam_api_flat.cpp
|
||||
@@ -63,6 +64,8 @@ set(
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(
|
||||
SMOKE_API_SOURCES ${SMOKE_API_SOURCES}
|
||||
src/koalageddon/koalageddon.hpp
|
||||
src/koalageddon/koalageddon.cpp
|
||||
src/koalageddon/vstdlib.cpp
|
||||
src/koalageddon/steamclient.cpp
|
||||
src/steamclient_virtuals/client_app_manager.cpp
|
||||
|
||||
2
KoalaBox
2
KoalaBox
Submodule KoalaBox updated: 21b87810b7...d75714857d
@@ -5,5 +5,15 @@
|
||||
"override": [],
|
||||
"dlc_ids": [],
|
||||
"auto_inject_inventory": true,
|
||||
"inventory_items": []
|
||||
"inventory_items": [],
|
||||
"koalageddon_config": {
|
||||
"interface_interceptor_pattern": "55 8B EC 8B ?? ?? ?? ?? ?? 81 EC ?? ?? ?? ?? 53 FF 15",
|
||||
"function_ordinal_pattern": "8B 80 ?? ?? ?? ?? FF D0",
|
||||
"callback_interceptor_address_offset": 1,
|
||||
"callback_address_offset": 20,
|
||||
"callback_data_offset": 0,
|
||||
"callback_name_offset": 4,
|
||||
"steamclient_interceptor_function_address_offset": 2,
|
||||
"steamclient_interceptor_function_address_offset_client_user": 5
|
||||
}
|
||||
}
|
||||
|
||||
7
src/core/macros.hpp
Normal file
7
src/core/macros.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define DLL_EXPORT(TYPE) extern "C" [[maybe_unused]] __declspec( dllexport ) TYPE __cdecl
|
||||
#define VIRTUAL(TYPE) __declspec(noinline) TYPE __fastcall
|
||||
|
||||
#define DETOUR(FUNC, ADDRESS) hook::detour_or_warn(ADDRESS, #FUNC, reinterpret_cast<FunctionAddress>(FUNC));
|
||||
#define DETOUR_ORIGINAL(FUNC) DETOUR(FUNC, smoke_api::original_library)
|
||||
58
src/koalageddon/koalageddon.cpp
Normal file
58
src/koalageddon/koalageddon.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <build_config.h>
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <koalabox/dll_monitor.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#include <koalabox/patcher.hpp>
|
||||
#include <core/macros.hpp>
|
||||
|
||||
namespace koalageddon {
|
||||
KoalageddonConfig config = {};
|
||||
|
||||
/**
|
||||
* @return A string representing the source of the config.
|
||||
*/
|
||||
String init_koalageddon_config() {
|
||||
try {
|
||||
// First try to read a local config override
|
||||
config = smoke_api::config.koalageddon_config.get<KoalageddonConfig>();
|
||||
return "local config override";
|
||||
} catch (const Exception& ex) {
|
||||
logger->debug("Local config parse exception: {}", ex.what());
|
||||
}
|
||||
|
||||
// TODO: Remote source with local cache
|
||||
|
||||
// Finally, fallback on the default config
|
||||
return "default config bundled in the binary";
|
||||
}
|
||||
|
||||
void init() {
|
||||
#ifndef _WIN64
|
||||
logger->info("🐨 Detected Koalageddon mode 💥");
|
||||
|
||||
const auto kg_config_source = init_koalageddon_config();
|
||||
logger->info("Loaded Koalageddon config from the {}", kg_config_source);
|
||||
|
||||
dll_monitor::init({VSTDLIB_DLL, STEAMCLIENT_DLL}, [](const HMODULE& library, const String& name) {
|
||||
smoke_api::original_library = library;
|
||||
|
||||
if (name == VSTDLIB_DLL) {
|
||||
// Family Sharing functions
|
||||
DETOUR_ORIGINAL(Coroutine_Create)
|
||||
} else if (name == STEAMCLIENT_DLL) {
|
||||
// Unlocking functions
|
||||
auto interface_interceptor_address = (FunctionAddress) patcher::find_pattern_address(
|
||||
win_util::get_module_info(library),
|
||||
"SteamClient_Interface_Interceptor",
|
||||
config.interface_interceptor_pattern
|
||||
);
|
||||
|
||||
if (interface_interceptor_address) {
|
||||
DETOUR(SteamClient_Interface_Interceptor, interface_interceptor_address)
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
68
src/koalageddon/koalageddon.hpp
Normal file
68
src/koalageddon/koalageddon.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalabox/koalabox.hpp>
|
||||
#include <core/macros.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace koalageddon {
|
||||
using namespace koalabox;
|
||||
|
||||
struct KoalageddonConfig {
|
||||
String interface_interceptor_pattern = "55 8B EC 8B ?? ?? ?? ?? ?? 81 EC ?? ?? ?? ?? 53 FF 15";
|
||||
|
||||
// Offset values are interpreted according to pointer arithmetic rules, i.e.
|
||||
// 1 unit offset represents 4 and 8 bytes in 32-bit and 64-bit architectures respectively.
|
||||
uint32_t callback_interceptor_address_offset = 1;
|
||||
uint32_t callback_address_offset = 20;
|
||||
uint32_t callback_data_offset = 0;
|
||||
uint32_t callback_name_offset = 4;
|
||||
uint32_t steamclient_interceptor_function_address_offset = 2;
|
||||
uint32_t steamclient_interceptor_function_address_offset_client_user = 5;
|
||||
uint32_t IClientAppManager_IsAppDlcInstalled_ordinal = 8;
|
||||
uint32_t IClientApps_GetDLCCount_ordinal = 8;
|
||||
uint32_t IClientApps_BGetDLCDataByIndex_ordinal = 9;
|
||||
uint32_t IClientInventory_GetResultStatus_ordinal = 0;
|
||||
uint32_t IClientInventory_GetResultItems_ordinal = 2;
|
||||
uint32_t IClientInventory_GetResultItemProperty_ordinal = 3;
|
||||
uint32_t IClientInventory_CheckResultSteamID_ordinal = 5;
|
||||
uint32_t IClientInventory_GetAllItems_ordinal = 8;
|
||||
uint32_t IClientInventory_GetItemsByID_ordinal = 9;
|
||||
uint32_t IClientInventory_SerializeResult_ordinal = 6;
|
||||
uint32_t IClientInventory_GetItemDefinitionIDs_ordinal = 19;
|
||||
uint32_t IClientUser_BIsSubscribedApp_ordinal = 191;
|
||||
|
||||
// We do not use *_WITH_DEFAULT macro to ensure that overriding
|
||||
// the koalageddon config requires definition of all keys
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(
|
||||
KoalageddonConfig, // NOLINT(misc-const-correctness)
|
||||
interface_interceptor_pattern,
|
||||
callback_interceptor_address_offset,
|
||||
callback_address_offset,
|
||||
callback_data_offset,
|
||||
callback_name_offset,
|
||||
steamclient_interceptor_function_address_offset,
|
||||
steamclient_interceptor_function_address_offset_client_user,
|
||||
IClientAppManager_IsAppDlcInstalled_ordinal,
|
||||
IClientApps_GetDLCCount_ordinal,
|
||||
IClientApps_BGetDLCDataByIndex_ordinal,
|
||||
IClientInventory_GetResultStatus_ordinal,
|
||||
IClientInventory_GetResultItems_ordinal,
|
||||
IClientInventory_GetResultItemProperty_ordinal,
|
||||
IClientInventory_CheckResultSteamID_ordinal,
|
||||
IClientInventory_GetAllItems_ordinal,
|
||||
IClientInventory_GetItemsByID_ordinal,
|
||||
IClientInventory_SerializeResult_ordinal,
|
||||
IClientInventory_GetItemDefinitionIDs_ordinal,
|
||||
IClientUser_BIsSubscribedApp_ordinal
|
||||
)
|
||||
};
|
||||
|
||||
extern KoalageddonConfig config;
|
||||
|
||||
void init();
|
||||
}
|
||||
|
||||
typedef uint32_t HCoroutine;
|
||||
|
||||
DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, struct CoroutineData* data);
|
||||
DLL_EXPORT(void) SteamClient_Interface_Interceptor(const char* interface_name, const char* function_name);
|
||||
@@ -1,62 +1,163 @@
|
||||
#include <build_config.h>
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/patcher.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
void hook_function(
|
||||
const void* interface_address,
|
||||
const FunctionAddress& hook_function,
|
||||
const String& name,
|
||||
const int ordinal
|
||||
) {
|
||||
static Set<String> hooked_functions;
|
||||
/*
|
||||
* We need an interface=>function ordinal map in order to hook steam interface function
|
||||
* via virtual function pointer swap. We could construct it by exploiting these code chunks:
|
||||
*
|
||||
* 8B01 | mov eax, dword ptr ds:[ecx]
|
||||
* 68 ???????? | push steamclient.function_name
|
||||
* 68 ???????? | push steamclient.interface_name
|
||||
*
|
||||
* Step 1: Find all addresses that begin with pattern
|
||||
* 8B 01 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? FF 10
|
||||
* Step 2: Extract function and interface name pointers by adding 3 and 8 respectively
|
||||
* Step 3: Starting from the found address, and until the function epilogue, scan for either instructions:
|
||||
*
|
||||
* (1) FF50 ?? | call dword ptr ds:[eax+0x??]
|
||||
* or
|
||||
* (2) 8B40 ?? | mov eax, dword ptr ds:[eax+??]
|
||||
* FFD0 | call eax
|
||||
*
|
||||
* In the case (1), the offset is encoded in the found call instruction.
|
||||
* In the case (2), the offset is encoded in the instruction preceding the call.
|
||||
*
|
||||
* ROADBLOCK: There is actually a case (3) which calls a local variable (ebp-??),
|
||||
* which itself is constructed over multiple instruction calls, making it non-deterministic.
|
||||
* Until this roadblock is resolved, automatic detection of ordinals remains non-viable.
|
||||
*/
|
||||
[[maybe_unused]] Map<String, uint32_t> construct_interface_function_ordinal_map() {
|
||||
auto* const steamclient_handle = win_util::get_module_handle_or_throw(STEAMCLIENT_DLL);
|
||||
const auto steamclient_module_info = win_util::get_module_info_or_throw(steamclient_handle);
|
||||
|
||||
if (interface_address == nullptr) {
|
||||
return;
|
||||
auto* byte_pointer = (uint8_t*) steamclient_module_info.lpBaseOfDll;
|
||||
while (byte_pointer) {
|
||||
// Search until the end of DLL section
|
||||
auto rva = (DWORD) byte_pointer - (DWORD) steamclient_module_info.lpBaseOfDll;
|
||||
|
||||
// This pattern needs to be parameterized if this method ever gets implemented.
|
||||
const String interface_function_chunk_pattern = "8B 01 68 ?? ?? ?? ?? 68";
|
||||
|
||||
byte_pointer = reinterpret_cast<uint8_t*>(
|
||||
patcher::find_pattern_address(
|
||||
byte_pointer,
|
||||
steamclient_module_info.SizeOfImage - rva,
|
||||
"interface=>function chunk",
|
||||
interface_function_chunk_pattern,
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
// const auto* interface_name = *reinterpret_cast<char**>(byte_pointer + 3);
|
||||
// const auto* function_name = *reinterpret_cast<char**>(byte_pointer + 8);
|
||||
|
||||
byte_pointer += interface_function_chunk_pattern.size();
|
||||
byte_pointer += interface_function_chunk_pattern.size();
|
||||
|
||||
static const auto is_epilogue = [](const uint8_t* instruction_byte) {
|
||||
const uint8_t epilogue[]{
|
||||
0x8B, 0xE5, // mov esp,ebp
|
||||
0x5D, // pop ebp
|
||||
0xC3, // ret
|
||||
};
|
||||
|
||||
for (auto i = 0; i < sizeof(epilogue); i++) {
|
||||
if (epilogue[i] != instruction_byte[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hooked_functions.contains(name)) {
|
||||
return;
|
||||
return true;
|
||||
};
|
||||
|
||||
static const auto is_call_dword = [](const uint8_t* instruction_byte) {
|
||||
return instruction_byte[0] == 0xFF && instruction_byte[1] == 0x50;
|
||||
};
|
||||
|
||||
// static const auto is_call_eax = [](const uint8_t* instruction_byte) {
|
||||
// return instruction_byte[0] == 0x8B && instruction_byte[1] == 0x40 &&
|
||||
// instruction_byte[3] == 0xFF && instruction_byte[4] == 0xD0;
|
||||
// };
|
||||
|
||||
while (!is_epilogue(byte_pointer)) {
|
||||
if (is_call_dword(byte_pointer)) {
|
||||
// Find a way to determine offset
|
||||
}
|
||||
|
||||
hook::swap_virtual_func_or_throw(interface_address, name, ordinal, hook_function);
|
||||
byte_pointer++;
|
||||
}
|
||||
}
|
||||
|
||||
hooked_functions.insert(name);
|
||||
return {};
|
||||
}
|
||||
|
||||
#define DEFINE_INTERFACE(INTERFACE) constexpr auto INTERFACE = #INTERFACE;
|
||||
|
||||
DEFINE_INTERFACE(IClientAppManager)
|
||||
DEFINE_INTERFACE(IClientApps)
|
||||
DEFINE_INTERFACE(IClientInventory)
|
||||
DEFINE_INTERFACE(IClientUser)
|
||||
|
||||
DLL_EXPORT(void) SteamClient_Interface_Interceptor(const char* interface_name, const char* function_name) {
|
||||
try {
|
||||
void**** parent_ebp;
|
||||
__asm mov parent_ebp, ebp
|
||||
// logger->trace("Intercepted interface function: '{}::{}'", interface_name, function_name);
|
||||
|
||||
const auto offset_one = koalageddon_config.steamclient_interceptor_function_address_offset_one;
|
||||
auto offset_two = koalageddon_config.steamclient_interceptor_function_address_offset_two;
|
||||
static Set<String> hooked_interfaces;
|
||||
|
||||
const auto compound_name = interface_name + String("::") + function_name;
|
||||
if (hooked_interfaces.size() < 4) {
|
||||
const void***** current_ebp;
|
||||
__asm mov current_ebp, ebp
|
||||
|
||||
// logger->trace("Intercepted interface function: '{}'", compound_name);
|
||||
auto* const parent_ebp = *current_ebp;
|
||||
|
||||
#define HOOK_INTERFACE(FUNC) hook_function( \
|
||||
parent_ebp[offset_one][offset_two], \
|
||||
(FunctionAddress) FUNC, \
|
||||
const auto* interface_address = parent_ebp[
|
||||
util::strings_are_equal(interface_name, IClientUser)
|
||||
? koalageddon::config.steamclient_interceptor_function_address_offset_client_user
|
||||
: koalageddon::config.steamclient_interceptor_function_address_offset
|
||||
];
|
||||
|
||||
const auto hook_if_needed = [&](const String& name, const std::function<void()>& block) {
|
||||
|
||||
const auto is_target_interface = interface_name == name;
|
||||
const auto is_hooked = hooked_interfaces.contains(name);
|
||||
const auto is_valid_address = interface_address != nullptr;
|
||||
|
||||
if (is_target_interface && !is_hooked && is_valid_address) {
|
||||
block();
|
||||
hooked_interfaces.insert(name);
|
||||
}
|
||||
};
|
||||
|
||||
#define HOOK_INTERFACE(FUNC) hook::swap_virtual_func_or_throw( \
|
||||
interface_address, \
|
||||
#FUNC, \
|
||||
koalageddon_config.FUNC##_ordinal \
|
||||
koalageddon::config.FUNC##_ordinal, \
|
||||
(FunctionAddress) FUNC \
|
||||
);
|
||||
|
||||
if (util::strings_are_equal(interface_name, "IClientAppManager")) {
|
||||
hook_if_needed(IClientAppManager, [&]() {
|
||||
HOOK_INTERFACE(IClientAppManager_IsAppDlcInstalled)
|
||||
// TODO: Investigate IClientAppManager::BIsDlcEnabled
|
||||
// TODO: Investigate IClientAppManager::GetDlcSizes
|
||||
// TODO: Investigate IClientAppManager::GetInstalledApps
|
||||
} else if (util::strings_are_equal(interface_name, "IClientApps")) {
|
||||
});
|
||||
|
||||
hook_if_needed(IClientApps, [&]() {
|
||||
HOOK_INTERFACE(IClientApps_GetDLCCount)
|
||||
HOOK_INTERFACE(IClientApps_BGetDLCDataByIndex)
|
||||
// TODO: Investigate IClientApps::GetAppKVRaw
|
||||
// TODO: Investigate IClientApps::GetAppDataSection
|
||||
// TODO: Investigate IClientApps::GetAppData
|
||||
} else if (util::strings_are_equal(interface_name, "IClientInventory")) {
|
||||
});
|
||||
|
||||
hook_if_needed(IClientInventory, [&]() {
|
||||
HOOK_INTERFACE(IClientInventory_GetResultStatus)
|
||||
HOOK_INTERFACE(IClientInventory_GetResultItems)
|
||||
HOOK_INTERFACE(IClientInventory_GetResultItemProperty)
|
||||
@@ -65,21 +166,22 @@ DLL_EXPORT(void) SteamClient_Interface_Interceptor(const char* interface_name, c
|
||||
HOOK_INTERFACE(IClientInventory_GetItemsByID)
|
||||
HOOK_INTERFACE(IClientInventory_SerializeResult)
|
||||
HOOK_INTERFACE(IClientInventory_GetItemDefinitionIDs)
|
||||
} else if (util::strings_are_equal(interface_name, "IClientUser")) {
|
||||
// NOTE: parent_ebp[offset_one][offset_two] will always be 0 for IClientUser interface.
|
||||
});
|
||||
|
||||
hook_if_needed(IClientUser, [&]() {
|
||||
// NOTE: parent_ebp[function_offset] will always be 0 for IClientUser interface.
|
||||
// Probably because it actually represents a handle with 0 being the default user.
|
||||
// However, the real interface is still accessible at a neighboring offset.
|
||||
offset_two = koalageddon_config.steamclient_interceptor_function_address_offset_two_client_user;
|
||||
|
||||
HOOK_INTERFACE(IClientUser_IsSubscribedApp)
|
||||
HOOK_INTERFACE(IClientUser_BIsSubscribedApp)
|
||||
// TODO: Investigate IClientUser::GetConfigString
|
||||
// TODO: Investigate IClientUser::GetConfigStoreKeyName
|
||||
// TODO: Investigate IClientUser::GetSubscribedApps
|
||||
// TODO: Investigate IClientUser::GetUserConfigFolder
|
||||
// TODO: Investigate IClientUser::GetAppIDForGameID
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Investigate IClientDeviceAuth::GetSharedLibraryLockedBy?
|
||||
}
|
||||
|
||||
GET_ORIGINAL_FUNCTION(SteamClient_Interface_Interceptor)
|
||||
SteamClient_Interface_Interceptor_o(interface_name, function_name);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
|
||||
#include <koalabox/hook.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
#define DETOUR(FUNC, address) hook::detour((FunctionAddress) (address), #FUNC, (FunctionAddress) (FUNC));
|
||||
//#define DETOUR_STRICT(FUNC, address) hook::detour((FunctionAddress) (address), #FUNC, (FunctionAddress) (FUNC));
|
||||
|
||||
VIRTUAL(bool) SharedLicensesLockStatus(PARAMS(void* arg)) { // NOLINT(misc-unused-parameters)
|
||||
logger->debug("{} -> instance: {}, arg: {}", __func__, fmt::ptr(THIS), fmt::ptr(arg));
|
||||
@@ -18,22 +19,22 @@ VIRTUAL(bool) SharedLibraryStopPlaying(PARAMS(void* arg)) { // NOLINT(misc-unuse
|
||||
}
|
||||
|
||||
struct CallbackData {
|
||||
void* get_callback_intercept_address() {
|
||||
return reinterpret_cast<void**>(this)[koalageddon_config.callback_interceptor_address_offset];
|
||||
FunctionAddress get_callback_intercept_address() {
|
||||
return reinterpret_cast<FunctionAddress*>(this)[koalageddon::config.callback_interceptor_address_offset];
|
||||
}
|
||||
|
||||
void* get_callback_address() {
|
||||
return reinterpret_cast<void**>(this)[koalageddon_config.callback_address_offset];
|
||||
FunctionAddress get_callback_address() {
|
||||
return reinterpret_cast<FunctionAddress*>(this)[koalageddon::config.callback_data_offset];
|
||||
}
|
||||
};
|
||||
|
||||
struct CoroutineData {
|
||||
CallbackData* get_callback_data() {
|
||||
return reinterpret_cast<CallbackData**>(this)[koalageddon_config.callback_data_offset];
|
||||
return reinterpret_cast<CallbackData**>(this)[koalageddon::config.callback_data_offset];
|
||||
}
|
||||
|
||||
const char* get_callback_name() {
|
||||
return reinterpret_cast<const char**>(this)[koalageddon_config.callback_name_offset];
|
||||
return reinterpret_cast<const char**>(this)[koalageddon::config.callback_name_offset];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,7 +85,6 @@ DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, CoroutineData* d
|
||||
std::call_once(flag, [&]() {
|
||||
logger->debug("Coroutine_Create -> callback: {}, data: {}", callback_address, fmt::ptr(data));
|
||||
|
||||
|
||||
DETOUR(VStdLib_Callback_Interceptor, data->get_callback_data()->get_callback_intercept_address())
|
||||
});
|
||||
|
||||
|
||||
@@ -8,18 +8,14 @@
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/loader.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#ifndef _WIN64
|
||||
#include <koalabox/patcher.hpp>
|
||||
#endif
|
||||
|
||||
#define DETOUR_EX(FUNC, ADDRESS) hook::detour_or_warn(ADDRESS, #FUNC, reinterpret_cast<FunctionAddress>(FUNC));
|
||||
#define DETOUR(FUNC) hook::detour_or_warn(original_library, #FUNC, reinterpret_cast<FunctionAddress>(FUNC));
|
||||
#ifndef _WIN64
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#endif
|
||||
|
||||
namespace smoke_api {
|
||||
Config config = {}; // NOLINT(cert-err58-cpp)
|
||||
|
||||
KoalageddonConfig koalageddon_config = {};
|
||||
|
||||
HMODULE original_library = nullptr;
|
||||
|
||||
bool is_hook_mode = false;
|
||||
@@ -31,53 +27,6 @@ namespace smoke_api {
|
||||
config = config_parser::parse<Config>(self_directory / PROJECT_NAME".json");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A string representing the source of the config.
|
||||
*/
|
||||
String init_koalageddon_config() {
|
||||
try {
|
||||
// First try to read a local config override
|
||||
koalageddon_config = config.koalageddon_config.get<KoalageddonConfig>();
|
||||
return "local config override";
|
||||
} catch (const Exception& ex) {
|
||||
logger->debug("Local config parse exception: {}", ex.what());
|
||||
}
|
||||
|
||||
// TODO: Remote source with local cache
|
||||
|
||||
// Finally, fallback on the default config
|
||||
return "default config bundled in the binary";
|
||||
}
|
||||
|
||||
void init_koalageddon_mode() {
|
||||
#ifndef _WIN64
|
||||
logger->info("🐨 Detected Koalageddon mode 💥");
|
||||
|
||||
const auto kg_config_source = init_koalageddon_config();
|
||||
logger->info("Loaded Koalageddon config from the {}", kg_config_source);
|
||||
|
||||
dll_monitor::init({VSTDLIB_DLL, STEAMCLIENT_DLL}, [](const HMODULE& library, const String& name) {
|
||||
original_library = library;
|
||||
|
||||
if (name == VSTDLIB_DLL) {
|
||||
// Family Sharing functions
|
||||
DETOUR(Coroutine_Create)
|
||||
} else if (name == STEAMCLIENT_DLL) {
|
||||
// Unlocking functions
|
||||
auto interface_interceptor_address = (FunctionAddress) patcher::find_pattern_address(
|
||||
win_util::get_module_info(library),
|
||||
"SteamClient_Interface_Interceptor",
|
||||
koalageddon_config.interface_interceptor_pattern
|
||||
);
|
||||
|
||||
if (interface_interceptor_address) {
|
||||
DETOUR_EX(SteamClient_Interface_Interceptor, interface_interceptor_address)
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void init_proxy_mode() {
|
||||
logger->info("🔀 Detected proxy mode");
|
||||
|
||||
@@ -90,7 +39,7 @@ namespace smoke_api {
|
||||
dll_monitor::init(STEAMCLIENT_DLL, [](const HMODULE& library) {
|
||||
original_library = library;
|
||||
|
||||
DETOUR(CreateInterface)
|
||||
DETOUR_ORIGINAL(CreateInterface)
|
||||
});
|
||||
|
||||
// Hooking steam_api has show itself to be less desirable than steamclient
|
||||
@@ -139,8 +88,9 @@ namespace smoke_api {
|
||||
if (is_hook_mode) {
|
||||
hook::init(true);
|
||||
|
||||
// TODO: Check if it's steam from valve
|
||||
if (util::strings_are_equal(exe_name, "steam.exe") && !util::is_x64()) {
|
||||
init_koalageddon_mode();
|
||||
koalageddon::init();
|
||||
} else {
|
||||
init_hook_mode();
|
||||
}
|
||||
|
||||
@@ -24,58 +24,6 @@
|
||||
namespace smoke_api {
|
||||
using namespace koalabox;
|
||||
|
||||
struct KoalageddonConfig {
|
||||
String interface_interceptor_pattern = "55 8B EC 8B ?? ?? ?? ?? ?? 81 EC ?? ?? ?? ?? 53 FF 15";
|
||||
|
||||
// Offset values are interpreted according to pointer arithmetic rules, i.e.
|
||||
// 1 unit offset represents 4 and 8 bytes in 32-bit and 64-bit architectures respectively.
|
||||
uint32_t callback_interceptor_address_offset = 1;
|
||||
uint32_t callback_address_offset = 20;
|
||||
uint32_t callback_data_offset = 0;
|
||||
uint32_t callback_name_offset = 4;
|
||||
uint32_t steamclient_interceptor_function_address_offset_one = 0;
|
||||
uint32_t steamclient_interceptor_function_address_offset_two = 2;
|
||||
uint32_t steamclient_interceptor_function_address_offset_two_client_user = 5;
|
||||
uint32_t IClientAppManager_IsAppDlcInstalled_ordinal = 8;
|
||||
uint32_t IClientApps_GetDLCCount_ordinal = 8;
|
||||
uint32_t IClientApps_BGetDLCDataByIndex_ordinal = 9;
|
||||
uint32_t IClientInventory_GetResultStatus_ordinal = 0;
|
||||
uint32_t IClientInventory_GetResultItems_ordinal = 2;
|
||||
uint32_t IClientInventory_GetResultItemProperty_ordinal = 3;
|
||||
uint32_t IClientInventory_CheckResultSteamID_ordinal = 5;
|
||||
uint32_t IClientInventory_GetAllItems_ordinal = 8;
|
||||
uint32_t IClientInventory_GetItemsByID_ordinal = 9;
|
||||
uint32_t IClientInventory_SerializeResult_ordinal = 6;
|
||||
uint32_t IClientInventory_GetItemDefinitionIDs_ordinal = 19;
|
||||
uint32_t IClientUser_IsSubscribedApp_ordinal = 191;
|
||||
|
||||
// We do not use *_WITH_DEFAULT macro to ensure that overriding
|
||||
// the koalageddon config requires definition of all keys
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(
|
||||
KoalageddonConfig, // NOLINT(misc-const-correctness)
|
||||
interface_interceptor_pattern,
|
||||
callback_interceptor_address_offset,
|
||||
callback_address_offset,
|
||||
callback_data_offset,
|
||||
callback_name_offset,
|
||||
steamclient_interceptor_function_address_offset_one,
|
||||
steamclient_interceptor_function_address_offset_two,
|
||||
steamclient_interceptor_function_address_offset_two_client_user,
|
||||
IClientAppManager_IsAppDlcInstalled_ordinal,
|
||||
IClientApps_GetDLCCount_ordinal,
|
||||
IClientApps_BGetDLCDataByIndex_ordinal,
|
||||
IClientInventory_GetResultStatus_ordinal,
|
||||
IClientInventory_GetResultItems_ordinal,
|
||||
IClientInventory_GetResultItemProperty_ordinal,
|
||||
IClientInventory_CheckResultSteamID_ordinal,
|
||||
IClientInventory_GetAllItems_ordinal,
|
||||
IClientInventory_GetItemsByID_ordinal,
|
||||
IClientInventory_SerializeResult_ordinal,
|
||||
IClientInventory_GetItemDefinitionIDs_ordinal,
|
||||
IClientUser_IsSubscribedApp_ordinal
|
||||
)
|
||||
};
|
||||
|
||||
struct Config {
|
||||
uint32_t $version = 2;
|
||||
bool logging = false;
|
||||
@@ -102,8 +50,6 @@ namespace smoke_api {
|
||||
|
||||
extern Config config;
|
||||
|
||||
extern KoalageddonConfig koalageddon_config;
|
||||
|
||||
extern HMODULE original_library;
|
||||
|
||||
extern bool is_hook_mode;
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace steam_functions {
|
||||
}
|
||||
|
||||
static std::mutex section;
|
||||
std::lock_guard<std::mutex> guard(section);
|
||||
const 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);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalabox/koalabox.hpp>
|
||||
|
||||
#include <steam_types/steam_types.hpp>
|
||||
#include <core/macros.hpp>
|
||||
|
||||
/**
|
||||
* By default, virtual functions are declared with __thiscall
|
||||
@@ -37,9 +37,6 @@
|
||||
#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;
|
||||
@@ -53,8 +50,6 @@ typedef __int32 HSteamUser;
|
||||
typedef uint32_t AppId_t;
|
||||
typedef uint64_t CSteamID;
|
||||
|
||||
typedef uint32_t HCoroutine;
|
||||
|
||||
// TODO: Refactor into multiple headers
|
||||
|
||||
// ISteamClient
|
||||
@@ -116,11 +111,6 @@ DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemsByID(
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_SerializeResult(ISteamInventory*, SteamInventoryResult_t, void*, uint32_t*);
|
||||
DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionIDs(ISteamInventory*, SteamItemDef_t*, uint32_t*);
|
||||
|
||||
// Koalageddon mode
|
||||
|
||||
DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, struct CoroutineData* data);
|
||||
DLL_EXPORT(void) SteamClient_Interface_Interceptor(const char* interface_name, const char* function_name);
|
||||
|
||||
// IClientApps
|
||||
VIRTUAL(int) IClientApps_GetDLCCount(PARAMS(AppId_t));
|
||||
VIRTUAL(bool) IClientApps_BGetDLCDataByIndex(PARAMS(AppId_t, int, AppId_t*, bool*, char*, int));
|
||||
@@ -129,14 +119,13 @@ VIRTUAL(bool) IClientApps_BGetDLCDataByIndex(PARAMS(AppId_t, int, AppId_t*, bool
|
||||
VIRTUAL(bool) IClientAppManager_IsAppDlcInstalled(PARAMS(AppId_t, AppId_t));
|
||||
|
||||
// IClientUser
|
||||
VIRTUAL(bool) IClientUser_IsSubscribedApp(PARAMS(AppId_t));
|
||||
VIRTUAL(bool) IClientUser_BIsSubscribedApp(PARAMS(AppId_t));
|
||||
|
||||
// IClientInventory
|
||||
VIRTUAL(EResult) IClientInventory_GetResultStatus(PARAMS(SteamInventoryResult_t));
|
||||
VIRTUAL(bool) IClientInventory_GetResultItems(
|
||||
PARAMS(SteamInventoryResult_t, SteamItemDetails_t*, uint32_t, uint32_t *)
|
||||
);
|
||||
//////
|
||||
VIRTUAL(bool) IClientInventory_GetResultItemProperty(
|
||||
PARAMS(SteamInventoryResult_t, uint32_t, const char*, char*, uint32_t, uint32_t*)
|
||||
);
|
||||
|
||||
@@ -66,8 +66,8 @@ namespace steam_inventory {
|
||||
}
|
||||
});
|
||||
}
|
||||
const auto auto_injected_count = auto_inventory_items.size();
|
||||
|
||||
const auto auto_injected_count = auto_inventory_items.size();
|
||||
|
||||
if (not pOutItemsArray) {
|
||||
// If pOutItemsArray is NULL then we must set the array size.
|
||||
|
||||
@@ -19,7 +19,6 @@ VIRTUAL(bool) IClientInventory_GetResultItems(
|
||||
uint32_t * punOutItemsArraySize // 1st pass: ptr to out array size, 2nd pass: ptr to 0
|
||||
)
|
||||
) {
|
||||
|
||||
return steam_inventory::GetResultItems(
|
||||
__func__, resultHandle, pOutItemsArray, punOutItemsArraySize,
|
||||
[&]() {
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
VIRTUAL(bool) IClientUser_IsSubscribedApp(PARAMS(AppId_t app_id)) { // NOLINT(misc-unused-parameters)
|
||||
VIRTUAL(bool) IClientUser_BIsSubscribedApp(PARAMS(AppId_t app_id)) { // NOLINT(misc-unused-parameters)
|
||||
return steam_apps::IsDlcUnlocked(__func__, 0, app_id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user