mirror of
https://github.com/Detanup01/gbe_fork.git
synced 2024-11-26 20:54:00 +08:00
rewrite cold client loader to use simpleini lib + use multibyte std::string + new logger
This commit is contained in:
parent
e03ed9e1cd
commit
5cccad0e67
@ -5,6 +5,12 @@
|
|||||||
#include "pe_helpers/pe_helpers.hpp"
|
#include "pe_helpers/pe_helpers.hpp"
|
||||||
#include "dbg_log/dbg_log.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
|
// C RunTime Header Files
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
@ -12,56 +18,58 @@
|
|||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
static std::wstring IniFile{};
|
static CSimpleIniA local_ini{true};
|
||||||
static const std::wstring dbg_file = pe_helpers::get_current_exe_path_w() + pe_helpers::get_current_exe_name_w() + L".log";
|
static dbg_log logger{pe_helpers::get_current_exe_path() + pe_helpers::get_current_exe_name() + ".log"};
|
||||||
constexpr static const char STEAM_UNIVERSE[] = "Public";
|
constexpr static const wchar_t STEAM_UNIVERSE[] = L"Public";
|
||||||
constexpr static const char STEAM_URL_PROTOCOL[] = "URL:steam protocol";
|
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 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.
|
// Declare some variables to be used for Steam registry.
|
||||||
const DWORD UserId = 0x03100004771F810D & 0xffffffff;
|
const DWORD UserId = 0x03100004771F810D & 0xffffffff;
|
||||||
const DWORD ProcessID = GetCurrentProcessId();
|
const DWORD ProcessID = GetCurrentProcessId();
|
||||||
|
|
||||||
std::wstring ClientPath{};
|
static std::string IniFile{};
|
||||||
std::wstring Client64Path{};
|
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<std::wstring> dlls_to_inject{};
|
static bool ForceInjectSteamClient{};
|
||||||
|
static bool ForceInjectGameOverlayRenderer{};
|
||||||
|
static bool IgnoreInjectionError{};
|
||||||
|
static bool IgnoreLoaderArchDifference{};
|
||||||
|
static bool ResumeByDebugger{};
|
||||||
|
|
||||||
|
static long PersistentMode{};
|
||||||
|
|
||||||
|
static std::vector<uint8_t> exe_header{};
|
||||||
|
static std::vector<std::string> dlls_to_inject{};
|
||||||
|
|
||||||
|
|
||||||
|
static std::vector<uint8_t> get_pe_header(const std::string &filepath)
|
||||||
|
|
||||||
std::wstring get_ini_value(LPCWSTR section, LPCWSTR key, LPCWSTR default_val = NULL)
|
|
||||||
{
|
|
||||||
std::vector<wchar_t> 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<uint8_t> get_pe_header(const std::wstring &filepath)
|
|
||||||
{
|
{
|
||||||
try {
|
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;
|
if (!file.is_open()) throw;
|
||||||
|
|
||||||
file.seekg(0, std::ios::beg);
|
file.seekg(0, std::ios::beg);
|
||||||
@ -73,64 +81,65 @@ static std::vector<uint8_t> get_pe_header(const std::wstring &filepath)
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch(const std::exception& e) {
|
} 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<uint8_t>();
|
return std::vector<uint8_t>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::wstring> collect_dlls_to_inject(const std::wstring &extra_dlls_folder, bool is_exe_32, std::wstring &failed_dlls)
|
static std::vector<std::string> 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";
|
const auto load_order_file = std::filesystem::u8path(DllsToInjectFolder) / "load_order.txt";
|
||||||
std::vector<std::wstring> dlls_to_inject{};
|
std::vector<std::string> dlls_to_inject{};
|
||||||
for (auto const& dir_entry :
|
for (const auto &dir_entry :
|
||||||
std::filesystem::recursive_directory_iterator(extra_dlls_folder, std::filesystem::directory_options::follow_directory_symlink)) {
|
std::filesystem::recursive_directory_iterator(DllsToInjectFolder, std::filesystem::directory_options::follow_directory_symlink)
|
||||||
|
) {
|
||||||
if (std::filesystem::is_directory(dir_entry.path())) continue;
|
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
|
// 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);
|
auto dll_header = get_pe_header(dll_path);
|
||||||
if (dll_header.empty()) {
|
if (dll_header.empty()) {
|
||||||
dbg_log::write(L"Failed to get PE header of dll: '" + dll_path + L"'");
|
logger.write("Failed to get PE header of dll: '" + dll_path + "'");
|
||||||
failed_dlls += dll_path + L"\n";
|
failed_dlls += dll_path + "\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_dll_32 = pe_helpers::is_module_32((HMODULE)&dll_header[0]);
|
const 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]);
|
const 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
|
if (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");
|
logger.write("Dll '" + dll_path + "' is neither 32 nor 64 bit and will be ignored");
|
||||||
failed_dlls += dll_path + L"\n";
|
failed_dlls += dll_path + "\n";
|
||||||
continue;
|
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);
|
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 {
|
} else {
|
||||||
dbg_log::write(L"Dll '" + dll_path + L"' has a different arch than the exe and will be ignored");
|
logger.write("Dll '" + dll_path + "' has a different arch than the exe and will be ignored");
|
||||||
failed_dlls += dll_path + L"\n";
|
failed_dlls += dll_path + "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool only_specified = false;
|
bool load_only_specified_dlls = false;
|
||||||
std::vector<std::wstring> ordered_dlls_to_inject{};
|
std::vector<std::string> ordered_dlls_to_inject{};
|
||||||
{
|
{
|
||||||
dbg_log::write(L"Searching for load order file: '" + load_order_file.wstring() + L"'");
|
logger.write("Searching for load order file: '" + load_order_file.u8string() + "'");
|
||||||
auto f_order = std::wifstream(load_order_file, std::ios::in);
|
auto f_order = std::ifstream(load_order_file, std::ios::in);
|
||||||
if (f_order.is_open()) {
|
if (f_order.is_open()) {
|
||||||
only_specified = true;
|
load_only_specified_dlls = true;
|
||||||
dbg_log::write(L"Reading load order file...");
|
logger.write(L"Reading load order file...");
|
||||||
std::wstring line{};
|
std::string line{};
|
||||||
while (std::getline(f_order, line)) {
|
while (std::getline(f_order, line)) {
|
||||||
auto abs = common_helpers::to_absolute(line, extra_dlls_folder);
|
auto abs = common_helpers::to_absolute(line, DllsToInjectFolder);
|
||||||
dbg_log::write(L"Load order line: '" + abs + L"'");
|
logger.write("Load order line: '" + abs + "'");
|
||||||
auto it = std::find_if(dlls_to_inject.begin(), dlls_to_inject.end(), [&abs](const std::wstring &dll_to_inject) {
|
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);
|
return common_helpers::str_cmp_insensitive(dll_to_inject, abs);
|
||||||
});
|
});
|
||||||
if (it != dlls_to_inject.end()) {
|
if (dlls_to_inject.end() != it) {
|
||||||
dbg_log::write("Found the dll specified by the load order line");
|
logger.write("Found the dll specified by the load order line");
|
||||||
ordered_dlls_to_inject.push_back(*it);
|
ordered_dlls_to_inject.push_back(*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,21 +147,12 @@ static std::vector<std::wstring> collect_dlls_to_inject(const std::wstring &extr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (only_specified) return ordered_dlls_to_inject;
|
if (load_only_specified_dlls) {
|
||||||
|
return ordered_dlls_to_inject;
|
||||||
|
} else {
|
||||||
return 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool orig_steam_hkcu = false;
|
bool orig_steam_hkcu = false;
|
||||||
WCHAR OrgSteamCDir_hkcu[8192] = { 0 };
|
WCHAR OrgSteamCDir_hkcu[8192] = { 0 };
|
||||||
@ -168,20 +168,22 @@ bool patch_registry_hkcu()
|
|||||||
DWORD keyType = REG_SZ;
|
DWORD keyType = REG_SZ;
|
||||||
RegQueryValueExW(Registrykey, L"SteamClientDll", 0, &keyType, (LPBYTE)&OrgSteamCDir_hkcu, &Size1_hkcu);
|
RegQueryValueExW(Registrykey, L"SteamClientDll", 0, &keyType, (LPBYTE)&OrgSteamCDir_hkcu, &Size1_hkcu);
|
||||||
RegQueryValueExW(Registrykey, L"SteamClientDll64", 0, &keyType, (LPBYTE)&OrgSteamCDir64_hkcu, &Size2_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,
|
} 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) {
|
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 {
|
} 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegSetValueExA(Registrykey, "ActiveUser", NULL, REG_DWORD, (const BYTE *)&UserId, sizeof(DWORD));
|
RegSetValueExW(Registrykey, L"ActiveUser", NULL, REG_DWORD, (const BYTE *)&UserId, sizeof(DWORD));
|
||||||
RegSetValueExA(Registrykey, "pid", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD));
|
RegSetValueExW(Registrykey, L"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]));
|
auto client_path = common_helpers::to_wstr(ClientPath);
|
||||||
RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (const BYTE *)Client64Path.c_str(), (Client64Path.size() + 1) * sizeof(Client64Path[0]));
|
auto client64_path = common_helpers::to_wstr(Client64Path);
|
||||||
RegSetValueExA(Registrykey, "Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE));
|
RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (const BYTE*)(client_path).c_str(), static_cast<DWORD>((client_path.size() + 1) * sizeof(client_path[0])));
|
||||||
|
RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (const BYTE*)client64_path.c_str(), static_cast<DWORD>((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);
|
RegCloseKey(Registrykey);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -190,7 +192,7 @@ void cleanup_registry_hkcu()
|
|||||||
{
|
{
|
||||||
if (!orig_steam_hkcu) return;
|
if (!orig_steam_hkcu) return;
|
||||||
|
|
||||||
dbg_log::write("restoring registry entries (HKCU)");
|
logger.write("restoring registry entries (HKCU)");
|
||||||
HKEY Registrykey = { 0 };
|
HKEY Registrykey = { 0 };
|
||||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
|
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
|
||||||
// Restore the values.
|
// Restore the values.
|
||||||
@ -200,7 +202,7 @@ void cleanup_registry_hkcu()
|
|||||||
// Close the HKEY Handle.
|
// Close the HKEY Handle.
|
||||||
RegCloseKey(Registrykey);
|
RegCloseKey(Registrykey);
|
||||||
} else {
|
} 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.
|
// Get original values to restore later.
|
||||||
DWORD keyType = REG_SZ;
|
DWORD keyType = REG_SZ;
|
||||||
RegQueryValueExW(Registrykey, L"InstallPath", 0, &keyType, (LPBYTE)&OrgInstallPath_hklm, &Size1_hklm);
|
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,
|
} else if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, 0, REG_OPTION_NON_VOLATILE,
|
||||||
KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) {
|
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 {
|
} 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto my_path = pe_helpers::get_current_exe_path_w();
|
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(), (my_path.size() + 1) * sizeof(my_path[0]));
|
RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast<DWORD>((my_path.size() + 1) * sizeof(my_path[0])));
|
||||||
RegSetValueExA(Registrykey, "SteamPID", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD));
|
RegSetValueExW(Registrykey, L"SteamPID", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD));
|
||||||
RegSetValueExA(Registrykey, "Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE));
|
RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE));
|
||||||
RegCloseKey(Registrykey);
|
RegCloseKey(Registrykey);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -237,13 +239,13 @@ void cleanup_registry_hklm()
|
|||||||
{
|
{
|
||||||
if (!orig_steam_hklm) return;
|
if (!orig_steam_hklm) return;
|
||||||
|
|
||||||
dbg_log::write("restoring registry entries (HKLM)");
|
logger.write("restoring registry entries (HKLM)");
|
||||||
HKEY Registrykey = { 0 };
|
HKEY Registrykey = { 0 };
|
||||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
|
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);
|
RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE *)OrgInstallPath_hklm, Size1_hklm);
|
||||||
RegCloseKey(Registrykey);
|
RegCloseKey(Registrykey);
|
||||||
} else {
|
} 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 };
|
HKEY Registrykey = { 0 };
|
||||||
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
|
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
|
||||||
orig_steam_hkcs_1 = true;
|
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,
|
} else if (RegCreateKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, 0, REG_OPTION_NON_VOLATILE,
|
||||||
KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) {
|
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 {
|
} 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,22 +274,22 @@ bool patch_registry_hkcs()
|
|||||||
// Get original values to restore later.
|
// Get original values to restore later.
|
||||||
DWORD keyType = REG_SZ;
|
DWORD keyType = REG_SZ;
|
||||||
RegQueryValueExW(Registrykey_2, L"", 0, &keyType, (LPBYTE)&OrgCommand_hkcs, &Size1_hkcs);
|
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,
|
} 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) {
|
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 {
|
} 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);
|
RegCloseKey(Registrykey);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegSetValueExA(Registrykey, "", NULL, REG_SZ, (const BYTE *)STEAM_URL_PROTOCOL, (DWORD)sizeof(STEAM_URL_PROTOCOL));
|
RegSetValueExW(Registrykey, L"", 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"URL Protocol", NULL, REG_SZ, (const BYTE *)L"", (DWORD)sizeof(L""));
|
||||||
RegCloseKey(Registrykey);
|
RegCloseKey(Registrykey);
|
||||||
|
|
||||||
const auto cmd = pe_helpers::get_current_exe_path_w() + L"steam.exe -- \"%1\"";
|
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(), (cmd.size() + 1) * sizeof(cmd[0]));
|
RegSetValueExW(Registrykey_2, L"", NULL, REG_SZ, (const BYTE*)cmd.c_str(), static_cast<DWORD>((cmd.size() + 1) * sizeof(cmd[0])));
|
||||||
RegCloseKey(Registrykey_2);
|
RegCloseKey(Registrykey_2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -295,31 +297,31 @@ bool patch_registry_hkcs()
|
|||||||
void cleanup_registry_hkcs()
|
void cleanup_registry_hkcs()
|
||||||
{
|
{
|
||||||
if (orig_steam_hkcs_2) {
|
if (orig_steam_hkcs_2) {
|
||||||
dbg_log::write("restoring registry entries (HKCS) #1");
|
logger.write("restoring registry entries (HKCS) #1");
|
||||||
HKEY Registrykey = { 0 };
|
HKEY Registrykey = { 0 };
|
||||||
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam\\Shell\\Open\\Command", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
|
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);
|
RegSetValueExW(Registrykey, L"", NULL, REG_SZ, (const BYTE *)OrgCommand_hkcs, Size1_hkcs);
|
||||||
RegCloseKey(Registrykey);
|
RegCloseKey(Registrykey);
|
||||||
} else {
|
} 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) {
|
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 };
|
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());
|
SetEnvironmentVariableA("SteamAppId", AppId.c_str());
|
||||||
SetEnvironmentVariableW(L"SteamGameId", AppId.c_str());
|
SetEnvironmentVariableA("SteamGameId", AppId.c_str());
|
||||||
// this env var conflicts with Steam Input
|
// 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
|
// these 2 wil be overridden by the emu
|
||||||
SetEnvironmentVariableW(L"SteamAppUser", L"cold_player");
|
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"SteamClientLaunch", L"1");
|
||||||
SetEnvironmentVariableW(L"SteamEnv", 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() + std::filesystem::u8path(pe_helpers::get_current_exe_name()).stem().u8string() + ".ini";
|
||||||
|
logger.write("Searching for configuration file: " + IniFile);
|
||||||
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);
|
|
||||||
if (!common_helpers::file_exist(IniFile)) {
|
if (!common_helpers::file_exist(IniFile)) {
|
||||||
IniFile = pe_helpers::get_current_exe_path_w() + L"ColdClientLoader.ini";
|
IniFile = pe_helpers::get_current_exe_path() + "ColdClientLoader.ini";
|
||||||
dbg_log::write(L"Searching for configuration file: " + IniFile);
|
logger.write("Searching for configuration file: " + IniFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!common_helpers::file_exist(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);
|
MessageBoxA(NULL, "Couldn't find the configuration file.", "ColdClientLoader", MB_ICONERROR);
|
||||||
dbg_log::close();
|
|
||||||
return 1;
|
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(
|
ClientPath = common_helpers::to_absolute(
|
||||||
get_ini_value(L"SteamClient", L"SteamClientDll"),
|
local_ini.GetValue("SteamClient", "SteamClientDll", ""),
|
||||||
pe_helpers::get_current_exe_path_w()
|
pe_helpers::get_current_exe_path()
|
||||||
);
|
);
|
||||||
Client64Path = common_helpers::to_absolute(
|
Client64Path = common_helpers::to_absolute(
|
||||||
get_ini_value(L"SteamClient", L"SteamClient64Dll"),
|
local_ini.GetValue("SteamClient", "SteamClient64Dll", ""),
|
||||||
pe_helpers::get_current_exe_path_w()
|
pe_helpers::get_current_exe_path()
|
||||||
);
|
);
|
||||||
std::wstring ExeFile = common_helpers::to_absolute(
|
ExeFile = common_helpers::to_absolute(
|
||||||
get_ini_value(L"SteamClient", L"Exe"),
|
local_ini.GetValue("SteamClient", "Exe", ""),
|
||||||
pe_helpers::get_current_exe_path_w()
|
pe_helpers::get_current_exe_path()
|
||||||
);
|
);
|
||||||
std::wstring ExeRunDir = common_helpers::to_absolute(
|
ExeRunDir = common_helpers::to_absolute(
|
||||||
get_ini_value(L"SteamClient", L"ExeRunDir"),
|
local_ini.GetValue("SteamClient", "ExeRunDir", ""),
|
||||||
pe_helpers::get_current_exe_path_w()
|
pe_helpers::get_current_exe_path()
|
||||||
);
|
);
|
||||||
std::wstring ExeCommandLine = get_ini_value(L"SteamClient", L"ExeCommandLine");
|
ExeCommandLine = local_ini.GetValue("SteamClient", "ExeCommandLine", "");
|
||||||
std::wstring AppId = get_ini_value(L"SteamClient", L"AppId");
|
AppId = local_ini.GetValue("SteamClient", "AppId", "");
|
||||||
|
|
||||||
// dlls to inject
|
// dlls to inject
|
||||||
std::wstring ForceInjectSteamClient = get_ini_value(L"Injection", L"ForceInjectSteamClient");
|
ForceInjectSteamClient = local_ini.GetBoolValue("Injection", "ForceInjectSteamClient", false);
|
||||||
std::wstring ForceInjectGameOverlayRenderer = get_ini_value(L"Injection", L"ForceInjectGameOverlayRenderer");
|
ForceInjectGameOverlayRenderer = local_ini.GetBoolValue("Injection", "ForceInjectGameOverlayRenderer", false);
|
||||||
std::wstring DllsToInjectFolder = common_helpers::to_absolute(
|
DllsToInjectFolder = common_helpers::to_absolute(
|
||||||
get_ini_value(L"Injection", L"DllsToInjectFolder"),
|
local_ini.GetValue("Injection", "DllsToInjectFolder", ""),
|
||||||
pe_helpers::get_current_exe_path_w()
|
pe_helpers::get_current_exe_path()
|
||||||
);
|
);
|
||||||
std::wstring IgnoreInjectionError = get_ini_value(L"Injection", L"IgnoreInjectionError", L"1");
|
IgnoreInjectionError = local_ini.GetBoolValue("Injection", "IgnoreInjectionError", true);
|
||||||
std::wstring IgnoreLoaderArchDifference = get_ini_value(L"Injection", L"IgnoreLoaderArchDifference", L"0");
|
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
|
// debug
|
||||||
std::wstring ResumeByDebugger = get_ini_value(L"Debug", L"ResumeByDebugger");
|
ResumeByDebugger = local_ini.GetBoolValue("Debug", "ResumeByDebugger", false);
|
||||||
|
|
||||||
to_bool_ini_val(ForceInjectSteamClient);
|
if (PersistentMode != 0 && PersistentMode != 1 && PersistentMode != 2) PersistentMode = 0;
|
||||||
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";
|
|
||||||
|
|
||||||
// log everything
|
// log everything
|
||||||
dbg_log::write(L"SteamClient::Exe: " + ExeFile);
|
logger.write("SteamClient::Exe: " + ExeFile);
|
||||||
dbg_log::write(L"SteamClient::ExeRunDir: " + ExeRunDir);
|
logger.write("SteamClient::ExeRunDir: " + ExeRunDir);
|
||||||
dbg_log::write(L"SteamClient::ExeCommandLine: " + ExeCommandLine);
|
logger.write("SteamClient::ExeCommandLine: " + ExeCommandLine);
|
||||||
dbg_log::write(L"SteamClient::AppId: " + AppId);
|
logger.write("SteamClient::AppId: " + AppId);
|
||||||
dbg_log::write(L"SteamClient::SteamClient: " + ClientPath);
|
logger.write("SteamClient::SteamClient: " + ClientPath);
|
||||||
dbg_log::write(L"SteamClient::SteamClient64Dll: " + Client64Path);
|
logger.write("SteamClient::SteamClient64Dll: " + Client64Path);
|
||||||
dbg_log::write(L"SteamClient::PersistentMode: " + PersistentMode);
|
logger.write("SteamClient::PersistentMode: " + std::to_string(PersistentMode));
|
||||||
dbg_log::write(L"SteamClient::ForceInjectSteamClient: " + ForceInjectSteamClient);
|
logger.write("SteamClient::ForceInjectSteamClient: " + std::to_string(ForceInjectSteamClient));
|
||||||
dbg_log::write(L"SteamClient::ForceInjectGameOverlayRenderer: " + ForceInjectGameOverlayRenderer);
|
logger.write("SteamClient::ForceInjectGameOverlayRenderer: " + std::to_string(ForceInjectGameOverlayRenderer));
|
||||||
dbg_log::write(L"Injection::DllsToInjectFolder: " + DllsToInjectFolder);
|
logger.write("Injection::DllsToInjectFolder: " + DllsToInjectFolder);
|
||||||
dbg_log::write(L"Injection::IgnoreInjectionError: " + IgnoreInjectionError);
|
logger.write("Injection::IgnoreInjectionError: " + std::to_string(IgnoreInjectionError));
|
||||||
dbg_log::write(L"Injection::IgnoreLoaderArchDifference: " + IgnoreLoaderArchDifference);
|
logger.write("Injection::IgnoreLoaderArchDifference: " + std::to_string(IgnoreLoaderArchDifference));
|
||||||
dbg_log::write(L"Debug::ResumeByDebugger: " + ResumeByDebugger);
|
logger.write("Debug::ResumeByDebugger: " + std::to_string(ResumeByDebugger));
|
||||||
|
|
||||||
if (!common_helpers::file_exist(Client64Path)) {
|
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);
|
MessageBoxA(NULL, "Couldn't find the requested SteamClient64Dll.", "ColdClientLoader", MB_ICONERROR);
|
||||||
dbg_log::close();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!common_helpers::file_exist(ClientPath)) {
|
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);
|
MessageBoxA(NULL, "Couldn't find the requested SteamClientDll.", "ColdClientLoader", MB_ICONERROR);
|
||||||
dbg_log::close();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!common_helpers::file_exist(ExeFile)) {
|
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);
|
MessageBoxA(NULL, "Couldn't find the requested Exe file.", "ColdClientLoader", MB_ICONERROR);
|
||||||
dbg_log::close();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ExeRunDir.empty()) {
|
if (ExeRunDir.empty()) {
|
||||||
ExeRunDir = std::filesystem::path(ExeFile).parent_path().wstring();
|
ExeRunDir = std::filesystem::u8path(ExeFile).parent_path().u8string();
|
||||||
dbg_log::write(L"Setting ExeRunDir to: " + ExeRunDir);
|
logger.write("Setting ExeRunDir to: " + ExeRunDir);
|
||||||
}
|
}
|
||||||
if (!common_helpers::dir_exist(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);
|
MessageBoxA(NULL, "Couldn't find the requested Exe run dir.", "ColdClientLoader", MB_ICONERROR);
|
||||||
dbg_log::close();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,90 +449,86 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
|||||||
if (DllsToInjectFolder.size()) {
|
if (DllsToInjectFolder.size()) {
|
||||||
isDllsToInjectFolderFound = common_helpers::dir_exist(DllsToInjectFolder);
|
isDllsToInjectFolderFound = common_helpers::dir_exist(DllsToInjectFolder);
|
||||||
if (!isDllsToInjectFolderFound) {
|
if (!isDllsToInjectFolderFound) {
|
||||||
dbg_log::write("Couldn't find the requested folder of dlls to inject");
|
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_ICONERROR);
|
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
|
// 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;
|
// return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto exe_header = get_pe_header(ExeFile);
|
exe_header = get_pe_header(ExeFile);
|
||||||
if (exe_header.empty()) {
|
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);
|
MessageBoxA(NULL, "Couldn't read the exe header.", "ColdClientLoader", MB_ICONERROR);
|
||||||
dbg_log::close();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_exe_32 = pe_helpers::is_module_32((HMODULE)&exe_header[0]);
|
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]);
|
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
|
if (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)");
|
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);
|
MessageBoxA(NULL, "The requested exe is invalid (neither 32 nor 64 bit)", "ColdClientLoader", MB_ICONERROR);
|
||||||
dbg_log::close();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (is_exe_32) {
|
if (is_exe_32) {
|
||||||
dbg_log::write("Detected exe arch: x32");
|
logger.write("Detected exe arch: x32");
|
||||||
} else {
|
} else {
|
||||||
dbg_log::write("Detected exe arch: x64");
|
logger.write("Detected exe arch: x64");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loader_is_32) {
|
if (loader_is_32) {
|
||||||
dbg_log::write("Detected loader arch: x32");
|
logger.write("Detected loader arch: x32");
|
||||||
} else {
|
} else {
|
||||||
dbg_log::write("Detected loader arch: x64");
|
logger.write("Detected loader arch: x64");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loader_is_32 != is_exe_32) {
|
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");
|
logger.write("Arch of loader and requested exe are different, it is advised to use the appropriate one");
|
||||||
if (IgnoreLoaderArchDifference.empty()) {
|
if (!IgnoreLoaderArchDifference) {
|
||||||
MessageBoxA(NULL, "Arch of loader and requested exe are different,\nit is advised to use the appropriate one.", "ColdClientLoader", MB_OK);
|
MessageBoxA(NULL, "Arch of loader and requested exe are different,\nit is advised to use the appropriate one.", "ColdClientLoader", MB_OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDllsToInjectFolderFound) {
|
if (isDllsToInjectFolderFound) {
|
||||||
std::wstring failed_dlls{};
|
std::string failed_dlls{};
|
||||||
dlls_to_inject = collect_dlls_to_inject(DllsToInjectFolder, is_exe_32, failed_dlls);
|
dlls_to_inject = collect_dlls_to_inject(is_exe_32, failed_dlls);
|
||||||
if (failed_dlls.size() && IgnoreInjectionError.empty()) {
|
if (failed_dlls.size() && !IgnoreInjectionError) {
|
||||||
int choice = MessageBoxW(
|
int choice = MessageBoxA(
|
||||||
NULL,
|
NULL,
|
||||||
(L"The following dlls cannot be injected:\n" + failed_dlls + L"\nContinue ?").c_str(),
|
("The following dlls cannot be injected:\n" + failed_dlls + "\nContinue ?").c_str(),
|
||||||
L"ColdClientLoader",
|
"ColdClientLoader",
|
||||||
MB_YESNO);
|
MB_YESNO);
|
||||||
if (choice != IDYES) {
|
if (choice != IDYES) {
|
||||||
dbg_log::close();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ForceInjectGameOverlayRenderer.size()) {
|
if (ForceInjectGameOverlayRenderer) {
|
||||||
if (is_exe_32) {
|
if (is_exe_32) {
|
||||||
std::wstring GameOverlayPath = common_helpers::to_absolute(
|
auto GameOverlayPath = common_helpers::to_absolute(
|
||||||
L"GameOverlayRenderer.dll",
|
"GameOverlayRenderer.dll",
|
||||||
std::filesystem::path(ClientPath).parent_path().wstring()
|
std::filesystem::u8path(ClientPath).parent_path().u8string()
|
||||||
);
|
);
|
||||||
if (!common_helpers::file_exist(GameOverlayPath)) {
|
if (!common_helpers::file_exist(GameOverlayPath)) {
|
||||||
dbg_log::write("Couldn't find GameOverlayRenderer.dll");
|
logger.write("Couldn't find GameOverlayRenderer.dll");
|
||||||
MessageBoxA(NULL, "Couldn't find GameOverlayRenderer.dll.", "ColdClientLoader", MB_ICONERROR);
|
MessageBoxA(NULL, "Couldn't find GameOverlayRenderer.dll to inject.", "ColdClientLoader", MB_ICONWARNING);
|
||||||
} else {
|
} else {
|
||||||
dlls_to_inject.insert(dlls_to_inject.begin(), GameOverlayPath);
|
dlls_to_inject.insert(dlls_to_inject.begin(), GameOverlayPath);
|
||||||
}
|
}
|
||||||
} else { // 64
|
} else { // 64
|
||||||
std::wstring GameOverlay64Path = common_helpers::to_absolute(
|
auto GameOverlay64Path = common_helpers::to_absolute(
|
||||||
L"GameOverlayRenderer64.dll",
|
"GameOverlayRenderer64.dll",
|
||||||
std::filesystem::path(Client64Path).parent_path().wstring()
|
std::filesystem::u8path(Client64Path).parent_path().u8string()
|
||||||
);
|
);
|
||||||
if (!common_helpers::file_exist(GameOverlay64Path)) {
|
if (!common_helpers::file_exist(GameOverlay64Path)) {
|
||||||
dbg_log::write("Couldn't find GameOverlayRenderer64.dll");
|
logger.write("Couldn't find GameOverlayRenderer64.dll");
|
||||||
MessageBoxA(NULL, "Couldn't find GameOverlayRenderer64.dll.", "ColdClientLoader", MB_ICONERROR);
|
MessageBoxA(NULL, "Couldn't find GameOverlayRenderer64.dll to inject.", "ColdClientLoader", MB_ICONWARNING);
|
||||||
} else {
|
} else {
|
||||||
dlls_to_inject.insert(dlls_to_inject.begin(), GameOverlay64Path);
|
dlls_to_inject.insert(dlls_to_inject.begin(), GameOverlay64Path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ForceInjectSteamClient.size()) {
|
if (ForceInjectSteamClient) {
|
||||||
if (is_exe_32) {
|
if (is_exe_32) {
|
||||||
dlls_to_inject.insert(dlls_to_inject.begin(), ClientPath);
|
dlls_to_inject.insert(dlls_to_inject.begin(), ClientPath);
|
||||||
} else {
|
} 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]) {
|
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);
|
set_steam_env_vars(AppId);
|
||||||
} else {
|
} 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);
|
MessageBoxA(NULL, "You forgot to set the AppId.", "ColdClientLoader", MB_ICONERROR);
|
||||||
return 1;
|
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_1[] = L"steam://run/";
|
||||||
constexpr const static wchar_t STEAM_LAUNCH_CMD_2[] = L"-- \"steam://rungameid/";
|
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
|
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();
|
auto my_cmd = lpCmdLine && lpCmdLine[0]
|
||||||
dbg_log::write(L"persistent mode 2 detecting steam launch cmd from: '" + my_cmd + L"'");
|
? 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) {
|
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"));
|
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")) );
|
||||||
dbg_log::write("persistent mode 2 got steam launch cmd #1");
|
logger.write("persistent mode 2 got steam launch cmd #1");
|
||||||
} else if (my_cmd.find(STEAM_LAUNCH_CMD_2) == 0) {
|
} 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"));
|
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")) );
|
||||||
dbg_log::write("persistent mode 2 got steam launch cmd #2");
|
logger.write("persistent mode 2 got steam launch cmd #2");
|
||||||
} else {
|
} 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()) {
|
if (AppId.size()) {
|
||||||
set_steam_env_vars(AppId);
|
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 {
|
} 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()) {
|
if (!patch_registry_hkcu()) {
|
||||||
dbg_log::close();
|
|
||||||
MessageBoxA(NULL, "Unable to patch Registry (HKCU).", "ColdClientLoader", MB_ICONERROR);
|
MessageBoxA(NULL, "Unable to patch Registry (HKCU).", "ColdClientLoader", MB_ICONERROR);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!patch_registry_hklm()) {
|
if (!patch_registry_hklm()) {
|
||||||
cleanup_registry_hkcu();
|
cleanup_registry_hkcu();
|
||||||
|
|
||||||
dbg_log::close();
|
|
||||||
MessageBoxA(NULL, "Unable to patch Registry (HKLM).", "ColdClientLoader", MB_ICONERROR);
|
MessageBoxA(NULL, "Unable to patch Registry (HKLM).", "ColdClientLoader", MB_ICONERROR);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
patch_registry_hkcs();
|
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_hkcu();
|
||||||
// cleanup_registry_hklm();
|
// cleanup_registry_hklm();
|
||||||
|
|
||||||
// dbg_log::close();
|
// logger.write("Unable to patch Registry (HKCS).");
|
||||||
// MessageBoxA(NULL, "Unable to patch Registry (HKCS).", "ColdClientLoader", MB_ICONERROR);
|
// MessageBoxA(NULL, "Unable to patch Registry (HKCS).", "ColdClientLoader", MB_ICONERROR);
|
||||||
// return 1;
|
// 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 };
|
STARTUPINFOW info = { 0 };
|
||||||
|
|
||||||
SecureZeroMemory(&info, sizeof(info));
|
SecureZeroMemory(&info, sizeof(info));
|
||||||
@ -597,33 +609,33 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
|||||||
PROCESS_INFORMATION processInfo = { 0 };
|
PROCESS_INFORMATION processInfo = { 0 };
|
||||||
SecureZeroMemory(&processInfo, sizeof(processInfo));
|
SecureZeroMemory(&processInfo, sizeof(processInfo));
|
||||||
|
|
||||||
dbg_log::write("spawning the requested EXE file");
|
logger.write("spawning the requested EXE file");
|
||||||
WCHAR CommandLine[16384] = { 0 };
|
const auto exe_file = common_helpers::to_wstr(ExeFile);
|
||||||
_snwprintf(CommandLine, _countof(CommandLine), L"\"%ls\" %ls %ls", ExeFile.c_str(), ExeCommandLine.c_str(), lpCmdLine);
|
std::wstringstream cmdline{};
|
||||||
if (!CreateProcessW(ExeFile.c_str(), CommandLine, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, ExeRunDir.c_str(), &info, &processInfo)) {
|
cmdline << L"\"" << exe_file << L"\" " << common_helpers::to_wstr(ExeCommandLine) << L" " << lpCmdLine;
|
||||||
dbg_log::write("Unable to load the requested EXE file");
|
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_hkcu();
|
||||||
cleanup_registry_hklm();
|
cleanup_registry_hklm();
|
||||||
cleanup_registry_hkcs();
|
cleanup_registry_hkcs();
|
||||||
|
|
||||||
dbg_log::close();
|
|
||||||
MessageBoxA(NULL, "Unable to load the requested EXE file.", "ColdClientLoader", MB_ICONERROR);
|
MessageBoxA(NULL, "Unable to load the requested EXE file.", "ColdClientLoader", MB_ICONERROR);
|
||||||
return 1;
|
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) {
|
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;
|
const char *err_inject = nullptr;
|
||||||
DWORD code = pe_helpers::loadlib_remote(processInfo.hProcess, dll, &err_inject);
|
DWORD code = pe_helpers::loadlib_remote(processInfo.hProcess, dll, &err_inject);
|
||||||
if (code != ERROR_SUCCESS) {
|
if (code != ERROR_SUCCESS) {
|
||||||
auto err_full =
|
auto err_full =
|
||||||
L"Failed to inject the dll: " + dll + L"\n" +
|
std::string("Failed to inject the dll: ") + dll + "\n" +
|
||||||
common_helpers::str_to_w(err_inject) + L"\n" +
|
err_inject + "\n" +
|
||||||
common_helpers::str_to_w(pe_helpers::get_err_string(code)) + L"\n" +
|
pe_helpers::get_err_string(code) + "\n" +
|
||||||
L"Error code = " + std::to_wstring(code) + L"\n";
|
"Error code = " + std::to_string(code) + "\n";
|
||||||
dbg_log::write(err_full);
|
logger.write(err_full);
|
||||||
if (IgnoreInjectionError.empty()) {
|
if (!IgnoreInjectionError) {
|
||||||
TerminateProcess(processInfo.hProcess, 1);
|
TerminateProcess(processInfo.hProcess, 1);
|
||||||
CloseHandle(processInfo.hProcess);
|
CloseHandle(processInfo.hProcess);
|
||||||
CloseHandle(processInfo.hThread);
|
CloseHandle(processInfo.hThread);
|
||||||
@ -632,25 +644,26 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
|||||||
cleanup_registry_hklm();
|
cleanup_registry_hklm();
|
||||||
cleanup_registry_hkcs();
|
cleanup_registry_hkcs();
|
||||||
|
|
||||||
dbg_log::close();
|
MessageBoxA(NULL, err_full.c_str(), "ColdClientLoader", MB_ICONERROR);
|
||||||
MessageBoxW(NULL, err_full.c_str(), L"ColdClientLoader", MB_ICONERROR);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dbg_log::write("Injected!");
|
logger.write("Injected!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// run
|
// run
|
||||||
if (ResumeByDebugger.empty()) {
|
if (!ResumeByDebugger) {
|
||||||
dbg_log::write("resuming the main thread of the exe");
|
logger.write("resuming the main thread of the exe");
|
||||||
// MessageBoxA(nullptr, "Wait then press ok", "Wait some seconds", MB_OK);
|
// 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
|
// 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);
|
ResumeThread(processInfo.hThread);
|
||||||
} else {
|
} else {
|
||||||
std::string msg = "Attach a debugger now to PID " + std::to_string(processInfo.dwProcessId) + " and resume its main thread";
|
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);
|
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);
|
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);
|
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);
|
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_hkcu();
|
||||||
cleanup_registry_hklm();
|
cleanup_registry_hklm();
|
||||||
cleanup_registry_hkcs();
|
cleanup_registry_hkcs();
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg_log::close();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user