mirror of
https://github.com/acidicoala/SmokeAPI.git
synced 2026-01-30 16:32:51 -05:00
Reworked late hooking
This commit is contained in:
@@ -9,7 +9,8 @@ add_subdirectory(tools)
|
|||||||
|
|
||||||
set_32_and_64(STEAMAPI_DLL steam_api)
|
set_32_and_64(STEAMAPI_DLL steam_api)
|
||||||
set_32_and_64(STEAMCLIENT_DLL steamclient)
|
set_32_and_64(STEAMCLIENT_DLL steamclient)
|
||||||
set_32_and_64(STEAM_API_DLL steam_api.dll steam_api64.dll)
|
set_32_and_64(STEAM_API_DLL steam_api steam_api64)
|
||||||
|
set_32_and_64(SMOKEAPI_DLL SmokeAPI32 SmokeAPI64)
|
||||||
|
|
||||||
configure_build_config(extra_build_config)
|
configure_build_config(extra_build_config)
|
||||||
|
|
||||||
@@ -74,11 +75,11 @@ target_link_libraries(SmokeAPI_static PUBLIC SmokeAPI::common)
|
|||||||
|
|
||||||
add_library(SmokeAPI SHARED ${SMOKE_API_SOURCES})
|
add_library(SmokeAPI SHARED ${SMOKE_API_SOURCES})
|
||||||
target_link_libraries(SmokeAPI PUBLIC SmokeAPI::common)
|
target_link_libraries(SmokeAPI PUBLIC SmokeAPI::common)
|
||||||
set_target_properties(SmokeAPI PROPERTIES RUNTIME_OUTPUT_NAME ${STEAMAPI_DLL})
|
set_target_properties(SmokeAPI PROPERTIES RUNTIME_OUTPUT_NAME ${SMOKEAPI_DLL})
|
||||||
configure_version_resource(
|
configure_version_resource(
|
||||||
TARGET SmokeAPI
|
TARGET SmokeAPI
|
||||||
FILE_DESC "Steamworks DLC unlocker"
|
FILE_DESC "Steamworks DLC unlocker"
|
||||||
ORIG_NAME SmokeAPI
|
ORIG_NAME ${SMOKEAPI_DLL}
|
||||||
)
|
)
|
||||||
target_include_directories(SmokeAPI PRIVATE
|
target_include_directories(SmokeAPI PRIVATE
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src"
|
||||||
@@ -97,7 +98,7 @@ configure_linker_exports(
|
|||||||
HEADER_NAME "linker_exports_for_steam_api"
|
HEADER_NAME "linker_exports_for_steam_api"
|
||||||
FORWARDED_DLL "${STEAMAPI_DLL}_o"
|
FORWARDED_DLL "${STEAMAPI_DLL}_o"
|
||||||
INPUT_SOURCES_DIR ""
|
INPUT_SOURCES_DIR ""
|
||||||
DLL_FILES_GLOB "${CMAKE_CURRENT_SOURCE_DIR}/res/steamworks/*/binaries/${STEAM_API_DLL}"
|
DLL_FILES_GLOB "${CMAKE_CURRENT_SOURCE_DIR}/res/steamworks/*/binaries/${STEAM_API_DLL}.dll"
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_linker_exports(
|
configure_linker_exports(
|
||||||
|
|||||||
2
KoalaBox
2
KoalaBox
Submodule KoalaBox updated: 1fd92cf817...25d3d0a883
12
README.adoc
12
README.adoc
@@ -79,7 +79,9 @@ If it doesn't work, try installing it in proxy mode.
|
|||||||
=== 🪝 Hook mode
|
=== 🪝 Hook mode
|
||||||
|
|
||||||
. Download the latest SmokeAPI release zip from {smokeapi_release}.
|
. Download the latest SmokeAPI release zip from {smokeapi_release}.
|
||||||
. From SmokeAPI archive unpack `steam_api.dll` or `steam_api64.dll`, depending on the game bitness, rename it to `version.dll`, and place it next to the game exe file.
|
. From SmokeAPI archive unpack `SmokeAPI32.dll` / `SmokeAPI64.dll`, depending on the game bitness
|
||||||
|
. Rename the unpacked DLL to `version.dll`.
|
||||||
|
. Place `version.dll` next to the game's `.exe` file.
|
||||||
|
|
||||||
=== 🪝 Hook mode (Alternative installation)
|
=== 🪝 Hook mode (Alternative installation)
|
||||||
|
|
||||||
@@ -92,7 +94,8 @@ For example, assuming that the game loads `winmm.dll`:
|
|||||||
. Download the latest Koaloader release zip from https://github.com/acidicoala/Koaloader/releases/latest[Koaloader Releases].
|
. Download the latest Koaloader release zip from https://github.com/acidicoala/Koaloader/releases/latest[Koaloader Releases].
|
||||||
. From Koaloader archive unpack `winmm.dll` from `winmm-32` or `winmm-64`, depending on the game bitness, and place it next to the game exe file.
|
. From Koaloader archive unpack `winmm.dll` from `winmm-32` or `winmm-64`, depending on the game bitness, and place it next to the game exe file.
|
||||||
. Download the latest SmokeAPI release zip from {smokeapi_release}.
|
. Download the latest SmokeAPI release zip from {smokeapi_release}.
|
||||||
. From SmokeAPI archive unpack `steam_api.dll` or `steam_api64.dll`, depending on the game bitness, rename it to `SmokeAPI.dll`, and place it next to the game exe file.
|
. From SmokeAPI archive unpack `SmokeAPI32.dll` / `SmokeAPI64.dll`, depending on the game bitness.
|
||||||
|
. Place the unpacked DLL next to the game's `exe` file.
|
||||||
|
|
||||||
[[special_k_note]]
|
[[special_k_note]]
|
||||||
IMPORTANT: There are games which have extra protections that break hook mode.
|
IMPORTANT: There are games which have extra protections that break hook mode.
|
||||||
@@ -102,12 +105,13 @@ In such cases, it might be worth trying {special_k}, which can inject SmokeAPI a
|
|||||||
|
|
||||||
. Find `steam_api.dll` / `steam_api64.dll` file in game directory, and rename it to `steam_api_o.dll` / `steam_api64_o.dll`.
|
. Find `steam_api.dll` / `steam_api64.dll` file in game directory, and rename it to `steam_api_o.dll` / `steam_api64_o.dll`.
|
||||||
. Download the latest SmokeAPI release zip from {smokeapi_release}.
|
. Download the latest SmokeAPI release zip from {smokeapi_release}.
|
||||||
. From SmokeAPI archive unpack `steam_api.dll`/`steam_api64.dll`, depending on the game bitness, and place it next to the original steam_api DLL file.
|
. From SmokeAPI archive unpack `SmokeAPI32.dll` / `SmokeAPI64.dll`, depending on the game bitness.
|
||||||
|
. Rename the unpacked DLL to `steam_api.dll` / `steam_api64.dll` and place it next to the `steam_api_o.dll` / `steam_api64_o.dll` file.
|
||||||
|
|
||||||
IMPORTANT: There are games which have extra protections that break proxy mode.
|
IMPORTANT: There are games which have extra protections that break proxy mode.
|
||||||
In such cases, see the note on <<special_k_note, Hook mode with Special K>>
|
In such cases, see the note on <<special_k_note, Hook mode with Special K>>
|
||||||
|
|
||||||
---
|
'''
|
||||||
|
|
||||||
If the unlocker is not working as expected, then please fully read the https://gist.github.com/acidicoala/2c131cb90e251f97c0c1dbeaf2c174dc[Generic Unlocker Installation Instructions] before seeking support in the {forum-topic}.
|
If the unlocker is not working as expected, then please fully read the https://gist.github.com/acidicoala/2c131cb90e251f97c0c1dbeaf2c174dc[Generic Unlocker Installation Instructions] before seeking support in the {forum-topic}.
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#include <regex>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <koalabox/config.hpp>
|
#include <koalabox/config.hpp>
|
||||||
#include <koalabox/dll_monitor.hpp>
|
#include <koalabox/dll_monitor.hpp>
|
||||||
#include <koalabox/globals.hpp>
|
#include <koalabox/globals.hpp>
|
||||||
@@ -9,11 +12,13 @@
|
|||||||
#include <koalabox/util.hpp>
|
#include <koalabox/util.hpp>
|
||||||
#include <koalabox/win.hpp>
|
#include <koalabox/win.hpp>
|
||||||
|
|
||||||
#include "build_config.h"
|
|
||||||
|
|
||||||
#include "smoke_api.hpp"
|
#include "smoke_api.hpp"
|
||||||
|
|
||||||
#include "smoke_api/config.hpp"
|
#include "smoke_api/config.hpp"
|
||||||
#include "smoke_api/steamclient/steamclient.hpp"
|
#include "smoke_api/steamclient/steamclient.hpp"
|
||||||
|
#include "steam_api/steam_interfaces.hpp"
|
||||||
|
|
||||||
|
#include "build_config.h"
|
||||||
|
|
||||||
// Hooking steam_api has shown itself to be less desirable than steamclient
|
// Hooking steam_api has shown itself to be less desirable than steamclient
|
||||||
// for the reasons outlined below:
|
// for the reasons outlined below:
|
||||||
@@ -38,11 +43,59 @@ namespace {
|
|||||||
|
|
||||||
HMODULE original_steamapi_handle = nullptr;
|
HMODULE original_steamapi_handle = nullptr;
|
||||||
|
|
||||||
|
std::set<std::string> find_steamclient_versions(const HMODULE steamapi_handle) noexcept {
|
||||||
|
try {
|
||||||
|
std::set<std::string> versions;
|
||||||
|
const auto rdata = kb::win::get_pe_section_or_throw(steamapi_handle, ".rdata").to_string();
|
||||||
|
|
||||||
|
const std::regex pattern(R"(SteamClient\d{3})");
|
||||||
|
auto matches_begin = std::sregex_iterator(rdata.begin(), rdata.end(), pattern);
|
||||||
|
auto matches_end = std::sregex_iterator();
|
||||||
|
|
||||||
|
for(std::sregex_iterator i = matches_begin; i != matches_end; ++i) {
|
||||||
|
versions.insert(i->str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions;
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
LOG_ERROR("{} -> insert error: {}", __func__, e.what());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once CppDFAConstantFunctionResult
|
||||||
|
bool on_steamclient_loaded(const HMODULE steamclient_handle) noexcept {
|
||||||
|
auto* const steamapi_handle = original_steamapi_handle
|
||||||
|
? original_steamapi_handle
|
||||||
|
: GetModuleHandle(TEXT(STEAMAPI_DLL));
|
||||||
|
if(!steamapi_handle) {
|
||||||
|
LOG_ERROR("{} -> {} is not loaded", __func__, STEAMAPI_DLL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const auto CreateInterface$ = KB_WIN_GET_PROC(steamclient_handle, CreateInterface);
|
||||||
|
|
||||||
|
const auto steamclient_versions = find_steamclient_versions(steamapi_handle);
|
||||||
|
for(const auto& steamclient_version : steamclient_versions) {
|
||||||
|
if(CreateInterface$(steamclient_version.c_str(), nullptr)) {
|
||||||
|
LOG_WARN("'{}' was already initialized. SmokeAPI might not work as expected.", steamclient_version);
|
||||||
|
LOG_WARN("Probable cause: SmokeAPI was injected too late. If possible, try injecting it earlier.");
|
||||||
|
|
||||||
|
steam_interfaces::hook_steamclient_interface(steamclient_handle, steamclient_version);
|
||||||
|
} else {
|
||||||
|
LOG_INFO("'{}' is not initialized. Waiting for initialization.", steamclient_version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KB_HOOK_DETOUR_MODULE(CreateInterface, steamclient_handle);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void start_dll_listener() {
|
void start_dll_listener() {
|
||||||
kb::dll_monitor::init_listener(
|
kb::dll_monitor::init_listener(
|
||||||
{STEAMCLIENT_DLL},
|
{
|
||||||
[&](const HMODULE& module_handle, auto&) {
|
{STEAMCLIENT_DLL, on_steamclient_loaded}
|
||||||
KB_HOOK_DETOUR_MODULE(CreateInterface, module_handle);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -82,6 +135,7 @@ namespace smoke_api {
|
|||||||
self_path,
|
self_path,
|
||||||
STEAMAPI_DLL
|
STEAMAPI_DLL
|
||||||
);
|
);
|
||||||
|
|
||||||
start_dll_listener();
|
start_dll_listener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <koalabox/logger.hpp>
|
#include <koalabox/logger.hpp>
|
||||||
|
|
||||||
#include "steam_api/steam_interfaces.hpp"
|
|
||||||
#include "steam_client.hpp"
|
#include "steam_client.hpp"
|
||||||
|
#include "steam_api/steam_interfaces.hpp"
|
||||||
|
|
||||||
namespace steam_client {
|
namespace steam_client {
|
||||||
void* GetGenericInterface(
|
void* GetGenericInterface(
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "steam_api/steam_interfaces.hpp"
|
#include "steam_api/steam_interfaces.hpp"
|
||||||
#include "smoke_api/smoke_api.hpp"
|
#include "smoke_api/smoke_api.hpp"
|
||||||
|
#include "smoke_api/steamclient/steamclient.hpp"
|
||||||
#include "virtuals/steam_api_virtuals.hpp"
|
#include "virtuals/steam_api_virtuals.hpp"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -18,14 +19,15 @@ namespace {
|
|||||||
void* function_address; // e.g. ISteamClient_GetISteamApps
|
void* function_address; // e.g. ISteamClient_GetISteamApps
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Split fallback into low and high versions
|
struct interface_data_t { // NOLINT(*-exception-escape)
|
||||||
struct interface_data { // NOLINT(*-exception-escape)
|
|
||||||
std::string fallback_version; // e.g. "SteamClient021"
|
std::string fallback_version; // e.g. "SteamClient021"
|
||||||
|
// Key is function name without interface prefix
|
||||||
std::map<std::string, interface_entry> entry_map;
|
std::map<std::string, interface_entry> entry_map;
|
||||||
// e.g. {ENTRY(ISteamClient, GetISteamApps), ...}
|
// e.g. {ENTRY(ISteamClient, GetISteamApps), ...}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::map<std::string, interface_data> get_virtual_hook_map() {
|
// Key is interface name, e.g. "SteamClient"
|
||||||
|
std::map<std::string, interface_data_t> get_virtual_hook_map() {
|
||||||
#define ENTRY(INTERFACE, FUNC) \
|
#define ENTRY(INTERFACE, FUNC) \
|
||||||
{ \
|
{ \
|
||||||
#FUNC, { \
|
#FUNC, { \
|
||||||
@@ -36,7 +38,7 @@ namespace {
|
|||||||
return {
|
return {
|
||||||
{
|
{
|
||||||
STEAM_APPS,
|
STEAM_APPS,
|
||||||
interface_data{
|
interface_data_t{
|
||||||
.fallback_version = "STEAMAPPS_INTERFACE_VERSION008",
|
.fallback_version = "STEAMAPPS_INTERFACE_VERSION008",
|
||||||
.entry_map = {
|
.entry_map = {
|
||||||
ENTRY(ISteamApps, BIsSubscribedApp),
|
ENTRY(ISteamApps, BIsSubscribedApp),
|
||||||
@@ -48,7 +50,7 @@ namespace {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
STEAM_CLIENT,
|
STEAM_CLIENT,
|
||||||
interface_data{
|
interface_data_t{
|
||||||
.fallback_version = "SteamClient021",
|
.fallback_version = "SteamClient021",
|
||||||
.entry_map = {
|
.entry_map = {
|
||||||
ENTRY(ISteamClient, GetISteamApps),
|
ENTRY(ISteamClient, GetISteamApps),
|
||||||
@@ -60,7 +62,7 @@ namespace {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
STEAM_GAME_SERVER,
|
STEAM_GAME_SERVER,
|
||||||
interface_data{
|
interface_data_t{
|
||||||
.fallback_version = "SteamGameServer015",
|
.fallback_version = "SteamGameServer015",
|
||||||
.entry_map = {
|
.entry_map = {
|
||||||
ENTRY(ISteamGameServer, UserHasLicenseForApp),
|
ENTRY(ISteamGameServer, UserHasLicenseForApp),
|
||||||
@@ -69,7 +71,7 @@ namespace {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
STEAM_HTTP,
|
STEAM_HTTP,
|
||||||
interface_data{
|
interface_data_t{
|
||||||
.fallback_version = "STEAMHTTP_INTERFACE_VERSION003",
|
.fallback_version = "STEAMHTTP_INTERFACE_VERSION003",
|
||||||
.entry_map = {
|
.entry_map = {
|
||||||
ENTRY(ISteamHTTP, GetHTTPResponseBodyData),
|
ENTRY(ISteamHTTP, GetHTTPResponseBodyData),
|
||||||
@@ -80,7 +82,7 @@ namespace {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
STEAM_INVENTORY,
|
STEAM_INVENTORY,
|
||||||
interface_data{
|
interface_data_t{
|
||||||
.fallback_version = "STEAMINVENTORY_INTERFACE_V003",
|
.fallback_version = "STEAMINVENTORY_INTERFACE_V003",
|
||||||
.entry_map = {
|
.entry_map = {
|
||||||
ENTRY(ISteamInventory, GetResultStatus),
|
ENTRY(ISteamInventory, GetResultStatus),
|
||||||
@@ -95,7 +97,7 @@ namespace {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
STEAM_USER,
|
STEAM_USER,
|
||||||
interface_data{
|
interface_data_t{
|
||||||
.fallback_version = "SteamUser023",
|
.fallback_version = "SteamUser023",
|
||||||
.entry_map = {
|
.entry_map = {
|
||||||
ENTRY(ISteamUser, UserHasLicenseForApp),
|
ENTRY(ISteamUser, UserHasLicenseForApp),
|
||||||
@@ -105,8 +107,14 @@ namespace {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto read_interface_lookup() {
|
// Key is function name, Value is ordinal
|
||||||
std::map<std::string, std::map<std::string, uint16_t>> lookup_map;
|
using ordinal_map_t = std::map<std::string, uint16_t>;
|
||||||
|
|
||||||
|
// Key is interface version string
|
||||||
|
using lookup_map_t = std::map<std::string, ordinal_map_t>;
|
||||||
|
|
||||||
|
lookup_map_t read_interface_lookup() {
|
||||||
|
lookup_map_t lookup_map;
|
||||||
|
|
||||||
const auto lookup_str = b::embed<"res/interface_lookup.json">().str();
|
const auto lookup_str = b::embed<"res/interface_lookup.json">().str();
|
||||||
const auto lookup_json = nlohmann::json::parse(lookup_str);
|
const auto lookup_json = nlohmann::json::parse(lookup_str);
|
||||||
@@ -140,9 +148,9 @@ namespace steam_interfaces {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param interface_ptr Pointer to the interface
|
* @param interface_ptr Pointer to the interface
|
||||||
* @param version_string Example: 'SteamClient020'
|
* @param version_string Example: 'SteamClient007'
|
||||||
*/
|
*/
|
||||||
void hook_virtuals(void* interface_ptr, const std::string& version_string) {
|
void hook_virtuals(const void* interface_ptr, const std::string& version_string) {
|
||||||
if(interface_ptr == nullptr) {
|
if(interface_ptr == nullptr) {
|
||||||
// Game has tried to use an interface before initializing steam api
|
// Game has tried to use an interface before initializing steam api
|
||||||
// This does happen in practice.
|
// This does happen in practice.
|
||||||
@@ -152,14 +160,10 @@ namespace steam_interfaces {
|
|||||||
static std::mutex section;
|
static std::mutex section;
|
||||||
const std::lock_guard guard(section);
|
const std::lock_guard guard(section);
|
||||||
|
|
||||||
static std::set<void*> processed_interfaces;
|
static std::set<const void*> processed_interfaces;
|
||||||
|
|
||||||
if(processed_interfaces.contains(interface_ptr)) {
|
if(processed_interfaces.contains(interface_ptr)) {
|
||||||
LOG_DEBUG(
|
LOG_DEBUG("Interface '{}' @ {} has already been processed.", version_string, interface_ptr);
|
||||||
"Interface '{}' @ {} has already been processed.",
|
|
||||||
version_string,
|
|
||||||
interface_ptr
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
processed_interfaces.insert(interface_ptr);
|
processed_interfaces.insert(interface_ptr);
|
||||||
@@ -170,11 +174,7 @@ namespace steam_interfaces {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(
|
LOG_INFO("Processing '{}' @ {} found in virtual hook map", version_string, interface_ptr);
|
||||||
"Processing '{}' @ {} found in virtual hook map",
|
|
||||||
version_string,
|
|
||||||
interface_ptr
|
|
||||||
);
|
|
||||||
|
|
||||||
const auto& lookup = find_lookup(version_string, data.fallback_version);
|
const auto& lookup = find_lookup(version_string, data.fallback_version);
|
||||||
|
|
||||||
@@ -194,4 +194,63 @@ namespace steam_interfaces {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hook_steamclient_interface(
|
||||||
|
const HMODULE steamclient_handle,
|
||||||
|
const std::string& steam_client_interface_version
|
||||||
|
) noexcept {
|
||||||
|
try {
|
||||||
|
// Create a copy for modification
|
||||||
|
auto virtual_hook_map = get_virtual_hook_map();
|
||||||
|
|
||||||
|
// Remove steam client map since we don't want to hook its methods
|
||||||
|
virtual_hook_map.erase(STEAM_CLIENT);
|
||||||
|
|
||||||
|
// Map remaining virtual hook map to a set of keys
|
||||||
|
const auto prefixes = std::views::keys(virtual_hook_map) | std::ranges::to<std::set>();
|
||||||
|
|
||||||
|
// Prepare HSteamPipe and HSteamUser
|
||||||
|
const auto CreateInterface$ = KB_WIN_GET_PROC(steamclient_handle, CreateInterface);
|
||||||
|
const auto* const THIS = CreateInterface$(
|
||||||
|
steam_client_interface_version.c_str(), nullptr
|
||||||
|
);
|
||||||
|
hook_virtuals(THIS, steam_client_interface_version);
|
||||||
|
|
||||||
|
const auto interface_lookup = read_interface_lookup();
|
||||||
|
for(const auto& interface_version : interface_lookup | std::views::keys) {
|
||||||
|
// SteamUser and SteamPipe handles must match the ones previously used by the game,
|
||||||
|
// otherwise SteamAPI will just create new instances of interfaces, instead of returning
|
||||||
|
// existing instances that are used by the game. Usually these handles default to 1,
|
||||||
|
// but if a game creates several of them, then we need to somehow find them out dynamically.
|
||||||
|
constexpr auto steam_pipe = 1;
|
||||||
|
constexpr auto steam_user = 1;
|
||||||
|
|
||||||
|
const bool should_hook = std::ranges::any_of(
|
||||||
|
prefixes,
|
||||||
|
[&](const auto& prefix) {
|
||||||
|
return std::ranges::starts_with(interface_version, prefix);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if(not should_hook) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* const interface_ptr = ISteamClient_GetISteamGenericInterface(
|
||||||
|
ARGS(steam_user, steam_pipe, interface_version.c_str())
|
||||||
|
);
|
||||||
|
|
||||||
|
if(not interface_ptr) {
|
||||||
|
LOG_ERROR("Failed to get generic interface: '{}'", interface_version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISteamClient_ReleaseUser(ARGS(steam_pipe, steam_user));
|
||||||
|
// ISteamClient_BReleaseSteamPipe(ARGS(steam_pipe));
|
||||||
|
|
||||||
|
kb::hook::unhook_vt_all(THIS);
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
LOG_ERROR("{} -> Unhandled exception: {}", __func__, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,16 @@
|
|||||||
#include "smoke_api/types.hpp"
|
#include "smoke_api/types.hpp"
|
||||||
|
|
||||||
namespace steam_interfaces {
|
namespace steam_interfaces {
|
||||||
|
void hook_virtuals(const void* interface_ptr, const std::string& version_string);
|
||||||
|
|
||||||
void hook_virtuals(void* interface_ptr, const std::string& version_string);
|
/**
|
||||||
|
* A fallback mechanism used when SteamAPI has already been initialized.
|
||||||
|
* It will hook the SteamClient interface and hook its interface accessors.
|
||||||
|
* This allows us to hook interfaces that are no longer being created,
|
||||||
|
* such as in the case of late injection.
|
||||||
|
*/
|
||||||
|
void hook_steamclient_interface(
|
||||||
|
HMODULE steamclient_handle,
|
||||||
|
const std::string& steam_client_interface_version
|
||||||
|
) noexcept;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ VIRTUAL(void*) ISteamClient_GetISteamApps(
|
|||||||
PARAMS(
|
PARAMS(
|
||||||
const HSteamUser hSteamUser,
|
const HSteamUser hSteamUser,
|
||||||
const HSteamPipe hSteamPipe,
|
const HSteamPipe hSteamPipe,
|
||||||
const char* version
|
const char* pchVersion
|
||||||
)
|
)
|
||||||
) noexcept {
|
) noexcept {
|
||||||
return steam_client::GetGenericInterface(
|
return steam_client::GetGenericInterface(
|
||||||
__func__,
|
__func__,
|
||||||
version,
|
pchVersion,
|
||||||
SWAPPED_CALL_CLOSURE(ISteamClient_GetISteamApps, ARGS(hSteamUser, hSteamPipe, version))
|
SWAPPED_CALL_CLOSURE(ISteamClient_GetISteamApps, ARGS(hSteamUser, hSteamPipe, pchVersion))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,13 +21,13 @@ VIRTUAL(void*) ISteamClient_GetISteamUser(
|
|||||||
PARAMS(
|
PARAMS(
|
||||||
const HSteamUser hSteamUser,
|
const HSteamUser hSteamUser,
|
||||||
const HSteamPipe hSteamPipe,
|
const HSteamPipe hSteamPipe,
|
||||||
const char* version
|
const char* pchVersion
|
||||||
)
|
)
|
||||||
) noexcept {
|
) noexcept {
|
||||||
return steam_client::GetGenericInterface(
|
return steam_client::GetGenericInterface(
|
||||||
__func__,
|
__func__,
|
||||||
version,
|
pchVersion,
|
||||||
SWAPPED_CALL_CLOSURE(ISteamClient_GetISteamUser, ARGS(hSteamUser, hSteamPipe, version))
|
SWAPPED_CALL_CLOSURE(ISteamClient_GetISteamUser, ARGS(hSteamUser, hSteamPipe, pchVersion))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ VIRTUAL(void*) ISteamClient_GetISteamUser(PARAMS(HSteamUser, HSteamPipe, const c
|
|||||||
VIRTUAL(void*) ISteamClient_GetISteamGenericInterface(
|
VIRTUAL(void*) ISteamClient_GetISteamGenericInterface(
|
||||||
PARAMS(HSteamUser, HSteamPipe, const char*)
|
PARAMS(HSteamUser, HSteamPipe, const char*)
|
||||||
|
|
||||||
|
|
||||||
) noexcept;
|
) noexcept;
|
||||||
VIRTUAL(void*) ISteamClient_GetISteamInventory(
|
VIRTUAL(void*) ISteamClient_GetISteamInventory(
|
||||||
PARAMS(HSteamUser, HSteamPipe, const char*)
|
PARAMS(HSteamUser, HSteamPipe, const char*)
|
||||||
|
|
||||||
|
|
||||||
) noexcept;
|
) noexcept;
|
||||||
|
|
||||||
// ISteamHTTP
|
// ISteamHTTP
|
||||||
@@ -47,6 +49,7 @@ VIRTUAL(bool) ISteamInventory_GetItemsByID(
|
|||||||
VIRTUAL(bool) ISteamInventory_SerializeResult(
|
VIRTUAL(bool) ISteamInventory_SerializeResult(
|
||||||
PARAMS(SteamInventoryResult_t, void*, uint32_t*)
|
PARAMS(SteamInventoryResult_t, void*, uint32_t*)
|
||||||
|
|
||||||
|
|
||||||
) noexcept;
|
) noexcept;
|
||||||
VIRTUAL(bool) ISteamInventory_GetItemDefinitionIDs(PARAMS(SteamItemDef_t*, uint32_t*)) noexcept;
|
VIRTUAL(bool) ISteamInventory_GetItemDefinitionIDs(PARAMS(SteamItemDef_t*, uint32_t*)) noexcept;
|
||||||
VIRTUAL(bool) ISteamInventory_CheckResultSteamID(PARAMS(SteamInventoryResult_t, CSteamID)) noexcept;
|
VIRTUAL(bool) ISteamInventory_CheckResultSteamID(PARAMS(SteamInventoryResult_t, CSteamID)) noexcept;
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <koalabox/hook.hpp>
|
#include <koalabox/hook.hpp>
|
||||||
|
#include <koalabox/logger.hpp>
|
||||||
|
|
||||||
#include "smoke_api/steamclient/steamclient.hpp"
|
#include "smoke_api/steamclient/steamclient.hpp"
|
||||||
|
|
||||||
#include "smoke_api/types.hpp"
|
#include "smoke_api/types.hpp"
|
||||||
|
|
||||||
#include "steam_api/steam_client.hpp"
|
#include "steam_api/steam_client.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SmokeAPI implementation
|
* SmokeAPI implementation
|
||||||
*/
|
*/
|
||||||
C_DECL(void*) CreateInterface(const char* interface_version, int* out_result) {
|
C_DECL(void*) CreateInterface(const char* interface_version, create_interface_result* out_result) {
|
||||||
|
// Mutex here helps us detect unintended recursion early on by throwing an exception.
|
||||||
|
static std::mutex section;
|
||||||
|
const std::lock_guard lock(section);
|
||||||
|
|
||||||
return steam_client::GetGenericInterface(
|
return steam_client::GetGenericInterface(
|
||||||
__func__,
|
__func__,
|
||||||
interface_version,
|
interface_version,
|
||||||
|
|||||||
@@ -2,4 +2,13 @@
|
|||||||
|
|
||||||
#include "smoke_api/types.hpp"
|
#include "smoke_api/types.hpp"
|
||||||
|
|
||||||
C_DECL(void*) CreateInterface(const char* interface_version, int* out_result);
|
enum class create_interface_result {
|
||||||
|
Success = 0,
|
||||||
|
Error = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param interface_version Example: STEAMAPPS_INTERFACE_VERSION008
|
||||||
|
* @param out_result Pointer to the result enum that will be written to. Can be nullptr.
|
||||||
|
*/
|
||||||
|
C_DECL(void*) CreateInterface(const char* interface_version, create_interface_result* out_result);
|
||||||
|
|||||||
Reference in New Issue
Block a user