From 91aac61e230499052eea49a3474af52bb5643557 Mon Sep 17 00:00:00 2001 From: otavepto <153766569+otavepto@users.noreply.github.com> Date: Sun, 14 Jan 2024 02:29:02 +0200 Subject: [PATCH] a working stub patcher + lots of changes to cold client loader --- .github/workflows/build-win.yml | 4 +- README.md | 8 +- build_win.bat | 85 +++--- helpers/common_helpers.cpp | 2 +- helpers/pe_helpers.cpp | 56 +++- helpers/pe_helpers/pe_helpers.hpp | 10 + post_build/README.experimental_steamclient.md | 17 +- resources/README.md | 1 + resources/win/file_dos_stub/file_dos_stub.cpp | 4 +- .../win/file_dos_stub/file_dos_stub_32.exe | Bin 175616 -> 175616 bytes .../win/file_dos_stub/file_dos_stub_64.exe | Bin 225792 -> 225792 bytes .../win/ColdClientLoader.cpp | 246 ++++++++++++++---- .../win/ColdClientLoader.ini | 14 +- .../win/extra_protection/dllmain.cpp | 23 ++ .../extra_protection/stubdrm_v3.hpp | 8 + .../win/extra_protection/stubdrm_v3.cpp | 190 ++++++++++++++ 16 files changed, 551 insertions(+), 117 deletions(-) create mode 100644 tools/steamclient_loader/win/extra_protection/dllmain.cpp create mode 100644 tools/steamclient_loader/win/extra_protection/extra_protection/stubdrm_v3.hpp create mode 100644 tools/steamclient_loader/win/extra_protection/stubdrm_v3.cpp diff --git a/.github/workflows/build-win.yml b/.github/workflows/build-win.yml index 2e215c72..a2e6f49a 100644 --- a/.github/workflows/build-win.yml +++ b/.github/workflows/build-win.yml @@ -148,7 +148,7 @@ jobs: - name: Build release mode shell: cmd working-directory: ${{ github.workspace }} - run: build_win.bat release + run: build_win.bat +exclient-extra-32 +exclient-extra-64 release ### package (release mode) - name: Package build (release) @@ -170,7 +170,7 @@ jobs: - name: Build debug mode shell: cmd working-directory: ${{ github.workspace }} - run: build_win.bat debug + run: build_win.bat +exclient-extra-32 +exclient-extra-64 debug ### package (debug mode) - name: Package build (debug) diff --git a/README.md b/README.md index 9d4fb479..d3634226 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,13 @@ Arguments you can pass to this script: * `-exclient-32`: prevent building steamclient `steamclient.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 >>>>>>>>> ___ diff --git a/build_win.bat b/build_win.bat index 025a455d..1cc632b9 100644 --- a/build_win.bat +++ b/build_win.bat @@ -30,6 +30,9 @@ set /a BUILD_EXPCLIENT64=1 set /a BUILD_EXPCLIENT_LDR_32=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_LOBBY=1 @@ -67,6 +70,10 @@ set /a VERBOSE=0 set /a BUILD_EXPCLIENT_LDR_32=0 ) else if "%~1"=="-exclient-ldr-64" ( 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" ( set /a BUILD_TOOL_FIND_ITFS=0 ) else if "%~1"=="-tool-lobby" ( @@ -308,9 +315,6 @@ call :build_rsrc "%win_resources_src_dir%\launcher\32\resources.rc" "%win_resour echo: & echo: if %BUILD_LIB32% equ 1 ( - if not exist "%build_root_dir%\x32" ( - mkdir "%build_root_dir%\x32" - ) call :compile_lib32 || ( set /a last_code+=1 ) @@ -318,9 +322,6 @@ if %BUILD_LIB32% equ 1 ( ) if %BUILD_EXP_LIB32% equ 1 ( - if not exist "%experimental_dir%\x32" ( - mkdir "%experimental_dir%\x32" - ) call :compile_experimental_lib32 || ( set /a last_code+=1 ) @@ -328,9 +329,6 @@ if %BUILD_EXP_LIB32% equ 1 ( ) if %BUILD_EXP_CLIENT32% equ 1 ( - if not exist "%experimental_dir%\x32" ( - mkdir "%experimental_dir%\x32" - ) call :compile_experimental_client32 || ( set /a last_code+=1 ) @@ -338,9 +336,6 @@ if %BUILD_EXP_CLIENT32% equ 1 ( ) if %BUILD_EXPCLIENT32% equ 1 ( - if not exist "%steamclient_dir%" ( - mkdir "%steamclient_dir%" - ) call :compile_experimentalclient_32 || ( set /a last_code+=1 ) @@ -349,19 +344,14 @@ if %BUILD_EXPCLIENT32% equ 1 ( :: steamclient_loader if %BUILD_EXPCLIENT_LDR_32% equ 1 ( - if not exist "%steamclient_dir%" ( - mkdir "%steamclient_dir%" - ) call :compile_experimentalclient_ldr_32 || ( set /a last_code+=1 ) echo: & echo: ) - if not exist "%steamclient_dir%" ( - mkdir "%steamclient_dir%" - ) - call :compile_experimentalclient_ldr || ( +if %BUILD_EXPCLIENT_EXTRA_32% equ 1 ( + call :compile_experimentalclient_extra_32 || ( set /a last_code+=1 ) echo: & echo: @@ -369,18 +359,12 @@ if %BUILD_EXPCLIENT_LDR_32% equ 1 ( :: tools (x32) if %BUILD_TOOL_FIND_ITFS% equ 1 ( - if not exist "%find_interfaces_dir%" ( - mkdir "%find_interfaces_dir%" - ) call :compile_tool_itf || ( set /a last_code+=1 ) echo: & echo: ) if %BUILD_TOOL_LOBBY% equ 1 ( - if not exist "%lobby_connect_dir%" ( - mkdir "%lobby_connect_dir%" - ) call :compile_tool_lobby || ( set /a last_code+=1 ) @@ -440,9 +424,6 @@ call :build_rsrc "%win_resources_src_dir%\launcher\64\resources.rc" "%win_resour echo: & echo: if %BUILD_LIB64% equ 1 ( - if not exist "%build_root_dir%\x64" ( - mkdir "%build_root_dir%\x64" - ) call :compile_lib64 || ( set /a last_code+=1 ) @@ -450,9 +431,6 @@ if %BUILD_LIB64% equ 1 ( ) if %BUILD_EXP_LIB64% equ 1 ( - if not exist "%experimental_dir%\x64" ( - mkdir "%experimental_dir%\x64" - ) call :compile_experimental_lib64 || ( set /a last_code+=1 ) @@ -460,9 +438,6 @@ if %BUILD_EXP_LIB64% equ 1 ( ) if %BUILD_EXP_CLIENT64% equ 1 ( - if not exist "%experimental_dir%\x64" ( - mkdir "%experimental_dir%\x64" - ) call :compile_experimental_client64 || ( set /a last_code+=1 ) @@ -470,9 +445,6 @@ if %BUILD_EXP_CLIENT64% equ 1 ( ) if %BUILD_EXPCLIENT64% equ 1 ( - if not exist "%steamclient_dir%" ( - mkdir "%steamclient_dir%" - ) call :compile_experimentalclient_64 || ( set /a last_code+=1 ) @@ -481,15 +453,19 @@ if %BUILD_EXPCLIENT64% equ 1 ( :: steamclient_loader if %BUILD_EXPCLIENT_LDR_64% equ 1 ( - if not exist "%steamclient_dir%" ( - mkdir "%steamclient_dir%" - ) call :compile_experimentalclient_ldr_64 || ( set /a last_code+=1 ) 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% @@ -596,6 +572,16 @@ 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% @@ -689,6 +675,19 @@ 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 @@ -799,6 +798,12 @@ 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" ( 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: diff --git a/helpers/common_helpers.cpp b/helpers/common_helpers.cpp index d7017896..d0ae0e46 100644 --- a/helpers/common_helpers.cpp +++ b/helpers/common_helpers.cpp @@ -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()) { return path; diff --git a/helpers/pe_helpers.cpp b/helpers/pe_helpers.cpp index 7aa47e5d..6274db32 100644 --- a/helpers/pe_helpers.cpp +++ b/helpers/pe_helpers.cpp @@ -20,7 +20,7 @@ static inline uint8_t char_to_byte(const char 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/ 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); } -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; } -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; } @@ -190,7 +190,10 @@ bool pe_helpers::replace_memory(uint8_t *mem, size_t size, const std::string &re } 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_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) { - 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) { - 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) @@ -339,6 +342,7 @@ DWORD pe_helpers::loadlib_remote(HANDLE hProcess, const std::wstring &lib_fullpa } WaitForSingleObject(remote_thread, INFINITE); + CloseHandle(remote_thread); // cleanup allcoated page VirtualFreeEx( @@ -419,3 +423,43 @@ bool pe_helpers::ends_with_i(PUNICODE_STRING target, const std::wstring &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; +} diff --git a/helpers/pe_helpers/pe_helpers.hpp b/helpers/pe_helpers/pe_helpers.hpp index 0005e9cd..66a1339f 100644 --- a/helpers/pe_helpers/pe_helpers.hpp +++ b/helpers/pe_helpers/pe_helpers.hpp @@ -16,6 +16,12 @@ typedef struct SectionHeadersResult } 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); 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); +MEMORY_BASIC_INFORMATION get_mem_page_details(const void* mem); + +size_t get_current_exe_mem_size(); + } diff --git a/post_build/README.experimental_steamclient.md b/post_build/README.experimental_steamclient.md index acb413c6..d1307012 100644 --- a/post_build/README.experimental_steamclient.md +++ b/post_build/README.experimental_steamclient.md @@ -19,15 +19,18 @@ You do not need to create a `steam_interfaces.txt` file for the `steamclient` ve * `steamclient_loader.exe` 2. Edit `ColdClientLoader.ini` and specify: * `AppId`: the app ID - * `Exe`: the path to the game's executable/launcher, either full path or relative to this `.ini` file - * `ExeRunDir`: geenrally this must be set to the folder containing the game's exe + * `Exe`: the path to the game's executable/launcher, either full path or relative to this loader + * `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` 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(64).dll`, either full path or relative to this `.ini` file - * For debug **build** only: - * `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. + * `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 loader + * `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. + 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`. diff --git a/resources/README.md b/resources/README.md index a446619a..394cefe3 100644 --- a/resources/README.md +++ b/resources/README.md @@ -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` * [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` + * [file_dos_stub](./win/file_dos_stub/): contains an immitation of how the DOS stub is manipulated after build diff --git a/resources/win/file_dos_stub/file_dos_stub.cpp b/resources/win/file_dos_stub/file_dos_stub.cpp index 374a59c7..190b4831 100644 --- a/resources/win/file_dos_stub/file_dos_stub.cpp +++ b/resources/win/file_dos_stub/file_dos_stub.cpp @@ -28,8 +28,8 @@ static std::vector load_file_partial(std::fstream &file) auto org_pos = file.tellg(); file.seekg(0, std::ios::beg); - // 2MB is enough - std::vector data(2 * 1024 * 1024, 0); + // 1MB is enough + std::vector data(1 * 1024 * 1024, 0); file.read((char *)&data[0], data.size()); file.seekg(org_pos, std::ios::beg); diff --git a/resources/win/file_dos_stub/file_dos_stub_32.exe b/resources/win/file_dos_stub/file_dos_stub_32.exe index 3647f105539d0d97e245664231e9dea5a50c0a63..621d99762253983fb313113186c8ff17ef043923 100644 GIT binary patch delta 643 zcma)4-%C?r82+B)u?}Z5HgZ2$Ta%&Xg0f8Zs~^kTrPBl;4t0oFowWN9zN)Z%Om zQtCj`Y85M8tk!Yx(MhIM%0ouRu7s4Wnn@}QCi4b}1>^jb;MPocBO(jF4@jU(lI<&l zZ$uXZdu1@N@8WYNU_{u!j|SRTQDY4V|*88ND6kGr?MJ!piJ`c zZg@1;xD!9MoBETe+Xy<*q&i38MH9a?iU4%_f14V$P19qrbi>Yd}e6kMl`I>-WIaF_l{6({|lj7#kT+e delta 643 zcma)4?@Lor7(UP4wZ=AMqxJ*a+7P2HD%-?SW|mpA=m#ru5|&`LsW}W0VfASeli(ID zZg49|8vEFmZWld))QrM!`3Hpk_|S(5Qi~wmhg$EYf1nS~d3erw-sgSK;UrfL$yG!8 zj&623Q;?#5c;p}cW`l=aXOVj|jCthUfd+Wg6M?7?CiQt=z)18d)&Oi%t3!*sz~uS+ z35fE(w8OzFovhZn|G_aPRmuYv#x6yqjha!XWdTHakW{Dok$26iLB}v$U;*T_}@0 zxC4I8HR{H9EvK&$AGQz&{kW=CJ#M9Tj2{Z(9oZHR;IfJ2mE-8u5zlwyrN(2Y@!L-B z_#kL-o*zVq4jyGahM#Csz6_xQPQ^Wp0G#~7FhVfu|4nMqCe4n(-USC=%;B)svpNSS zk*xqPy!=Q3X8^Bqw@8yR{CA87*xe{FUW(t!ksb77*=`u%Z{!$|RbK%vn&-Y8Kjs zQm=I|%7~y;=;y*?MPUXNEkrEQh=kSxAzw&F7-QbHwLhWr=EL)S&i6To=XqRy!Q~e^ z+IVf+t%uTT5jy#vn3kd=JKLL6H|2~M4iTawR%vu*a2^rS7?C``#A8`_?y0VsU4}8a ztYIFrp2EhtM0}7Y)MQp?dy%yPQsvdDM;rmS} z1wOGX2mHV3R4_`_Wo~mDCWxZoH>Q16U6`?FnEN%IHu4CM;(^0yqfV zr@Mj>b&rZkXmDdz_k!ZgQS=Vh6~t&fKGGV2TZekuA#f#z>|8?J3$ad{RTAQgK&d{F$Z2W}bvkRg_1fJQbzI@-N>TjUPuj z66I)=K2~Ao+_%H~$0mJTr)5b;I8)AbrWmf6Iraa2$i@lXq{!arsI}HK9JbfeymbC9 K`yPFxmQX4RL#1pki(Hlt^%b<#i!vmZqKIgd&{^O`%b6*I zP+#*<^aF29C4zXfV@72^2x%c^nHJQA?Lx>4V-d!fZ`<0R&~x+Q`JU(dJcsifhevdH z#DP9R=^cIGN;4rvzHiD%O(Es|9q9*i7YnBeNg?P##tbJ0|9pbMl zkw{_SOVx3-hJ60g3SAsB_{v$tLv)kbT)_I~G`{FUfEj{RrfQ8TWp;$xxQyf>(p~V&6If@MpKJliz*bj`eTjMx_ z8uoP@S;%HF6L7>jZ-nldr0Saa@C16Qki_&p9LH_Ts z|9qsXJIyvknu#4eIgV~2RtW4a@L+*9pLu!$^<& z!~7VghgX@Yb1eA(`0^?pEQ;z5o>bDoRQ-sVM*rVIR?12Y!aZ@ASX$fK8>Kp1Lu(`d JX%OPe{sKVi_Im&T diff --git a/tools/steamclient_loader/win/ColdClientLoader.cpp b/tools/steamclient_loader/win/ColdClientLoader.cpp index a3acaa21..b5f40a05 100644 --- a/tools/steamclient_loader/win/ColdClientLoader.cpp +++ b/tools/steamclient_loader/win/ColdClientLoader.cpp @@ -37,6 +37,28 @@ std::wstring get_ini_value(LPCWSTR section, LPCWSTR key, LPCWSTR default_val = N return std::wstring(&buff[0], read_chars); } +static std::vector 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 data(2 * 1024 * 1024, 0); + file.read((char *)&data[0], data.size()); + file.close(); + + return data; + } + catch(const std::exception& e) + { + return std::vector(); + } +} + int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { 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 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 - dbg_log::write(L"SteamClient64Dll: " + Client64Path); - dbg_log::write(L"SteamClient: " + ClientPath); - dbg_log::write(L"Exe: " + ExeFile); - dbg_log::write(L"ExeRunDir: " + ExeRunDir); - dbg_log::write(L"ExeCommandLine: " + ExeCommandLine); - dbg_log::write(L"AppId: " + AppId); + dbg_log::write(L"SteamClient::Exe: " + ExeFile); + dbg_log::write(L"SteamClient::ExeRunDir: " + ExeRunDir); + dbg_log::write(L"SteamClient::ExeCommandLine: " + ExeCommandLine); + dbg_log::write(L"SteamClient::AppId: " + AppId); + dbg_log::write(L"SteamClient::SteamClient: " + ClientPath); + dbg_log::write(L"SteamClient::SteamClient64Dll: " + Client64Path); + dbg_log::write(L"SteamClient::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]) { SetEnvironmentVariableW(L"SteamAppId", AppId.c_str()); @@ -86,6 +122,24 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance 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)) { dbg_log::write("Couldn't find the requested SteamClient64Dll"); 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; } - 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); + 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") { + 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(); return 1; } - 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); + bool is_exe_32 = pe_helpers::is_module_32((HMODULE)&exe_header[0]); + bool is_exe_64 = pe_helpers::is_module_64((HMODULE)&exe_header[0]); + if ((!is_exe_32 && !is_exe_64) || (is_exe_32 && is_exe_64)) { // ARM, or just a regular file + dbg_log::write("The requested exe is invalid (neither 32 nor 64 bit)"); + MessageBoxA(NULL, "The requested exe is invalid (neither 32 nor 64 bit)", "ColdClientLoader", MB_ICONERROR); dbg_log::close(); return 1; } + if (is_exe_32) { + dbg_log::write("Detected exe arch: x32"); + } 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 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 }; // 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. 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 STARTUPINFOW info = { 0 }; SecureZeroMemory(&info, sizeof(info)); @@ -187,47 +320,50 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance 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; - 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) { - TerminateProcess(processInfo.hProcess, 1); - std::string err_full = - "Failed to inject the requested dll:\n" + - std::string(err_inject) + "\n" + - pe_helpers::get_err_string(code) + "\n" + - "Error code = " + std::to_string(code) + "\n"; + std::wstring err_full = + L"Failed to inject the dll: " + dll_path + 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); - MessageBoxA(NULL, err_full.c_str(), "ColdClientLoader", MB_ICONERROR); - dbg_log::close(); - return 1; + 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; + } + } else { + } } - bool run_exe = true; -#ifndef EMU_RELEASE_BUILD - std::wstring resume_by_dbg = get_ini_value(L"Debug", L"ResumeByDebugger"); - dbg_log::write(L"Debug::ResumeByDebugger: " + resume_by_dbg); - 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; + // run + if (resume_by_dbg.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); } -#endif - // run - if (run_exe) { - ResumeThread(processInfo.hThread); - } // wait WaitForSingleObject(processInfo.hThread, INFINITE); - CloseHandle(processInfo.hThread); 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) diff --git a/tools/steamclient_loader/win/ColdClientLoader.ini b/tools/steamclient_loader/win/ColdClientLoader.ini index c80df32b..f1768a45 100644 --- a/tools/steamclient_loader/win/ColdClientLoader.ini +++ b/tools/steamclient_loader/win/ColdClientLoader.ini @@ -1,7 +1,7 @@ -#My own modified version of ColdClientLoader originally by Rat431 +# modified version of ColdClientLoader originally by Rat431 [SteamClient] Exe=game.exe -ExeRunDir=. +ExeRunDir= ExeCommandLine= #IMPORTANT: AppId= @@ -9,8 +9,16 @@ AppId= SteamClientDll=steamclient.dll SteamClient64Dll=steamclient64.dll +# inject `steamclient(64).dll` +ForceInjectSteamClient=0 + [Debug] +# don't call `ResumeThread()` on the main thread after spawning the .exe ResumeByDebugger=0 [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 diff --git a/tools/steamclient_loader/win/extra_protection/dllmain.cpp b/tools/steamclient_loader/win/extra_protection/dllmain.cpp new file mode 100644 index 00000000..e274650e --- /dev/null +++ b/tools/steamclient_loader/win/extra_protection/dllmain.cpp @@ -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; +} diff --git a/tools/steamclient_loader/win/extra_protection/extra_protection/stubdrm_v3.hpp b/tools/steamclient_loader/win/extra_protection/extra_protection/stubdrm_v3.hpp new file mode 100644 index 00000000..cf48b355 --- /dev/null +++ b/tools/steamclient_loader/win/extra_protection/extra_protection/stubdrm_v3.hpp @@ -0,0 +1,8 @@ +#pragma once + +namespace stubdrm_v3 +{ + bool patch(); + + bool restore(); +} diff --git a/tools/steamclient_loader/win/extra_protection/stubdrm_v3.cpp b/tools/steamclient_loader/win/extra_protection/stubdrm_v3.cpp new file mode 100644 index 00000000..c8554d6e --- /dev/null +++ b/tools/steamclient_loader/win/extra_protection/stubdrm_v3.cpp @@ -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 +#include +#include +#include + +// 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(); +}