mirror of
https://github.com/Detanup01/gbe_fork.git
synced 2024-11-30 14:25:36 +08:00
a working stub patcher + lots of changes to cold client loader
This commit is contained in:
parent
00ace6727d
commit
91aac61e23
4
.github/workflows/build-win.yml
vendored
4
.github/workflows/build-win.yml
vendored
@ -148,7 +148,7 @@ jobs:
|
|||||||
- name: Build release mode
|
- name: Build release mode
|
||||||
shell: cmd
|
shell: cmd
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
run: build_win.bat release
|
run: build_win.bat +exclient-extra-32 +exclient-extra-64 release
|
||||||
|
|
||||||
### package (release mode)
|
### package (release mode)
|
||||||
- name: Package build (release)
|
- name: Package build (release)
|
||||||
@ -170,7 +170,7 @@ jobs:
|
|||||||
- name: Build debug mode
|
- name: Build debug mode
|
||||||
shell: cmd
|
shell: cmd
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
run: build_win.bat debug
|
run: build_win.bat +exclient-extra-32 +exclient-extra-64 debug
|
||||||
|
|
||||||
### package (debug mode)
|
### package (debug mode)
|
||||||
- name: Package build (debug)
|
- name: Package build (debug)
|
||||||
|
@ -149,7 +149,13 @@ Arguments you can pass to this script:
|
|||||||
|
|
||||||
* `-exclient-32`: prevent building steamclient `steamclient.dll`
|
* `-exclient-32`: prevent building steamclient `steamclient.dll`
|
||||||
* `-exclient-64`: prevent building steamclient `steamclient64.dll`
|
* `-exclient-64`: prevent building steamclient `steamclient64.dll`
|
||||||
* `-exclient-ldr`: prevent building steamclient `steamclient_loader.exe`
|
* `-exclient-ldr-32`: prevent building steamclient loader (32) `steamclient_loader_32.exe`
|
||||||
|
* `-exclient-ldr-64`: prevent building steamclient loader (64) `steamclient_loader_64.exe`
|
||||||
|
|
||||||
|
>>>>>>>>> ___
|
||||||
|
|
||||||
|
* `+exclient-extra-32`: build the 32 bit version of the additional dll `steamclient_extra.dll` which is injected by the client loader
|
||||||
|
* `+exclient-extra-64`: build the 64 bit version of the additional dll `steamclient_extra64.dll` which is injected by the client loader
|
||||||
|
|
||||||
>>>>>>>>> ___
|
>>>>>>>>> ___
|
||||||
|
|
||||||
|
@ -30,6 +30,9 @@ set /a BUILD_EXPCLIENT64=1
|
|||||||
set /a BUILD_EXPCLIENT_LDR_32=1
|
set /a BUILD_EXPCLIENT_LDR_32=1
|
||||||
set /a BUILD_EXPCLIENT_LDR_64=1
|
set /a BUILD_EXPCLIENT_LDR_64=1
|
||||||
|
|
||||||
|
set /a BUILD_EXPCLIENT_EXTRA_32=0
|
||||||
|
set /a BUILD_EXPCLIENT_EXTRA_64=0
|
||||||
|
|
||||||
set /a BUILD_TOOL_FIND_ITFS=1
|
set /a BUILD_TOOL_FIND_ITFS=1
|
||||||
set /a BUILD_TOOL_LOBBY=1
|
set /a BUILD_TOOL_LOBBY=1
|
||||||
|
|
||||||
@ -67,6 +70,10 @@ set /a VERBOSE=0
|
|||||||
set /a BUILD_EXPCLIENT_LDR_32=0
|
set /a BUILD_EXPCLIENT_LDR_32=0
|
||||||
) else if "%~1"=="-exclient-ldr-64" (
|
) else if "%~1"=="-exclient-ldr-64" (
|
||||||
set /a BUILD_EXPCLIENT_LDR_64=0
|
set /a BUILD_EXPCLIENT_LDR_64=0
|
||||||
|
) else if "%~1"=="+exclient-extra-32" (
|
||||||
|
set /a BUILD_EXPCLIENT_EXTRA_32=1
|
||||||
|
) else if "%~1"=="+exclient-extra-64" (
|
||||||
|
set /a BUILD_EXPCLIENT_EXTRA_64=1
|
||||||
) else if "%~1"=="-tool-itf" (
|
) else if "%~1"=="-tool-itf" (
|
||||||
set /a BUILD_TOOL_FIND_ITFS=0
|
set /a BUILD_TOOL_FIND_ITFS=0
|
||||||
) else if "%~1"=="-tool-lobby" (
|
) else if "%~1"=="-tool-lobby" (
|
||||||
@ -308,9 +315,6 @@ call :build_rsrc "%win_resources_src_dir%\launcher\32\resources.rc" "%win_resour
|
|||||||
echo: & echo:
|
echo: & echo:
|
||||||
|
|
||||||
if %BUILD_LIB32% equ 1 (
|
if %BUILD_LIB32% equ 1 (
|
||||||
if not exist "%build_root_dir%\x32" (
|
|
||||||
mkdir "%build_root_dir%\x32"
|
|
||||||
)
|
|
||||||
call :compile_lib32 || (
|
call :compile_lib32 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
@ -318,9 +322,6 @@ if %BUILD_LIB32% equ 1 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if %BUILD_EXP_LIB32% equ 1 (
|
if %BUILD_EXP_LIB32% equ 1 (
|
||||||
if not exist "%experimental_dir%\x32" (
|
|
||||||
mkdir "%experimental_dir%\x32"
|
|
||||||
)
|
|
||||||
call :compile_experimental_lib32 || (
|
call :compile_experimental_lib32 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
@ -328,9 +329,6 @@ if %BUILD_EXP_LIB32% equ 1 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if %BUILD_EXP_CLIENT32% equ 1 (
|
if %BUILD_EXP_CLIENT32% equ 1 (
|
||||||
if not exist "%experimental_dir%\x32" (
|
|
||||||
mkdir "%experimental_dir%\x32"
|
|
||||||
)
|
|
||||||
call :compile_experimental_client32 || (
|
call :compile_experimental_client32 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
@ -338,9 +336,6 @@ if %BUILD_EXP_CLIENT32% equ 1 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if %BUILD_EXPCLIENT32% equ 1 (
|
if %BUILD_EXPCLIENT32% equ 1 (
|
||||||
if not exist "%steamclient_dir%" (
|
|
||||||
mkdir "%steamclient_dir%"
|
|
||||||
)
|
|
||||||
call :compile_experimentalclient_32 || (
|
call :compile_experimentalclient_32 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
@ -349,19 +344,14 @@ if %BUILD_EXPCLIENT32% equ 1 (
|
|||||||
|
|
||||||
:: steamclient_loader
|
:: steamclient_loader
|
||||||
if %BUILD_EXPCLIENT_LDR_32% equ 1 (
|
if %BUILD_EXPCLIENT_LDR_32% equ 1 (
|
||||||
if not exist "%steamclient_dir%" (
|
|
||||||
mkdir "%steamclient_dir%"
|
|
||||||
)
|
|
||||||
call :compile_experimentalclient_ldr_32 || (
|
call :compile_experimentalclient_ldr_32 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
echo: & echo:
|
echo: & echo:
|
||||||
)
|
)
|
||||||
|
|
||||||
if not exist "%steamclient_dir%" (
|
if %BUILD_EXPCLIENT_EXTRA_32% equ 1 (
|
||||||
mkdir "%steamclient_dir%"
|
call :compile_experimentalclient_extra_32 || (
|
||||||
)
|
|
||||||
call :compile_experimentalclient_ldr || (
|
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
echo: & echo:
|
echo: & echo:
|
||||||
@ -369,18 +359,12 @@ if %BUILD_EXPCLIENT_LDR_32% equ 1 (
|
|||||||
|
|
||||||
:: tools (x32)
|
:: tools (x32)
|
||||||
if %BUILD_TOOL_FIND_ITFS% equ 1 (
|
if %BUILD_TOOL_FIND_ITFS% equ 1 (
|
||||||
if not exist "%find_interfaces_dir%" (
|
|
||||||
mkdir "%find_interfaces_dir%"
|
|
||||||
)
|
|
||||||
call :compile_tool_itf || (
|
call :compile_tool_itf || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
echo: & echo:
|
echo: & echo:
|
||||||
)
|
)
|
||||||
if %BUILD_TOOL_LOBBY% equ 1 (
|
if %BUILD_TOOL_LOBBY% equ 1 (
|
||||||
if not exist "%lobby_connect_dir%" (
|
|
||||||
mkdir "%lobby_connect_dir%"
|
|
||||||
)
|
|
||||||
call :compile_tool_lobby || (
|
call :compile_tool_lobby || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
@ -440,9 +424,6 @@ call :build_rsrc "%win_resources_src_dir%\launcher\64\resources.rc" "%win_resour
|
|||||||
echo: & echo:
|
echo: & echo:
|
||||||
|
|
||||||
if %BUILD_LIB64% equ 1 (
|
if %BUILD_LIB64% equ 1 (
|
||||||
if not exist "%build_root_dir%\x64" (
|
|
||||||
mkdir "%build_root_dir%\x64"
|
|
||||||
)
|
|
||||||
call :compile_lib64 || (
|
call :compile_lib64 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
@ -450,9 +431,6 @@ if %BUILD_LIB64% equ 1 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if %BUILD_EXP_LIB64% equ 1 (
|
if %BUILD_EXP_LIB64% equ 1 (
|
||||||
if not exist "%experimental_dir%\x64" (
|
|
||||||
mkdir "%experimental_dir%\x64"
|
|
||||||
)
|
|
||||||
call :compile_experimental_lib64 || (
|
call :compile_experimental_lib64 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
@ -460,9 +438,6 @@ if %BUILD_EXP_LIB64% equ 1 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if %BUILD_EXP_CLIENT64% equ 1 (
|
if %BUILD_EXP_CLIENT64% equ 1 (
|
||||||
if not exist "%experimental_dir%\x64" (
|
|
||||||
mkdir "%experimental_dir%\x64"
|
|
||||||
)
|
|
||||||
call :compile_experimental_client64 || (
|
call :compile_experimental_client64 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
@ -470,9 +445,6 @@ if %BUILD_EXP_CLIENT64% equ 1 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if %BUILD_EXPCLIENT64% equ 1 (
|
if %BUILD_EXPCLIENT64% equ 1 (
|
||||||
if not exist "%steamclient_dir%" (
|
|
||||||
mkdir "%steamclient_dir%"
|
|
||||||
)
|
|
||||||
call :compile_experimentalclient_64 || (
|
call :compile_experimentalclient_64 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
@ -481,15 +453,19 @@ if %BUILD_EXPCLIENT64% equ 1 (
|
|||||||
|
|
||||||
:: steamclient_loader
|
:: steamclient_loader
|
||||||
if %BUILD_EXPCLIENT_LDR_64% equ 1 (
|
if %BUILD_EXPCLIENT_LDR_64% equ 1 (
|
||||||
if not exist "%steamclient_dir%" (
|
|
||||||
mkdir "%steamclient_dir%"
|
|
||||||
)
|
|
||||||
call :compile_experimentalclient_ldr_64 || (
|
call :compile_experimentalclient_ldr_64 || (
|
||||||
set /a last_code+=1
|
set /a last_code+=1
|
||||||
)
|
)
|
||||||
echo: & echo:
|
echo: & echo:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if %BUILD_EXPCLIENT_EXTRA_64% equ 1 (
|
||||||
|
call :compile_experimentalclient_extra_64 || (
|
||||||
|
set /a last_code+=1
|
||||||
|
)
|
||||||
|
echo: & echo:
|
||||||
|
)
|
||||||
|
|
||||||
endlocal & set /a last_code=%last_code%
|
endlocal & set /a last_code=%last_code%
|
||||||
|
|
||||||
|
|
||||||
@ -596,6 +572,16 @@ endlocal & exit /b %_exit%
|
|||||||
)
|
)
|
||||||
endlocal & exit /b %_exit%
|
endlocal & exit /b %_exit%
|
||||||
|
|
||||||
|
:compile_experimentalclient_extra_32
|
||||||
|
setlocal
|
||||||
|
echo // building library steamclient_extra.dll - 32
|
||||||
|
set src_files="%win_resources_out_dir%\rsrc-client-32.res" "%tools_src_dir%\steamclient_loader\win\extra_protection\*.cpp" "helpers\pe_helpers.cpp" "helpers\common_helpers.cpp" "%libs_dir%\detours\*.cpp"
|
||||||
|
set extra_inc_dirs=/I"%tools_src_dir%\steamclient_loader\win\extra_protection" /I"pe_helpers"
|
||||||
|
call :build_for 1 0 "%steamclient_dir%\extra_dlls\steamclient_extra.dll" src_files extra_inc_dirs
|
||||||
|
set /a _exit=%errorlevel%
|
||||||
|
if %_exit% equ 0 (
|
||||||
|
call :change_dos_stub 1 "%steamclient_dir%\extra_dlls\steamclient_extra.dll"
|
||||||
|
call "%signer_tool%" "%steamclient_dir%\extra_dlls\steamclient_extra.dll"
|
||||||
)
|
)
|
||||||
endlocal & exit /b %_exit%
|
endlocal & exit /b %_exit%
|
||||||
|
|
||||||
@ -689,6 +675,19 @@ endlocal & exit /b %_exit%
|
|||||||
)
|
)
|
||||||
endlocal & exit /b %_exit%
|
endlocal & exit /b %_exit%
|
||||||
|
|
||||||
|
:compile_experimentalclient_extra_64
|
||||||
|
setlocal
|
||||||
|
echo // building library steamclient_extra64.dll - 64
|
||||||
|
set src_files="%win_resources_out_dir%\rsrc-client-64.res" "%tools_src_dir%\steamclient_loader\win\extra_protection\*.cpp" "helpers\pe_helpers.cpp" "helpers\common_helpers.cpp" "%libs_dir%\detours\*.cpp"
|
||||||
|
set extra_inc_dirs=/I"%tools_src_dir%\steamclient_loader\win\extra_protection" /I"pe_helpers"
|
||||||
|
call :build_for 0 0 "%steamclient_dir%\extra_dlls\steamclient_extra64.dll" src_files extra_inc_dirs
|
||||||
|
set /a _exit=%errorlevel%
|
||||||
|
if %_exit% equ 0 (
|
||||||
|
call :change_dos_stub 0 "%steamclient_dir%\extra_dlls\steamclient_extra64.dll"
|
||||||
|
call "%signer_tool%" "%steamclient_dir%\extra_dlls\steamclient_extra64.dll"
|
||||||
|
)
|
||||||
|
endlocal & exit /b %_exit%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
:err_msg
|
:err_msg
|
||||||
@ -799,6 +798,12 @@ exit /b 1
|
|||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for /f "usebackq tokens=* delims=" %%A in ('"%_out_filepath%"') do (
|
||||||
|
if not exist "%%~dpA" (
|
||||||
|
mkdir "%%~dpA"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if "%VERBOSE%" equ "1" (
|
if "%VERBOSE%" equ "1" (
|
||||||
echo cl.exe %_target_args% /Fo%_build_tmp%\ /Fe%_build_tmp%\ %debug_info% %debug_info_format% %optimization_level% %release_defs% %_extra_defs% %_runtime_type% %_target_inc_dirs% %_extra_inc_dirs% %_all_src% %_target_libs% %_extra_libs% /link %_target_linker_args% /OUT:"%_out_filepath%"
|
echo cl.exe %_target_args% /Fo%_build_tmp%\ /Fe%_build_tmp%\ %debug_info% %debug_info_format% %optimization_level% %release_defs% %_extra_defs% %_runtime_type% %_target_inc_dirs% %_extra_inc_dirs% %_all_src% %_target_libs% %_extra_libs% /link %_target_linker_args% /OUT:"%_out_filepath%"
|
||||||
echo:
|
echo:
|
||||||
|
@ -106,7 +106,7 @@ bool common_helpers::ends_with_i(const std::wstring &target, const std::wstring
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path to_absolute_impl(std::filesystem::path &path, std::filesystem::path &base)
|
std::filesystem::path to_absolute_impl(const std::filesystem::path &path, const std::filesystem::path &base)
|
||||||
{
|
{
|
||||||
if (path.is_absolute()) {
|
if (path.is_absolute()) {
|
||||||
return path;
|
return path;
|
||||||
|
@ -20,7 +20,7 @@ static inline uint8_t char_to_byte(const char c)
|
|||||||
return (uint8_t)c;
|
return (uint8_t)c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline PIMAGE_NT_HEADERS get_nt_header(HMODULE hModule)
|
PIMAGE_NT_HEADERS pe_helpers::get_nt_header(HMODULE hModule)
|
||||||
{
|
{
|
||||||
// https://dev.to/wireless90/validating-the-pe-signature-my-av-flagged-me-windows-pe-internals-2m5o/
|
// https://dev.to/wireless90/validating-the-pe-signature-my-av-flagged-me-windows-pe-internals-2m5o/
|
||||||
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)(char*)hModule;
|
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)(char*)hModule;
|
||||||
@ -29,12 +29,12 @@ static inline PIMAGE_NT_HEADERS get_nt_header(HMODULE hModule)
|
|||||||
return (PIMAGE_NT_HEADERS)((char*)hModule + newExeHeaderOffset);
|
return (PIMAGE_NT_HEADERS)((char*)hModule + newExeHeaderOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline PIMAGE_FILE_HEADER get_file_header(HMODULE hModule)
|
PIMAGE_FILE_HEADER pe_helpers::get_file_header(HMODULE hModule)
|
||||||
{
|
{
|
||||||
return &get_nt_header(hModule)->FileHeader;
|
return &get_nt_header(hModule)->FileHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline PIMAGE_OPTIONAL_HEADER get_optional_header(HMODULE hModule)
|
PIMAGE_OPTIONAL_HEADER pe_helpers::get_optional_header(HMODULE hModule)
|
||||||
{
|
{
|
||||||
return &get_nt_header(hModule)->OptionalHeader;
|
return &get_nt_header(hModule)->OptionalHeader;
|
||||||
}
|
}
|
||||||
@ -190,7 +190,10 @@ bool pe_helpers::replace_memory(uint8_t *mem, size_t size, const std::string &re
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto &rp : replace_bytes) {
|
for (auto &rp : replace_bytes) {
|
||||||
if (rp.first == 0x00) continue;
|
if (rp.first == 0x00) {
|
||||||
|
++mem;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const uint8_t b_mem = (uint8_t)(*mem & (uint8_t)~rp.first);
|
const uint8_t b_mem = (uint8_t)(*mem & (uint8_t)~rp.first);
|
||||||
const uint8_t b_replace = (uint8_t)(rp.second & rp.first);
|
const uint8_t b_replace = (uint8_t)(rp.second & rp.first);
|
||||||
@ -231,12 +234,12 @@ std::string pe_helpers::get_err_string(DWORD code)
|
|||||||
|
|
||||||
bool pe_helpers::is_module_64(HMODULE hModule)
|
bool pe_helpers::is_module_64(HMODULE hModule)
|
||||||
{
|
{
|
||||||
return !!(get_file_header(hModule)->Machine == IMAGE_FILE_MACHINE_AMD64);
|
return (get_file_header(hModule)->Machine == IMAGE_FILE_MACHINE_AMD64);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pe_helpers::is_module_32(HMODULE hModule)
|
bool pe_helpers::is_module_32(HMODULE hModule)
|
||||||
{
|
{
|
||||||
return !!(get_file_header(hModule)->Machine == IMAGE_FILE_MACHINE_I386);
|
return (get_file_header(hModule)->Machine == IMAGE_FILE_MACHINE_I386);
|
||||||
}
|
}
|
||||||
|
|
||||||
pe_helpers::SectionHeadersResult pe_helpers::get_section_headers(HMODULE hModule)
|
pe_helpers::SectionHeadersResult pe_helpers::get_section_headers(HMODULE hModule)
|
||||||
@ -339,6 +342,7 @@ DWORD pe_helpers::loadlib_remote(HANDLE hProcess, const std::wstring &lib_fullpa
|
|||||||
}
|
}
|
||||||
|
|
||||||
WaitForSingleObject(remote_thread, INFINITE);
|
WaitForSingleObject(remote_thread, INFINITE);
|
||||||
|
CloseHandle(remote_thread);
|
||||||
|
|
||||||
// cleanup allcoated page
|
// cleanup allcoated page
|
||||||
VirtualFreeEx(
|
VirtualFreeEx(
|
||||||
@ -419,3 +423,43 @@ bool pe_helpers::ends_with_i(PUNICODE_STRING target, const std::wstring &query)
|
|||||||
query
|
query
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MEMORY_BASIC_INFORMATION pe_helpers::get_mem_page_details(const void* mem)
|
||||||
|
{
|
||||||
|
MEMORY_BASIC_INFORMATION mbi{};
|
||||||
|
if (VirtualQuery(mem, &mbi, sizeof(mbi))) {
|
||||||
|
return mbi;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pe_helpers::get_current_exe_mem_size()
|
||||||
|
{
|
||||||
|
auto hmod = GetModuleHandleW(NULL);
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
MEMORY_BASIC_INFORMATION mbi{};
|
||||||
|
if (!VirtualQuery((LPVOID)hmod, &mbi, sizeof(mbi))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size = mbi.RegionSize; // PE header
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sections = get_section_headers(hmod);
|
||||||
|
if (!sections.count) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sections.count; ++i) {
|
||||||
|
auto section = sections.ptr[i];
|
||||||
|
MEMORY_BASIC_INFORMATION mbi{};
|
||||||
|
if (!VirtualQuery((LPVOID)((uint8_t *)hmod + section.VirtualAddress), &mbi, sizeof(mbi))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size = mbi.RegionSize; // actual section size in mem
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
@ -16,6 +16,12 @@ typedef struct SectionHeadersResult
|
|||||||
} SectionHeadersResult_t;
|
} SectionHeadersResult_t;
|
||||||
|
|
||||||
|
|
||||||
|
PIMAGE_NT_HEADERS get_nt_header(HMODULE hModule);
|
||||||
|
|
||||||
|
PIMAGE_FILE_HEADER get_file_header(HMODULE hModule);
|
||||||
|
|
||||||
|
PIMAGE_OPTIONAL_HEADER get_optional_header(HMODULE hModule);
|
||||||
|
|
||||||
uint8_t* search_memory(uint8_t *mem, size_t size, const std::string &search_patt);
|
uint8_t* search_memory(uint8_t *mem, size_t size, const std::string &search_patt);
|
||||||
|
|
||||||
bool replace_memory(uint8_t *mem, size_t size, const std::string &replace_patt, HANDLE hProcess);
|
bool replace_memory(uint8_t *mem, size_t size, const std::string &replace_patt, HANDLE hProcess);
|
||||||
@ -40,4 +46,8 @@ const std::wstring get_current_exe_path_w();
|
|||||||
|
|
||||||
bool ends_with_i(PUNICODE_STRING target, const std::wstring &query);
|
bool ends_with_i(PUNICODE_STRING target, const std::wstring &query);
|
||||||
|
|
||||||
|
MEMORY_BASIC_INFORMATION get_mem_page_details(const void* mem);
|
||||||
|
|
||||||
|
size_t get_current_exe_mem_size();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,18 @@ You do not need to create a `steam_interfaces.txt` file for the `steamclient` ve
|
|||||||
* `steamclient_loader.exe`
|
* `steamclient_loader.exe`
|
||||||
2. Edit `ColdClientLoader.ini` and specify:
|
2. Edit `ColdClientLoader.ini` and specify:
|
||||||
* `AppId`: the app ID
|
* `AppId`: the app ID
|
||||||
* `Exe`: the path to the game's executable/launcher, either full path or relative to this `.ini` file
|
* `Exe`: the path to the game's executable/launcher, either full path or relative to this loader
|
||||||
* `ExeRunDir`: geenrally this must be set to the folder containing the game's exe
|
* `ExeRunDir`: generally this must be set to the folder containing the game's exe, if left empty then it will be automatically set to the folder containing the game's exe.
|
||||||
* `ExeCommandLine` additional args to pass to the exe, example: `-dx11 -windowed`
|
* `ExeCommandLine` additional args to pass to the exe, example: `-dx11 -windowed`
|
||||||
Optionally you can specify a different location for `steamclient(64).dll`:
|
Optionally you can specify a different location for `steamclient(64).dll`:
|
||||||
* `SteamClientDll`: path to `steamclient.dll`, either full path or relative to this `.ini` file
|
* `SteamClientDll`: path to `steamclient.dll`, either full path or relative to this loader
|
||||||
* `SteamClientDll`: path to `steamclient(64).dll`, either full path or relative to this `.ini` file
|
* `SteamClientDll`: path to `steamclient(64).dll`, either full path or relative to this loader
|
||||||
* For debug **build** only:
|
* `ForceInjectSteamClient`: force inject `steamclient(64).dll` instead of letting the app load it automatically
|
||||||
* `ResumeByDebugger`: setting this to `1` or 'y' or `true` will prevent the loader from calling `ResumeThread` on the main thread after spawning the .exe, and it will display a mesage with the process ID (PID) so you attach your debugger on it.
|
* `ResumeByDebugger`: setting this to `1` or `y` or `true` will prevent the loader from calling `ResumeThread()` on the main thread after spawning the .exe, and it will display a mesage with the process ID (PID) so you attach your debugger on it.
|
||||||
Note that you have to resume the main thread from the debugger after attaching, also the entry breakpoint may not be set automatically, but you can do that manually.
|
Note that you have to resume the main thread from the debugger after attaching, also the entry breakpoint may not be set automatically, but you can do that manually.
|
||||||
|
* `DllsToInjectFolder`: path to a folder containing dlls to force inject into the app upon start,
|
||||||
|
the loader will attempt to detect the dll architecture (32 or 64 bit), if it didn't match the architecture of the exe, then it will ignored
|
||||||
|
* `IgnoreInjectionError`: setting this to `1` or `y` or `true` will prevent the loader from displaying an error message when a dll injection fails
|
||||||
|
|
||||||
|
|
||||||
**Note** that any arguments passed to `steamclient_loader.exe` via command line will be passed to the target `.exe`.
|
**Note** that any arguments passed to `steamclient_loader.exe` via command line will be passed to the target `.exe`.
|
||||||
|
@ -13,4 +13,5 @@ This directory contains additional resources used during build.
|
|||||||
* [api](./win/api/): contains an immitation of the resources found in `steam_api(64).dll`
|
* [api](./win/api/): contains an immitation of the resources found in `steam_api(64).dll`
|
||||||
* [client](./win/client/): contains an immitation of the resources found in `steamclient(64).dll`
|
* [client](./win/client/): contains an immitation of the resources found in `steamclient(64).dll`
|
||||||
* [launcher](./win/launcher/): contains an immitation of the resources found in `steam.exe`
|
* [launcher](./win/launcher/): contains an immitation of the resources found in `steam.exe`
|
||||||
|
* [file_dos_stub](./win/file_dos_stub/): contains an immitation of how the DOS stub is manipulated after build
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ static std::vector<uint8_t> load_file_partial(std::fstream &file)
|
|||||||
auto org_pos = file.tellg();
|
auto org_pos = file.tellg();
|
||||||
file.seekg(0, std::ios::beg);
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
// 2MB is enough
|
// 1MB is enough
|
||||||
std::vector<uint8_t> data(2 * 1024 * 1024, 0);
|
std::vector<uint8_t> data(1 * 1024 * 1024, 0);
|
||||||
file.read((char *)&data[0], data.size());
|
file.read((char *)&data[0], data.size());
|
||||||
|
|
||||||
file.seekg(org_pos, std::ios::beg);
|
file.seekg(org_pos, std::ios::beg);
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -37,6 +37,28 @@ std::wstring get_ini_value(LPCWSTR section, LPCWSTR key, LPCWSTR default_val = N
|
|||||||
return std::wstring(&buff[0], read_chars);
|
return std::wstring(&buff[0], read_chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<uint8_t> get_pe_header(const std::wstring &filepath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::ifstream file(filepath, std::ios::binary);
|
||||||
|
if (!file.is_open()) throw;
|
||||||
|
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
// 2MB is enough
|
||||||
|
std::vector<uint8_t> data(2 * 1024 * 1024, 0);
|
||||||
|
file.read((char *)&data[0], data.size());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch(const std::exception& e)
|
||||||
|
{
|
||||||
|
return std::vector<uint8_t>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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_ LPWSTR lpCmdLine, _In_ int nCmdShow)
|
||||||
{
|
{
|
||||||
dbg_log::init(dbg_file.c_str());
|
dbg_log::init(dbg_file.c_str());
|
||||||
@ -67,14 +89,28 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
|||||||
);
|
);
|
||||||
std::wstring ExeCommandLine = get_ini_value(L"SteamClient", L"ExeCommandLine");
|
std::wstring ExeCommandLine = get_ini_value(L"SteamClient", L"ExeCommandLine");
|
||||||
std::wstring AppId = get_ini_value(L"SteamClient", L"AppId");
|
std::wstring AppId = get_ini_value(L"SteamClient", L"AppId");
|
||||||
|
std::wstring InjectClient = get_ini_value(L"SteamClient", L"ForceInjectSteamClient");
|
||||||
|
|
||||||
|
std::wstring resume_by_dbg = get_ini_value(L"Debug", L"ResumeByDebugger");
|
||||||
|
|
||||||
|
// dlls to inject
|
||||||
|
std::wstring extra_dlls_folder = common_helpers::to_absolute(
|
||||||
|
get_ini_value(L"Extra", L"DllsToInjectFolder"),
|
||||||
|
pe_helpers::get_current_exe_path_w()
|
||||||
|
);
|
||||||
|
std::wstring IgnoreInjectionError = get_ini_value(L"Extra", L"IgnoreInjectionError", L"1");
|
||||||
|
|
||||||
// log everything
|
// log everything
|
||||||
dbg_log::write(L"SteamClient64Dll: " + Client64Path);
|
dbg_log::write(L"SteamClient::Exe: " + ExeFile);
|
||||||
dbg_log::write(L"SteamClient: " + ClientPath);
|
dbg_log::write(L"SteamClient::ExeRunDir: " + ExeRunDir);
|
||||||
dbg_log::write(L"Exe: " + ExeFile);
|
dbg_log::write(L"SteamClient::ExeCommandLine: " + ExeCommandLine);
|
||||||
dbg_log::write(L"ExeRunDir: " + ExeRunDir);
|
dbg_log::write(L"SteamClient::AppId: " + AppId);
|
||||||
dbg_log::write(L"ExeCommandLine: " + ExeCommandLine);
|
dbg_log::write(L"SteamClient::SteamClient: " + ClientPath);
|
||||||
dbg_log::write(L"AppId: " + AppId);
|
dbg_log::write(L"SteamClient::SteamClient64Dll: " + Client64Path);
|
||||||
|
dbg_log::write(L"SteamClient::ForceInjectSteamClient: " + InjectClient);
|
||||||
|
dbg_log::write(L"Debug::ResumeByDebugger: " + resume_by_dbg);
|
||||||
|
dbg_log::write(L"Extra::DllsToInjectFolder: " + extra_dlls_folder);
|
||||||
|
dbg_log::write(L"Extra::IgnoreInjectionError: " + IgnoreInjectionError);
|
||||||
|
|
||||||
if (AppId.size() && AppId[0]) {
|
if (AppId.size() && AppId[0]) {
|
||||||
SetEnvironmentVariableW(L"SteamAppId", AppId.c_str());
|
SetEnvironmentVariableW(L"SteamAppId", AppId.c_str());
|
||||||
@ -86,6 +122,24 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!common_helpers::file_exist(ExeFile)) {
|
||||||
|
dbg_log::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 exe run dir to: " + ExeRunDir);
|
||||||
|
}
|
||||||
|
if (!common_helpers::dir_exist(ExeRunDir)) {
|
||||||
|
dbg_log::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;
|
||||||
|
}
|
||||||
|
|
||||||
if (!common_helpers::file_exist(Client64Path)) {
|
if (!common_helpers::file_exist(Client64Path)) {
|
||||||
dbg_log::write("Couldn't find the requested SteamClient64Dll");
|
dbg_log::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);
|
||||||
@ -100,19 +154,115 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!common_helpers::file_exist(ExeFile)) {
|
for (auto &c : resume_by_dbg) {
|
||||||
dbg_log::write("Couldn't find the requested Exe file");
|
c = (wchar_t)std::tolower((int)c);
|
||||||
MessageBoxA(NULL, "Couldn't find the requested Exe file.", "ColdClientLoader", MB_ICONERROR);
|
}
|
||||||
|
if (resume_by_dbg != L"1" && resume_by_dbg != L"y" && resume_by_dbg != L"yes" && resume_by_dbg != L"true") {
|
||||||
|
resume_by_dbg.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &c : IgnoreInjectionError) {
|
||||||
|
c = (wchar_t)std::tolower((int)c);
|
||||||
|
}
|
||||||
|
if (IgnoreInjectionError != L"1" && IgnoreInjectionError != L"y" && IgnoreInjectionError != L"yes" && IgnoreInjectionError != L"true") {
|
||||||
|
IgnoreInjectionError.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &c : InjectClient) {
|
||||||
|
c = (wchar_t)std::tolower((int)c);
|
||||||
|
}
|
||||||
|
if (InjectClient != L"1" && InjectClient != L"y" && InjectClient != L"yes" && InjectClient != L"true") {
|
||||||
|
InjectClient.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extra_dlls_folder.size()) {
|
||||||
|
if (!common_helpers::dir_exist(extra_dlls_folder)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto exe_header = get_pe_header(ExeFile);
|
||||||
|
if (exe_header.empty()) {
|
||||||
|
dbg_log::write("Couldn't read the exe header");
|
||||||
|
MessageBoxA(NULL, "Couldn't read the exe header.", "ColdClientLoader", MB_ICONERROR);
|
||||||
dbg_log::close();
|
dbg_log::close();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!common_helpers::dir_exist(ExeRunDir)) {
|
bool is_exe_32 = pe_helpers::is_module_32((HMODULE)&exe_header[0]);
|
||||||
dbg_log::write("Couldn't find the requested Exe run dir");
|
bool is_exe_64 = pe_helpers::is_module_64((HMODULE)&exe_header[0]);
|
||||||
MessageBoxA(NULL, "Couldn't find the requested Exe run dir.", "ColdClientLoader", MB_ICONERROR);
|
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)");
|
||||||
|
MessageBoxA(NULL, "The requested exe is invalid (neither 32 nor 64 bit)", "ColdClientLoader", MB_ICONERROR);
|
||||||
dbg_log::close();
|
dbg_log::close();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (is_exe_32) {
|
||||||
|
dbg_log::write("Detected exe arch: x32");
|
||||||
|
} else {
|
||||||
|
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))) {
|
||||||
|
dbg_log::write("Detected loader arch: x32");
|
||||||
|
} else {
|
||||||
|
dbg_log::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");
|
||||||
|
MessageBoxA(NULL, "Arch of loader and requested exe are different,\nit is advised to use the appropriate one.", "ColdClientLoader", MB_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::wstring> dlls_to_inject{};
|
||||||
|
if (extra_dlls_folder.size()) {
|
||||||
|
std::wstring failed_dlls = std::wstring{};
|
||||||
|
|
||||||
|
for (auto const& dir_entry :
|
||||||
|
std::filesystem::recursive_directory_iterator(extra_dlls_folder, 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_header = get_pe_header(dll_path);
|
||||||
|
if (dll_header.empty()) {
|
||||||
|
dbg_log::write(L"Failed to get PE header of dll: " + dll_path);
|
||||||
|
failed_dlls += dll_path + L"\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";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((is_dll_32 && is_exe_32) || (is_dll_64 && is_exe_64)) {
|
||||||
|
dlls_to_inject.push_back(dll_path);
|
||||||
|
dbg_log::write(L"Dll " + dll_path + L" will be injected");
|
||||||
|
} 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failed_dlls.size() && IgnoreInjectionError.empty()) {
|
||||||
|
int choice = MessageBoxW(
|
||||||
|
NULL,
|
||||||
|
(L"The following dlls cannot be injected:\n" + failed_dlls + L"\nContinue ?").c_str(),
|
||||||
|
L"ColdClientLoader",
|
||||||
|
MB_YESNO);
|
||||||
|
if (choice != IDYES) {
|
||||||
|
dbg_log::close();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HKEY Registrykey = { 0 };
|
HKEY Registrykey = { 0 };
|
||||||
// Declare some variables to be used for Steam registry.
|
// Declare some variables to be used for Steam registry.
|
||||||
@ -152,23 +302,6 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
|||||||
// Close the HKEY Handle.
|
// Close the HKEY Handle.
|
||||||
RegCloseKey(Registrykey);
|
RegCloseKey(Registrykey);
|
||||||
|
|
||||||
// dll to inject
|
|
||||||
bool inject_extra_dll = false;
|
|
||||||
std::wstring extra_dll = common_helpers::to_absolute(
|
|
||||||
get_ini_value(L"Extra", L"InjectDll"),
|
|
||||||
pe_helpers::get_current_exe_path_w()
|
|
||||||
);
|
|
||||||
if (extra_dll.size()) {
|
|
||||||
dbg_log::write(L"InjectDll: " + extra_dll);
|
|
||||||
if (!common_helpers::file_exist(extra_dll)) {
|
|
||||||
dbg_log::write("Couldn't find the requested dll to inject");
|
|
||||||
MessageBoxA(NULL, "Couldn't find the requested dll to inject.", "ColdClientLoader", MB_ICONERROR);
|
|
||||||
dbg_log::close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
inject_extra_dll = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// spawn the exe
|
// spawn the exe
|
||||||
STARTUPINFOW info = { 0 };
|
STARTUPINFOW info = { 0 };
|
||||||
SecureZeroMemory(&info, sizeof(info));
|
SecureZeroMemory(&info, sizeof(info));
|
||||||
@ -187,47 +320,50 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inject_extra_dll) {
|
if (InjectClient.size()) {
|
||||||
|
if (is_exe_32) {
|
||||||
|
dlls_to_inject.insert(dlls_to_inject.begin(), ClientPath);
|
||||||
|
} else {
|
||||||
|
dlls_to_inject.insert(dlls_to_inject.begin(), Client64Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &dll_path : dlls_to_inject) {
|
||||||
const char *err_inject = nullptr;
|
const char *err_inject = nullptr;
|
||||||
DWORD code = pe_helpers::loadlib_remote(processInfo.hProcess, extra_dll, &err_inject);
|
DWORD code = pe_helpers::loadlib_remote(processInfo.hProcess, dll_path, &err_inject);
|
||||||
if (code != ERROR_SUCCESS) {
|
if (code != ERROR_SUCCESS) {
|
||||||
TerminateProcess(processInfo.hProcess, 1);
|
std::wstring err_full =
|
||||||
std::string err_full =
|
L"Failed to inject the dll: " + dll_path + L"\n" +
|
||||||
"Failed to inject the requested dll:\n" +
|
common_helpers::str_to_w(err_inject) + L"\n" +
|
||||||
std::string(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);
|
dbg_log::write(err_full);
|
||||||
MessageBoxA(NULL, err_full.c_str(), "ColdClientLoader", MB_ICONERROR);
|
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();
|
dbg_log::close();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool run_exe = true;
|
// run
|
||||||
#ifndef EMU_RELEASE_BUILD
|
if (resume_by_dbg.empty()) {
|
||||||
std::wstring resume_by_dbg = get_ini_value(L"Debug", L"ResumeByDebugger");
|
ResumeThread(processInfo.hThread);
|
||||||
dbg_log::write(L"Debug::ResumeByDebugger: " + resume_by_dbg);
|
} else {
|
||||||
for (auto &c : resume_by_dbg) {
|
|
||||||
c = (wchar_t)std::tolower((int)c);
|
|
||||||
}
|
|
||||||
if (resume_by_dbg == L"1" || resume_by_dbg == L"y" || resume_by_dbg == L"yes" || resume_by_dbg == L"true") {
|
|
||||||
run_exe = false;
|
|
||||||
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);
|
dbg_log::write(msg);
|
||||||
MessageBoxA(NULL, msg.c_str(), "ColdClientLoader", MB_OK);
|
MessageBoxA(NULL, msg.c_str(), "ColdClientLoader", MB_OK);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// run
|
|
||||||
if (run_exe) {
|
|
||||||
ResumeThread(processInfo.hThread);
|
|
||||||
}
|
|
||||||
// wait
|
// wait
|
||||||
WaitForSingleObject(processInfo.hThread, INFINITE);
|
WaitForSingleObject(processInfo.hThread, INFINITE);
|
||||||
|
|
||||||
CloseHandle(processInfo.hThread);
|
|
||||||
CloseHandle(processInfo.hProcess);
|
CloseHandle(processInfo.hProcess);
|
||||||
|
CloseHandle(processInfo.hThread);
|
||||||
|
|
||||||
if (orig_steam) {
|
if (orig_steam) {
|
||||||
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)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#My own modified version of ColdClientLoader originally by Rat431
|
# modified version of ColdClientLoader originally by Rat431
|
||||||
[SteamClient]
|
[SteamClient]
|
||||||
Exe=game.exe
|
Exe=game.exe
|
||||||
ExeRunDir=.
|
ExeRunDir=
|
||||||
ExeCommandLine=
|
ExeCommandLine=
|
||||||
#IMPORTANT:
|
#IMPORTANT:
|
||||||
AppId=
|
AppId=
|
||||||
@ -9,8 +9,16 @@ AppId=
|
|||||||
SteamClientDll=steamclient.dll
|
SteamClientDll=steamclient.dll
|
||||||
SteamClient64Dll=steamclient64.dll
|
SteamClient64Dll=steamclient64.dll
|
||||||
|
|
||||||
|
# inject `steamclient(64).dll`
|
||||||
|
ForceInjectSteamClient=0
|
||||||
|
|
||||||
[Debug]
|
[Debug]
|
||||||
|
# don't call `ResumeThread()` on the main thread after spawning the .exe
|
||||||
ResumeByDebugger=0
|
ResumeByDebugger=0
|
||||||
|
|
||||||
[Extra]
|
[Extra]
|
||||||
InjectDll=
|
; path to a folder containing dlls to force inject into the app upon start
|
||||||
|
; extra_dlls
|
||||||
|
DllsToInjectFolder=
|
||||||
|
; don't display an error message when a dll injection fails
|
||||||
|
IgnoreInjectionError=1
|
||||||
|
23
tools/steamclient_loader/win/extra_protection/dllmain.cpp
Normal file
23
tools/steamclient_loader/win/extra_protection/dllmain.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "pe_helpers/pe_helpers.hpp"
|
||||||
|
#include "extra_protection/stubdrm_v3.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
BOOL APIENTRY DllMain(
|
||||||
|
HMODULE hModule,
|
||||||
|
DWORD reason,
|
||||||
|
LPVOID lpReserved)
|
||||||
|
{
|
||||||
|
switch (reason)
|
||||||
|
{
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
stubdrm_v3::patch();
|
||||||
|
break;
|
||||||
|
case DLL_THREAD_ATTACH:
|
||||||
|
case DLL_THREAD_DETACH:
|
||||||
|
break;
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
stubdrm_v3::restore();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace stubdrm_v3
|
||||||
|
{
|
||||||
|
bool patch();
|
||||||
|
|
||||||
|
bool restore();
|
||||||
|
}
|
190
tools/steamclient_loader/win/extra_protection/stubdrm_v3.cpp
Normal file
190
tools/steamclient_loader/win/extra_protection/stubdrm_v3.cpp
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
#include "pe_helpers/pe_helpers.hpp"
|
||||||
|
#include "common_helpers/common_helpers.hpp"
|
||||||
|
#include "extra_protection/stubdrm_v3.hpp"
|
||||||
|
#include "detours/detours.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
#include <mutex>
|
||||||
|
#include <intrin.h>
|
||||||
|
|
||||||
|
// patt 1 is a bunch of checks for registry + files validity (including custom DOS stub)
|
||||||
|
// patt 2 is again a bunch of checks + creates some interfaces via steamclient + calls getappownershipticket()
|
||||||
|
#ifdef _WIN64
|
||||||
|
const static std::string stub_detection_patt_v31 = "FF 94 24 ?? ?? ?? ?? 88 44 24 ?? 0F BE 44 24 ?? 83 ?? 30 74 ?? E9";
|
||||||
|
|
||||||
|
const static std::string stub_search_patt_1_v31 = "E8 ?? ?? ?? ?? 84 C0 75 ?? B0 33 E9";
|
||||||
|
const static std::string stub_replace_patt_1_v31 = "B8 01 00 00 00 ?? ?? EB";
|
||||||
|
|
||||||
|
const static std::string stub_search_patt_2_v31 = "E8 ?? ?? ?? ?? 44 0F B6 ?? 3C 30 0F 84 ?? ?? ?? ?? 3C 35 0F 85 ?? ?? ?? ?? 8B ?? ?? FF 15";
|
||||||
|
const static std::string stub_replace_patt_2_v31 = "B8 30 00 00 00 ?? ?? ?? ?? ?? ?? 90 E9";
|
||||||
|
#else // _WIN64
|
||||||
|
const static std::string stub_detection_patt_v31 = "FF 95 ?? ?? ?? ?? 88 45 ?? 0F BE 4D ?? 83 ?? 30 74 ?? E9";
|
||||||
|
|
||||||
|
const static std::string stub_search_patt_1_v31 = "5? 5? E8 ?? ?? ?? ?? 83 C4 08 84 C0 75 ?? B0 33";
|
||||||
|
const static std::string stub_replace_patt_1_v31 = "?? ?? B8 01 00 00 00 ?? ?? ?? ?? ?? EB";
|
||||||
|
|
||||||
|
const static std::string stub_search_patt_2_v31 = "E8 ?? ?? ?? ?? 83 C4 04 88 45 ?? 3C 30 0F 84 ?? ?? ?? ?? 3C 35 75 ?? 8B ?? ?? FF 15";
|
||||||
|
const static std::string stub_replace_patt_2_v31 = "B8 30 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? 90 E9";
|
||||||
|
#endif // _WIN64
|
||||||
|
|
||||||
|
|
||||||
|
static std::recursive_mutex mtx_win32_api{};
|
||||||
|
static uint8_t *proc_addr_base = (uint8_t *)GetModuleHandleW(NULL);
|
||||||
|
static uint8_t *bind_addr_base = nullptr;
|
||||||
|
static uint8_t *bind_addr_end = nullptr;
|
||||||
|
|
||||||
|
bool restore_win32_apis();
|
||||||
|
|
||||||
|
static void patch_if_possible(void *ret_addr)
|
||||||
|
{
|
||||||
|
if (!ret_addr) return;
|
||||||
|
auto page_details = pe_helpers::get_mem_page_details(ret_addr);
|
||||||
|
if (!page_details.BaseAddress || page_details.AllocationProtect != PAGE_READWRITE) return;
|
||||||
|
|
||||||
|
auto mem = pe_helpers::search_memory(
|
||||||
|
(uint8_t *)page_details.BaseAddress,
|
||||||
|
page_details.RegionSize,
|
||||||
|
stub_search_patt_1_v31);
|
||||||
|
if (!mem) return;
|
||||||
|
|
||||||
|
auto size_until_match = (uint8_t *)mem - (uint8_t *)page_details.BaseAddress;
|
||||||
|
bool ok = pe_helpers::replace_memory(
|
||||||
|
(uint8_t *)mem,
|
||||||
|
page_details.RegionSize - size_until_match,
|
||||||
|
stub_replace_patt_1_v31,
|
||||||
|
GetCurrentProcess());
|
||||||
|
if (!ok) return;
|
||||||
|
|
||||||
|
mem = pe_helpers::search_memory(
|
||||||
|
(uint8_t *)page_details.BaseAddress,
|
||||||
|
page_details.RegionSize,
|
||||||
|
stub_search_patt_2_v31);
|
||||||
|
if (!mem) return;
|
||||||
|
|
||||||
|
size_until_match = (uint8_t *)mem - (uint8_t *)page_details.BaseAddress;
|
||||||
|
pe_helpers::replace_memory(
|
||||||
|
(uint8_t *)mem,
|
||||||
|
page_details.RegionSize - size_until_match,
|
||||||
|
stub_replace_patt_2_v31,
|
||||||
|
GetCurrentProcess());
|
||||||
|
|
||||||
|
restore_win32_apis();
|
||||||
|
|
||||||
|
// MessageBoxA(NULL, ("ret addr = " + std::to_string((size_t)ret_addr)).c_str(), "Patched", MB_OK);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/cpp/intrinsics/addressofreturnaddress
|
||||||
|
static bool GetTickCount_hooked = false;
|
||||||
|
static decltype(GetTickCount) *actual_GetTickCount = GetTickCount;
|
||||||
|
__declspec(noinline)
|
||||||
|
static DWORD WINAPI GetTickCount_hook()
|
||||||
|
{
|
||||||
|
std::lock_guard lk(mtx_win32_api);
|
||||||
|
|
||||||
|
if (GetTickCount_hooked) { // american truck doesn't call GetModuleHandleA
|
||||||
|
void* *ret_ptr = (void**)_AddressOfReturnAddress();
|
||||||
|
patch_if_possible(*ret_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actual_GetTickCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetModuleHandleA_hooked = false;
|
||||||
|
static decltype(GetModuleHandleA) *actual_GetModuleHandleA = GetModuleHandleA;
|
||||||
|
__declspec(noinline)
|
||||||
|
static HMODULE WINAPI GetModuleHandleA_hook(
|
||||||
|
LPCSTR lpModuleName
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::lock_guard lk(mtx_win32_api);
|
||||||
|
|
||||||
|
if (GetModuleHandleA_hooked &&
|
||||||
|
lpModuleName &&
|
||||||
|
common_helpers::ends_with_i(lpModuleName, "ntdll.dll")) {
|
||||||
|
void* *ret_ptr = (void**)_AddressOfReturnAddress();
|
||||||
|
patch_if_possible(*ret_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actual_GetModuleHandleA(lpModuleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetModuleHandleExA_hooked = false;
|
||||||
|
static decltype(GetModuleHandleExA) *actual_GetModuleHandleExA = GetModuleHandleExA;
|
||||||
|
__declspec(noinline)
|
||||||
|
static BOOL WINAPI GetModuleHandleExA_hook(
|
||||||
|
DWORD dwFlags,
|
||||||
|
LPCSTR lpModuleName,
|
||||||
|
HMODULE *phModule
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::lock_guard lk(mtx_win32_api);
|
||||||
|
|
||||||
|
if (GetModuleHandleExA_hooked &&
|
||||||
|
(dwFlags == (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)) &&
|
||||||
|
((uint8_t *)lpModuleName >= bind_addr_base && (uint8_t *)lpModuleName < bind_addr_end)) {
|
||||||
|
void* *ret_ptr = (void**)_AddressOfReturnAddress();
|
||||||
|
patch_if_possible(*ret_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actual_GetModuleHandleExA(dwFlags, lpModuleName, phModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool redirect_win32_apis()
|
||||||
|
{
|
||||||
|
if (DetourTransactionBegin() != NO_ERROR) return false;
|
||||||
|
if (DetourUpdateThread(GetCurrentThread()) != NO_ERROR) return false;
|
||||||
|
|
||||||
|
if (DetourAttach((PVOID *)&actual_GetTickCount, GetTickCount_hook) != NO_ERROR) return false;
|
||||||
|
if (DetourAttach((PVOID *)&actual_GetModuleHandleA, GetModuleHandleA_hook) != NO_ERROR) return false;
|
||||||
|
if (DetourAttach((PVOID *)&actual_GetModuleHandleExA, GetModuleHandleExA_hook) != NO_ERROR) return false;
|
||||||
|
bool ret = DetourTransactionCommit() == NO_ERROR;
|
||||||
|
if (ret) {
|
||||||
|
GetTickCount_hooked = true;
|
||||||
|
GetModuleHandleA_hooked = true;
|
||||||
|
GetModuleHandleExA_hooked = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool restore_win32_apis()
|
||||||
|
{
|
||||||
|
GetTickCount_hooked = false;
|
||||||
|
GetModuleHandleA_hooked = false;
|
||||||
|
GetModuleHandleExA_hooked = false;
|
||||||
|
|
||||||
|
if (DetourTransactionBegin() != NO_ERROR) return false;
|
||||||
|
if (DetourUpdateThread(GetCurrentThread()) != NO_ERROR) return false;
|
||||||
|
|
||||||
|
DetourDetach((PVOID *)&actual_GetTickCount, GetTickCount_hook);
|
||||||
|
DetourDetach((PVOID *)&actual_GetModuleHandleA, GetModuleHandleA_hook);
|
||||||
|
DetourDetach((PVOID *)&actual_GetModuleHandleExA, GetModuleHandleExA_hook);
|
||||||
|
return DetourTransactionCommit() == NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stubdrm_v3::patch()
|
||||||
|
{
|
||||||
|
auto bind_section = pe_helpers::get_section_header_with_name(((HMODULE)proc_addr_base), ".bind");
|
||||||
|
if (!bind_section) return false; // we don't have .bind section
|
||||||
|
|
||||||
|
bind_addr_base = proc_addr_base + bind_section->VirtualAddress;
|
||||||
|
MEMORY_BASIC_INFORMATION mbi{};
|
||||||
|
if (!VirtualQuery((LPVOID)bind_addr_base, &mbi, sizeof(mbi))) return false;
|
||||||
|
|
||||||
|
bind_addr_end = bind_addr_base + mbi.RegionSize;
|
||||||
|
auto addrOfEntry = proc_addr_base + pe_helpers::get_optional_header((HMODULE)proc_addr_base)->AddressOfEntryPoint;
|
||||||
|
if (addrOfEntry < bind_addr_base || addrOfEntry >= bind_addr_end) return false; // entry addr is not inside .bind
|
||||||
|
|
||||||
|
auto mem = pe_helpers::search_memory(
|
||||||
|
bind_addr_base,
|
||||||
|
bind_section->Misc.VirtualSize,
|
||||||
|
stub_detection_patt_v31);
|
||||||
|
if (!mem) return false; // known sequence of code wasn't found
|
||||||
|
|
||||||
|
return redirect_win32_apis();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stubdrm_v3::restore()
|
||||||
|
{
|
||||||
|
return restore_win32_apis();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user