2022-05-21 06:04:08 -04:00
|
|
|
#include <std_include.hpp>
|
|
|
|
|
|
|
|
#include "loader/component_loader.hpp"
|
2022-11-09 12:10:34 -05:00
|
|
|
#include "loader/loader.hpp"
|
2022-05-21 06:04:08 -04:00
|
|
|
|
|
|
|
#include <utils/finally.hpp>
|
|
|
|
#include <utils/hook.hpp>
|
|
|
|
#include <utils/nt.hpp>
|
2022-06-26 04:07:46 -04:00
|
|
|
#include <utils/io.hpp>
|
|
|
|
|
|
|
|
#include <steam/steam.hpp>
|
2022-05-21 06:04:08 -04:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
volatile bool g_call_tls_callbacks = false;
|
2022-06-26 04:07:46 -04:00
|
|
|
std::pair<void**, void*> g_original_import{};
|
|
|
|
|
2022-09-17 06:19:59 -04:00
|
|
|
DECLSPEC_NORETURN void WINAPI exit_hook(const uint32_t code)
|
2022-05-23 11:57:45 -04:00
|
|
|
{
|
|
|
|
component_loader::pre_destroy();
|
2022-09-17 06:19:59 -04:00
|
|
|
ExitProcess(code);
|
2022-05-23 11:57:45 -04:00
|
|
|
}
|
2022-05-21 06:04:08 -04:00
|
|
|
|
2022-06-26 04:07:46 -04:00
|
|
|
std::pair<void**, void*> patch_steam_import(const std::string& func, void* function)
|
2022-05-21 06:04:08 -04:00
|
|
|
{
|
2022-06-26 04:07:46 -04:00
|
|
|
static const utils::nt::library game{};
|
2022-05-23 11:57:45 -04:00
|
|
|
|
2022-06-26 04:07:46 -04:00
|
|
|
const auto game_entry = game.get_iat_entry("steam_api64.dll", func);
|
|
|
|
if (!game_entry)
|
2022-05-21 06:04:08 -04:00
|
|
|
{
|
2022-06-26 04:07:46 -04:00
|
|
|
throw std::runtime_error("Import '" + func + "' not found!");
|
|
|
|
}
|
2022-05-21 06:04:08 -04:00
|
|
|
|
2022-06-26 04:07:46 -04:00
|
|
|
const auto original_import = game_entry;
|
|
|
|
utils::hook::set(game_entry, function);
|
|
|
|
return {game_entry, original_import};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool restart_app_if_necessary_stub()
|
|
|
|
{
|
|
|
|
const std::string steam_path = steam::SteamAPI_GetSteamInstallPath();
|
|
|
|
if (steam_path.empty() || !::utils::io::file_exists(steam_path + "/steam.exe"))
|
|
|
|
{
|
|
|
|
MessageBoxA(nullptr, "Steam must be installed for the game to run. Please install Steam!", "Error",
|
|
|
|
MB_ICONERROR);
|
|
|
|
ShellExecuteA(nullptr, "open", "https://store.steampowered.com/about/", nullptr, nullptr, SW_SHOWNORMAL);
|
|
|
|
TerminateProcess(GetCurrentProcess(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
utils::hook::set(g_original_import.first, g_original_import.second);
|
|
|
|
patch_steam_import("SteamAPI_Shutdown", steam::SteamAPI_Shutdown);
|
|
|
|
|
|
|
|
component_loader::post_unpack();
|
|
|
|
return steam::SteamAPI_RestartAppIfNecessary();
|
|
|
|
}
|
|
|
|
|
|
|
|
void patch_imports()
|
|
|
|
{
|
|
|
|
patch_steam_import("SteamAPI_RegisterCallback", steam::SteamAPI_RegisterCallback);
|
|
|
|
patch_steam_import("SteamAPI_RegisterCallResult", steam::SteamAPI_RegisterCallResult);
|
|
|
|
patch_steam_import("SteamGameServer_Shutdown", steam::SteamGameServer_Shutdown);
|
|
|
|
patch_steam_import("SteamGameServer_RunCallbacks", steam::SteamGameServer_RunCallbacks);
|
|
|
|
patch_steam_import("SteamGameServer_GetHSteamPipe", steam::SteamGameServer_GetHSteamPipe);
|
|
|
|
patch_steam_import("SteamGameServer_GetHSteamUser", steam::SteamGameServer_GetHSteamUser);
|
|
|
|
patch_steam_import("SteamInternal_GameServer_Init", steam::SteamInternal_GameServer_Init);
|
|
|
|
patch_steam_import("SteamAPI_UnregisterCallResult", steam::SteamAPI_UnregisterCallResult);
|
|
|
|
patch_steam_import("SteamAPI_UnregisterCallback", steam::SteamAPI_UnregisterCallback);
|
|
|
|
patch_steam_import("SteamAPI_RunCallbacks", steam::SteamAPI_RunCallbacks);
|
|
|
|
patch_steam_import("SteamInternal_CreateInterface", steam::SteamInternal_CreateInterface);
|
|
|
|
patch_steam_import("SteamAPI_GetHSteamUser", steam::SteamAPI_GetHSteamUser);
|
|
|
|
patch_steam_import("SteamAPI_GetHSteamPipe", steam::SteamAPI_GetHSteamPipe);
|
|
|
|
patch_steam_import("SteamAPI_Init", steam::SteamAPI_Init);
|
|
|
|
//patch_steam_import("SteamAPI_Shutdown", steam::SteamAPI_Shutdown);
|
|
|
|
g_original_import = patch_steam_import("SteamAPI_RestartAppIfNecessary", restart_app_if_necessary_stub);
|
|
|
|
|
|
|
|
utils::hook::set(utils::nt::library{}.get_iat_entry("kernel32.dll", "ExitProcess"), exit_hook);
|
2022-05-23 11:57:45 -04:00
|
|
|
}
|
2022-05-21 06:04:08 -04:00
|
|
|
|
2022-09-18 02:48:12 -04:00
|
|
|
void remove_crash_file()
|
|
|
|
{
|
|
|
|
const utils::nt::library game{};
|
|
|
|
const auto game_file = game.get_path();
|
|
|
|
auto game_path = std::filesystem::path(game_file);
|
|
|
|
game_path.replace_extension(".start");
|
|
|
|
|
|
|
|
utils::io::remove_file(game_path.generic_string());
|
|
|
|
}
|
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
PIMAGE_TLS_CALLBACK* get_tls_callbacks()
|
2022-05-23 11:57:45 -04:00
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
const utils::nt::library game{};
|
|
|
|
const auto& entry = game.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
|
|
|
|
if (!entry.VirtualAddress || !entry.Size)
|
2022-05-23 11:57:45 -04:00
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
return nullptr;
|
2022-05-23 11:57:45 -04:00
|
|
|
}
|
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
const auto* tls_dir = reinterpret_cast<IMAGE_TLS_DIRECTORY*>(game.get_ptr() + entry.VirtualAddress);
|
|
|
|
return reinterpret_cast<PIMAGE_TLS_CALLBACK*>(tls_dir->AddressOfCallBacks);
|
2022-05-23 11:57:45 -04:00
|
|
|
}
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
void run_tls_callbacks(const DWORD reason)
|
2022-05-28 13:13:44 -04:00
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
if (!g_call_tls_callbacks)
|
2022-05-28 13:13:44 -04:00
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
return;
|
2022-05-28 13:13:44 -04:00
|
|
|
}
|
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
auto* callback = get_tls_callbacks();
|
|
|
|
while (callback && *callback)
|
2022-05-28 13:13:44 -04:00
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
(*callback)(GetModuleHandleA(nullptr), reason, nullptr);
|
|
|
|
++callback;
|
2022-05-28 13:13:44 -04:00
|
|
|
}
|
2022-11-09 12:10:34 -05:00
|
|
|
}
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
[[maybe_unused]] thread_local struct tls_runner
|
|
|
|
{
|
|
|
|
tls_runner()
|
2022-05-28 13:13:44 -04:00
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
run_tls_callbacks(DLL_THREAD_ATTACH);
|
2022-05-28 13:13:44 -04:00
|
|
|
}
|
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
~tls_runner()
|
2022-05-28 13:13:44 -04:00
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
run_tls_callbacks(DLL_THREAD_DETACH);
|
2022-05-28 13:13:44 -04:00
|
|
|
}
|
2022-11-09 12:10:34 -05:00
|
|
|
} tls_runner;
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
FARPROC load_process(const std::string& procname)
|
|
|
|
{
|
|
|
|
const auto proc = loader::load_binary(procname);
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
auto* const peb = reinterpret_cast<PPEB>(__readgsqword(0x60));
|
|
|
|
peb->Reserved3[1] = proc.get_ptr();
|
|
|
|
static_assert(offsetof(PEB, Reserved3[1]) == 0x10);
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
return FARPROC(proc.get_ptr() + proc.get_relative_entry_point());
|
2022-05-28 13:13:44 -04:00
|
|
|
}
|
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
int main()
|
2022-05-28 13:13:44 -04:00
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
FARPROC entry_point{};
|
|
|
|
srand(uint32_t(time(nullptr)) ^ ~(GetTickCount() * GetCurrentProcessId()));
|
2022-05-28 13:13:44 -04:00
|
|
|
|
|
|
|
{
|
2022-11-09 12:10:34 -05:00
|
|
|
auto premature_shutdown = true;
|
|
|
|
const auto _ = utils::finally([&premature_shutdown]()
|
|
|
|
{
|
|
|
|
if (premature_shutdown)
|
|
|
|
{
|
|
|
|
component_loader::pre_destroy();
|
|
|
|
}
|
|
|
|
});
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
try
|
|
|
|
{
|
|
|
|
remove_crash_file();
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 14:19:08 -05:00
|
|
|
if (!component_loader::pre_load())
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
entry_point = load_process("BlackOps3.exe");
|
|
|
|
if (!entry_point)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Unable to load binary into memory");
|
|
|
|
}
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
patch_imports();
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 14:19:08 -05:00
|
|
|
if (!component_loader::post_load())
|
2022-11-09 12:10:34 -05:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
2022-05-28 13:13:44 -04:00
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
premature_shutdown = false;
|
|
|
|
}
|
|
|
|
catch (std::exception& e)
|
|
|
|
{
|
2022-11-09 12:18:39 -05:00
|
|
|
MessageBoxA(GetForegroundWindow(), e.what(), "ERROR", MB_ICONERROR);
|
2022-11-09 12:10:34 -05:00
|
|
|
return 1;
|
|
|
|
}
|
2022-05-28 13:13:44 -04:00
|
|
|
}
|
2022-05-21 06:04:08 -04:00
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
g_call_tls_callbacks = true;
|
|
|
|
return static_cast<int>(entry_point());
|
2022-05-23 11:57:45 -04:00
|
|
|
}
|
2022-05-21 06:04:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-11-09 12:10:34 -05:00
|
|
|
int __stdcall WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
|
|
|
{
|
|
|
|
return main();
|
2022-05-21 06:04:08 -04:00
|
|
|
}
|