mirror of
https://github.com/acidicoala/SmokeAPI.git
synced 2025-12-05 21:15:39 -05:00
Fixed koalageddon mode
This commit is contained in:
2
KoalaBox
2
KoalaBox
Submodule KoalaBox updated: d75714857d...2e44fecbe5
@@ -6,14 +6,5 @@
|
||||
"dlc_ids": [],
|
||||
"auto_inject_inventory": true,
|
||||
"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
|
||||
}
|
||||
"koalageddon_config": null
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
#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 = {};
|
||||
@@ -13,12 +10,13 @@ namespace koalageddon {
|
||||
* @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());
|
||||
logger->debug("Local koalageddon config parse exception: {}", ex.what());
|
||||
}
|
||||
|
||||
// TODO: Remote source with local cache
|
||||
@@ -35,22 +33,18 @@ namespace koalageddon {
|
||||
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;
|
||||
try {
|
||||
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)
|
||||
if (name == VSTDLIB_DLL) {
|
||||
// VStdLib DLL handles Family Sharing functions
|
||||
init_vstdlib_hooks();
|
||||
} else if (name == STEAMCLIENT_DLL) {
|
||||
// SteamClient DLL handles unlocking functions
|
||||
init_steamclient_hooks();
|
||||
}
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Koalageddon mode dll monitor init error. Module: '{}', Message: {}", name, ex.what());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
@@ -8,16 +8,16 @@ 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 vstdlib_callback_interceptor_address_offset = 1;
|
||||
uint32_t vstdlib_callback_address_offset = 20;
|
||||
uint32_t vstdlib_callback_data_offset = 0;
|
||||
uint32_t vstdlib_callback_name_offset = 4;
|
||||
|
||||
String steamclient_interface_interceptor_pattern = "55 8B EC 8B ?? ?? ?? ?? ?? 81 EC ?? ?? ?? ?? 53 FF 15";
|
||||
String steamclient_interface_demux_pattern = "55 8B EC 83 EC ?? ?? ?? ?? 8B ?? ?? B8 ?? ?? ?? ?? 8B D9";
|
||||
|
||||
uint32_t IClientAppManager_IsAppDlcInstalled_ordinal = 8;
|
||||
uint32_t IClientApps_GetDLCCount_ordinal = 8;
|
||||
uint32_t IClientApps_BGetDLCDataByIndex_ordinal = 9;
|
||||
@@ -35,13 +35,14 @@ namespace koalageddon {
|
||||
// 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,
|
||||
vstdlib_callback_interceptor_address_offset,
|
||||
vstdlib_callback_address_offset,
|
||||
vstdlib_callback_data_offset,
|
||||
vstdlib_callback_name_offset,
|
||||
|
||||
steamclient_interface_interceptor_pattern,
|
||||
steamclient_interface_demux_pattern,
|
||||
|
||||
IClientAppManager_IsAppDlcInstalled_ordinal,
|
||||
IClientApps_GetDLCCount_ordinal,
|
||||
IClientApps_BGetDLCDataByIndex_ordinal,
|
||||
@@ -60,9 +61,7 @@ namespace koalageddon {
|
||||
extern KoalageddonConfig config;
|
||||
|
||||
void init();
|
||||
|
||||
void init_steamclient_hooks();
|
||||
void init_vstdlib_hooks();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -5,10 +5,35 @@
|
||||
#include <koalabox/patcher.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <Zydis/Zydis.h>
|
||||
#include <Zydis/DecoderTypes.h>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
/*
|
||||
#define DEMUX_DECL(INTERFACE) \
|
||||
constexpr auto INTERFACE = #INTERFACE; \
|
||||
DLL_EXPORT(void) INTERFACE##_Demux(const void*, const void*, const void*, const void*); \
|
||||
|
||||
DEMUX_DECL(IClientAppManager)
|
||||
DEMUX_DECL(IClientApps)
|
||||
DEMUX_DECL(IClientInventory)
|
||||
DEMUX_DECL(IClientUser)
|
||||
|
||||
|
||||
DLL_EXPORT(void) SteamClient_Interface_Interceptor(const char* interface_name, const char* function_name);
|
||||
|
||||
namespace koalageddon {
|
||||
// Maps interface name to interface pointer
|
||||
Map<String, const void*> interface_name_pointer_map{}; // NOLINT(cert-err58-cpp)
|
||||
std::mutex map_mutex;
|
||||
|
||||
Set<String> hooked_interfaces; // NOLINT(cert-err58-cpp)
|
||||
|
||||
ZydisDecoder decoder = {};
|
||||
|
||||
constexpr auto INTERFACE_TARGET_COUNT = 4;
|
||||
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
@@ -33,128 +58,250 @@ using namespace smoke_api;
|
||||
* 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);
|
||||
[[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);
|
||||
|
||||
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;
|
||||
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";
|
||||
// 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
|
||||
)
|
||||
);
|
||||
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);
|
||||
// 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();
|
||||
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
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
for (auto i = 0; i < sizeof(epilogue); i++) {
|
||||
if (epilogue[i] != instruction_byte[i]) {
|
||||
return false;
|
||||
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
|
||||
}
|
||||
|
||||
byte_pointer++;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FunctionAddress get_absolute_address(ZydisDecodedInstruction instruction, FunctionAddress address) {
|
||||
const auto op = instruction.operands[0];
|
||||
|
||||
if (op.imm.is_relative) {
|
||||
ZyanU64 absolute_address;
|
||||
ZydisCalcAbsoluteAddress(&instruction, &op, address, &absolute_address);
|
||||
|
||||
return absolute_address;
|
||||
}
|
||||
|
||||
return (FunctionAddress) op.imm.value.u;
|
||||
}
|
||||
|
||||
const char* find_interface_name(FunctionAddress demux_address) {
|
||||
auto* instruction_pointer = (uint8_t*) demux_address;
|
||||
ZydisDecodedInstruction instruction{};
|
||||
while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, instruction_pointer, 1024, &instruction))) {
|
||||
if (instruction.mnemonic == ZYDIS_MNEMONIC_PUSH) {
|
||||
const auto op = instruction.operands[0];
|
||||
|
||||
if (
|
||||
op.type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
op.visibility == ZYDIS_OPERAND_VISIBILITY_EXPLICIT &&
|
||||
op.encoding == ZYDIS_OPERAND_ENCODING_SIMM16_32_32
|
||||
) {
|
||||
const auto* name_address = reinterpret_cast<char*>(op.imm.value.u);
|
||||
const auto is_valid = util::is_valid_pointer(name_address);
|
||||
if (is_valid && String(name_address).starts_with("IClient")) {
|
||||
return name_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
instruction_pointer += instruction.length;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void init_steamclient_hooks() {
|
||||
ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LEGACY_32, ZYDIS_ADDRESS_WIDTH_32);
|
||||
|
||||
const HMODULE module_handle = win_util::get_module_handle_or_throw(STEAMCLIENT_DLL);
|
||||
const auto module_info = win_util::get_module_info_or_throw(module_handle);
|
||||
|
||||
const auto start_address = reinterpret_cast<FunctionAddress>(module_info.lpBaseOfDll);
|
||||
auto* terminal_address = (uint8_t*) (start_address + module_info.SizeOfImage);
|
||||
|
||||
// First, find the interface demux
|
||||
const auto* interface_demux_address = patcher::find_pattern_address(
|
||||
module_info,
|
||||
"interface demux",
|
||||
config.steamclient_interface_demux_pattern
|
||||
);
|
||||
|
||||
// Then iterate over each function demux call
|
||||
|
||||
auto* instruction_pointer = (uint8_t*) interface_demux_address;
|
||||
ZydisDecodedInstruction previous_instruction{};
|
||||
ZydisDecodedInstruction instruction{};
|
||||
while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, instruction_pointer, 10, &instruction))) {
|
||||
if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP && previous_instruction.mnemonic == ZYDIS_MNEMONIC_CALL) {
|
||||
|
||||
// For every such call, extract a function demux address
|
||||
const auto call_demux_address = (FunctionAddress) (
|
||||
instruction_pointer - previous_instruction.length
|
||||
);
|
||||
const auto function_demux_address = get_absolute_address(
|
||||
previous_instruction, call_demux_address
|
||||
);
|
||||
|
||||
if (function_demux_address == 0) {
|
||||
logger->warn("Failed to extract absolute address of call at {}", (void*) call_demux_address);
|
||||
} else {
|
||||
// Then use this address to extract the interface name
|
||||
const char* interface_name_address = find_interface_name(function_demux_address);
|
||||
|
||||
if (interface_name_address == nullptr) {
|
||||
logger->warn(
|
||||
"Failed to extract interface name address of function demux at {}",
|
||||
(void*) function_demux_address
|
||||
);
|
||||
} else {
|
||||
const String interface_name((char*) interface_name_address);
|
||||
|
||||
logger->debug("Detected interface: '{}'", interface_name);
|
||||
|
||||
// Finally, hook the demux functions of interest
|
||||
|
||||
if (IClientAppManager == interface_name) {
|
||||
DETOUR(IClientAppManager_Demux, function_demux_address)
|
||||
} else if (IClientApps == interface_name) {
|
||||
DETOUR(IClientApps_Demux, function_demux_address)
|
||||
} else if (IClientInventory == interface_name) {
|
||||
DETOUR(IClientInventory_Demux, function_demux_address)
|
||||
} else if (IClientUser == interface_name) {
|
||||
DETOUR(IClientUser_Demux, function_demux_address)
|
||||
}
|
||||
|
||||
// Update the terminal address to limit the search scope only to relevant portion of the code
|
||||
auto* function_epilogue = (uint8_t*) get_absolute_address(
|
||||
instruction, (FunctionAddress) instruction_pointer
|
||||
);
|
||||
|
||||
if (function_epilogue == nullptr) {
|
||||
logger->warn(
|
||||
"Failed to extract absolute address of jmp at {}",
|
||||
(void*) instruction_pointer
|
||||
);
|
||||
} else {
|
||||
terminal_address = function_epilogue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
previous_instruction = instruction;
|
||||
instruction_pointer += instruction.length;
|
||||
if (instruction_pointer >= terminal_address) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
byte_pointer++;
|
||||
auto interface_interceptor_address = (FunctionAddress) patcher::find_pattern_address(
|
||||
module_info,
|
||||
"SteamClient_Interface_Interceptor",
|
||||
config.steamclient_interface_interceptor_pattern
|
||||
);
|
||||
|
||||
if (interface_interceptor_address) {
|
||||
DETOUR(SteamClient_Interface_Interceptor, interface_interceptor_address)
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#define DEFINE_INTERFACE(INTERFACE) constexpr auto INTERFACE = #INTERFACE;
|
||||
|
||||
DEFINE_INTERFACE(IClientAppManager)
|
||||
DEFINE_INTERFACE(IClientApps)
|
||||
DEFINE_INTERFACE(IClientInventory)
|
||||
DEFINE_INTERFACE(IClientUser)
|
||||
|
||||
/**
|
||||
* This function intercepts interface name and function names, which we need to determine which functions to hook.
|
||||
* Unfortunately we can't reliably get interface pointer in this function, hence we need to hook corresponding
|
||||
* parent demux functions which will contain the interface pointer as the first parameter.
|
||||
*/
|
||||
DLL_EXPORT(void) SteamClient_Interface_Interceptor(const char* interface_name, const char* function_name) {
|
||||
try {
|
||||
// logger->trace("Intercepted interface function: '{}::{}'", interface_name, function_name);
|
||||
const std::lock_guard<std::mutex> guard(koalageddon::map_mutex);
|
||||
|
||||
static Set<String> hooked_interfaces;
|
||||
const auto needs_hooking = koalageddon::hooked_interfaces.size() < koalageddon::INTERFACE_TARGET_COUNT;
|
||||
const auto has_interface_pointer = koalageddon::interface_name_pointer_map.contains(interface_name);
|
||||
|
||||
if (hooked_interfaces.size() < 4) {
|
||||
const void***** current_ebp;
|
||||
__asm mov current_ebp, ebp
|
||||
// logger->trace(
|
||||
// "Intercepted interface function: '{}::{}'",
|
||||
// interface_name, function_name
|
||||
// );
|
||||
|
||||
auto* const parent_ebp = *current_ebp;
|
||||
|
||||
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
|
||||
];
|
||||
if (needs_hooking && has_interface_pointer) {
|
||||
const auto* interface_address = koalageddon::interface_name_pointer_map[interface_name];
|
||||
|
||||
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_hooked = koalageddon::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);
|
||||
koalageddon::hooked_interfaces.insert(name);
|
||||
}
|
||||
};
|
||||
|
||||
#define HOOK_INTERFACE(FUNC) hook::swap_virtual_func_or_throw( \
|
||||
interface_address, \
|
||||
(void*) interface_address, \
|
||||
#FUNC, \
|
||||
koalageddon::config.FUNC##_ordinal, \
|
||||
koalageddon::config.FUNC##_ordinal, \
|
||||
(FunctionAddress) FUNC \
|
||||
);
|
||||
|
||||
hook_if_needed(IClientAppManager, [&]() {
|
||||
HOOK_INTERFACE(IClientAppManager_IsAppDlcInstalled)
|
||||
// TODO: Investigate IClientAppManager::BIsDlcEnabled
|
||||
// TODO: Investigate IClientAppManager::GetDlcSizes
|
||||
// TODO: Investigate IClientAppManager::GetInstalledApps
|
||||
});
|
||||
|
||||
hook_if_needed(IClientApps, [&]() {
|
||||
HOOK_INTERFACE(IClientApps_GetDLCCount)
|
||||
HOOK_INTERFACE(IClientApps_BGetDLCDataByIndex)
|
||||
// TODO: Investigate IClientApps::GetAppKVRaw
|
||||
// TODO: Investigate IClientApps::GetAppDataSection
|
||||
// TODO: Investigate IClientApps::GetAppData
|
||||
});
|
||||
|
||||
hook_if_needed(IClientInventory, [&]() {
|
||||
@@ -169,18 +316,8 @@ DLL_EXPORT(void) SteamClient_Interface_Interceptor(const char* interface_name, c
|
||||
});
|
||||
|
||||
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.
|
||||
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)
|
||||
@@ -189,3 +326,28 @@ DLL_EXPORT(void) SteamClient_Interface_Interceptor(const char* interface_name, c
|
||||
logger->error("{} -> Error: {}", __func__, ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This macro will generate a definition of a demux function,
|
||||
* which will cache the interface pointer in the local map.
|
||||
*/
|
||||
#define DEMUX_IMPL(INTERFACE) \
|
||||
DLL_EXPORT(void) INTERFACE##_Demux( \
|
||||
const void* arg1, \
|
||||
const void* arg2, \
|
||||
const void* arg3, \
|
||||
const void* arg4 \
|
||||
) { \
|
||||
if(!koalageddon::hooked_interfaces.contains(INTERFACE)){ \
|
||||
const std::lock_guard<std::mutex> guard(koalageddon::map_mutex); \
|
||||
koalageddon::interface_name_pointer_map[INTERFACE] = arg1; \
|
||||
} \
|
||||
GET_ORIGINAL_FUNCTION(INTERFACE##_Demux) \
|
||||
INTERFACE##_Demux_o(arg1, arg2, arg3, arg4); \
|
||||
}
|
||||
|
||||
DEMUX_IMPL(IClientAppManager)
|
||||
DEMUX_IMPL(IClientApps)
|
||||
DEMUX_IMPL(IClientInventory)
|
||||
DEMUX_IMPL(IClientUser)
|
||||
|
||||
@@ -6,7 +6,14 @@
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
//#define DETOUR_STRICT(FUNC, address) hook::detour((FunctionAddress) (address), #FUNC, (FunctionAddress) (FUNC));
|
||||
typedef uint32_t HCoroutine;
|
||||
DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, struct CoroutineData* data);
|
||||
|
||||
namespace koalageddon {
|
||||
void init_vstdlib_hooks() {
|
||||
DETOUR_ORIGINAL(Coroutine_Create)
|
||||
}
|
||||
}
|
||||
|
||||
VIRTUAL(bool) SharedLicensesLockStatus(PARAMS(void* arg)) { // NOLINT(misc-unused-parameters)
|
||||
logger->debug("{} -> instance: {}, arg: {}", __func__, fmt::ptr(THIS), fmt::ptr(arg));
|
||||
@@ -20,21 +27,21 @@ VIRTUAL(bool) SharedLibraryStopPlaying(PARAMS(void* arg)) { // NOLINT(misc-unuse
|
||||
|
||||
struct CallbackData {
|
||||
FunctionAddress get_callback_intercept_address() {
|
||||
return reinterpret_cast<FunctionAddress*>(this)[koalageddon::config.callback_interceptor_address_offset];
|
||||
return reinterpret_cast<FunctionAddress*>(this)[koalageddon::config.vstdlib_callback_interceptor_address_offset];
|
||||
}
|
||||
|
||||
FunctionAddress get_callback_address() {
|
||||
return reinterpret_cast<FunctionAddress*>(this)[koalageddon::config.callback_data_offset];
|
||||
return reinterpret_cast<FunctionAddress*>(this)[koalageddon::config.vstdlib_callback_address_offset];
|
||||
}
|
||||
};
|
||||
|
||||
struct CoroutineData {
|
||||
CallbackData* get_callback_data() {
|
||||
return reinterpret_cast<CallbackData**>(this)[koalageddon::config.callback_data_offset];
|
||||
return reinterpret_cast<CallbackData**>(this)[koalageddon::config.vstdlib_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.vstdlib_callback_name_offset];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -88,12 +88,16 @@ namespace smoke_api {
|
||||
if (is_hook_mode) {
|
||||
hook::init(true);
|
||||
|
||||
#ifdef _WIN64
|
||||
init_hook_mode();
|
||||
#else
|
||||
// TODO: Check if it's steam from valve
|
||||
if (util::strings_are_equal(exe_name, "steam.exe") && !util::is_x64()) {
|
||||
if (util::strings_are_equal(exe_name, "steam.exe")) {
|
||||
koalageddon::init();
|
||||
} else {
|
||||
init_hook_mode();
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
init_proxy_mode();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_inventory.hpp>
|
||||
|
||||
// TODO: Figure out why it doesn't work in koalageddon mode
|
||||
namespace steam_inventory {
|
||||
|
||||
EResult GetResultStatus(
|
||||
|
||||
@@ -39,7 +39,6 @@ VIRTUAL(bool) IClientInventory_GetResultItems(
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Verify this function [ ] signature, in-game [ ]
|
||||
VIRTUAL(bool) IClientInventory_GetResultItemProperty(
|
||||
PARAMS(
|
||||
SteamInventoryResult_t resultHandle,
|
||||
@@ -62,7 +61,6 @@ VIRTUAL(bool) IClientInventory_GetResultItemProperty(
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Verify this function [x] signature, in-game [ ]
|
||||
VIRTUAL(bool) IClientInventory_CheckResultSteamID(
|
||||
PARAMS(
|
||||
SteamInventoryResult_t resultHandle,
|
||||
@@ -76,8 +74,6 @@ VIRTUAL(bool) IClientInventory_CheckResultSteamID(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// TODO: Verify this function [x] signature, in-game [ ]
|
||||
VIRTUAL(bool) IClientInventory_GetAllItems(PARAMS(SteamInventoryResult_t* pResultHandle)) {
|
||||
return steam_inventory::GetAllItems(__func__, pResultHandle, [&]() {
|
||||
GET_ORIGINAL_VIRTUAL_FUNCTION(IClientInventory_GetAllItems)
|
||||
@@ -86,7 +82,6 @@ VIRTUAL(bool) IClientInventory_GetAllItems(PARAMS(SteamInventoryResult_t* pResul
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Verify this function [x] signature, in-game [ ]
|
||||
VIRTUAL(bool) IClientInventory_GetItemsByID(
|
||||
PARAMS(
|
||||
SteamInventoryResult_t* pResultHandle,
|
||||
@@ -101,7 +96,6 @@ VIRTUAL(bool) IClientInventory_GetItemsByID(
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Verify this function [x] signature, in-game [ ]
|
||||
VIRTUAL(bool) IClientInventory_SerializeResult(
|
||||
PARAMS(
|
||||
SteamInventoryResult_t resultHandle,
|
||||
|
||||
Reference in New Issue
Block a user