mirror of
https://github.com/Detanup01/gbe_fork.git
synced 2024-12-24 09:24:15 +08:00
added persistent modes to cold client loader + refactored ini sections
This commit is contained in:
parent
7c1f7dc4ab
commit
3482c6fae3
4
.github/workflows/build-linux.yml
vendored
4
.github/workflows/build-linux.yml
vendored
@ -156,7 +156,7 @@ jobs:
|
||||
run: sudo chmod 77 package_linux.sh && sudo ./package_linux.sh release
|
||||
|
||||
### upload artifact/package to github Actions (release mode)
|
||||
- name: Upload build pacakge (release)
|
||||
- name: Upload build package (release)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "build-linux-release-${{ github.sha }}"
|
||||
@ -178,7 +178,7 @@ jobs:
|
||||
run: sudo chmod 77 package_linux.sh && sudo ./package_linux.sh debug
|
||||
|
||||
### upload artifact/package to github Actions (debug mode)
|
||||
- name: Upload build pacakge (debug)
|
||||
- name: Upload build package (debug)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "build-linux-debug-${{ github.sha }}"
|
||||
|
4
.github/workflows/build-win.yml
vendored
4
.github/workflows/build-win.yml
vendored
@ -157,7 +157,7 @@ jobs:
|
||||
run: package_win.bat release
|
||||
|
||||
### upload artifact/package to github Actions (release mode)
|
||||
- name: Upload build pacakge (release)
|
||||
- name: Upload build package (release)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "build-win-release-${{ github.sha }}"
|
||||
@ -179,7 +179,7 @@ jobs:
|
||||
run: package_win.bat debug
|
||||
|
||||
### upload artifact/package to github Actions (debug mode)
|
||||
- name: Upload build pacakge (debug)
|
||||
- name: Upload build package (debug)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "build-win-debug-${{ github.sha }}"
|
||||
|
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,3 +1,13 @@
|
||||
## 2024/2/7
|
||||
|
||||
* new persistent modes for cold client loader, mode 2 is a more accurate simulation and allows launching apps from their .exe
|
||||
|
||||
---
|
||||
|
||||
* **[Breaking]** changed the ini sections of the cold client loader
|
||||
|
||||
---
|
||||
|
||||
## 2024/1/26
|
||||
|
||||
* **[Detanup01]** added a new command line option for the tool `generate_emu_config` to disable the generation of `disable_xxx.txt` files,
|
||||
|
@ -107,7 +107,7 @@ This will:
|
||||
Additional arguments you can pass to this script:
|
||||
* `-j <n>`: build with `<n>` parallel jobs, by default 70% of the available threads
|
||||
* `-verbose`: output compiler/linker commands used by `CMAKE`
|
||||
* `-packages_only`: install the required Linux pacakges via `apt isntall` and exit (don't rebuild)
|
||||
* `-packages_only`: install the required Linux packages via `apt isntall` and exit (don't rebuild)
|
||||
|
||||
---
|
||||
|
||||
|
@ -34,6 +34,12 @@ You do not need to create a `steam_interfaces.txt` file for the `steamclient` ve
|
||||
* `IgnoreLoaderArchDifference`: don't display an error message if the architecture of the loader is different from the app.
|
||||
this will result in a silent failure if a dll injection didn't succeed.
|
||||
both the loader and the app must have the same arch for the injection to work
|
||||
* `Mode` (in `[Persistence]` section):
|
||||
- 0 = turned off
|
||||
- 1 = loader will spawn the exe and keep hanging in the background until you press "OK"
|
||||
- 2 = loader will NOT spawn exe, it will just setup the required environemnt and keep hanging
|
||||
in the background until you run the exe manually, and press "OK" when you've finished playing.
|
||||
This should help in scenarios where an external app has to launch the game, it is also recommended to run the loader with admin rights in this mode
|
||||
|
||||
|
||||
**Note** that any arguments passed to `steamclient_loader.exe` via command line will be passed to the target `.exe`.
|
||||
|
@ -13,11 +13,29 @@
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
|
||||
static const std::wstring IniFile = pe_helpers::get_current_exe_path_w() + L"ColdClientLoader.ini";
|
||||
static const std::wstring dbg_file = pe_helpers::get_current_exe_path_w() + L"COLD_LDR_LOG.txt";
|
||||
constexpr static const char STEAM_UNIVERSE[] = "Public";
|
||||
constexpr static const char STEAM_URL_PROTOCOL[] = "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");
|
||||
|
||||
// Declare some variables to be used for Steam registry.
|
||||
const DWORD UserId = 0x03100004771F810D & 0xffffffff;
|
||||
const DWORD ProcessID = GetCurrentProcessId();
|
||||
|
||||
std::wstring ClientPath{};
|
||||
std::wstring Client64Path{};
|
||||
|
||||
std::vector<std::wstring> dlls_to_inject{};
|
||||
|
||||
|
||||
|
||||
|
||||
std::wstring get_ini_value(LPCWSTR section, LPCWSTR key, LPCWSTR default_val = NULL)
|
||||
{
|
||||
@ -144,6 +162,175 @@ static void to_bool_ini_val(std::wstring &val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool orig_steam_hkcu = false;
|
||||
WCHAR OrgSteamCDir_hkcu[8192] = { 0 };
|
||||
DWORD Size1_hkcu = _countof(OrgSteamCDir_hkcu);
|
||||
WCHAR OrgSteamCDir64_hkcu[8192] = { 0 };
|
||||
DWORD Size2_hkcu = _countof(OrgSteamCDir64_hkcu);
|
||||
bool patch_registry_hkcu()
|
||||
{
|
||||
HKEY Registrykey = { 0 };
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
|
||||
orig_steam_hkcu = true;
|
||||
// Get original values to restore later.
|
||||
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");
|
||||
} 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");
|
||||
} else {
|
||||
dbg_log::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));
|
||||
RegCloseKey(Registrykey);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cleanup_registry_hkcu()
|
||||
{
|
||||
if (!orig_steam_hkcu) return;
|
||||
|
||||
dbg_log::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.
|
||||
RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (LPBYTE)OrgSteamCDir_hkcu, Size1_hkcu);
|
||||
RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (LPBYTE)OrgSteamCDir64_hkcu, Size2_hkcu);
|
||||
|
||||
// Close the HKEY Handle.
|
||||
RegCloseKey(Registrykey);
|
||||
} else {
|
||||
dbg_log::write("Unable to restore the original registry entry (HKCU), error = " + std::to_string(GetLastError()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool orig_steam_hklm = false;
|
||||
WCHAR OrgInstallPath_hklm[8192] = { 0 };
|
||||
DWORD Size1_hklm = _countof(OrgInstallPath_hklm);
|
||||
bool patch_registry_hklm()
|
||||
{
|
||||
HKEY Registrykey = { 0 };
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
|
||||
orig_steam_hklm = true;
|
||||
// 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");
|
||||
} 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");
|
||||
} else {
|
||||
dbg_log::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));
|
||||
RegCloseKey(Registrykey);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cleanup_registry_hklm()
|
||||
{
|
||||
if (!orig_steam_hklm) return;
|
||||
|
||||
dbg_log::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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool orig_steam_hkcs_1 = false;
|
||||
bool orig_steam_hkcs_2 = false;
|
||||
WCHAR OrgCommand_hkcs[8192] = { 0 };
|
||||
DWORD Size1_hkcs = _countof(OrgCommand_hkcs);
|
||||
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");
|
||||
} 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");
|
||||
} else {
|
||||
dbg_log::write("Unable to patch Registry (HKCS) #1, error = " + std::to_string(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
HKEY Registrykey_2 = { 0 };
|
||||
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam\\Shell\\Open\\Command", 0, KEY_ALL_ACCESS, &Registrykey_2) == ERROR_SUCCESS) {
|
||||
orig_steam_hkcs_2 = true;
|
||||
// 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");
|
||||
} 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");
|
||||
} else {
|
||||
dbg_log::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(""));
|
||||
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]));
|
||||
RegCloseKey(Registrykey_2);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cleanup_registry_hkcs()
|
||||
{
|
||||
if (orig_steam_hkcs_2) {
|
||||
dbg_log::write("restoring registry entries (HKCU) #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()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!orig_steam_hkcs_1) {
|
||||
dbg_log::write("removing registry entries (HKCU) #2 (added by loader)");
|
||||
HKEY Registrykey = { 0 };
|
||||
RegDeleteKeyA(HKEY_CLASSES_ROOT, "steam");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void set_steam_env_vars(const std::wstring &AppId)
|
||||
{
|
||||
SetEnvironmentVariableW(L"SteamAppId", AppId.c_str());
|
||||
SetEnvironmentVariableW(L"SteamGameId", AppId.c_str());
|
||||
SetEnvironmentVariableW(L"SteamOverlayGameId", AppId.c_str());
|
||||
}
|
||||
|
||||
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
|
||||
{
|
||||
dbg_log::init(dbg_file.c_str());
|
||||
@ -155,13 +342,12 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::wstring Client64Path = common_helpers::to_absolute(
|
||||
get_ini_value(L"SteamClient", L"SteamClient64Dll"),
|
||||
ClientPath = common_helpers::to_absolute(
|
||||
get_ini_value(L"SteamClient", L"SteamClientDll"),
|
||||
pe_helpers::get_current_exe_path_w()
|
||||
);
|
||||
|
||||
std::wstring ClientPath = common_helpers::to_absolute(
|
||||
get_ini_value(L"SteamClient", L"SteamClientDll"),
|
||||
Client64Path = common_helpers::to_absolute(
|
||||
get_ini_value(L"SteamClient", L"SteamClient64Dll"),
|
||||
pe_helpers::get_current_exe_path_w()
|
||||
);
|
||||
std::wstring ExeFile = common_helpers::to_absolute(
|
||||
@ -174,22 +360,26 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
);
|
||||
std::wstring ExeCommandLine = get_ini_value(L"SteamClient", L"ExeCommandLine");
|
||||
std::wstring AppId = get_ini_value(L"SteamClient", L"AppId");
|
||||
std::wstring ForceInjectSteamClient = get_ini_value(L"SteamClient", L"ForceInjectSteamClient");
|
||||
|
||||
std::wstring ResumeByDebugger = get_ini_value(L"Debug", L"ResumeByDebugger");
|
||||
|
||||
// dlls to inject
|
||||
std::wstring ForceInjectSteamClient = get_ini_value(L"Injection", L"ForceInjectSteamClient");
|
||||
std::wstring DllsToInjectFolder = common_helpers::to_absolute(
|
||||
get_ini_value(L"Extra", L"DllsToInjectFolder"),
|
||||
get_ini_value(L"Injection", L"DllsToInjectFolder"),
|
||||
pe_helpers::get_current_exe_path_w()
|
||||
);
|
||||
std::wstring IgnoreInjectionError = get_ini_value(L"Extra", L"IgnoreInjectionError", L"1");
|
||||
std::wstring IgnoreLoaderArchDifference = get_ini_value(L"Extra", L"IgnoreLoaderArchDifference", L"0");
|
||||
std::wstring IgnoreInjectionError = get_ini_value(L"Injection", L"IgnoreInjectionError", L"1");
|
||||
std::wstring IgnoreLoaderArchDifference = get_ini_value(L"Injection", L"IgnoreLoaderArchDifference", L"0");
|
||||
|
||||
std::wstring PersistentMode = get_ini_value(L"Persistence", L"Mode", L"0");
|
||||
|
||||
// debug
|
||||
std::wstring ResumeByDebugger = get_ini_value(L"Debug", L"ResumeByDebugger");
|
||||
|
||||
to_bool_ini_val(ResumeByDebugger);
|
||||
to_bool_ini_val(ForceInjectSteamClient);
|
||||
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
|
||||
dbg_log::write(L"SteamClient::Exe: " + ExeFile);
|
||||
@ -198,19 +388,24 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
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"Injection::DllsToInjectFolder: " + DllsToInjectFolder);
|
||||
dbg_log::write(L"Injection::IgnoreInjectionError: " + IgnoreInjectionError);
|
||||
dbg_log::write(L"Injection::IgnoreLoaderArchDifference: " + IgnoreLoaderArchDifference);
|
||||
dbg_log::write(L"Debug::ResumeByDebugger: " + ResumeByDebugger);
|
||||
dbg_log::write(L"Extra::DllsToInjectFolder: " + DllsToInjectFolder);
|
||||
dbg_log::write(L"Extra::IgnoreInjectionError: " + IgnoreInjectionError);
|
||||
dbg_log::write(L"Extra::IgnoreLoaderArchDifference: " + IgnoreLoaderArchDifference);
|
||||
|
||||
if (AppId.size() && AppId[0]) {
|
||||
SetEnvironmentVariableW(L"SteamAppId", AppId.c_str());
|
||||
SetEnvironmentVariableW(L"SteamGameId", AppId.c_str());
|
||||
SetEnvironmentVariableW(L"SteamOverlayGameId", AppId.c_str());
|
||||
} else {
|
||||
dbg_log::write("You forgot to set the AppId");
|
||||
MessageBoxA(NULL, "You forgot to set the AppId.", "ColdClientLoader", MB_ICONERROR);
|
||||
if (!common_helpers::file_exist(Client64Path)) {
|
||||
dbg_log::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");
|
||||
MessageBoxA(NULL, "Couldn't find the requested SteamClientDll.", "ColdClientLoader", MB_ICONERROR);
|
||||
dbg_log::close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -231,30 +426,19 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
dbg_log::close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!common_helpers::file_exist(Client64Path)) {
|
||||
dbg_log::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");
|
||||
MessageBoxA(NULL, "Couldn't find the requested SteamClientDll.", "ColdClientLoader", MB_ICONERROR);
|
||||
dbg_log::close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
bool isDllsToInjectFolderFound = false;
|
||||
if (DllsToInjectFolder.size()) {
|
||||
if (!common_helpers::dir_exist(DllsToInjectFolder)) {
|
||||
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);
|
||||
dbg_log::close();
|
||||
return 1;
|
||||
// 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);
|
||||
if (exe_header.empty()) {
|
||||
dbg_log::write("Couldn't read the exe header");
|
||||
@ -277,8 +461,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
dbg_log::write("Detected exe arch: x64");
|
||||
}
|
||||
|
||||
bool loader_is_32 = pe_helpers::is_module_32(GetModuleHandleW(NULL));
|
||||
if (pe_helpers::is_module_32(GetModuleHandleW(NULL))) {
|
||||
if (loader_is_32) {
|
||||
dbg_log::write("Detected loader arch: x32");
|
||||
} else {
|
||||
dbg_log::write("Detected loader arch: x64");
|
||||
@ -291,8 +474,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::wstring> dlls_to_inject{};
|
||||
if (DllsToInjectFolder.size()) {
|
||||
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()) {
|
||||
@ -307,63 +489,6 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HKEY Registrykey = { 0 };
|
||||
// Declare some variables to be used for Steam registry.
|
||||
DWORD UserId = 0x03100004771F810D & 0xffffffff;
|
||||
DWORD ProcessID = GetCurrentProcessId();
|
||||
|
||||
bool orig_steam = false;
|
||||
DWORD keyType = REG_SZ;
|
||||
WCHAR OrgSteamCDir[8192] = { 0 };
|
||||
WCHAR OrgSteamCDir64[8192] = { 0 };
|
||||
DWORD Size1 = _countof(OrgSteamCDir);
|
||||
DWORD Size2 = _countof(OrgSteamCDir64);
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS)
|
||||
{
|
||||
orig_steam = true;
|
||||
// Get original values to restore later.
|
||||
RegQueryValueExW(Registrykey, L"SteamClientDll", 0, &keyType, (LPBYTE)& OrgSteamCDir, &Size1);
|
||||
RegQueryValueExW(Registrykey, L"SteamClientDll64", 0, &keyType, (LPBYTE)& OrgSteamCDir64, &Size2);
|
||||
} 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("Unable to patch Steam process informations on the Windows registry (ActiveProcess), error = " + std::to_string(GetLastError()));
|
||||
MessageBoxA(NULL, "Unable to patch Steam process informations on the Windows registry.", "ColdClientLoader", MB_ICONERROR);
|
||||
dbg_log::close();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set values to Windows registry.
|
||||
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));
|
||||
|
||||
// Close the HKEY Handle.
|
||||
RegCloseKey(Registrykey);
|
||||
|
||||
// spawn the exe
|
||||
STARTUPINFOW info = { 0 };
|
||||
SecureZeroMemory(&info, sizeof(info));
|
||||
info.cb = sizeof(info);
|
||||
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
SecureZeroMemory(&processInfo, sizeof(processInfo));
|
||||
|
||||
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");
|
||||
MessageBoxA(NULL, "Unable to load the requested EXE file.", "ColdClientLoader", MB_ICONERROR);
|
||||
dbg_log::close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ForceInjectSteamClient.size()) {
|
||||
if (is_exe_32) {
|
||||
dlls_to_inject.insert(dlls_to_inject.begin(), ClientPath);
|
||||
@ -371,57 +496,143 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
dlls_to_inject.insert(dlls_to_inject.begin(), Client64Path);
|
||||
}
|
||||
}
|
||||
for (const auto &dll : dlls_to_inject) {
|
||||
dbg_log::write(L"Injecting dll: '" + dll + L"' ...");
|
||||
const char *err_inject = nullptr;
|
||||
DWORD code = pe_helpers::loadlib_remote(processInfo.hProcess, dll, &err_inject);
|
||||
if (code != ERROR_SUCCESS) {
|
||||
std::wstring 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()) {
|
||||
TerminateProcess(processInfo.hProcess, 1);
|
||||
CloseHandle(processInfo.hProcess);
|
||||
CloseHandle(processInfo.hThread);
|
||||
MessageBoxW(NULL, err_full.c_str(), L"ColdClientLoader", MB_ICONERROR);
|
||||
dbg_log::close();
|
||||
return 1;
|
||||
|
||||
if (PersistentMode != L"2" ) {
|
||||
if (AppId.size() && AppId[0]) {
|
||||
set_steam_env_vars(AppId);
|
||||
} else {
|
||||
dbg_log::write("You forgot to set the AppId");
|
||||
MessageBoxA(NULL, "You forgot to set the AppId.", "ColdClientLoader", MB_ICONERROR);
|
||||
return 1;
|
||||
}
|
||||
} else { // steam://run/
|
||||
constexpr const static wchar_t STEAM_LAUNCH_CMD_1[] = L"steam://run/";
|
||||
constexpr const static wchar_t STEAM_LAUNCH_CMD_2[] = L"run/";
|
||||
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);
|
||||
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");
|
||||
} 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");
|
||||
} else {
|
||||
dbg_log::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);
|
||||
} else {
|
||||
dbg_log::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;
|
||||
}
|
||||
|
||||
// if (!patch_registry_hkcs()) { // this fails due to admin rights, not a big deal
|
||||
// cleanup_registry_hkcu();
|
||||
// cleanup_registry_hklm();
|
||||
|
||||
// dbg_log::close();
|
||||
// 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
|
||||
STARTUPINFOW info = { 0 };
|
||||
|
||||
SecureZeroMemory(&info, sizeof(info));
|
||||
info.cb = sizeof(info);
|
||||
|
||||
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");
|
||||
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");
|
||||
for (const auto &dll : dlls_to_inject) {
|
||||
dbg_log::write(L"Injecting dll: '" + dll + L"' ...");
|
||||
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()) {
|
||||
TerminateProcess(processInfo.hProcess, 1);
|
||||
CloseHandle(processInfo.hProcess);
|
||||
CloseHandle(processInfo.hThread);
|
||||
|
||||
cleanup_registry_hkcu();
|
||||
cleanup_registry_hklm();
|
||||
cleanup_registry_hkcs();
|
||||
|
||||
dbg_log::close();
|
||||
MessageBoxW(NULL, err_full.c_str(), L"ColdClientLoader", MB_ICONERROR);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
dbg_log::write("Injected!");
|
||||
}
|
||||
} else {
|
||||
dbg_log::write("Injected!");
|
||||
}
|
||||
|
||||
// run
|
||||
if (ResumeByDebugger.empty()) {
|
||||
dbg_log::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));
|
||||
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);
|
||||
MessageBoxA(NULL, msg.c_str(), "ColdClientLoader", MB_OK);
|
||||
}
|
||||
|
||||
// wait
|
||||
WaitForSingleObject(processInfo.hThread, INFINITE);
|
||||
|
||||
CloseHandle(processInfo.hProcess);
|
||||
CloseHandle(processInfo.hThread);
|
||||
}
|
||||
|
||||
// run
|
||||
if (ResumeByDebugger.empty()) {
|
||||
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);
|
||||
MessageBoxA(NULL, msg.c_str(), "ColdClientLoader", MB_OK);
|
||||
if (PersistentMode == L"1" || (PersistentMode == L"2" && AppId.empty()) ) {
|
||||
MessageBoxA(NULL, "Press OK when you have finished playing to close the loader", "Cold Client Loader (waiting)", MB_OK);
|
||||
}
|
||||
|
||||
// wait
|
||||
WaitForSingleObject(processInfo.hThread, INFINITE);
|
||||
|
||||
CloseHandle(processInfo.hProcess);
|
||||
CloseHandle(processInfo.hThread);
|
||||
|
||||
if (orig_steam) {
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS)
|
||||
{
|
||||
// Restore the values.
|
||||
RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (LPBYTE)OrgSteamCDir, Size1);
|
||||
RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (LPBYTE)OrgSteamCDir64, Size2);
|
||||
|
||||
// Close the HKEY Handle.
|
||||
RegCloseKey(Registrykey);
|
||||
} else {
|
||||
dbg_log::write("Unable to restore the original Steam process informations in the Windows registry, error = " + std::to_string(GetLastError()));
|
||||
}
|
||||
if (PersistentMode != L"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();
|
||||
|
@ -6,7 +6,7 @@ Exe=game.exe
|
||||
ExeRunDir=
|
||||
# any additional args to pass, ex: -dx11, also any args passed to the loader will be passed to the app
|
||||
ExeCommandLine=
|
||||
# IMPORTANT
|
||||
# IMPORTANT, unless [Persistence] Mode=2
|
||||
AppId=
|
||||
|
||||
# path to the steamclient dlls, both must be set,
|
||||
@ -14,14 +14,10 @@ AppId=
|
||||
SteamClientDll=steamclient.dll
|
||||
SteamClient64Dll=steamclient64.dll
|
||||
|
||||
[Injection]
|
||||
# force inject steamclient dll instead of waiting for the app to load it
|
||||
ForceInjectSteamClient=0
|
||||
|
||||
[Debug]
|
||||
# don't call `ResumeThread()` on the main thread after spawning the .exe
|
||||
ResumeByDebugger=0
|
||||
|
||||
[Extra]
|
||||
# path to a folder containing some dlls to inject into the app upon start
|
||||
# this folder will be traversed recursively
|
||||
# additionally, inside this folder you can create a file called `load_order.txt` and
|
||||
@ -30,9 +26,22 @@ ResumeByDebugger=0
|
||||
# example:
|
||||
#DllsToInjectFolder=extra_dlls
|
||||
DllsToInjectFolder=
|
||||
|
||||
# don't display an error message when a dll injection fails
|
||||
IgnoreInjectionError=1
|
||||
# don't display an error message if the architecture of the loader is different from the app
|
||||
# this will result in a silent failure if a dll injection didn't succeed
|
||||
# both the loader and the app must have the same arch for the injection to work
|
||||
IgnoreLoaderArchDifference=0
|
||||
|
||||
[Persistence]
|
||||
# 0 = turned off
|
||||
# 1 = loader will spawn the exe and keep hanging in the background until you press "OK"
|
||||
# 2 = loader will NOT spawn exe, it will just setup the required environemnt and keep hanging in the background
|
||||
# you have to run the Exe manually, and finally press "OK" when you've finished playing
|
||||
# it is advised to run the loader as admin in this mode
|
||||
Mode=0
|
||||
|
||||
[Debug]
|
||||
# don't call `ResumeThread()` on the main thread after spawning the .exe
|
||||
ResumeByDebugger=0
|
||||
|
Loading…
x
Reference in New Issue
Block a user