mirror of
https://github.com/acidicoala/SmokeAPI.git
synced 2025-12-05 21:15:39 -05:00
Fixed build errors
This commit is contained in:
1
.idea/dictionaries/project.xml
generated
1
.idea/dictionaries/project.xml
generated
@@ -5,6 +5,7 @@
|
||||
<w>indicies</w>
|
||||
<w>koalabox</w>
|
||||
<w>koaloader</w>
|
||||
<w>wstr</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
@@ -93,6 +93,11 @@ endif ()
|
||||
|
||||
add_library(SmokeAPI SHARED ${SMOKE_API_SOURCES} ${VERSION_RESOURCE})
|
||||
|
||||
# There is a weird bug where c++23 features are not enabled in x64 builds,
|
||||
# while they are available in x86. I've no idea what causes this discrepancy,
|
||||
# but manually setting the MSVC compiler option fixes this issue.
|
||||
target_compile_options(SmokeAPI PRIVATE /std:c++latest)
|
||||
|
||||
configure_linker_exports(
|
||||
TARGET SmokeAPI
|
||||
FORWARDED_DLL "${STEAMAPI_DLL}_o"
|
||||
|
||||
2
KoalaBox
2
KoalaBox
Submodule KoalaBox updated: afb09983ca...63837c420c
@@ -1,9 +1,10 @@
|
||||
#include <core/types.hpp>
|
||||
#include <common/steamclient_exports.hpp>
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
|
||||
DLL_EXPORT(void*) CreateInterface(const char* interface_string, int* out_result) {
|
||||
return steam_client::GetGenericInterface(
|
||||
__func__, interface_string, [&]() {
|
||||
__func__, interface_string, [&] {
|
||||
GET_ORIGINAL_HOOKED_FUNCTION(CreateInterface)
|
||||
|
||||
return CreateInterface_o(interface_string, out_result);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/globals.hpp>
|
||||
#include <koalabox/core.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
// ReSharper disable once CppUnusedIncludeDirective
|
||||
#include <koalabox/hook.hpp> // Used by several macros
|
||||
#include <koalabox/logger.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
#include <smoke_api/config.hpp>
|
||||
|
||||
|
||||
// DLL_EXPORT(void) SteamAPI_Shutdown1() {
|
||||
// LOG_INFO("{} -> Game requested shutdown", __func__);
|
||||
//
|
||||
// ORIGINAL_FUNCTION_STEAMAPI(SteamAPI_Shutdown)();
|
||||
// }
|
||||
|
||||
/*DLL_EXPORT(void) SteamAPI_Shutdown2() {
|
||||
LOG_INFO("{} -> Game requested shutdown", __func__);
|
||||
|
||||
ORIGINAL_FUNCTION_STEAMAPI(SteamAPI_Shutdown)();
|
||||
}*/
|
||||
#include <core/globals.hpp>
|
||||
|
||||
// TODO: Detour in hook mode
|
||||
DLL_EXPORT(bool) SteamAPI_RestartAppIfNecessary(
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <koalabox/logger.hpp>
|
||||
|
||||
#include <core/globals.hpp>
|
||||
#include <steam_impl/steam_apps.hpp>
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
#include <steam_impl/steam_inventory.hpp>
|
||||
#include <steam_impl/steam_user.hpp>
|
||||
#include <steam_impl/steam_impl.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
|
||||
// ISteamApps
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <core/globals.hpp>
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
|
||||
DLL_EXPORT(void*) SteamInternal_FindOrCreateUserInterface(HSteamUser hSteamUser, const char* version) {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
#include <regex>
|
||||
|
||||
#include <koalabox/logger.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#include <koalabox/util.hpp>
|
||||
#include <regex>
|
||||
|
||||
#include <core/globals.hpp>
|
||||
#include <steam_impl/steam_client.hpp>
|
||||
|
||||
/**
|
||||
* Searches the `.rdata` section of the original dll for the full interface version string
|
||||
@@ -40,7 +43,7 @@ DLL_EXPORT(void*) SteamClient() {
|
||||
static auto version = get_versioned_interface(STEAM_CLIENT, "006");
|
||||
|
||||
return steam_client::GetGenericInterface(
|
||||
__func__, version, [&]() {
|
||||
__func__, version, [&] {
|
||||
GET_ORIGINAL_FUNCTION_STEAMAPI(SteamClient)
|
||||
|
||||
return SteamClient_o();
|
||||
@@ -64,7 +67,7 @@ DLL_EXPORT(void*) SteamUser() {
|
||||
static auto version = get_versioned_interface(STEAM_USER, "012");
|
||||
|
||||
return steam_client::GetGenericInterface(
|
||||
__func__, version, [&]() {
|
||||
__func__, version, [&] {
|
||||
GET_ORIGINAL_FUNCTION_STEAMAPI(SteamUser)
|
||||
|
||||
return SteamUser_o();
|
||||
@@ -76,7 +79,7 @@ DLL_EXPORT(void*) SteamInventory() {
|
||||
static auto version = get_versioned_interface(STEAM_INVENTORY, "001");
|
||||
|
||||
return steam_client::GetGenericInterface(
|
||||
__func__, version, [&]() {
|
||||
__func__, version, [&] {
|
||||
GET_ORIGINAL_FUNCTION_STEAMAPI(SteamInventory)
|
||||
|
||||
return SteamInventory_o();
|
||||
|
||||
@@ -80,8 +80,8 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify that it's steam from valve, and not some other executable coincidentally named
|
||||
// steam
|
||||
// Verify that it's steam from valve,
|
||||
// and not some other executable coincidentally named steam
|
||||
|
||||
const HMODULE steam_handle = koalabox::win_util::get_module_handle_or_throw(nullptr);
|
||||
const auto manifest = koalabox::win_util::get_module_manifest(steam_handle);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <core/types.hpp>
|
||||
#include <core/api.hpp>
|
||||
|
||||
namespace steam_apps {
|
||||
namespace {
|
||||
/// Steamworks may max GetDLCCount value at 64, depending on how much unowned DLCs the user has.
|
||||
/// Despite this limit, some games with more than 64 DLCs still keep using this method.
|
||||
/// This means we have to get extra DLC IDs from local config, remote config, or cache.
|
||||
@@ -43,9 +43,10 @@ namespace steam_apps {
|
||||
// by aggregating results from all the sources into a single set.
|
||||
Vector<DLC> aggregated_dlcs;
|
||||
|
||||
const auto append_dlcs = [&](const Vector<DLC>& source, const String& source_name) {
|
||||
LOG_DEBUG("App ID {} has {} DLCs defined in {}", app_id, source.size(), source_name);
|
||||
aggregated_dlcs < append > source;
|
||||
const auto append_dlcs = [&](const Vector<DLC>& dlc_list, const String& source_name) {
|
||||
LOG_DEBUG("App ID {} has {} DLCs defined in {}", app_id, dlc_list.size(), source_name);
|
||||
// Append DLCs to aggregated DLCs
|
||||
std::ranges::copy(dlc_list, std::back_inserter(aggregated_dlcs));
|
||||
};
|
||||
|
||||
append_dlcs(smoke_api::config::get_extra_dlcs(app_id), "local config");
|
||||
@@ -71,6 +72,9 @@ namespace steam_apps {
|
||||
|
||||
smoke_api::app_cache::save_dlcs(app_id, aggregated_dlcs);
|
||||
}
|
||||
}
|
||||
|
||||
namespace steam_apps {
|
||||
|
||||
bool IsDlcUnlocked(
|
||||
const String& function_name,
|
||||
@@ -114,7 +118,7 @@ namespace steam_apps {
|
||||
|
||||
return total_count(static_cast<int>(app_dlcs[app_id].size()));
|
||||
} catch (const Exception& e) {
|
||||
LOG_ERROR(" Uncaught exception: {}", function_name, e.what());
|
||||
LOG_ERROR("Uncaught exception: {}", function_name, e.what());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ VIRTUAL(bool) IClientUser_BIsSubscribedApp(PARAMS(AppId_t dlc_id)) {
|
||||
return IClientUser_BIsSubscribedApp_o(ARGS(dlc_id));
|
||||
});
|
||||
} catch (const Exception& e) {
|
||||
LOG_ERROR("{} -> Error: {}", __func__, e.what())
|
||||
LOG_ERROR("{} -> Error: {}", __func__, e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <store_mode/steamclient/steamclient.hpp>
|
||||
#include <store_mode/store.hpp>
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/logger.hpp>
|
||||
#include <koalabox/util.hpp>
|
||||
#include <store_mode/steamclient/steamclient.hpp>
|
||||
#include <store_mode/store.hpp>
|
||||
|
||||
#include <Zydis/Zydis.h>
|
||||
#include <Zydis/DecoderTypes.h>
|
||||
#include <Zydis/Zydis.h>
|
||||
|
||||
namespace store::steamclient {
|
||||
using namespace koalabox;
|
||||
@@ -31,6 +31,7 @@ namespace store::steamclient {
|
||||
uintptr_t start_address
|
||||
);
|
||||
|
||||
// clang-format off
|
||||
#define CONSTRUCT_ORDINAL_MAP(INTERFACE) \
|
||||
construct_ordinal_map(#INTERFACE, ordinal_map[#INTERFACE], function_selector_address);
|
||||
|
||||
@@ -38,11 +39,11 @@ namespace store::steamclient {
|
||||
interface, \
|
||||
#INTERFACE"_"#FUNC, \
|
||||
ordinal_map[#INTERFACE][#FUNC], \
|
||||
reinterpret_cast<uintptr_t>(INTERFACE##_##FUNC) \
|
||||
reinterpret_cast<uintptr_t>(INTERFACE## _## FUNC) \
|
||||
);
|
||||
|
||||
#define SELECTOR_IMPLEMENTATION(INTERFACE, FUNC_BODY) \
|
||||
DLL_EXPORT(void) INTERFACE##_Selector( \
|
||||
DLL_EXPORT(void) INTERFACE## _Selector( \
|
||||
void* interface, \
|
||||
void* arg2, \
|
||||
void* arg3, \
|
||||
@@ -52,8 +53,8 @@ namespace store::steamclient {
|
||||
interface_name_to_address_map[#INTERFACE] = interface; \
|
||||
[&]()FUNC_BODY(); \
|
||||
}) \
|
||||
GET_ORIGINAL_HOOKED_FUNCTION(INTERFACE##_Selector) \
|
||||
INTERFACE##_Selector_o(interface, arg2, arg3, arg4); \
|
||||
GET_ORIGINAL_HOOKED_FUNCTION(INTERFACE## _Selector) \
|
||||
INTERFACE## _Selector_o(interface, arg2, arg3, arg4); \
|
||||
}
|
||||
|
||||
SELECTOR_IMPLEMENTATION(IClientAppManager, {
|
||||
@@ -87,10 +88,12 @@ namespace store::steamclient {
|
||||
#define DETOUR_SELECTOR(INTERFACE) \
|
||||
if(interface_name == #INTERFACE){ \
|
||||
CONSTRUCT_ORDINAL_MAP(INTERFACE) \
|
||||
DETOUR_ADDRESS(INTERFACE##_Selector, function_selector_address) \
|
||||
DETOUR_ADDRESS(INTERFACE## _Selector, function_selector_address) \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
void detour_interface_selector(const String& interface_name, uintptr_t function_selector_address) {
|
||||
void
|
||||
detour_interface_selector(const String& interface_name, uintptr_t function_selector_address) {
|
||||
LOG_DEBUG("Detected interface: '{}'", interface_name);
|
||||
|
||||
DETOUR_SELECTOR(IClientAppManager)
|
||||
@@ -100,8 +103,12 @@ namespace store::steamclient {
|
||||
DETOUR_SELECTOR(IClientUtils)
|
||||
}
|
||||
|
||||
uintptr_t get_absolute_address(ZydisDecodedInstruction instruction, uintptr_t address) {
|
||||
const auto operand = instruction.operands[0];
|
||||
uintptr_t get_absolute_address(
|
||||
const ZydisDecodedInstruction instruction, //
|
||||
const ZydisDecodedOperand operands[], //
|
||||
const uintptr_t address
|
||||
) {
|
||||
const auto operand = operands[0];
|
||||
|
||||
if (operand.imm.is_relative) {
|
||||
ZyanU64 absolute_address;
|
||||
@@ -110,11 +117,14 @@ namespace store::steamclient {
|
||||
return absolute_address;
|
||||
}
|
||||
|
||||
return (uintptr_t) operand.imm.value.u;
|
||||
return (uintptr_t)operand.imm.value.u;
|
||||
}
|
||||
|
||||
bool is_push_immediate(const ZydisDecodedInstruction& instruction) {
|
||||
const auto& operand = instruction.operands[0];
|
||||
bool is_push_immediate(
|
||||
const ZydisDecodedInstruction& instruction, //
|
||||
const ZydisDecodedOperand operands[]
|
||||
) {
|
||||
const auto& operand = operands[0];
|
||||
|
||||
return instruction.mnemonic == ZYDIS_MNEMONIC_PUSH &&
|
||||
operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
@@ -122,8 +132,11 @@ namespace store::steamclient {
|
||||
operand.encoding == ZYDIS_OPERAND_ENCODING_SIMM16_32_32;
|
||||
}
|
||||
|
||||
std::optional<String> get_string_argument(const ZydisDecodedInstruction& instruction) {
|
||||
const auto* name_address = reinterpret_cast<char*>(instruction.operands[0].imm.value.u);
|
||||
std::optional<String> get_string_argument(
|
||||
const ZydisDecodedInstruction& instruction, //
|
||||
const ZydisDecodedOperand operands[]
|
||||
) {
|
||||
const auto* name_address = reinterpret_cast<char*>(operands[0].imm.value.u);
|
||||
if (util::is_valid_pointer(name_address)) {
|
||||
return name_address;
|
||||
}
|
||||
@@ -132,20 +145,22 @@ namespace store::steamclient {
|
||||
|
||||
std::optional<String> get_instruction_string(
|
||||
const ZydisDecodedInstruction& instruction,
|
||||
const ZydisDecodedOperand operands[],
|
||||
const uintptr_t address
|
||||
) {
|
||||
const auto buffer_size = 64;
|
||||
char buffer[buffer_size] = {};
|
||||
|
||||
if (ZYAN_SUCCESS(
|
||||
ZydisFormatterFormatInstruction(
|
||||
if (ZYAN_SUCCESS(ZydisFormatterFormatInstruction(
|
||||
&formatter,
|
||||
&instruction,
|
||||
operands,
|
||||
instruction.operand_count,
|
||||
buffer,
|
||||
buffer_size,
|
||||
address
|
||||
)
|
||||
)) {
|
||||
address,
|
||||
ZYAN_NULL
|
||||
))) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -155,16 +170,18 @@ namespace store::steamclient {
|
||||
std::optional<String> find_interface_name(uintptr_t selector_address) {
|
||||
auto current_address = selector_address;
|
||||
ZydisDecodedInstruction instruction{};
|
||||
while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(
|
||||
&decoder,
|
||||
(void*) current_address,
|
||||
MAX_INSTRUCTION_SIZE,
|
||||
&instruction
|
||||
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
||||
while (ZYAN_SUCCESS(ZydisDecoderDecodeFull(
|
||||
&decoder, // decoder
|
||||
reinterpret_cast<void*>(current_address), // buffer
|
||||
MAX_INSTRUCTION_SIZE, // length
|
||||
&instruction, // instruction
|
||||
operands // operands
|
||||
))) {
|
||||
const auto debug_str = get_instruction_string(instruction, current_address);
|
||||
// const auto debug_str = get_instruction_string(instruction, current_address);
|
||||
|
||||
if (is_push_immediate(instruction)) {
|
||||
auto string_opt = get_string_argument(instruction);
|
||||
if (is_push_immediate(instruction, operands)) {
|
||||
auto string_opt = get_string_argument(instruction, operands);
|
||||
|
||||
if (string_opt && string_opt->starts_with("IClient")) {
|
||||
return string_opt;
|
||||
@@ -181,24 +198,29 @@ namespace store::steamclient {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
struct full_instruction {
|
||||
const ZydisDecodedInstruction instruction;
|
||||
const std::vector<ZydisDecodedOperand> operands;
|
||||
};
|
||||
|
||||
/**
|
||||
* Recursively walks through the code, until a return instruction is reached.
|
||||
* Recursion occurs whenever a jump/branch is encountered.
|
||||
*/
|
||||
template<typename T>
|
||||
void visit_code( // NOLINT(misc-no-recursion)
|
||||
Set<uintptr_t>& visited_addresses,
|
||||
std::set<uintptr_t>& visited_addresses,
|
||||
uintptr_t start_address,
|
||||
T context,
|
||||
const Function<bool(
|
||||
const std::function<bool(
|
||||
const ZydisDecodedInstruction& instruction,
|
||||
const ZydisDecodedOperand& operand,
|
||||
const ZydisDecodedOperand operand[],
|
||||
const uintptr_t& current_address,
|
||||
T& context,
|
||||
const std::list<ZydisDecodedInstruction>& instruction_list
|
||||
const std::list<full_instruction>& instruction_list
|
||||
)>& callback
|
||||
) {
|
||||
LOG_TRACE("{} -> start_address: {}", __func__, (void*) start_address);
|
||||
LOG_TRACE("{} -> start_address: {}", __func__, reinterpret_cast<void*>(start_address));
|
||||
|
||||
if (visited_addresses.contains(start_address)) {
|
||||
LOG_TRACE("Breaking recursion due to visited address");
|
||||
@@ -206,34 +228,36 @@ namespace store::steamclient {
|
||||
}
|
||||
|
||||
auto current_address = start_address;
|
||||
std::list instruction_list{ZydisDecodedInstruction{}};
|
||||
std::list<full_instruction> instruction_list;
|
||||
|
||||
ZydisDecodedInstruction instruction{};
|
||||
while (
|
||||
ZYAN_SUCCESS(
|
||||
ZydisDecoderDecodeBuffer(
|
||||
&decoder,
|
||||
(void*) current_address,
|
||||
MAX_INSTRUCTION_SIZE,
|
||||
&instruction
|
||||
)
|
||||
)) {
|
||||
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
||||
while (ZYAN_SUCCESS(ZydisDecoderDecodeFull(
|
||||
&decoder, // decoder
|
||||
(void*)current_address, // buffer
|
||||
MAX_INSTRUCTION_SIZE, // length
|
||||
&instruction, // instructions
|
||||
operands // operands
|
||||
))) {
|
||||
visited_addresses.insert(current_address);
|
||||
|
||||
LOG_TRACE(
|
||||
"{} -> visiting {} │ {}",
|
||||
__func__, (void*) current_address, *get_instruction_string(instruction, current_address)
|
||||
__func__,
|
||||
reinterpret_cast<void*>(current_address),
|
||||
*get_instruction_string(instruction, operands, current_address)
|
||||
);
|
||||
|
||||
const auto operand = instruction.operands[0];
|
||||
|
||||
const auto should_return = callback(instruction, operand, current_address, context, instruction_list);
|
||||
const auto should_return =
|
||||
callback(instruction, operands, current_address, context, instruction_list);
|
||||
|
||||
if (should_return) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (instruction.meta.category == ZYDIS_CATEGORY_COND_BR) {
|
||||
const auto jump_taken_destination = get_absolute_address(instruction, current_address);
|
||||
const auto jump_taken_destination =
|
||||
get_absolute_address(instruction, operands, current_address);
|
||||
const auto jump_not_taken_destination = current_address + instruction.length;
|
||||
|
||||
visit_code(visited_addresses, jump_taken_destination, context, callback);
|
||||
@@ -243,24 +267,28 @@ namespace store::steamclient {
|
||||
return;
|
||||
}
|
||||
|
||||
if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP && operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
|
||||
const auto jump_destination = get_absolute_address(instruction, current_address);
|
||||
const auto& operand = operands[0];
|
||||
|
||||
if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP &&
|
||||
operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
|
||||
const auto jump_destination =
|
||||
get_absolute_address(instruction, operands, current_address);
|
||||
|
||||
visit_code(visited_addresses, jump_destination, context, callback);
|
||||
|
||||
LOG_TRACE("{} -> Breaking recursion due to an unconditional jump", __func__)
|
||||
LOG_TRACE("{} -> Breaking recursion due to an unconditional jump", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (instruction.mnemonic == ZYDIS_MNEMONIC_JMP &&
|
||||
operand.type == ZYDIS_OPERAND_TYPE_MEMORY &&
|
||||
operand.mem.scale == sizeof(uintptr_t) &&
|
||||
operand.mem.disp.has_displacement) {
|
||||
// Special handling for jump tables. Guaranteed to be present in the interface selector.
|
||||
const auto* table = (uintptr_t*) operand.mem.disp.value;
|
||||
operand.mem.scale == sizeof(uintptr_t) && operand.mem.disp.has_displacement) {
|
||||
// Special handling for jump tables. Guaranteed to be present in the interface
|
||||
// selector.
|
||||
const auto* table = (uintptr_t*)operand.mem.disp.value;
|
||||
|
||||
const auto* table_entry = table;
|
||||
while (util::is_valid_pointer((void*) *table_entry)) {
|
||||
while (util::is_valid_pointer((void*)*table_entry)) {
|
||||
visit_code(visited_addresses, *table_entry, context, callback);
|
||||
|
||||
table_entry++;
|
||||
@@ -275,10 +303,16 @@ namespace store::steamclient {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// We push items to the front so that it becomes easy to iterate over instructions
|
||||
// in reverse order of addition.
|
||||
instruction_list.push_front(instruction);
|
||||
|
||||
std::vector<ZydisDecodedOperand> operand_list;
|
||||
for (auto i = 0U; i < instruction.operand_count; i++) {
|
||||
operand_list.emplace_back(operands[i]);
|
||||
}
|
||||
instruction_list.push_front(
|
||||
{.instruction = instruction, .operands = std::move(operand_list)}
|
||||
);
|
||||
current_address += instruction.length;
|
||||
}
|
||||
}
|
||||
@@ -289,35 +323,39 @@ namespace store::steamclient {
|
||||
uintptr_t start_address
|
||||
) {
|
||||
Set<uintptr_t> visited_addresses;
|
||||
visit_code<InstructionContext>(visited_addresses, start_address, {}, [&](
|
||||
const ZydisDecodedInstruction& instruction,
|
||||
const ZydisDecodedOperand& operand,
|
||||
visit_code<InstructionContext>(
|
||||
visited_addresses,
|
||||
start_address,
|
||||
{},
|
||||
[&](const ZydisDecodedInstruction& instruction,
|
||||
const ZydisDecodedOperand operands[],
|
||||
const auto&,
|
||||
InstructionContext& context,
|
||||
const std::list<ZydisDecodedInstruction>& instruction_list
|
||||
) {
|
||||
if (context.function_name && function_name_to_ordinal_map.contains(*context.function_name)) {
|
||||
const std::list<full_instruction>& instruction_list) {
|
||||
if (context.function_name &&
|
||||
function_name_to_ordinal_map.contains(*context.function_name)) {
|
||||
// Avoid duplicate work
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& last_instruction = instruction_list.front();
|
||||
const auto& last = instruction_list.front();
|
||||
|
||||
const auto is_mov_base_esp = instruction.mnemonic == ZYDIS_MNEMONIC_MOV &&
|
||||
instruction.operand_count == 2 &&
|
||||
instruction.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
instruction.operands[1].reg.value == ZYDIS_REGISTER_ESP;
|
||||
operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
operands[1].reg.value == ZYDIS_REGISTER_ESP;
|
||||
|
||||
if (!context.base_register && is_mov_base_esp) {
|
||||
// Save base register
|
||||
context.base_register = instruction.operands[0].reg.value;
|
||||
} else if (is_push_immediate(last_instruction) &&
|
||||
is_push_immediate(instruction) &&
|
||||
!context.function_name) {
|
||||
context.base_register = operands[0].reg.value;
|
||||
} else if ( //
|
||||
is_push_immediate( last.instruction, last.operands.data() ) &&
|
||||
is_push_immediate(instruction, operands) && !context.function_name) {
|
||||
// The very first 2 consecutive pushes indicate interface and function names.
|
||||
// However, subsequent pushes may contain irrelevant strings.
|
||||
const auto push_string_1 = get_string_argument(last_instruction);
|
||||
const auto push_string_2 = get_string_argument(instruction);
|
||||
const auto push_string_1 =
|
||||
get_string_argument(last.instruction, last.operands.data());
|
||||
const auto push_string_2 = get_string_argument(instruction, operands);
|
||||
|
||||
if (push_string_1 && push_string_2) {
|
||||
if (*push_string_1 == target_interface) {
|
||||
@@ -326,7 +364,8 @@ namespace store::steamclient {
|
||||
context.function_name = push_string_1;
|
||||
}
|
||||
|
||||
if (context.function_name && function_name_to_ordinal_map.contains(*context.function_name)) {
|
||||
if (context.function_name &&
|
||||
function_name_to_ordinal_map.contains(*context.function_name)) {
|
||||
// Bail early to avoid duplicate work
|
||||
return true;
|
||||
}
|
||||
@@ -338,28 +377,30 @@ namespace store::steamclient {
|
||||
const auto& base_register = *(context.base_register);
|
||||
const auto& function_name = *(context.function_name);
|
||||
|
||||
|
||||
std::optional<uint32_t> offset;
|
||||
|
||||
auto last_destination_reg = ZYDIS_REGISTER_NONE;
|
||||
bool is_derived_from_base_reg = false;
|
||||
|
||||
const auto& operand = operands[0];
|
||||
|
||||
// Sometimes the offset is present in the call instruction itself,
|
||||
// hence we can immediately obtain it.
|
||||
if (operand.type == ZYDIS_OPERAND_TYPE_MEMORY && operand.mem.base != ZYDIS_REGISTER_NONE) {
|
||||
if (operand.type == ZYDIS_OPERAND_TYPE_MEMORY &&
|
||||
operand.mem.base != ZYDIS_REGISTER_NONE) {
|
||||
offset = static_cast<uint32_t>(operand.mem.disp.value);
|
||||
last_destination_reg = operand.mem.base;
|
||||
} else if (operand.type == ZYDIS_OPERAND_TYPE_REGISTER) {
|
||||
last_destination_reg = operand.reg.value;
|
||||
}
|
||||
|
||||
for (const auto& previous_instruction: instruction_list) {
|
||||
const auto& destination_operand = previous_instruction.operands[0];
|
||||
const auto& source_operand = previous_instruction.operands[1];
|
||||
for (const auto& previous : instruction_list) {
|
||||
const auto& destination_operand = previous.operands[0];
|
||||
const auto& source_operand = previous.operands[1];
|
||||
|
||||
// Extract offset if necessary
|
||||
if (previous_instruction.mnemonic == ZYDIS_MNEMONIC_MOV &&
|
||||
previous_instruction.operand_count == 2 &&
|
||||
if (previous.instruction.mnemonic == ZYDIS_MNEMONIC_MOV &&
|
||||
previous.instruction.operand_count == 2 &&
|
||||
destination_operand.reg.value == last_destination_reg &&
|
||||
source_operand.type == ZYDIS_OPERAND_TYPE_MEMORY) {
|
||||
|
||||
@@ -367,8 +408,9 @@ namespace store::steamclient {
|
||||
if (source_mem.base == base_register &&
|
||||
source_mem.disp.has_displacement &&
|
||||
source_mem.disp.value == 8) {
|
||||
// We have verified that the chain eventually leads up to the base register.
|
||||
// Hence, we can conclude that the offset is valid.
|
||||
// We have verified that the chain eventually leads up to the
|
||||
// base register. Hence, we can conclude that the offset is
|
||||
// valid.
|
||||
is_derived_from_base_reg = true;
|
||||
break;
|
||||
}
|
||||
@@ -385,7 +427,12 @@ namespace store::steamclient {
|
||||
if (offset && is_derived_from_base_reg) {
|
||||
const auto ordinal = *offset / sizeof(uintptr_t);
|
||||
|
||||
LOG_DEBUG("Found function ordinal {}::{}@{}", target_interface, function_name, ordinal);
|
||||
LOG_DEBUG(
|
||||
"Found function ordinal {}::{}@{}",
|
||||
target_interface,
|
||||
function_name,
|
||||
ordinal
|
||||
);
|
||||
|
||||
function_name_to_ordinal_map[function_name] = ordinal;
|
||||
return true;
|
||||
@@ -402,17 +449,21 @@ namespace store::steamclient {
|
||||
const uintptr_t start_address,
|
||||
Set<uintptr_t>& visited_addresses
|
||||
) {
|
||||
visit_code<nullptr_t>(visited_addresses, start_address, nullptr, [](
|
||||
const ZydisDecodedInstruction& instruction,
|
||||
const ZydisDecodedOperand& operand,
|
||||
const auto& current_address,
|
||||
auto,
|
||||
const auto&
|
||||
) {
|
||||
if (instruction.mnemonic == ZYDIS_MNEMONIC_CALL && operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
|
||||
LOG_TRACE("Found call instruction at {}", (void*) current_address);
|
||||
visit_code<nullptr_t>(
|
||||
visited_addresses,
|
||||
start_address,
|
||||
nullptr,
|
||||
[](const ZydisDecodedInstruction& instruction,
|
||||
const ZydisDecodedOperand operands[],
|
||||
const auto& current_address,
|
||||
auto,
|
||||
const auto&) {
|
||||
if (instruction.mnemonic == ZYDIS_MNEMONIC_CALL &&
|
||||
operands[0].type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
|
||||
LOG_TRACE("Found call instruction at {}", (void*)current_address);
|
||||
|
||||
const auto function_selector_address = get_absolute_address(instruction, current_address);
|
||||
const auto function_selector_address =
|
||||
get_absolute_address(instruction, operands, current_address);
|
||||
|
||||
const auto interface_name_opt = find_interface_name(function_selector_address);
|
||||
|
||||
@@ -429,16 +480,16 @@ namespace store::steamclient {
|
||||
}
|
||||
|
||||
void process_client_engine(uintptr_t interface) {
|
||||
const auto* steam_client_internal = ((uintptr_t***) interface)[
|
||||
store::config.client_engine_steam_client_internal_ordinal
|
||||
];
|
||||
const auto interface_selector_address = (*steam_client_internal)[
|
||||
store::config.steam_client_internal_interface_selector_ordinal
|
||||
];
|
||||
const auto* steam_client_internal =
|
||||
((uintptr_t***)interface)[store::config.client_engine_steam_client_internal_ordinal];
|
||||
const auto interface_selector_address = (*steam_client_internal
|
||||
)[store::config.steam_client_internal_interface_selector_ordinal];
|
||||
|
||||
LOG_DEBUG("Found interface selector at: {}", (void*) interface_selector_address);
|
||||
LOG_DEBUG("Found interface selector at: {}", (void*)interface_selector_address);
|
||||
|
||||
if (ZYAN_FAILED(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LEGACY_32, ZYDIS_STACK_WIDTH_32))) {
|
||||
if (ZYAN_FAILED(
|
||||
ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LEGACY_32, ZYDIS_STACK_WIDTH_32)
|
||||
)) {
|
||||
LOG_ERROR("Failed to initialize zydis decoder");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
#include <store_mode/store_api.hpp>
|
||||
#include <store_mode/store_cache.hpp>
|
||||
#include <store_mode/vstdlib/vstdlib.hpp>
|
||||
#include <core/globals.hpp>
|
||||
|
||||
namespace store {
|
||||
|
||||
StoreConfig config; // NOLINT(cert-err58-cpp)
|
||||
namespace {
|
||||
using namespace store;
|
||||
|
||||
/**
|
||||
* @return A string representing the source of the config.
|
||||
*/
|
||||
* @return A string representing the source of the config.
|
||||
*/
|
||||
void init_store_config() {
|
||||
const auto print_source = [](const String& source) {
|
||||
LOG_INFO("Loaded Store config from the {}", source);
|
||||
@@ -50,47 +50,62 @@ namespace store {
|
||||
config = {};
|
||||
}
|
||||
|
||||
// Finally, fetch the remote config from GitHub, and inform user about the need to restart Steam,
|
||||
// if a new config has been fetched
|
||||
NEW_THREAD({
|
||||
// Finally, fetch the remote config from GitHub, and inform user about the need to restart
|
||||
// Steam, if a new config has been fetched
|
||||
std::thread([] {
|
||||
try {
|
||||
const auto github_config_opt = api::fetch_store_config();
|
||||
if (!github_config_opt) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto github_config = *github_config_opt;
|
||||
|
||||
store_cache::save_store_config(github_config);
|
||||
|
||||
if (github_config == config) {
|
||||
LOG_DEBUG("Fetched Store config is equal to existing config");
|
||||
|
||||
(spdlog::default_logger_raw())
|
||||
->log(
|
||||
spdlog::source_loc{
|
||||
"store.cpp", 66, static_cast<const char*>(__FUNCTION__)
|
||||
},
|
||||
spdlog::level::debug,
|
||||
"Fetched Store config is equal to existing config"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Fetched a new Store config")
|
||||
|
||||
::MessageBox(
|
||||
(spdlog::default_logger_raw())
|
||||
->log(
|
||||
spdlog::source_loc{"store.cpp", 66, static_cast<const char*>(__FUNCTION__)},
|
||||
spdlog::level::debug,
|
||||
"Fetched a new Store config"
|
||||
);
|
||||
MessageBoxW(
|
||||
nullptr,
|
||||
TEXT(
|
||||
"SmokeAPI has downloaded an updated config for Store mode. "
|
||||
"Please restart Steam in order to apply the new Store config. "
|
||||
),
|
||||
TEXT("SmokeAPI - Store"),
|
||||
MB_SETFOREGROUND | MB_ICONINFORMATION | MB_OK
|
||||
L"SmokeAPI has downloaded an updated config for Store mode. "
|
||||
"Please restart Steam in order to apply the new Store config. ",
|
||||
L"SmokeAPI - Store",
|
||||
0x00010000L | 0x00000040L | 0x00000000L
|
||||
);
|
||||
} catch (const Exception& ex) {
|
||||
LOG_ERROR("Failed to get remote store_mode config: {}", ex.what());
|
||||
(spdlog::default_logger_raw())
|
||||
->log(
|
||||
spdlog::source_loc{"store.cpp", 66, static_cast<const char*>(__FUNCTION__)},
|
||||
spdlog::level::err,
|
||||
"Failed to get remote store_mode config: {}",
|
||||
ex.what()
|
||||
);
|
||||
}
|
||||
})
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
namespace store {
|
||||
StoreConfig config; // NOLINT(cert-err58-cpp)
|
||||
|
||||
void init_store_mode() {
|
||||
init_store_config();
|
||||
|
||||
koalabox::dll_monitor::init_listener(
|
||||
{VSTDLIB_DLL, STEAMCLIENT_DLL}, [](const HMODULE& module_handle, const String& name) {
|
||||
{VSTDLIB_DLL, STEAMCLIENT_DLL},
|
||||
[](const HMODULE& module_handle, const String& name) {
|
||||
try {
|
||||
if (koalabox::str::eq(name, VSTDLIB_DLL)) {
|
||||
// VStdLib DLL handles Family Sharing functions
|
||||
@@ -108,32 +123,35 @@ namespace store {
|
||||
DETOUR_STEAMCLIENT(CreateInterface)
|
||||
}
|
||||
|
||||
if (globals::vstdlib_module != nullptr && globals::steamclient_module != nullptr) {
|
||||
if (globals::vstdlib_module != nullptr &&
|
||||
globals::steamclient_module != nullptr) {
|
||||
koalabox::dll_monitor::shutdown_listener();
|
||||
}
|
||||
} catch (const Exception& ex) {
|
||||
LOG_ERROR(
|
||||
"Error listening to DLL load events. Module: '{}', Message: {}",
|
||||
name, ex.what()
|
||||
name,
|
||||
ex.what()
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
NEW_THREAD({
|
||||
koalabox::ipc::init_pipe_server("smokeapi.store.steam", [](const koalabox::ipc::Request& request) {
|
||||
koalabox::ipc::Response response;
|
||||
|
||||
if (request.name < equals > "config::reload") {
|
||||
smoke_api::config::ReloadConfig();
|
||||
response.success = true;
|
||||
} else {
|
||||
response.success = false;
|
||||
response.data["error_message"] = "Invalid request name: " + request.name;
|
||||
std::thread([=] {
|
||||
koalabox::ipc::init_pipe_server(
|
||||
"smokeapi.store.steam",
|
||||
[](const koalabox::ipc::Request& request) {
|
||||
koalabox::ipc::Response response;
|
||||
if (koalabox::str::eq(request.name, "config::reload")) {
|
||||
smoke_api::config::ReloadConfig();
|
||||
response.success = true;
|
||||
} else {
|
||||
response.success = false;
|
||||
response.data["error_message"] = "Invalid request name: " + request.name;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
return response;
|
||||
});
|
||||
})
|
||||
);
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace store::api {
|
||||
|
||||
return kg_config_json.get<StoreConfig>();
|
||||
} catch (const Exception& e) {
|
||||
LOG_ERROR("Failed to fetch Store config from GitHub: {}", e.what())
|
||||
LOG_ERROR("Failed to fetch Store config from GitHub: {}", e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace store::store_cache {
|
||||
try {
|
||||
return koalabox::cache::get(KEY_KG_CONFIG, Json(nullptr)).get<StoreConfig>();
|
||||
} catch (const Exception& e) {
|
||||
LOG_ERROR("Failed to get cached store_mode config: {}", e.what())
|
||||
LOG_ERROR("Failed to get cached store_mode config: {}", e.what());
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -17,11 +17,11 @@ namespace store::store_cache {
|
||||
|
||||
bool save_store_config(const StoreConfig& config) {
|
||||
try {
|
||||
LOG_DEBUG("Caching store_mode config")
|
||||
LOG_DEBUG("Caching store_mode config");
|
||||
|
||||
return koalabox::cache::put(KEY_KG_CONFIG, Json(config));
|
||||
} catch (const Exception& e) {
|
||||
LOG_ERROR("Failed to cache store_mode config: {}", e.what())
|
||||
LOG_ERROR("Failed to cache store_mode config: {}", e.what());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace store::vstdlib {
|
||||
VIRTUAL(bool) SharedLicensesLockStatus(PARAMS(void* arg)) {
|
||||
LOG_DEBUG("{}(this={}, arg={})", __func__, THIS, arg)
|
||||
LOG_DEBUG("{}(this={}, arg={})", __func__, THIS, arg);
|
||||
ARGS();
|
||||
return true;
|
||||
}
|
||||
|
||||
VIRTUAL(bool) SharedLibraryStopPlaying(PARAMS(void* arg)) {
|
||||
LOG_DEBUG("{}(this={}, arg={})", __func__, THIS, arg)
|
||||
LOG_DEBUG("{}(this={}, arg={})", __func__, THIS, arg);
|
||||
ARGS();
|
||||
return true;
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace store::vstdlib {
|
||||
|
||||
if (data && data->get_callback_name()) {
|
||||
const auto name = String(data->get_callback_name());
|
||||
LOG_TRACE("{}(ecx={}, edx={}, name='{}')", __func__, ARGS(), name)
|
||||
LOG_TRACE("{}(ecx={}, edx={}, name='{}')", __func__, ARGS(), name);
|
||||
if (name == "SharedLicensesLockStatus" && !lock_status_hooked) {
|
||||
DETOUR_ADDRESS(SharedLicensesLockStatus, data->get_callback_data()->get_callback_address())
|
||||
lock_status_hooked = true;
|
||||
|
||||
Reference in New Issue
Block a user