From 5cccad0e6751126f4c4dddcd2e155b791e852457 Mon Sep 17 00:00:00 2001 From: otavepto <153766569+otavepto@users.noreply.github.com> Date: Sat, 29 Jun 2024 06:47:13 +0300 Subject: [PATCH] rewrite cold client loader to use simpleini lib + use multibyte std::string + new logger --- .../win/ColdClientLoader.cpp | 512 +++++++++--------- 1 file changed, 262 insertions(+), 250 deletions(-) diff --git a/tools/steamclient_loader/win/ColdClientLoader.cpp b/tools/steamclient_loader/win/ColdClientLoader.cpp index 1722e22b..84951230 100644 --- a/tools/steamclient_loader/win/ColdClientLoader.cpp +++ b/tools/steamclient_loader/win/ColdClientLoader.cpp @@ -5,6 +5,12 @@ #include "pe_helpers/pe_helpers.hpp" #include "dbg_log/dbg_log.hpp" +#define SI_CONVERT_GENERIC +#define SI_SUPPORT_IOSTREAMS +#define SI_NO_MBCS +#include "simpleini/SimpleIni.h" +#include "utfcpp/utf8.h" + // C RunTime Header Files #include #include @@ -12,56 +18,58 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include +#include -static std::wstring IniFile{}; -static const std::wstring dbg_file = pe_helpers::get_current_exe_path_w() + pe_helpers::get_current_exe_name_w() + L".log"; -constexpr static const char STEAM_UNIVERSE[] = "Public"; -constexpr static const char STEAM_URL_PROTOCOL[] = "URL:steam protocol"; +static CSimpleIniA local_ini{true}; +static dbg_log logger{pe_helpers::get_current_exe_path() + pe_helpers::get_current_exe_name() + ".log"}; +constexpr static const wchar_t STEAM_UNIVERSE[] = L"Public"; +constexpr static const wchar_t STEAM_URL_PROTOCOL[] = L"URL:steam protocol"; const bool loader_is_32 = pe_helpers::is_module_32(GetModuleHandleW(NULL)); -const std::wstring hklm_path(loader_is_32 ? L"SOFTWARE\\Valve\\Steam" : L"SOFTWARE\\WOW6432Node\\Valve\\Steam"); +const std::wstring hklm_path(loader_is_32 + ? L"SOFTWARE\\Valve\\Steam" + : L"SOFTWARE\\WOW6432Node\\Valve\\Steam" +); // Declare some variables to be used for Steam registry. const DWORD UserId = 0x03100004771F810D & 0xffffffff; const DWORD ProcessID = GetCurrentProcessId(); -std::wstring ClientPath{}; -std::wstring Client64Path{}; +static std::string IniFile{}; +static std::string ClientPath{}; +static std::string Client64Path{}; +static std::string ExeFile{}; +static std::string ExeRunDir{}; +static std::string ExeCommandLine{}; +static std::string AppId{}; +static std::string DllsToInjectFolder{}; -std::vector dlls_to_inject{}; +static bool ForceInjectSteamClient{}; +static bool ForceInjectGameOverlayRenderer{}; +static bool IgnoreInjectionError{}; +static bool IgnoreLoaderArchDifference{}; +static bool ResumeByDebugger{}; + +static long PersistentMode{}; + +static std::vector exe_header{}; +static std::vector dlls_to_inject{}; - - -std::wstring get_ini_value(LPCWSTR section, LPCWSTR key, LPCWSTR default_val = NULL) -{ - std::vector buff(INT16_MAX); - DWORD read_chars = GetPrivateProfileStringW(section, key, default_val, &buff[0], (DWORD)buff.size(), IniFile.c_str()); - if (!read_chars) { - return {}; - } - - // "If neither lpAppName nor lpKeyName is NULL and the supplied destination buffer is too small to hold the requested string, the return value is equal to nSize minus one" - int trials = 3; - while ((read_chars == (buff.size() - 1)) && trials > 0) { - buff.resize(buff.size() * 2); - read_chars = GetPrivateProfileStringW(section, key, default_val, &buff[0], (DWORD)buff.size(), IniFile.c_str()); - --trials; - } - - return std::wstring(&buff[0], read_chars); -} - -static std::vector get_pe_header(const std::wstring &filepath) +static std::vector get_pe_header(const std::string &filepath) { try { - std::ifstream file(std::filesystem::path(filepath), std::ios::binary); + std::ifstream file(std::filesystem::u8path(filepath), std::ios::in | std::ios::binary); if (!file.is_open()) throw; file.seekg(0, std::ios::beg); @@ -73,64 +81,65 @@ static std::vector get_pe_header(const std::wstring &filepath) return data; } catch(const std::exception& e) { - dbg_log::write(std::string("Error reading PE header: ") + e.what()); + logger.write(std::string("Error reading PE header: ") + e.what()); return std::vector(); } } -static std::vector collect_dlls_to_inject(const std::wstring &extra_dlls_folder, bool is_exe_32, std::wstring &failed_dlls) +static std::vector collect_dlls_to_inject(const bool is_exe_32, std::string &failed_dlls) { - const auto load_order_file = std::filesystem::path(extra_dlls_folder) / "load_order.txt"; - std::vector dlls_to_inject{}; - for (auto const& dir_entry : - std::filesystem::recursive_directory_iterator(extra_dlls_folder, std::filesystem::directory_options::follow_directory_symlink)) { + const auto load_order_file = std::filesystem::u8path(DllsToInjectFolder) / "load_order.txt"; + std::vector dlls_to_inject{}; + for (const auto &dir_entry : + std::filesystem::recursive_directory_iterator(DllsToInjectFolder, std::filesystem::directory_options::follow_directory_symlink) + ) { if (std::filesystem::is_directory(dir_entry.path())) continue; - auto dll_path = dir_entry.path().wstring(); + auto dll_path = dir_entry.path().u8string(); // ignore this file if it is the load order file - if (common_helpers::str_cmp_insensitive(dll_path, load_order_file.wstring())) continue; + if (common_helpers::str_cmp_insensitive(dll_path, load_order_file.u8string())) continue; auto dll_header = get_pe_header(dll_path); if (dll_header.empty()) { - dbg_log::write(L"Failed to get PE header of dll: '" + dll_path + L"'"); - failed_dlls += dll_path + L"\n"; + logger.write("Failed to get PE header of dll: '" + dll_path + "'"); + failed_dlls += dll_path + "\n"; continue; } - bool is_dll_32 = pe_helpers::is_module_32((HMODULE)&dll_header[0]); - bool is_dll_64 = pe_helpers::is_module_64((HMODULE)&dll_header[0]); - if ((!is_dll_32 && !is_dll_64) || (is_dll_32 && is_dll_64)) { // ARM, or just a regular file - dbg_log::write(L"Dll '" + dll_path + L"' is neither 32 nor 64 bit and will be ignored"); - failed_dlls += dll_path + L"\n"; + const bool is_dll_32 = pe_helpers::is_module_32((HMODULE)&dll_header[0]); + const bool is_dll_64 = pe_helpers::is_module_64((HMODULE)&dll_header[0]); + if (is_dll_32 == is_dll_64) { // ARM, or just a regular file + logger.write("Dll '" + dll_path + "' is neither 32 nor 64 bit and will be ignored"); + failed_dlls += dll_path + "\n"; continue; } - if (is_dll_32 == is_exe_32) { // same arch + if (is_exe_32 == is_dll_32) { // same arch dlls_to_inject.push_back(dll_path); - dbg_log::write(L"Dll '" + dll_path + L"' is valid"); + logger.write("Dll '" + dll_path + "' is valid"); } else { - dbg_log::write(L"Dll '" + dll_path + L"' has a different arch than the exe and will be ignored"); - failed_dlls += dll_path + L"\n"; + logger.write("Dll '" + dll_path + "' has a different arch than the exe and will be ignored"); + failed_dlls += dll_path + "\n"; } } - bool only_specified = false; - std::vector ordered_dlls_to_inject{}; + bool load_only_specified_dlls = false; + std::vector ordered_dlls_to_inject{}; { - dbg_log::write(L"Searching for load order file: '" + load_order_file.wstring() + L"'"); - auto f_order = std::wifstream(load_order_file, std::ios::in); + logger.write("Searching for load order file: '" + load_order_file.u8string() + "'"); + auto f_order = std::ifstream(load_order_file, std::ios::in); if (f_order.is_open()) { - only_specified = true; - dbg_log::write(L"Reading load order file..."); - std::wstring line{}; + load_only_specified_dlls = true; + logger.write(L"Reading load order file..."); + std::string line{}; while (std::getline(f_order, line)) { - auto abs = common_helpers::to_absolute(line, extra_dlls_folder); - dbg_log::write(L"Load order line: '" + abs + L"'"); - auto it = std::find_if(dlls_to_inject.begin(), dlls_to_inject.end(), [&abs](const std::wstring &dll_to_inject) { - return common_helpers::str_cmp_insensitive(dll_to_inject, abs); + auto abs = common_helpers::to_absolute(line, DllsToInjectFolder); + logger.write("Load order line: '" + abs + "'"); + auto it = std::find_if(dlls_to_inject.begin(), dlls_to_inject.end(), [&abs](const std::string &dll_to_inject) { + return common_helpers::str_cmp_insensitive(dll_to_inject, abs); }); - if (it != dlls_to_inject.end()) { - dbg_log::write("Found the dll specified by the load order line"); + if (dlls_to_inject.end() != it) { + logger.write("Found the dll specified by the load order line"); ordered_dlls_to_inject.push_back(*it); } } @@ -138,22 +147,13 @@ static std::vector collect_dlls_to_inject(const std::wstring &extr } } - if (only_specified) return ordered_dlls_to_inject; - - return dlls_to_inject; -} - -static void to_bool_ini_val(std::wstring &val) -{ - for (auto &c : val) { - c = (wchar_t)std::tolower((int)c); - } - if (val != L"1" && val != L"y" && val != L"yes" && val != L"true") { - val.clear(); + if (load_only_specified_dlls) { + return ordered_dlls_to_inject; + } else { + return dlls_to_inject; } } - bool orig_steam_hkcu = false; WCHAR OrgSteamCDir_hkcu[8192] = { 0 }; DWORD Size1_hkcu = _countof(OrgSteamCDir_hkcu); @@ -168,20 +168,22 @@ bool patch_registry_hkcu() DWORD keyType = REG_SZ; RegQueryValueExW(Registrykey, L"SteamClientDll", 0, &keyType, (LPBYTE)&OrgSteamCDir_hkcu, &Size1_hkcu); RegQueryValueExW(Registrykey, L"SteamClientDll64", 0, &keyType, (LPBYTE)&OrgSteamCDir64_hkcu, &Size2_hkcu); - dbg_log::write("Found previous registry entry (HKCU) for Steam"); + logger.write("Found previous registry entry (HKCU) for Steam"); } else if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { - dbg_log::write("Created new registry entry (HKCU) for Steam"); + logger.write("Created new registry entry (HKCU) for Steam"); } else { - dbg_log::write("Unable to patch Registry (HKCU), error = " + std::to_string(GetLastError())); + logger.write("Unable to patch Registry (HKCU), error = " + std::to_string(GetLastError())); return false; } - RegSetValueExA(Registrykey, "ActiveUser", NULL, REG_DWORD, (const BYTE *)&UserId, sizeof(DWORD)); - RegSetValueExA(Registrykey, "pid", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD)); - RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (const BYTE *)ClientPath.c_str(), (ClientPath.size() + 1) * sizeof(ClientPath[0])); - RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (const BYTE *)Client64Path.c_str(), (Client64Path.size() + 1) * sizeof(Client64Path[0])); - RegSetValueExA(Registrykey, "Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE)); + RegSetValueExW(Registrykey, L"ActiveUser", NULL, REG_DWORD, (const BYTE *)&UserId, sizeof(DWORD)); + RegSetValueExW(Registrykey, L"pid", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD)); + auto client_path = common_helpers::to_wstr(ClientPath); + auto client64_path = common_helpers::to_wstr(Client64Path); + RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (const BYTE*)(client_path).c_str(), static_cast((client_path.size() + 1) * sizeof(client_path[0]))); + RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (const BYTE*)client64_path.c_str(), static_cast((client64_path.size() + 1) * sizeof(client64_path[0]))); + RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE)); RegCloseKey(Registrykey); return true; } @@ -190,7 +192,7 @@ void cleanup_registry_hkcu() { if (!orig_steam_hkcu) return; - dbg_log::write("restoring registry entries (HKCU)"); + logger.write("restoring registry entries (HKCU)"); HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { // Restore the values. @@ -200,7 +202,7 @@ void cleanup_registry_hkcu() // Close the HKEY Handle. RegCloseKey(Registrykey); } else { - dbg_log::write("Unable to restore the original registry entry (HKCU), error = " + std::to_string(GetLastError())); + logger.write("Unable to restore the original registry entry (HKCU), error = " + std::to_string(GetLastError())); } } @@ -216,19 +218,19 @@ bool patch_registry_hklm() // Get original values to restore later. DWORD keyType = REG_SZ; RegQueryValueExW(Registrykey, L"InstallPath", 0, &keyType, (LPBYTE)&OrgInstallPath_hklm, &Size1_hklm); - dbg_log::write("Found previous registry entry (HKLM) for Steam"); + logger.write("Found previous registry entry (HKLM) for Steam"); } else if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { - dbg_log::write("Created new registry entry (HKLM) for Steam"); + logger.write("Created new registry entry (HKLM) for Steam"); } else { - dbg_log::write("Unable to patch Registry (HKLM), error = " + std::to_string(GetLastError())); + logger.write("Unable to patch Registry (HKLM), error = " + std::to_string(GetLastError())); return false; } - const auto my_path = pe_helpers::get_current_exe_path_w(); - RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE *)my_path.c_str(), (my_path.size() + 1) * sizeof(my_path[0])); - RegSetValueExA(Registrykey, "SteamPID", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD)); - RegSetValueExA(Registrykey, "Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE)); + const auto my_path = common_helpers::to_wstr(pe_helpers::get_current_exe_path()); + RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast((my_path.size() + 1) * sizeof(my_path[0]))); + RegSetValueExW(Registrykey, L"SteamPID", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD)); + RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE)); RegCloseKey(Registrykey); return true; } @@ -237,13 +239,13 @@ void cleanup_registry_hklm() { if (!orig_steam_hklm) return; - dbg_log::write("restoring registry entries (HKLM)"); + logger.write("restoring registry entries (HKLM)"); HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE *)OrgInstallPath_hklm, Size1_hklm); RegCloseKey(Registrykey); } else { - dbg_log::write("Unable to restore the original registry entry (HKLM), error = " + std::to_string(GetLastError())); + logger.write("Unable to restore the original registry entry (HKLM), error = " + std::to_string(GetLastError())); } } @@ -257,12 +259,12 @@ bool patch_registry_hkcs() HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { orig_steam_hkcs_1 = true; - dbg_log::write("Found previous registry entry (HKCS) #1 for Steam"); + logger.write("Found previous registry entry (HKCS) #1 for Steam"); } else if (RegCreateKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { - dbg_log::write("Created new registry entry (HKCS) #1 for Steam"); + logger.write("Created new registry entry (HKCS) #1 for Steam"); } else { - dbg_log::write("Unable to patch Registry (HKCS) #1, error = " + std::to_string(GetLastError())); + logger.write("Unable to patch Registry (HKCS) #1, error = " + std::to_string(GetLastError())); return false; } @@ -272,22 +274,22 @@ bool patch_registry_hkcs() // Get original values to restore later. DWORD keyType = REG_SZ; RegQueryValueExW(Registrykey_2, L"", 0, &keyType, (LPBYTE)&OrgCommand_hkcs, &Size1_hkcs); - dbg_log::write("Found previous registry entry (HKCS) #2 for Steam"); + logger.write("Found previous registry entry (HKCS) #2 for Steam"); } else if (RegCreateKeyExW(HKEY_CLASSES_ROOT, L"steam\\Shell\\Open\\Command", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &Registrykey_2, NULL) == ERROR_SUCCESS) { - dbg_log::write("Created new registry entry (HKCS) #2 for Steam"); + logger.write("Created new registry entry (HKCS) #2 for Steam"); } else { - dbg_log::write("Unable to patch Registry (HKCS) #2, error = " + std::to_string(GetLastError())); + logger.write("Unable to patch Registry (HKCS) #2, error = " + std::to_string(GetLastError())); RegCloseKey(Registrykey); return false; } - RegSetValueExA(Registrykey, "", NULL, REG_SZ, (const BYTE *)STEAM_URL_PROTOCOL, (DWORD)sizeof(STEAM_URL_PROTOCOL)); - RegSetValueExA(Registrykey, "URL Protocol", NULL, REG_SZ, (const BYTE *)"", (DWORD)sizeof("")); + RegSetValueExW(Registrykey, L"", NULL, REG_SZ, (const BYTE *)STEAM_URL_PROTOCOL, (DWORD)sizeof(STEAM_URL_PROTOCOL)); + RegSetValueExW(Registrykey, L"URL Protocol", NULL, REG_SZ, (const BYTE *)L"", (DWORD)sizeof(L"")); RegCloseKey(Registrykey); - const auto cmd = pe_helpers::get_current_exe_path_w() + L"steam.exe -- \"%1\""; - RegSetValueExW(Registrykey_2, L"", NULL, REG_SZ, (const BYTE *)cmd.c_str(), (cmd.size() + 1) * sizeof(cmd[0])); + const auto cmd = common_helpers::to_wstr(pe_helpers::get_current_exe_path() + "steam.exe -- \"%1\""); + RegSetValueExW(Registrykey_2, L"", NULL, REG_SZ, (const BYTE*)cmd.c_str(), static_cast((cmd.size() + 1) * sizeof(cmd[0]))); RegCloseKey(Registrykey_2); return true; } @@ -295,31 +297,31 @@ bool patch_registry_hkcs() void cleanup_registry_hkcs() { if (orig_steam_hkcs_2) { - dbg_log::write("restoring registry entries (HKCS) #1"); + logger.write("restoring registry entries (HKCS) #1"); HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam\\Shell\\Open\\Command", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { RegSetValueExW(Registrykey, L"", NULL, REG_SZ, (const BYTE *)OrgCommand_hkcs, Size1_hkcs); RegCloseKey(Registrykey); } else { - dbg_log::write("Unable to restore the original registry entry (HKCS) #2, error = " + std::to_string(GetLastError())); + logger.write("Unable to restore the original registry entry (HKCS) #2, error = " + std::to_string(GetLastError())); } } if (!orig_steam_hkcs_1) { - dbg_log::write("removing registry entries (HKCS) #2 (added by loader)"); + logger.write("removing registry entries (HKCS) #2 (added by loader)"); HKEY Registrykey = { 0 }; - RegDeleteKeyA(HKEY_CLASSES_ROOT, "steam"); + RegDeleteKeyW(HKEY_CLASSES_ROOT, L"steam"); } } -void set_steam_env_vars(const std::wstring &AppId) +void set_steam_env_vars(const std::string &AppId) { - SetEnvironmentVariableW(L"SteamAppId", AppId.c_str()); - SetEnvironmentVariableW(L"SteamGameId", AppId.c_str()); + SetEnvironmentVariableA("SteamAppId", AppId.c_str()); + SetEnvironmentVariableA("SteamGameId", AppId.c_str()); // this env var conflicts with Steam Input - // SetEnvironmentVariableW(L"SteamOverlayGameId", AppId.c_str()); + // SetEnvironmentVariableA("SteamOverlayGameId", AppId.c_str()); // these 2 wil be overridden by the emu SetEnvironmentVariableW(L"SteamAppUser", L"cold_player"); @@ -327,113 +329,119 @@ void set_steam_env_vars(const std::wstring &AppId) SetEnvironmentVariableW(L"SteamClientLaunch", L"1"); SetEnvironmentVariableW(L"SteamEnv", L"1"); - SetEnvironmentVariableW(L"SteamPath", pe_helpers::get_current_exe_path_w().c_str()); + SetEnvironmentVariableW(L"SteamPath", common_helpers::to_wstr(pe_helpers::get_current_exe_path()).c_str()); } -int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR lpCmdLine, _In_ int nCmdShow) { - dbg_log::init(dbg_file.c_str()); - - IniFile = pe_helpers::get_current_exe_path_w() + std::filesystem::path(pe_helpers::get_current_exe_name_w()).stem().wstring() + L".ini"; - dbg_log::write(L"Searching for configuration file: " + IniFile); + IniFile = pe_helpers::get_current_exe_path() + std::filesystem::u8path(pe_helpers::get_current_exe_name()).stem().u8string() + ".ini"; + logger.write("Searching for configuration file: " + IniFile); if (!common_helpers::file_exist(IniFile)) { - IniFile = pe_helpers::get_current_exe_path_w() + L"ColdClientLoader.ini"; - dbg_log::write(L"Searching for configuration file: " + IniFile); + IniFile = pe_helpers::get_current_exe_path() + "ColdClientLoader.ini"; + logger.write("Searching for configuration file: " + IniFile); } if (!common_helpers::file_exist(IniFile)) { - dbg_log::write(L"Couldn't find the configuration file"); + logger.write("Couldn't find the configuration file"); MessageBoxA(NULL, "Couldn't find the configuration file.", "ColdClientLoader", MB_ICONERROR); - dbg_log::close(); return 1; } + { + std::ifstream ini_stream( std::filesystem::u8path(IniFile), std::ios::binary | std::ios::in); + if (!ini_stream.is_open()) { + logger.write("Failed to open the configuration file"); + MessageBoxA(NULL, "Failed to open the configuration file.", "ColdClientLoader", MB_ICONERROR); + return 1; + } + + auto err = local_ini.LoadData(ini_stream); + ini_stream.close(); + logger.write("result of parsing ini '%s' %i (success == 0)", IniFile.c_str(), (int)err); + if (err != SI_OK) { + logger.write("Failed to load the configuration file"); + MessageBoxA(NULL, "Failed to load the configuration file.", "ColdClientLoader", MB_ICONERROR); + return 1; + } + } + ClientPath = common_helpers::to_absolute( - get_ini_value(L"SteamClient", L"SteamClientDll"), - pe_helpers::get_current_exe_path_w() + local_ini.GetValue("SteamClient", "SteamClientDll", ""), + pe_helpers::get_current_exe_path() ); Client64Path = common_helpers::to_absolute( - get_ini_value(L"SteamClient", L"SteamClient64Dll"), - pe_helpers::get_current_exe_path_w() + local_ini.GetValue("SteamClient", "SteamClient64Dll", ""), + pe_helpers::get_current_exe_path() ); - std::wstring ExeFile = common_helpers::to_absolute( - get_ini_value(L"SteamClient", L"Exe"), - pe_helpers::get_current_exe_path_w() + ExeFile = common_helpers::to_absolute( + local_ini.GetValue("SteamClient", "Exe", ""), + pe_helpers::get_current_exe_path() ); - std::wstring ExeRunDir = common_helpers::to_absolute( - get_ini_value(L"SteamClient", L"ExeRunDir"), - pe_helpers::get_current_exe_path_w() + ExeRunDir = common_helpers::to_absolute( + local_ini.GetValue("SteamClient", "ExeRunDir", ""), + pe_helpers::get_current_exe_path() ); - std::wstring ExeCommandLine = get_ini_value(L"SteamClient", L"ExeCommandLine"); - std::wstring AppId = get_ini_value(L"SteamClient", L"AppId"); + ExeCommandLine = local_ini.GetValue("SteamClient", "ExeCommandLine", ""); + AppId = local_ini.GetValue("SteamClient", "AppId", ""); // dlls to inject - std::wstring ForceInjectSteamClient = get_ini_value(L"Injection", L"ForceInjectSteamClient"); - std::wstring ForceInjectGameOverlayRenderer = get_ini_value(L"Injection", L"ForceInjectGameOverlayRenderer"); - std::wstring DllsToInjectFolder = common_helpers::to_absolute( - get_ini_value(L"Injection", L"DllsToInjectFolder"), - pe_helpers::get_current_exe_path_w() + ForceInjectSteamClient = local_ini.GetBoolValue("Injection", "ForceInjectSteamClient", false); + ForceInjectGameOverlayRenderer = local_ini.GetBoolValue("Injection", "ForceInjectGameOverlayRenderer", false); + DllsToInjectFolder = common_helpers::to_absolute( + local_ini.GetValue("Injection", "DllsToInjectFolder", ""), + pe_helpers::get_current_exe_path() ); - std::wstring IgnoreInjectionError = get_ini_value(L"Injection", L"IgnoreInjectionError", L"1"); - std::wstring IgnoreLoaderArchDifference = get_ini_value(L"Injection", L"IgnoreLoaderArchDifference", L"0"); + IgnoreInjectionError = local_ini.GetBoolValue("Injection", "IgnoreInjectionError", true); + IgnoreLoaderArchDifference = local_ini.GetBoolValue("Injection", "IgnoreLoaderArchDifference", false); - std::wstring PersistentMode = get_ini_value(L"Persistence", L"Mode", L"0"); + PersistentMode = local_ini.GetLongValue("Persistence", "Mode", 0); // debug - std::wstring ResumeByDebugger = get_ini_value(L"Debug", L"ResumeByDebugger"); + ResumeByDebugger = local_ini.GetBoolValue("Debug", "ResumeByDebugger", false); - to_bool_ini_val(ForceInjectSteamClient); - to_bool_ini_val(ForceInjectGameOverlayRenderer); - to_bool_ini_val(IgnoreInjectionError); - to_bool_ini_val(IgnoreLoaderArchDifference); - to_bool_ini_val(ResumeByDebugger); - if (PersistentMode != L"0" && PersistentMode != L"1" && PersistentMode != L"2") PersistentMode = L"0"; + if (PersistentMode != 0 && PersistentMode != 1 && PersistentMode != 2) PersistentMode = 0; // log everything - dbg_log::write(L"SteamClient::Exe: " + ExeFile); - dbg_log::write(L"SteamClient::ExeRunDir: " + ExeRunDir); - dbg_log::write(L"SteamClient::ExeCommandLine: " + ExeCommandLine); - dbg_log::write(L"SteamClient::AppId: " + AppId); - dbg_log::write(L"SteamClient::SteamClient: " + ClientPath); - dbg_log::write(L"SteamClient::SteamClient64Dll: " + Client64Path); - dbg_log::write(L"SteamClient::PersistentMode: " + PersistentMode); - dbg_log::write(L"SteamClient::ForceInjectSteamClient: " + ForceInjectSteamClient); - dbg_log::write(L"SteamClient::ForceInjectGameOverlayRenderer: " + ForceInjectGameOverlayRenderer); - dbg_log::write(L"Injection::DllsToInjectFolder: " + DllsToInjectFolder); - dbg_log::write(L"Injection::IgnoreInjectionError: " + IgnoreInjectionError); - dbg_log::write(L"Injection::IgnoreLoaderArchDifference: " + IgnoreLoaderArchDifference); - dbg_log::write(L"Debug::ResumeByDebugger: " + ResumeByDebugger); + logger.write("SteamClient::Exe: " + ExeFile); + logger.write("SteamClient::ExeRunDir: " + ExeRunDir); + logger.write("SteamClient::ExeCommandLine: " + ExeCommandLine); + logger.write("SteamClient::AppId: " + AppId); + logger.write("SteamClient::SteamClient: " + ClientPath); + logger.write("SteamClient::SteamClient64Dll: " + Client64Path); + logger.write("SteamClient::PersistentMode: " + std::to_string(PersistentMode)); + logger.write("SteamClient::ForceInjectSteamClient: " + std::to_string(ForceInjectSteamClient)); + logger.write("SteamClient::ForceInjectGameOverlayRenderer: " + std::to_string(ForceInjectGameOverlayRenderer)); + logger.write("Injection::DllsToInjectFolder: " + DllsToInjectFolder); + logger.write("Injection::IgnoreInjectionError: " + std::to_string(IgnoreInjectionError)); + logger.write("Injection::IgnoreLoaderArchDifference: " + std::to_string(IgnoreLoaderArchDifference)); + logger.write("Debug::ResumeByDebugger: " + std::to_string(ResumeByDebugger)); if (!common_helpers::file_exist(Client64Path)) { - dbg_log::write("Couldn't find the requested SteamClient64Dll"); + logger.write("Couldn't find the requested SteamClient64Dll"); MessageBoxA(NULL, "Couldn't find the requested SteamClient64Dll.", "ColdClientLoader", MB_ICONERROR); - dbg_log::close(); return 1; } if (!common_helpers::file_exist(ClientPath)) { - dbg_log::write("Couldn't find the requested SteamClientDll"); + logger.write("Couldn't find the requested SteamClientDll"); MessageBoxA(NULL, "Couldn't find the requested SteamClientDll.", "ColdClientLoader", MB_ICONERROR); - dbg_log::close(); return 1; } if (!common_helpers::file_exist(ExeFile)) { - dbg_log::write("Couldn't find the requested Exe file"); + logger.write("Couldn't find the requested Exe file"); MessageBoxA(NULL, "Couldn't find the requested Exe file.", "ColdClientLoader", MB_ICONERROR); - dbg_log::close(); return 1; } if (ExeRunDir.empty()) { - ExeRunDir = std::filesystem::path(ExeFile).parent_path().wstring(); - dbg_log::write(L"Setting ExeRunDir to: " + ExeRunDir); + ExeRunDir = std::filesystem::u8path(ExeFile).parent_path().u8string(); + logger.write("Setting ExeRunDir to: " + ExeRunDir); } if (!common_helpers::dir_exist(ExeRunDir)) { - dbg_log::write("Couldn't find the requested Exe run dir"); + logger.write("Couldn't find the requested Exe run dir"); MessageBoxA(NULL, "Couldn't find the requested Exe run dir.", "ColdClientLoader", MB_ICONERROR); - dbg_log::close(); return 1; } @@ -441,90 +449,86 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance if (DllsToInjectFolder.size()) { isDllsToInjectFolderFound = common_helpers::dir_exist(DllsToInjectFolder); if (!isDllsToInjectFolderFound) { - dbg_log::write("Couldn't find the requested folder of dlls to inject"); - MessageBoxA(NULL, "Couldn't find the requested folder of dlls to inject.", "ColdClientLoader", MB_ICONERROR); + logger.write("Couldn't find the requested folder of dlls to inject"); + MessageBoxA(NULL, "Couldn't find the requested folder of dlls to inject.", "ColdClientLoader", MB_ICONWARNING); // it was requested to make this a non intrusive error, don't shutdown the whole thing because the folder is missing - // dbg_log::close(); // return 1; } } - auto exe_header = get_pe_header(ExeFile); + exe_header = get_pe_header(ExeFile); if (exe_header.empty()) { - dbg_log::write("Couldn't read the exe header"); + logger.write("Couldn't read the exe header"); MessageBoxA(NULL, "Couldn't read the exe header.", "ColdClientLoader", MB_ICONERROR); - dbg_log::close(); return 1; } bool is_exe_32 = pe_helpers::is_module_32((HMODULE)&exe_header[0]); bool is_exe_64 = pe_helpers::is_module_64((HMODULE)&exe_header[0]); - if ((!is_exe_32 && !is_exe_64) || (is_exe_32 && is_exe_64)) { // ARM, or just a regular file - dbg_log::write("The requested exe is invalid (neither 32 nor 64 bit)"); + if (is_exe_32 == is_exe_64) { // ARM, or just a regular file + logger.write("The requested exe is invalid (neither 32 nor 64 bit)"); MessageBoxA(NULL, "The requested exe is invalid (neither 32 nor 64 bit)", "ColdClientLoader", MB_ICONERROR); - dbg_log::close(); return 1; } if (is_exe_32) { - dbg_log::write("Detected exe arch: x32"); + logger.write("Detected exe arch: x32"); } else { - dbg_log::write("Detected exe arch: x64"); + logger.write("Detected exe arch: x64"); } if (loader_is_32) { - dbg_log::write("Detected loader arch: x32"); + logger.write("Detected loader arch: x32"); } else { - dbg_log::write("Detected loader arch: x64"); + logger.write("Detected loader arch: x64"); } if (loader_is_32 != is_exe_32) { - dbg_log::write("Arch of loader and requested exe are different, it is advised to use the appropriate one"); - if (IgnoreLoaderArchDifference.empty()) { + logger.write("Arch of loader and requested exe are different, it is advised to use the appropriate one"); + if (!IgnoreLoaderArchDifference) { MessageBoxA(NULL, "Arch of loader and requested exe are different,\nit is advised to use the appropriate one.", "ColdClientLoader", MB_OK); } } if (isDllsToInjectFolderFound) { - std::wstring failed_dlls{}; - dlls_to_inject = collect_dlls_to_inject(DllsToInjectFolder, is_exe_32, failed_dlls); - if (failed_dlls.size() && IgnoreInjectionError.empty()) { - int choice = MessageBoxW( + std::string failed_dlls{}; + dlls_to_inject = collect_dlls_to_inject(is_exe_32, failed_dlls); + if (failed_dlls.size() && !IgnoreInjectionError) { + int choice = MessageBoxA( NULL, - (L"The following dlls cannot be injected:\n" + failed_dlls + L"\nContinue ?").c_str(), - L"ColdClientLoader", + ("The following dlls cannot be injected:\n" + failed_dlls + "\nContinue ?").c_str(), + "ColdClientLoader", MB_YESNO); if (choice != IDYES) { - dbg_log::close(); return 1; } } } - if (ForceInjectGameOverlayRenderer.size()) { + if (ForceInjectGameOverlayRenderer) { if (is_exe_32) { - std::wstring GameOverlayPath = common_helpers::to_absolute( - L"GameOverlayRenderer.dll", - std::filesystem::path(ClientPath).parent_path().wstring() + auto GameOverlayPath = common_helpers::to_absolute( + "GameOverlayRenderer.dll", + std::filesystem::u8path(ClientPath).parent_path().u8string() ); if (!common_helpers::file_exist(GameOverlayPath)) { - dbg_log::write("Couldn't find GameOverlayRenderer.dll"); - MessageBoxA(NULL, "Couldn't find GameOverlayRenderer.dll.", "ColdClientLoader", MB_ICONERROR); + logger.write("Couldn't find GameOverlayRenderer.dll"); + MessageBoxA(NULL, "Couldn't find GameOverlayRenderer.dll to inject.", "ColdClientLoader", MB_ICONWARNING); } else { dlls_to_inject.insert(dlls_to_inject.begin(), GameOverlayPath); } } else { // 64 - std::wstring GameOverlay64Path = common_helpers::to_absolute( - L"GameOverlayRenderer64.dll", - std::filesystem::path(Client64Path).parent_path().wstring() + auto GameOverlay64Path = common_helpers::to_absolute( + "GameOverlayRenderer64.dll", + std::filesystem::u8path(Client64Path).parent_path().u8string() ); if (!common_helpers::file_exist(GameOverlay64Path)) { - dbg_log::write("Couldn't find GameOverlayRenderer64.dll"); - MessageBoxA(NULL, "Couldn't find GameOverlayRenderer64.dll.", "ColdClientLoader", MB_ICONERROR); + logger.write("Couldn't find GameOverlayRenderer64.dll"); + MessageBoxA(NULL, "Couldn't find GameOverlayRenderer64.dll to inject.", "ColdClientLoader", MB_ICONWARNING); } else { dlls_to_inject.insert(dlls_to_inject.begin(), GameOverlay64Path); } } } - if (ForceInjectSteamClient.size()) { + if (ForceInjectSteamClient) { if (is_exe_32) { dlls_to_inject.insert(dlls_to_inject.begin(), ClientPath); } else { @@ -532,11 +536,18 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance } } - if (PersistentMode != L"2" ) { + if (PersistentMode != 2) { if (AppId.size() && AppId[0]) { + try { + (void)std::stoul(AppId); + } catch(...) { + logger.write("AppId is not a valid number"); + MessageBoxA(NULL, "AppId is not a valid number.", "ColdClientLoader", MB_ICONERROR); + return 1; + } set_steam_env_vars(AppId); } else { - dbg_log::write("You forgot to set the AppId"); + logger.write("You forgot to set the AppId"); MessageBoxA(NULL, "You forgot to set the AppId.", "ColdClientLoader", MB_ICONERROR); return 1; } @@ -544,51 +555,52 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance constexpr const static wchar_t STEAM_LAUNCH_CMD_1[] = L"steam://run/"; constexpr const static wchar_t STEAM_LAUNCH_CMD_2[] = L"-- \"steam://rungameid/"; AppId.clear(); // we don't care about the app id in the ini - std::wstring my_cmd = lpCmdLine && lpCmdLine[0] ? std::wstring(lpCmdLine) : std::wstring(); - dbg_log::write(L"persistent mode 2 detecting steam launch cmd from: '" + my_cmd + L"'"); + auto my_cmd = lpCmdLine && lpCmdLine[0] + ? std::wstring(lpCmdLine) + : std::wstring(); + logger.write(L"persistent mode 2 detecting steam launch cmd from: '" + my_cmd + L"'"); if (my_cmd.find(STEAM_LAUNCH_CMD_1) == 0) { - AppId = my_cmd.substr(sizeof(STEAM_LAUNCH_CMD_1) / sizeof(wchar_t), my_cmd.find_first_of(L" \t")); - dbg_log::write("persistent mode 2 got steam launch cmd #1"); + AppId = common_helpers::to_str( my_cmd.substr(sizeof(STEAM_LAUNCH_CMD_1) / sizeof(STEAM_LAUNCH_CMD_1[0]), my_cmd.find_first_of(L" \t")) ); + logger.write("persistent mode 2 got steam launch cmd #1"); } else if (my_cmd.find(STEAM_LAUNCH_CMD_2) == 0) { - AppId = my_cmd.substr(sizeof(STEAM_LAUNCH_CMD_2) / sizeof(wchar_t), my_cmd.find_first_of(L" \t")); - dbg_log::write("persistent mode 2 got steam launch cmd #2"); + AppId = common_helpers::to_str( my_cmd.substr(sizeof(STEAM_LAUNCH_CMD_2) / sizeof(STEAM_LAUNCH_CMD_2[0]), my_cmd.find_first_of(L" \t")) ); + logger.write("persistent mode 2 got steam launch cmd #2"); } else { - dbg_log::write("persistent mode 2 didn't detect a valid steam launch cmd"); + logger.write("persistent mode 2 didn't detect a valid steam launch cmd"); } if (AppId.size()) { set_steam_env_vars(AppId); - dbg_log::write(L"persistent mode 2 will use app id = " + AppId); + logger.write("persistent mode 2 will use app id = " + AppId); } else { - dbg_log::write("persistent mode 2 didn't find app id"); + logger.write("persistent mode 2 didn't find app id"); } } if (!patch_registry_hkcu()) { - dbg_log::close(); MessageBoxA(NULL, "Unable to patch Registry (HKCU).", "ColdClientLoader", MB_ICONERROR); return 1; } if (!patch_registry_hklm()) { cleanup_registry_hkcu(); - - dbg_log::close(); MessageBoxA(NULL, "Unable to patch Registry (HKLM).", "ColdClientLoader", MB_ICONERROR); return 1; } patch_registry_hkcs(); - // if (!patch_registry_hkcs()) { // this fails due to admin rights, not a big deal + // this fails due to admin rights, not a big deal + // ---------------------------------------------- + // if (!patch_registry_hkcs()) { // cleanup_registry_hkcu(); // cleanup_registry_hklm(); - // dbg_log::close(); + // logger.write("Unable to patch Registry (HKCS)."); // MessageBoxA(NULL, "Unable to patch Registry (HKCS).", "ColdClientLoader", MB_ICONERROR); // return 1; // } - if (PersistentMode != L"2" || AppId.size()) { // persistent mode 0 or 1, or mode 2 with defined app id + if (PersistentMode != 2 || AppId.size()) { // persistent mode 0 or 1, or mode 2 with defined app id STARTUPINFOW info = { 0 }; SecureZeroMemory(&info, sizeof(info)); @@ -597,33 +609,33 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance PROCESS_INFORMATION processInfo = { 0 }; SecureZeroMemory(&processInfo, sizeof(processInfo)); - dbg_log::write("spawning the requested EXE file"); - WCHAR CommandLine[16384] = { 0 }; - _snwprintf(CommandLine, _countof(CommandLine), L"\"%ls\" %ls %ls", ExeFile.c_str(), ExeCommandLine.c_str(), lpCmdLine); - if (!CreateProcessW(ExeFile.c_str(), CommandLine, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, ExeRunDir.c_str(), &info, &processInfo)) { - dbg_log::write("Unable to load the requested EXE file"); + logger.write("spawning the requested EXE file"); + const auto exe_file = common_helpers::to_wstr(ExeFile); + std::wstringstream cmdline{}; + cmdline << L"\"" << exe_file << L"\" " << common_helpers::to_wstr(ExeCommandLine) << L" " << lpCmdLine; + auto CommandLine = cmdline.str(); + if (!CreateProcessW(exe_file.c_str(), (LPWSTR)CommandLine.c_str(), NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, common_helpers::to_wstr(ExeRunDir).c_str(), &info, &processInfo)) { + logger.write("Unable to load the requested EXE file, error = " + std::to_string(GetLastError())); cleanup_registry_hkcu(); cleanup_registry_hklm(); cleanup_registry_hkcs(); - - dbg_log::close(); MessageBoxA(NULL, "Unable to load the requested EXE file.", "ColdClientLoader", MB_ICONERROR); return 1; } - dbg_log::write("injecting " + std::to_string(dlls_to_inject.size()) + " dlls"); + logger.write("injecting " + std::to_string(dlls_to_inject.size()) + " dlls"); for (const auto &dll : dlls_to_inject) { - dbg_log::write(L"Injecting dll: '" + dll + L"' ..."); + logger.write("Injecting dll: '" + dll + "' ..."); const char *err_inject = nullptr; DWORD code = pe_helpers::loadlib_remote(processInfo.hProcess, dll, &err_inject); if (code != ERROR_SUCCESS) { auto err_full = - L"Failed to inject the dll: " + dll + L"\n" + - common_helpers::str_to_w(err_inject) + L"\n" + - common_helpers::str_to_w(pe_helpers::get_err_string(code)) + L"\n" + - L"Error code = " + std::to_wstring(code) + L"\n"; - dbg_log::write(err_full); - if (IgnoreInjectionError.empty()) { + std::string("Failed to inject the dll: ") + dll + "\n" + + err_inject + "\n" + + pe_helpers::get_err_string(code) + "\n" + + "Error code = " + std::to_string(code) + "\n"; + logger.write(err_full); + if (!IgnoreInjectionError) { TerminateProcess(processInfo.hProcess, 1); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); @@ -632,25 +644,26 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance cleanup_registry_hklm(); cleanup_registry_hkcs(); - dbg_log::close(); - MessageBoxW(NULL, err_full.c_str(), L"ColdClientLoader", MB_ICONERROR); + MessageBoxA(NULL, err_full.c_str(), "ColdClientLoader", MB_ICONERROR); return 1; } } else { - dbg_log::write("Injected!"); + logger.write("Injected!"); } } // run - if (ResumeByDebugger.empty()) { - dbg_log::write("resuming the main thread of the exe"); + if (!ResumeByDebugger) { + logger.write("resuming the main thread of the exe"); // MessageBoxA(nullptr, "Wait then press ok", "Wait some seconds", MB_OK); // games like house flipper 2 won't launch until the previous exe has exited, which takes some seconds - if (PersistentMode == L"2") std::this_thread::sleep_for(std::chrono::seconds(5)); + if (PersistentMode == 2) { + std::this_thread::sleep_for(std::chrono::seconds(5)); + } ResumeThread(processInfo.hThread); } else { std::string msg = "Attach a debugger now to PID " + std::to_string(processInfo.dwProcessId) + " and resume its main thread"; - dbg_log::write(msg); + logger.write(msg); MessageBoxA(NULL, msg.c_str(), "ColdClientLoader", MB_OK); } @@ -661,18 +674,17 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance CloseHandle(processInfo.hThread); } - if (PersistentMode == L"1" ) { + if (PersistentMode == 1 ) { MessageBoxA(NULL, "Press OK when you have finished playing to close the loader", "Cold Client Loader (waiting)", MB_OK); - } else if (PersistentMode == L"2" && AppId.empty()) { + } else if (PersistentMode == 2 && AppId.empty()) { MessageBoxA(NULL, "Start the game, then press OK when you have finished playing to close the loader", "Cold Client Loader (waiting)", MB_OK); } - if (PersistentMode != L"2" || AppId.empty()) { // persistent mode 0 or 1, or mode 2 without app id + if (PersistentMode != 2 || AppId.empty()) { // persistent mode 0 or 1, or mode 2 without app id cleanup_registry_hkcu(); cleanup_registry_hklm(); cleanup_registry_hkcs(); } - dbg_log::close(); return 0; }