diff --git a/README.md b/README.md index b7025cc..67f392c 100644 --- a/README.md +++ b/README.md @@ -8,43 +8,52 @@ Bypasses Steam API dll integrity/size check by hooking CreateFile API. ## Usage +* If you are using x32 version please remove `x32` from dll name. + ### Method 1 * Use CFF Explorer and add `SteamAPICheckBypass` dll import to game main exe, then put the `SteamAPICheckBypass(x32).dll` dll beside game exe. ### Method 2 (VersionShim) (x64 for pre-built version.dll) -* Put `SteamAPICheckBypass.dll` `version.dll` `libraries.txt` beside game exe. +* Put `SteamAPICheckBypass.dll` `version.dll` beside game exe. ## Configuration (Optional) -* Create `SteamAPICheckBypass.ini` and write file names you want to replace. Example: - -```text -[Replace] -OriginalFileFile=ReplaceFileName -steam_api64.dll=steam_api64.dll.bak -steam_api.dll=steam_api.dll.bak - -[AfterFirstTime] -OriginalFileFile=0 -steam_api.dll=0 +* Create `SteamAPICheckBypass.json` and write file names you want to replace. Example: + +```json +{ + "steam_api64.dll": + { + "mode": "file_redirect", + "to": "steam_api64.dll.bak", + "hook_times_mode": "nth_time_only", + "hook_time_n": 1 + }, + "game.exe": + { + "mode": "file_redirect", + "to": "game.exe.bak" + } +} + ``` -* `Replace`: The file name to replace. -* `AfterFirstTime`: Start to replace after the first read of the file. +* `mode`: `file_redirect` or `file_hide`. +* `to` : The target file relative path. +* `hook_times_mode`: `all`, `nth_time_only` or `not_nth_time_only`. +* `hook_time_n`: The nth time to hook / not hook. (Start from 1) +* hook time option is useful for game dynamically loads steam_api dll after/before check. ## Internal Process -1. The Crack will check `useinternallist` in the source code, if it's true, it will use `internalreplaceList`. - -2. The Crack will try to parse the ini file and add them into the replace list. - -3. If ini file not exist, try find `steam_api(64).dll.bak`, `steam_api(64).org`,`steam_api(64)_o.dll` and add the existed file into replace list. +* Please refer to [nt_file_dupe Readme](nt_file_dupe/README.md) for more information. ## Dependencies * +* ## Bugs diff --git a/Release_dlls/SteamAPICheckBypass.dll b/Release_dlls/SteamAPICheckBypass.dll index 6289a33..f7c5544 100644 Binary files a/Release_dlls/SteamAPICheckBypass.dll and b/Release_dlls/SteamAPICheckBypass.dll differ diff --git a/Release_dlls/SteamAPICheckBypass_x32.dll b/Release_dlls/SteamAPICheckBypass_x32.dll index dbef179..dbc8ba6 100644 Binary files a/Release_dlls/SteamAPICheckBypass_x32.dll and b/Release_dlls/SteamAPICheckBypass_x32.dll differ diff --git a/Release_dlls/libraries.txt b/Release_dlls/libraries.txt deleted file mode 100644 index 7e75f95..0000000 --- a/Release_dlls/libraries.txt +++ /dev/null @@ -1 +0,0 @@ -SteamAPICheckBypass.dll \ No newline at end of file diff --git a/Release_dlls/version.dll b/Release_dlls/version.dll index 7aa2667..75c4e3c 100644 Binary files a/Release_dlls/version.dll and b/Release_dlls/version.dll differ diff --git a/SteamAPICheckBypass.EXAMPLE.ini b/SteamAPICheckBypass.EXAMPLE.ini deleted file mode 100644 index baf12e8..0000000 --- a/SteamAPICheckBypass.EXAMPLE.ini +++ /dev/null @@ -1,7 +0,0 @@ -[Replace] -steam_api64.dll=steam_api64.dll.bak -steam_api.dll=steam_api.dll.bak - -[AfterFirstTime] -steam_api.dll=0 -steam_api.dll=0 diff --git a/SteamAPICheckBypass.EXAMPLE.json b/SteamAPICheckBypass.EXAMPLE.json new file mode 100644 index 0000000..1b05d96 --- /dev/null +++ b/SteamAPICheckBypass.EXAMPLE.json @@ -0,0 +1,15 @@ +{ + "steam_api64.dll": + { + "mode": "file_redirect", + "to": "steam_api64.dll.bak", + "hook_times_mode": "nth_time_only", + "hook_time_n": 1 + }, + "game.exe": + { + "mode": "file_redirect", + "to": "game.exe.bak" + } +} + \ No newline at end of file diff --git a/SteamAPICheckBypass.sln b/SteamAPICheckBypass.sln index 08f2734..0d9618c 100644 --- a/SteamAPICheckBypass.sln +++ b/SteamAPICheckBypass.sln @@ -3,9 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.7.34202.233 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SteamAPICheckBypass", "SteamAPICheckBypass\SteamAPICheckBypass.vcxproj", "{3D2E6283-1C6E-43EF-A72D-FD510D40ECF4}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nt_file_dupe", "nt_file_dupe\nt_file_dupe.vcxproj", "{22E4EBEB-B621-4252-AE46-665835385D64}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SteamAPICheckBypass_x32", "SteamAPICheckBypass_x32\SteamAPICheckBypass_x32.vcxproj", "{8A0F57D1-0E26-435B-9CFA-084D4424DD87}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dll_interface", "SteamAPICheckBypass\dll_interface.vcxproj", "{02A9F0E8-F3C9-407E-A7DC-CDC42430E626}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VersionShim", "VersionShim\VersionShim.vcxproj", "{7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,20 +17,30 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3D2E6283-1C6E-43EF-A72D-FD510D40ECF4}.Debug|x64.ActiveCfg = Debug|x64 - {3D2E6283-1C6E-43EF-A72D-FD510D40ECF4}.Debug|x64.Build.0 = Debug|x64 - {3D2E6283-1C6E-43EF-A72D-FD510D40ECF4}.Debug|x86.ActiveCfg = Debug|x64 - {3D2E6283-1C6E-43EF-A72D-FD510D40ECF4}.Release|x64.ActiveCfg = Release|x64 - {3D2E6283-1C6E-43EF-A72D-FD510D40ECF4}.Release|x64.Build.0 = Release|x64 - {3D2E6283-1C6E-43EF-A72D-FD510D40ECF4}.Release|x86.ActiveCfg = Release|x64 - {3D2E6283-1C6E-43EF-A72D-FD510D40ECF4}.Release|x86.Build.0 = Release|x64 - {8A0F57D1-0E26-435B-9CFA-084D4424DD87}.Debug|x64.ActiveCfg = Debug|Win32 - {8A0F57D1-0E26-435B-9CFA-084D4424DD87}.Debug|x86.ActiveCfg = Debug|Win32 - {8A0F57D1-0E26-435B-9CFA-084D4424DD87}.Debug|x86.Build.0 = Debug|Win32 - {8A0F57D1-0E26-435B-9CFA-084D4424DD87}.Release|x64.ActiveCfg = Release|Win32 - {8A0F57D1-0E26-435B-9CFA-084D4424DD87}.Release|x64.Build.0 = Release|Win32 - {8A0F57D1-0E26-435B-9CFA-084D4424DD87}.Release|x86.ActiveCfg = Release|Win32 - {8A0F57D1-0E26-435B-9CFA-084D4424DD87}.Release|x86.Build.0 = Release|Win32 + {22E4EBEB-B621-4252-AE46-665835385D64}.Debug|x64.ActiveCfg = Debug|x64 + {22E4EBEB-B621-4252-AE46-665835385D64}.Debug|x64.Build.0 = Debug|x64 + {22E4EBEB-B621-4252-AE46-665835385D64}.Debug|x86.ActiveCfg = Debug|Win32 + {22E4EBEB-B621-4252-AE46-665835385D64}.Debug|x86.Build.0 = Debug|Win32 + {22E4EBEB-B621-4252-AE46-665835385D64}.Release|x64.ActiveCfg = Release|x64 + {22E4EBEB-B621-4252-AE46-665835385D64}.Release|x64.Build.0 = Release|x64 + {22E4EBEB-B621-4252-AE46-665835385D64}.Release|x86.ActiveCfg = Release|Win32 + {22E4EBEB-B621-4252-AE46-665835385D64}.Release|x86.Build.0 = Release|Win32 + {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Debug|x64.ActiveCfg = Debug|x64 + {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Debug|x64.Build.0 = Debug|x64 + {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Debug|x86.ActiveCfg = Debug|Win32 + {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Debug|x86.Build.0 = Debug|Win32 + {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Release|x64.ActiveCfg = Release|x64 + {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Release|x64.Build.0 = Release|x64 + {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Release|x86.ActiveCfg = Release|Win32 + {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Release|x86.Build.0 = Release|Win32 + {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Debug|x64.ActiveCfg = Debug|x64 + {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Debug|x64.Build.0 = Debug|x64 + {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Debug|x86.ActiveCfg = Debug|x64 + {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Debug|x86.Build.0 = Debug|x64 + {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Release|x64.ActiveCfg = Release|x64 + {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Release|x64.Build.0 = Release|x64 + {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Release|x86.ActiveCfg = Release|x64 + {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SteamAPICheckBypass/Console.cpp b/SteamAPICheckBypass/Console.cpp deleted file mode 100644 index 1234436..0000000 --- a/SteamAPICheckBypass/Console.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "pch.h" -#include "Console.h" - -HANDLE _out = NULL, _old_out = NULL; -HANDLE _err = NULL, _old_err = NULL; -HANDLE _in = NULL, _old_in = NULL; - -void Console::Attach() -{ - _old_out = GetStdHandle(STD_OUTPUT_HANDLE); - _old_err = GetStdHandle(STD_ERROR_HANDLE); - _old_in = GetStdHandle(STD_INPUT_HANDLE); - - ::AllocConsole() && ::AttachConsole(GetCurrentProcessId()); - - _out = GetStdHandle(STD_OUTPUT_HANDLE); - _err = GetStdHandle(STD_ERROR_HANDLE); - _in = GetStdHandle(STD_INPUT_HANDLE); - - SetConsoleMode(_out, - ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); - - SetConsoleMode(_in, - ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS | - ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE); -} - -void Console::Detach() -{ - if (_out && _err && _in) { - FreeConsole(); - - if (_old_out) - SetStdHandle(STD_OUTPUT_HANDLE, _old_out); - if (_old_err) - SetStdHandle(STD_ERROR_HANDLE, _old_err); - if (_old_in) - SetStdHandle(STD_INPUT_HANDLE, _old_in); - } -} - -bool Console::Print(const char* fmt, ...) -{ - if (!_out) - return false; - - char buf[1024]; - va_list va; - - va_start(va, fmt); - _vsnprintf_s(buf, 1024, fmt, va); - va_end(va); - - return !!WriteConsoleA(_out, buf, static_cast(strlen(buf)), nullptr, nullptr); -} \ No newline at end of file diff --git a/SteamAPICheckBypass/Console.h b/SteamAPICheckBypass/Console.h deleted file mode 100644 index 131a4fa..0000000 --- a/SteamAPICheckBypass/Console.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CONSOLE_H -#define CONSOLE_H - -#include -#include - -namespace Console -{ - void Attach(); - void Detach(); - bool Print(const char* fmt, ...); -} - -#endif \ No newline at end of file diff --git a/SteamAPICheckBypass/SteamAPICheckBypass.vcxproj b/SteamAPICheckBypass/SteamAPICheckBypass.vcxproj deleted file mode 100644 index 6a218de..0000000 --- a/SteamAPICheckBypass/SteamAPICheckBypass.vcxproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - 17.0 - Win32Proj - {3d2e6283-1c6e-43ef-a72d-fd510d40ecf4} - SteamAPICheckBypass - 10.0 - - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - - - - - - - - - - - - - - Level3 - true - _DEBUG;STEAMAPICHECKBYPASS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - Async - - - Windows - true - false - - - - - Level3 - true - true - true - NDEBUG;STEAMAPICHECKBYPASS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - Async - - - Windows - true - true - true - false - - - - - - - - - - - - - Create - Create - - - - - - \ No newline at end of file diff --git a/SteamAPICheckBypass/SteamAPICheckBypass.vcxproj.filters b/SteamAPICheckBypass/SteamAPICheckBypass.vcxproj.filters deleted file mode 100644 index 3175590..0000000 --- a/SteamAPICheckBypass/SteamAPICheckBypass.vcxproj.filters +++ /dev/null @@ -1,42 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - - - 源文件 - - - 源文件 - - - 源文件 - - - \ No newline at end of file diff --git a/SteamAPICheckBypass/detours.h b/SteamAPICheckBypass/detours.h deleted file mode 100644 index bf50e8c..0000000 --- a/SteamAPICheckBypass/detours.h +++ /dev/null @@ -1,1059 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// -// Core Detours Functionality (detours.h of detours.lib) -// -// Microsoft Research Detours Package, Version 4.0.1 -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -#pragma once -#ifndef _DETOURS_H_ -#define _DETOURS_H_ - -#define DETOURS_VERSION 0x4c0c1 // 0xMAJORcMINORcPATCH - -////////////////////////////////////////////////////////////////////////////// -// - -#undef DETOURS_X64 -#undef DETOURS_X86 -#undef DETOURS_IA64 -#undef DETOURS_ARM -#undef DETOURS_ARM64 -#undef DETOURS_BITS -#undef DETOURS_32BIT -#undef DETOURS_64BIT - -#if defined(_X86_) -#define DETOURS_X86 -#define DETOURS_OPTION_BITS 64 - -#elif defined(_AMD64_) -#define DETOURS_X64 -#define DETOURS_OPTION_BITS 32 - -#elif defined(_IA64_) -#define DETOURS_IA64 -#define DETOURS_OPTION_BITS 32 - -#elif defined(_ARM_) -#define DETOURS_ARM - -#elif defined(_ARM64_) -#define DETOURS_ARM64 - -#else -#error Unknown architecture (x86, amd64, ia64, arm, arm64) -#endif - -#ifdef _WIN64 -#undef DETOURS_32BIT -#define DETOURS_64BIT 1 -#define DETOURS_BITS 64 -// If all 64bit kernels can run one and only one 32bit architecture. -//#define DETOURS_OPTION_BITS 32 -#else -#define DETOURS_32BIT 1 -#undef DETOURS_64BIT -#define DETOURS_BITS 32 -// If all 64bit kernels can run one and only one 32bit architecture. -//#define DETOURS_OPTION_BITS 32 -#endif - -#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS) - -////////////////////////////////////////////////////////////////////////////// -// - -#if (_MSC_VER < 1299) -typedef LONG LONG_PTR; -typedef ULONG ULONG_PTR; -#endif - -///////////////////////////////////////////////// SAL 2.0 Annotations w/o SAL. -// -// These definitions are include so that Detours will build even if the -// compiler doesn't have full SAL 2.0 support. -// -#ifndef DETOURS_DONT_REMOVE_SAL_20 - -#ifdef DETOURS_TEST_REMOVE_SAL_20 -#undef _Analysis_assume_ -#undef _Benign_race_begin_ -#undef _Benign_race_end_ -#undef _Field_range_ -#undef _Field_size_ -#undef _In_ -#undef _In_bytecount_ -#undef _In_count_ -#undef _In_opt_ -#undef _In_opt_bytecount_ -#undef _In_opt_count_ -#undef _In_opt_z_ -#undef _In_range_ -#undef _In_reads_ -#undef _In_reads_bytes_ -#undef _In_reads_opt_ -#undef _In_reads_opt_bytes_ -#undef _In_reads_or_z_ -#undef _In_z_ -#undef _Inout_ -#undef _Inout_opt_ -#undef _Inout_z_count_ -#undef _Out_ -#undef _Out_opt_ -#undef _Out_writes_ -#undef _Outptr_result_maybenull_ -#undef _Readable_bytes_ -#undef _Success_ -#undef _Writable_bytes_ -#undef _Pre_notnull_ -#endif - -#if defined(_Deref_out_opt_z_) && !defined(_Outptr_result_maybenull_) -#define _Outptr_result_maybenull_ _Deref_out_opt_z_ -#endif - -#if defined(_In_count_) && !defined(_In_reads_) -#define _In_reads_(x) _In_count_(x) -#endif - -#if defined(_In_opt_count_) && !defined(_In_reads_opt_) -#define _In_reads_opt_(x) _In_opt_count_(x) -#endif - -#if defined(_In_opt_bytecount_) && !defined(_In_reads_opt_bytes_) -#define _In_reads_opt_bytes_(x) _In_opt_bytecount_(x) -#endif - -#if defined(_In_bytecount_) && !defined(_In_reads_bytes_) -#define _In_reads_bytes_(x) _In_bytecount_(x) -#endif - -#ifndef _In_ -#define _In_ -#endif - -#ifndef _In_bytecount_ -#define _In_bytecount_(x) -#endif - -#ifndef _In_count_ -#define _In_count_(x) -#endif - -#ifndef _In_opt_ -#define _In_opt_ -#endif - -#ifndef _In_opt_bytecount_ -#define _In_opt_bytecount_(x) -#endif - -#ifndef _In_opt_count_ -#define _In_opt_count_(x) -#endif - -#ifndef _In_opt_z_ -#define _In_opt_z_ -#endif - -#ifndef _In_range_ -#define _In_range_(x,y) -#endif - -#ifndef _In_reads_ -#define _In_reads_(x) -#endif - -#ifndef _In_reads_bytes_ -#define _In_reads_bytes_(x) -#endif - -#ifndef _In_reads_opt_ -#define _In_reads_opt_(x) -#endif - -#ifndef _In_reads_opt_bytes_ -#define _In_reads_opt_bytes_(x) -#endif - -#ifndef _In_reads_or_z_ -#define _In_reads_or_z_ -#endif - -#ifndef _In_z_ -#define _In_z_ -#endif - -#ifndef _Inout_ -#define _Inout_ -#endif - -#ifndef _Inout_opt_ -#define _Inout_opt_ -#endif - -#ifndef _Inout_z_count_ -#define _Inout_z_count_(x) -#endif - -#ifndef _Out_ -#define _Out_ -#endif - -#ifndef _Out_opt_ -#define _Out_opt_ -#endif - -#ifndef _Out_writes_ -#define _Out_writes_(x) -#endif - -#ifndef _Outptr_result_maybenull_ -#define _Outptr_result_maybenull_ -#endif - -#ifndef _Writable_bytes_ -#define _Writable_bytes_(x) -#endif - -#ifndef _Readable_bytes_ -#define _Readable_bytes_(x) -#endif - -#ifndef _Success_ -#define _Success_(x) -#endif - -#ifndef _Pre_notnull_ -#define _Pre_notnull_ -#endif - -#ifdef DETOURS_INTERNAL - -#pragma warning(disable:4615) // unknown warning type (suppress with older compilers) - -#ifndef _Benign_race_begin_ -#define _Benign_race_begin_ -#endif - -#ifndef _Benign_race_end_ -#define _Benign_race_end_ -#endif - -#ifndef _Field_size_ -#define _Field_size_(x) -#endif - -#ifndef _Field_range_ -#define _Field_range_(x,y) -#endif - -#ifndef _Analysis_assume_ -#define _Analysis_assume_(x) -#endif - -#endif // DETOURS_INTERNAL -#endif // DETOURS_DONT_REMOVE_SAL_20 - -////////////////////////////////////////////////////////////////////////////// -// -#ifndef GUID_DEFINED -#define GUID_DEFINED -typedef struct _GUID -{ - DWORD Data1; - WORD Data2; - WORD Data3; - BYTE Data4[8]; -} GUID; - -#ifdef INITGUID -#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - const GUID name \ - = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } -#else -#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - const GUID name -#endif // INITGUID -#endif // !GUID_DEFINED - -#if defined(__cplusplus) -#ifndef _REFGUID_DEFINED -#define _REFGUID_DEFINED -#define REFGUID const GUID & -#endif // !_REFGUID_DEFINED -#else // !__cplusplus -#ifndef _REFGUID_DEFINED -#define _REFGUID_DEFINED -#define REFGUID const GUID * const -#endif // !_REFGUID_DEFINED -#endif // !__cplusplus - -#ifndef ARRAYSIZE -#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) -#endif - -// -////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - - /////////////////////////////////////////////////// Instruction Target Macros. - // -#define DETOUR_INSTRUCTION_TARGET_NONE ((PVOID)0) -#define DETOUR_INSTRUCTION_TARGET_DYNAMIC ((PVOID)(LONG_PTR)-1) -#define DETOUR_SECTION_HEADER_SIGNATURE 0x00727444 // "Dtr\0" - - extern const GUID DETOUR_EXE_RESTORE_GUID; - extern const GUID DETOUR_EXE_HELPER_GUID; - -#define DETOUR_TRAMPOLINE_SIGNATURE 0x21727444 // Dtr! - typedef struct _DETOUR_TRAMPOLINE DETOUR_TRAMPOLINE, * PDETOUR_TRAMPOLINE; - - /////////////////////////////////////////////////////////// Binary Structures. - // -#pragma pack(push, 8) - typedef struct _DETOUR_SECTION_HEADER - { - DWORD cbHeaderSize; - DWORD nSignature; - DWORD nDataOffset; - DWORD cbDataSize; - - DWORD nOriginalImportVirtualAddress; - DWORD nOriginalImportSize; - DWORD nOriginalBoundImportVirtualAddress; - DWORD nOriginalBoundImportSize; - - DWORD nOriginalIatVirtualAddress; - DWORD nOriginalIatSize; - DWORD nOriginalSizeOfImage; - DWORD cbPrePE; - - DWORD nOriginalClrFlags; - DWORD reserved1; - DWORD reserved2; - DWORD reserved3; - - // Followed by cbPrePE bytes of data. - } DETOUR_SECTION_HEADER, * PDETOUR_SECTION_HEADER; - - typedef struct _DETOUR_SECTION_RECORD - { - DWORD cbBytes; - DWORD nReserved; - GUID guid; - } DETOUR_SECTION_RECORD, * PDETOUR_SECTION_RECORD; - - typedef struct _DETOUR_CLR_HEADER - { - // Header versioning - ULONG cb; - USHORT MajorRuntimeVersion; - USHORT MinorRuntimeVersion; - - // Symbol table and startup information - IMAGE_DATA_DIRECTORY MetaData; - ULONG Flags; - - // Followed by the rest of the IMAGE_COR20_HEADER - } DETOUR_CLR_HEADER, * PDETOUR_CLR_HEADER; - - typedef struct _DETOUR_EXE_RESTORE - { - DWORD cb; - DWORD cbidh; - DWORD cbinh; - DWORD cbclr; - - PBYTE pidh; - PBYTE pinh; - PBYTE pclr; - - IMAGE_DOS_HEADER idh; - union { - IMAGE_NT_HEADERS inh; - IMAGE_NT_HEADERS32 inh32; - IMAGE_NT_HEADERS64 inh64; - BYTE raw[sizeof(IMAGE_NT_HEADERS64) + - sizeof(IMAGE_SECTION_HEADER) * 32]; - }; - DETOUR_CLR_HEADER clr; - - } DETOUR_EXE_RESTORE, * PDETOUR_EXE_RESTORE; - - typedef struct _DETOUR_EXE_HELPER - { - DWORD cb; - DWORD pid; - DWORD nDlls; - CHAR rDlls[4]; - } DETOUR_EXE_HELPER, * PDETOUR_EXE_HELPER; - -#pragma pack(pop) - -#define DETOUR_SECTION_HEADER_DECLARE(cbSectionSize) \ -{ \ - sizeof(DETOUR_SECTION_HEADER),\ - DETOUR_SECTION_HEADER_SIGNATURE,\ - sizeof(DETOUR_SECTION_HEADER),\ - (cbSectionSize),\ - \ - 0,\ - 0,\ - 0,\ - 0,\ - \ - 0,\ - 0,\ - 0,\ - 0,\ -} - - /////////////////////////////////////////////////////////////// Helper Macros. - // -#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) -#define DETOURS_STRINGIFY_(x) #x - -///////////////////////////////////////////////////////////// Binary Typedefs. -// - typedef BOOL(CALLBACK* PF_DETOUR_BINARY_BYWAY_CALLBACK)( - _In_opt_ PVOID pContext, - _In_opt_ LPCSTR pszFile, - _Outptr_result_maybenull_ LPCSTR* ppszOutFile); - - typedef BOOL(CALLBACK* PF_DETOUR_BINARY_FILE_CALLBACK)( - _In_opt_ PVOID pContext, - _In_ LPCSTR pszOrigFile, - _In_ LPCSTR pszFile, - _Outptr_result_maybenull_ LPCSTR* ppszOutFile); - - typedef BOOL(CALLBACK* PF_DETOUR_BINARY_SYMBOL_CALLBACK)( - _In_opt_ PVOID pContext, - _In_ ULONG nOrigOrdinal, - _In_ ULONG nOrdinal, - _Out_ ULONG* pnOutOrdinal, - _In_opt_ LPCSTR pszOrigSymbol, - _In_opt_ LPCSTR pszSymbol, - _Outptr_result_maybenull_ LPCSTR* ppszOutSymbol); - - typedef BOOL(CALLBACK* PF_DETOUR_BINARY_COMMIT_CALLBACK)( - _In_opt_ PVOID pContext); - - typedef BOOL(CALLBACK* PF_DETOUR_ENUMERATE_EXPORT_CALLBACK)(_In_opt_ PVOID pContext, - _In_ ULONG nOrdinal, - _In_opt_ LPCSTR pszName, - _In_opt_ PVOID pCode); - - typedef BOOL(CALLBACK* PF_DETOUR_IMPORT_FILE_CALLBACK)(_In_opt_ PVOID pContext, - _In_opt_ HMODULE hModule, - _In_opt_ LPCSTR pszFile); - - typedef BOOL(CALLBACK* PF_DETOUR_IMPORT_FUNC_CALLBACK)(_In_opt_ PVOID pContext, - _In_ DWORD nOrdinal, - _In_opt_ LPCSTR pszFunc, - _In_opt_ PVOID pvFunc); - - // Same as PF_DETOUR_IMPORT_FUNC_CALLBACK but extra indirection on last parameter. - typedef BOOL(CALLBACK* PF_DETOUR_IMPORT_FUNC_CALLBACK_EX)(_In_opt_ PVOID pContext, - _In_ DWORD nOrdinal, - _In_opt_ LPCSTR pszFunc, - _In_opt_ PVOID* ppvFunc); - - typedef VOID* PDETOUR_BINARY; - typedef VOID* PDETOUR_LOADED_BINARY; - - //////////////////////////////////////////////////////////// Transaction APIs. - // - LONG WINAPI DetourTransactionBegin(VOID); - LONG WINAPI DetourTransactionAbort(VOID); - LONG WINAPI DetourTransactionCommit(VOID); - LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID** pppFailedPointer); - - LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread); - - LONG WINAPI DetourAttach(_Inout_ PVOID* ppPointer, - _In_ PVOID pDetour); - - LONG WINAPI DetourAttachEx(_Inout_ PVOID* ppPointer, - _In_ PVOID pDetour, - _Out_opt_ PDETOUR_TRAMPOLINE* ppRealTrampoline, - _Out_opt_ PVOID* ppRealTarget, - _Out_opt_ PVOID* ppRealDetour); - - LONG WINAPI DetourDetach(_Inout_ PVOID* ppPointer, - _In_ PVOID pDetour); - - BOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore); - BOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain); - PVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound); - PVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound); - - ////////////////////////////////////////////////////////////// Code Functions. - // - PVOID WINAPI DetourFindFunction(_In_ LPCSTR pszModule, - _In_ LPCSTR pszFunction); - PVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer, - _Out_opt_ PVOID* ppGlobals); - PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, - _Inout_opt_ PVOID* ppDstPool, - _In_ PVOID pSrc, - _Out_opt_ PVOID* ppTarget, - _Out_opt_ LONG* plExtra); - BOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule, - _In_ BOOL fLimitReferencesToModule); - - ///////////////////////////////////////////////////// Loaded Binary Functions. - // - HMODULE WINAPI DetourGetContainingModule(_In_ PVOID pvAddr); - HMODULE WINAPI DetourEnumerateModules(_In_opt_ HMODULE hModuleLast); - PVOID WINAPI DetourGetEntryPoint(_In_opt_ HMODULE hModule); - ULONG WINAPI DetourGetModuleSize(_In_opt_ HMODULE hModule); - BOOL WINAPI DetourEnumerateExports(_In_ HMODULE hModule, - _In_opt_ PVOID pContext, - _In_ PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport); - BOOL WINAPI DetourEnumerateImports(_In_opt_ HMODULE hModule, - _In_opt_ PVOID pContext, - _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, - _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc); - - BOOL WINAPI DetourEnumerateImportsEx(_In_opt_ HMODULE hModule, - _In_opt_ PVOID pContext, - _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, - _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFuncEx); - - _Writable_bytes_(*pcbData) - _Readable_bytes_(*pcbData) - _Success_(return != NULL) - PVOID WINAPI DetourFindPayload(_In_opt_ HMODULE hModule, - _In_ REFGUID rguid, - _Out_ DWORD* pcbData); - - _Writable_bytes_(*pcbData) - _Readable_bytes_(*pcbData) - _Success_(return != NULL) - PVOID WINAPI DetourFindPayloadEx(_In_ REFGUID rguid, - _Out_ DWORD* pcbData); - - DWORD WINAPI DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule); - - ///////////////////////////////////////////////// Persistent Binary Functions. - // - - PDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE hFile); - - _Writable_bytes_(*pcbData) - _Readable_bytes_(*pcbData) - _Success_(return != NULL) - PVOID WINAPI DetourBinaryEnumeratePayloads(_In_ PDETOUR_BINARY pBinary, - _Out_opt_ GUID* pGuid, - _Out_ DWORD* pcbData, - _Inout_ DWORD* pnIterator); - - _Writable_bytes_(*pcbData) - _Readable_bytes_(*pcbData) - _Success_(return != NULL) - PVOID WINAPI DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary, - _In_ REFGUID rguid, - _Out_ DWORD* pcbData); - - PVOID WINAPI DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary, - _In_ REFGUID rguid, - _In_reads_opt_(cbData) PVOID pData, - _In_ DWORD cbData); - BOOL WINAPI DetourBinaryDeletePayload(_In_ PDETOUR_BINARY pBinary, _In_ REFGUID rguid); - BOOL WINAPI DetourBinaryPurgePayloads(_In_ PDETOUR_BINARY pBinary); - BOOL WINAPI DetourBinaryResetImports(_In_ PDETOUR_BINARY pBinary); - BOOL WINAPI DetourBinaryEditImports(_In_ PDETOUR_BINARY pBinary, - _In_opt_ PVOID pContext, - _In_opt_ PF_DETOUR_BINARY_BYWAY_CALLBACK pfByway, - _In_opt_ PF_DETOUR_BINARY_FILE_CALLBACK pfFile, - _In_opt_ PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbol, - _In_opt_ PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommit); - BOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pBinary, _In_ HANDLE hFile); - BOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary); - - /////////////////////////////////////////////////// Create Process & Load Dll. - // - typedef BOOL(WINAPI* PDETOUR_CREATE_PROCESS_ROUTINEA)( - _In_opt_ LPCSTR lpApplicationName, - _Inout_opt_ LPSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOA lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation); - - typedef BOOL(WINAPI* PDETOUR_CREATE_PROCESS_ROUTINEW)( - _In_opt_ LPCWSTR lpApplicationName, - _Inout_opt_ LPWSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCWSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOW lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation); - - BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName, - _Inout_opt_ LPSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOA lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ LPCSTR lpDllName, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName, - _Inout_opt_ LPWSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCWSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOW lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ LPCSTR lpDllName, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourCreateProcessWithDll DetourCreateProcessWithDllW -#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEW -#else -#define DetourCreateProcessWithDll DetourCreateProcessWithDllA -#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEA -#endif // !UNICODE - - BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName, - _Inout_opt_ LPSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOA lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ LPCSTR lpDllName, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName, - _Inout_opt_ LPWSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCWSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOW lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ LPCSTR lpDllName, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExW -#else -#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExA -#endif // !UNICODE - - BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName, - _Inout_opt_ LPSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOA lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ DWORD nDlls, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName, - _Inout_opt_ LPWSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCWSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOW lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ DWORD nDlls, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsW -#else -#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsA -#endif // !UNICODE - - BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid, - _In_ LPCSTR lpDllName, - _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid, - _In_ LPCSTR lpDllName, - _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourProcessViaHelper DetourProcessViaHelperW -#else -#define DetourProcessViaHelper DetourProcessViaHelperA -#endif // !UNICODE - - BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid, - _In_ DWORD nDlls, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid, - _In_ DWORD nDlls, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsW -#else -#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsA -#endif // !UNICODE - - BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_ DWORD nDlls); - - BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess, - _In_ HMODULE hImage, - _In_ BOOL bIs32Bit, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_ DWORD nDlls); - - BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess, - _In_ REFGUID rguid, - _In_reads_bytes_(cbData) PVOID pvData, - _In_ DWORD cbData); - BOOL WINAPI DetourRestoreAfterWith(VOID); - BOOL WINAPI DetourRestoreAfterWithEx(_In_reads_bytes_(cbData) PVOID pvData, - _In_ DWORD cbData); - BOOL WINAPI DetourIsHelperProcess(VOID); - VOID CALLBACK DetourFinishHelperProcess(_In_ HWND, - _In_ HINSTANCE, - _In_ LPSTR, - _In_ INT); - - // - ////////////////////////////////////////////////////////////////////////////// -#ifdef __cplusplus -} -#endif // __cplusplus - -//////////////////////////////////////////////// Detours Internal Definitions. -// -#ifdef __cplusplus -#ifdef DETOURS_INTERNAL - -#define NOTHROW -// #define NOTHROW (nothrow) - -////////////////////////////////////////////////////////////////////////////// -// -#if (_MSC_VER < 1299) -#include -typedef IMAGEHLP_MODULE IMAGEHLP_MODULE64; -typedef PIMAGEHLP_MODULE PIMAGEHLP_MODULE64; -typedef IMAGEHLP_SYMBOL SYMBOL_INFO; -typedef PIMAGEHLP_SYMBOL PSYMBOL_INFO; - -static inline -LONG InterlockedCompareExchange(_Inout_ LONG* ptr, _In_ LONG nval, _In_ LONG oval) -{ - return (LONG)::InterlockedCompareExchange((PVOID*)ptr, (PVOID)nval, (PVOID)oval); -} -#else -#pragma warning(push) -#pragma warning(disable:4091) // empty typedef -#include -#pragma warning(pop) -#endif - -#ifdef IMAGEAPI // defined by DBGHELP.H -typedef LPAPI_VERSION(NTAPI* PF_ImagehlpApiVersionEx)(_In_ LPAPI_VERSION AppVersion); - -typedef BOOL(NTAPI* PF_SymInitialize)(_In_ HANDLE hProcess, - _In_opt_ LPCSTR UserSearchPath, - _In_ BOOL fInvadeProcess); -typedef DWORD(NTAPI* PF_SymSetOptions)(_In_ DWORD SymOptions); -typedef DWORD(NTAPI* PF_SymGetOptions)(VOID); -typedef DWORD64(NTAPI* PF_SymLoadModule64)(_In_ HANDLE hProcess, - _In_opt_ HANDLE hFile, - _In_ LPSTR ImageName, - _In_opt_ LPSTR ModuleName, - _In_ DWORD64 BaseOfDll, - _In_opt_ DWORD SizeOfDll); -typedef BOOL(NTAPI* PF_SymGetModuleInfo64)(_In_ HANDLE hProcess, - _In_ DWORD64 qwAddr, - _Out_ PIMAGEHLP_MODULE64 ModuleInfo); -typedef BOOL(NTAPI* PF_SymFromName)(_In_ HANDLE hProcess, - _In_ LPSTR Name, - _Out_ PSYMBOL_INFO Symbol); - -typedef struct _DETOUR_SYM_INFO -{ - HANDLE hProcess; - HMODULE hDbgHelp; - PF_ImagehlpApiVersionEx pfImagehlpApiVersionEx; - PF_SymInitialize pfSymInitialize; - PF_SymSetOptions pfSymSetOptions; - PF_SymGetOptions pfSymGetOptions; - PF_SymLoadModule64 pfSymLoadModule64; - PF_SymGetModuleInfo64 pfSymGetModuleInfo64; - PF_SymFromName pfSymFromName; -} DETOUR_SYM_INFO, * PDETOUR_SYM_INFO; - -PDETOUR_SYM_INFO DetourLoadImageHlp(VOID); - -#endif // IMAGEAPI - -#if defined(_INC_STDIO) && !defined(_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS) -#error detours.h must be included before stdio.h (or at least define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS earlier) -#endif -#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 - -#ifndef DETOUR_TRACE -#if DETOUR_DEBUG -#define DETOUR_TRACE(x) printf x -#define DETOUR_BREAK() __debugbreak() -#include -#include -#else -#define DETOUR_TRACE(x) -#define DETOUR_BREAK() -#endif -#endif - -#if 1 || defined(DETOURS_IA64) - -// -// IA64 instructions are 41 bits, 3 per bundle, plus 5 bit bundle template => 128 bits per bundle. -// - -#define DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE (3) - -#define DETOUR_IA64_TEMPLATE_OFFSET (0) -#define DETOUR_IA64_TEMPLATE_SIZE (5) - -#define DETOUR_IA64_INSTRUCTION_SIZE (41) -#define DETOUR_IA64_INSTRUCTION0_OFFSET (DETOUR_IA64_TEMPLATE_SIZE) -#define DETOUR_IA64_INSTRUCTION1_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE) -#define DETOUR_IA64_INSTRUCTION2_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE + DETOUR_IA64_INSTRUCTION_SIZE) - -C_ASSERT(DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * DETOUR_IA64_INSTRUCTION_SIZE == 128); - -__declspec(align(16)) struct DETOUR_IA64_BUNDLE -{ -public: - union - { - BYTE data[16]; - UINT64 wide[2]; - }; - - enum { - A_UNIT = 1u, - I_UNIT = 2u, - M_UNIT = 3u, - B_UNIT = 4u, - F_UNIT = 5u, - L_UNIT = 6u, - X_UNIT = 7u, - }; - struct DETOUR_IA64_METADATA - { - ULONG nTemplate : 8; // Instruction template. - ULONG nUnit0 : 4; // Unit for slot 0 - ULONG nUnit1 : 4; // Unit for slot 1 - ULONG nUnit2 : 4; // Unit for slot 2 - }; - -protected: - static const DETOUR_IA64_METADATA s_rceCopyTable[33]; - - UINT RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const; - - bool RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst, - _In_ BYTE slot, - _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const; - - // 120 112 104 96 88 80 72 64 56 48 40 32 24 16 8 0 - // f. e. d. c. b. a. 9. 8. 7. 6. 5. 4. 3. 2. 1. 0. - - // 00 - // f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0. - // 0000 0000 0000 0000 0000 0000 0000 001f : Template [4..0] - // 0000 0000 0000 0000 0000 03ff ffff ffe0 : Zero [ 41.. 5] - // 0000 0000 0000 0000 0000 3c00 0000 0000 : Zero [ 45.. 42] - // 0000 0000 0007 ffff ffff c000 0000 0000 : One [ 82.. 46] - // 0000 0000 0078 0000 0000 0000 0000 0000 : One [ 86.. 83] - // 0fff ffff ff80 0000 0000 0000 0000 0000 : Two [123.. 87] - // f000 0000 0000 0000 0000 0000 0000 0000 : Two [127..124] - BYTE GetTemplate() const; - // Get 4 bit opcodes. - BYTE GetInst0() const; - BYTE GetInst1() const; - BYTE GetInst2() const; - BYTE GetUnit(BYTE slot) const; - BYTE GetUnit0() const; - BYTE GetUnit1() const; - BYTE GetUnit2() const; - // Get 37 bit data. - UINT64 GetData0() const; - UINT64 GetData1() const; - UINT64 GetData2() const; - - // Get/set the full 41 bit instructions. - UINT64 GetInstruction(BYTE slot) const; - UINT64 GetInstruction0() const; - UINT64 GetInstruction1() const; - UINT64 GetInstruction2() const; - void SetInstruction(BYTE slot, UINT64 instruction); - void SetInstruction0(UINT64 instruction); - void SetInstruction1(UINT64 instruction); - void SetInstruction2(UINT64 instruction); - - // Get/set bitfields. - static UINT64 GetBits(UINT64 Value, UINT64 Offset, UINT64 Count); - static UINT64 SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field); - - // Get specific read-only fields. - static UINT64 GetOpcode(UINT64 instruction); // 4bit opcode - static UINT64 GetX(UINT64 instruction); // 1bit opcode extension - static UINT64 GetX3(UINT64 instruction); // 3bit opcode extension - static UINT64 GetX6(UINT64 instruction); // 6bit opcode extension - - // Get/set specific fields. - static UINT64 GetImm7a(UINT64 instruction); - static UINT64 SetImm7a(UINT64 instruction, UINT64 imm7a); - static UINT64 GetImm13c(UINT64 instruction); - static UINT64 SetImm13c(UINT64 instruction, UINT64 imm13c); - static UINT64 GetSignBit(UINT64 instruction); - static UINT64 SetSignBit(UINT64 instruction, UINT64 signBit); - static UINT64 GetImm20a(UINT64 instruction); - static UINT64 SetImm20a(UINT64 instruction, UINT64 imm20a); - static UINT64 GetImm20b(UINT64 instruction); - static UINT64 SetImm20b(UINT64 instruction, UINT64 imm20b); - - static UINT64 SignExtend(UINT64 Value, UINT64 Offset); - - BOOL IsMovlGp() const; - - VOID SetInst(BYTE Slot, BYTE nInst); - VOID SetInst0(BYTE nInst); - VOID SetInst1(BYTE nInst); - VOID SetInst2(BYTE nInst); - VOID SetData(BYTE Slot, UINT64 nData); - VOID SetData0(UINT64 nData); - VOID SetData1(UINT64 nData); - VOID SetData2(UINT64 nData); - BOOL SetNop(BYTE Slot); - BOOL SetNop0(); - BOOL SetNop1(); - BOOL SetNop2(); - -public: - BOOL IsBrl() const; - VOID SetBrl(); - VOID SetBrl(UINT64 target); - UINT64 GetBrlTarget() const; - VOID SetBrlTarget(UINT64 target); - VOID SetBrlImm(UINT64 imm); - UINT64 GetBrlImm() const; - - UINT64 GetMovlGp() const; - VOID SetMovlGp(UINT64 gp); - - VOID SetStop(); - - UINT Copy(_Out_ DETOUR_IA64_BUNDLE* pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra = NULL) const; -}; -#endif // DETOURS_IA64 - -#ifdef DETOURS_ARM - -#define DETOURS_PFUNC_TO_PBYTE(p) ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1)) -#define DETOURS_PBYTE_TO_PFUNC(p) ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1)) - -#endif // DETOURS_ARM - -////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -#define DETOUR_OFFLINE_LIBRARY(x) \ -PVOID WINAPI DetourCopyInstruction##x(_In_opt_ PVOID pDst, \ - _Inout_opt_ PVOID *ppDstPool, \ - _In_ PVOID pSrc, \ - _Out_opt_ PVOID *ppTarget, \ - _Out_opt_ LONG *plExtra); \ - \ -BOOL WINAPI DetourSetCodeModule##x(_In_ HMODULE hModule, \ - _In_ BOOL fLimitReferencesToModule); \ - - DETOUR_OFFLINE_LIBRARY(X86) - DETOUR_OFFLINE_LIBRARY(X64) - DETOUR_OFFLINE_LIBRARY(ARM) - DETOUR_OFFLINE_LIBRARY(ARM64) - DETOUR_OFFLINE_LIBRARY(IA64) - -#undef DETOUR_OFFLINE_LIBRARY - - ////////////////////////////////////////////////////////////////////////////// - // - // Helpers for manipulating page protection. - // - - _Success_(return != FALSE) - BOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_ HANDLE hProcess, - _In_ PVOID pAddress, - _In_ SIZE_T nSize, - _In_ DWORD dwNewProtect, - _Out_ PDWORD pdwOldProtect); - - _Success_(return != FALSE) - BOOL WINAPI DetourVirtualProtectSameExecute(_In_ PVOID pAddress, - _In_ SIZE_T nSize, - _In_ DWORD dwNewProtect, - _Out_ PDWORD pdwOldProtect); -#ifdef __cplusplus -} -#endif // __cplusplus - -////////////////////////////////////////////////////////////////////////////// - -#define MM_ALLOCATION_GRANULARITY 0x10000 - -////////////////////////////////////////////////////////////////////////////// - -#endif // DETOURS_INTERNAL -#endif // __cplusplus - -#endif // _DETOURS_H_ -// -//////////////////////////////////////////////////////////////// End of File. diff --git a/SteamAPICheckBypass/detours.lib b/SteamAPICheckBypass/detours.lib deleted file mode 100644 index a6a663b..0000000 Binary files a/SteamAPICheckBypass/detours.lib and /dev/null differ diff --git a/SteamAPICheckBypass/dll_interface.vcxproj b/SteamAPICheckBypass/dll_interface.vcxproj new file mode 100644 index 0000000..b402314 --- /dev/null +++ b/SteamAPICheckBypass/dll_interface.vcxproj @@ -0,0 +1,249 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {02a9f0e8-f3c9-407e-a7dc-cdc42430e626} + dllinterface + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)bin\$(Configuration)\$(Platform)\$(ProjectName)\ + $(SolutionDir)bin\tmp\$(Configuration)\$(Platform)\$(ProjectName)\ + SteamAPICheckBypass_x32 + false + + + $(SolutionDir)bin\$(Configuration)\$(Platform)\$(ProjectName)\ + $(SolutionDir)bin\tmp\$(Configuration)\$(Platform)\$(ProjectName)\ + SteamAPICheckBypass_x32 + false + + + $(SolutionDir)bin\$(Configuration)\$(Platform)\$(ProjectName)\ + $(SolutionDir)bin\tmp\$(Configuration)\$(Platform)\$(ProjectName)\ + SteamAPICheckBypass + false + + + $(SolutionDir)bin\$(Configuration)\$(Platform)\$(ProjectName)\ + $(SolutionDir)bin\tmp\$(Configuration)\$(Platform)\$(ProjectName)\ + SteamAPICheckBypass + false + + + + Level3 + true + WIN32;_DEBUG;NTFSDUPE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + ..\nt_file_dupe\include;include;%(AdditionalIncludeDirectories) + stdcpp17 + stdc17 + true + true + None + false + true + + + Windows + DebugFull + false + true + false + false + NoErrorReport + exports.def + + + + + Level3 + true + true + true + WIN32;NDEBUG;NTFSDUPE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + ..\nt_file_dupe\include;include;%(AdditionalIncludeDirectories) + stdcpp17 + stdc17 + true + AnySuitable + Speed + true + true + MultiThreaded + true + None + false + true + + + Windows + true + true + false + false + false + false + false + NoErrorReport + exports.def + + + + + Level3 + true + _DEBUG;NTFSDUPE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + stdcpp17 + stdc17 + ..\nt_file_dupe\include;include;%(AdditionalIncludeDirectories) + true + true + None + false + true + + + Windows + DebugFull + false + true + false + false + NoErrorReport + exports.def + + + + + Level3 + true + true + true + NDEBUG;NTFSDUPE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + ..\nt_file_dupe\include;include;%(AdditionalIncludeDirectories) + stdcpp17 + stdc17 + true + AnySuitable + Speed + true + true + MultiThreaded + true + None + false + true + + + Windows + true + true + false + false + false + false + false + NoErrorReport + exports.def + + + + + + + + + {22e4ebeb-b621-4252-ae46-665835385d64} + + + + + + + + + + + + \ No newline at end of file diff --git a/SteamAPICheckBypass/dll_interface.vcxproj.filters b/SteamAPICheckBypass/dll_interface.vcxproj.filters new file mode 100644 index 0000000..181daa6 --- /dev/null +++ b/SteamAPICheckBypass/dll_interface.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {ebf2f168-126a-4813-8eaa-662709fb8740} + + + + + Interface + + + + + + + + + Interface + + + \ No newline at end of file diff --git a/SteamAPICheckBypass/dllmain.cpp b/SteamAPICheckBypass/dllmain.cpp index aef8f2d..90ceef1 100644 --- a/SteamAPICheckBypass/dllmain.cpp +++ b/SteamAPICheckBypass/dllmain.cpp @@ -1,528 +1,126 @@ -#include "pch.h" +#define WIN32_LEAN_AND_MEAN +#include -#include +#include "lib_main/lib_main.hpp" +#include "Helpers/Helpers.hpp" +#include "Configs/Configs.hpp" -#include "Console.h" -#include "detours.h" -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#pragma comment(lib, "detours.lib") -#pragma comment(lib, "ntdll.lib") - -struct Replace -{ - std::wstring origname; - std::wstring replacename; - bool replaceafterfirsttime; // Replace reading request after reading for first time - bool firstime = false; // first time read indicator, should always be false -}; - -//----------Configuration start--------------- - -bool useinternallist = false; //Use built-in replace list without reading .ini file - -bool debugprintpath = false; //Print the path of the file being read - -bool enabledebuglogfile = false; //Enable debug log file - -std::string logfilename = "SteamAPICheckBypass.log"; //Log file name - -std::wstring inifilename = L"\\SteamAPICheckBypass.ini"; //Ini file name - -std::vector internalreplaceList = { - {L"steam_api.dll", L"steam_api.org", false, false}, - {L"steam_api64.dll", L"steam_api64.org", false, false}, -};//internal replace list example - -//----------Configuration end----------------- - - -#pragma region Utils -bool isFileExist(const wchar_t* fileName) { - HANDLE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile != INVALID_HANDLE_VALUE) { - CloseHandle(hFile); - return true; - } - return false; -} - - -void PrintLog(std::string str) -{ - std::string logstr = "[SteamAPICheckBypass] " + str + "\n"; -#ifdef _DEBUG - Console::Print(logstr.c_str()); - if (enabledebuglogfile) - { - std::ofstream logfile; - logfile.open(logfilename, std::ios_base::app); - logfile << logstr; - } -#endif - int wideStrLength = MultiByteToWideChar(CP_UTF8, 0, logstr.c_str(), -1, nullptr, 0); - wchar_t* wideString = new wchar_t[wideStrLength]; - MultiByteToWideChar(CP_UTF8, 0, logstr.c_str(), -1, wideString, wideStrLength); - OutputDebugString(wideString); - delete[] wideString; -} - -wchar_t const* GetCurrentPath() -{ - wchar_t exePath[MAX_PATH]; - GetModuleFileNameW(NULL, exePath, MAX_PATH); - wchar_t* lastBackslash = wcsrchr(exePath, L'\\'); - if (lastBackslash != nullptr) { - *lastBackslash = L'\0'; // Null-terminate to get the directory path - } - return exePath; -} - -std::wstring utf8ToUtf16(const std::string& utf8Str) -{ - std::wstring_convert> conv; - return conv.from_bytes(utf8Str); -} - -std::string utf16ToUtf8(const std::wstring& utf16Str) -{ - std::wstring_convert> conv; - return conv.to_bytes(utf16Str); -} - -typedef enum _SECTION_INFORMATION_CLASS { - SectionBasicInformation, - SectionImageInformation -} SECTION_INFORMATION_CLASS, * PSECTION_INFORMATION_CLASS; -EXTERN_C NTSTATUS __stdcall NtQuerySection(HANDLE SectionHandle, SECTION_INFORMATION_CLASS InformationClass, PVOID InformationBuffer, ULONG InformationBufferSize, PULONG ResultLength); -EXTERN_C NTSTATUS __stdcall NtProtectVirtualMemory(HANDLE ProcessHandle, PVOID* BaseAddress, PULONG NumberOfBytesToProtect, ULONG NewAccessProtection, PULONG OldAccessProtection); -EXTERN_C NTSTATUS __stdcall NtPulseEvent(HANDLE EventHandle, PULONG PreviousState); - -void DisableVMP() -{ - // restore hook at NtProtectVirtualMemory - auto ntdll = GetModuleHandleA("ntdll.dll"); - if (ntdll == NULL) return; - - bool linux = GetProcAddress(ntdll, "wine_get_version") != nullptr; - void* routine = linux ? (void*)NtPulseEvent : (void*)NtQuerySection; - DWORD old; - VirtualProtect(NtProtectVirtualMemory, 1, PAGE_EXECUTE_READWRITE, &old); - *(uintptr_t*)NtProtectVirtualMemory = *(uintptr_t*)routine & ~(0xFFui64 << 32) | (uintptr_t)(*(uint32_t*)((uintptr_t)routine + 4) - 1) << 32; - VirtualProtect(NtProtectVirtualMemory, 1, old, &old); -} +#pragma region Forward functions to system version.dll +#pragma comment(linker, "/EXPORT:GetFileVersionInfoA=c:\\windows\\system32\\version.GetFileVersionInfoA") +#pragma comment(linker, "/EXPORT:GetFileVersionInfoByHandle=c:\\windows\\system32\\version.GetFileVersionInfoByHandle") +#pragma comment(linker, "/EXPORT:GetFileVersionInfoExA=c:\\windows\\system32\\version.GetFileVersionInfoExA") +#pragma comment(linker, "/EXPORT:GetFileVersionInfoExW=c:\\windows\\system32\\version.GetFileVersionInfoExW") +#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeA=c:\\windows\\system32\\version.GetFileVersionInfoSizeA") +#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExA=c:\\windows\\system32\\version.GetFileVersionInfoSizeExA") +#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExW=c:\\windows\\system32\\version.GetFileVersionInfoSizeExW") +#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeW=c:\\windows\\system32\\version.GetFileVersionInfoSizeW") +#pragma comment(linker, "/EXPORT:GetFileVersionInfoW=c:\\windows\\system32\\version.GetFileVersionInfoW") +#pragma comment(linker, "/EXPORT:VerFindFileA=c:\\windows\\system32\\version.VerFindFileA") +#pragma comment(linker, "/EXPORT:VerFindFileW=c:\\windows\\system32\\version.VerFindFileW") +#pragma comment(linker, "/EXPORT:VerInstallFileA=c:\\windows\\system32\\version.VerInstallFileA") +#pragma comment(linker, "/EXPORT:VerInstallFileW=c:\\windows\\system32\\version.VerInstallFileW") +#pragma comment(linker, "/EXPORT:VerLanguageNameA=c:\\windows\\system32\\version.VerLanguageNameA") +#pragma comment(linker, "/EXPORT:VerLanguageNameW=c:\\windows\\system32\\version.VerLanguageNameW") +#pragma comment(linker, "/EXPORT:VerQueryValueA=c:\\windows\\system32\\version.VerQueryValueA") +#pragma comment(linker, "/EXPORT:VerQueryValueW=c:\\windows\\system32\\version.VerQueryValueW") #pragma endregion -std::vector replaceList; - -typedef NTSTATUS(WINAPI* pNtCreateFile)( - PHANDLE FileHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - PLARGE_INTEGER AllocationSize, - ULONG FileAttributes, - ULONG ShareAccess, - ULONG CreateDisposition, - ULONG CreateOptions, - PVOID EaBuffer, - ULONG EaLength); - -pNtCreateFile oNtCreateFile = nullptr; - -typedef NTSTATUS(WINAPI* pNtOpenFile)( - PHANDLE FileHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - ULONG ShareAccess, - ULONG OpenOptions); - -pNtOpenFile oNtOpenFile = nullptr; - -std::wstring GetReplacedPath(std::wstring path) -{ - // Get the file name from the path - size_t lastSlash = path.find_last_of('/'); - size_t lastBackslash = path.find_last_of('\\'); - size_t lastSeparator = (lastSlash > lastBackslash) ? lastSlash : lastBackslash; - std::wstring filename = path.substr(lastSeparator + 1); - if (filename.find(utf8ToUtf16(logfilename)) == std::string::npos && debugprintpath) - { - PrintLog("Reading Path:" + utf16ToUtf8(path)); - } - // Check if the file name matches any entry in the replaceList - for (Replace& replace : replaceList) - { - if (filename.find(replace.origname) != std::wstring::npos) - { - - replace.firstime = true; - - if (replace.replaceafterfirsttime && !replace.firstime) - { - PrintLog("Reading " + utf16ToUtf8(replace.origname) + "for first time."); - break; - } - PrintLog("Reading " + utf16ToUtf8(replace.origname) + ",Replacing..."); - // Replace the path's filename with replacename - size_t pos = path.find_last_of(L"/\\"); - path = path.substr(0, pos + 1) + replace.replacename; - PrintLog("Replaced Path:" + utf16ToUtf8(path)); - // Set firstime to true if replaceafterfirsttime is true and firstime is false - - - break; // No need to check further once a replacement is made - } - } - - return path; -} - -NTSTATUS WINAPI NtCreateFileHook( - PHANDLE FileHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - PLARGE_INTEGER AllocationSize, - ULONG FileAttributes, - ULONG ShareAccess, - ULONG CreateDisposition, - ULONG CreateOptions, - PVOID EaBuffer, - ULONG EaLength) -{ - try - { - if (ObjectAttributes != nullptr && ObjectAttributes->ObjectName && - ObjectAttributes->ObjectName->Length && - ObjectAttributes->ObjectName->Buffer != nullptr && !IsBadReadPtr(ObjectAttributes->ObjectName->Buffer, sizeof(WCHAR)) && ObjectAttributes->ObjectName->Buffer[0]) { - std::wstring originalPath(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(WCHAR)); - std::wstring replacedPathStr = GetReplacedPath(originalPath); - UNICODE_STRING replacedPathUnicode; - RtlInitUnicodeString(&replacedPathUnicode, replacedPathStr.c_str()); - ObjectAttributes->ObjectName = &replacedPathUnicode; - return oNtCreateFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - AllocationSize, - FileAttributes, - ShareAccess, - CreateDisposition, - CreateOptions, - EaBuffer, - EaLength); - } - } - catch (...) - { - PrintLog("Error in NtCreateFileHook"); - } - - return oNtCreateFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - AllocationSize, - FileAttributes, - ShareAccess, - CreateDisposition, - CreateOptions, - EaBuffer, - EaLength); -} - -NTSTATUS WINAPI NtOpenFileHook( - PHANDLE FileHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - ULONG ShareAccess, - ULONG OpenOptions) -{ - try - { - if (ObjectAttributes != nullptr && ObjectAttributes->ObjectName && - ObjectAttributes->ObjectName->Length && - ObjectAttributes->ObjectName->Buffer != nullptr && !IsBadReadPtr(ObjectAttributes->ObjectName->Buffer, sizeof(WCHAR)) && ObjectAttributes->ObjectName->Buffer[0]) { - std::wstring originalPath(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(WCHAR)); - std::wstring replacedPathStr = GetReplacedPath(originalPath); - UNICODE_STRING replacedPathUnicode; - RtlInitUnicodeString(&replacedPathUnicode, replacedPathStr.c_str()); - ObjectAttributes->ObjectName = &replacedPathUnicode; - return oNtOpenFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - ShareAccess, - OpenOptions); - } - } - catch (...) - { - PrintLog("Error in NtOpenFileHook"); - } - - return oNtOpenFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - ShareAccess, - OpenOptions); -} - -void LoadHook() -{ - PrintLog("Starting to hook File APIs..."); - HMODULE hNtdll = GetModuleHandle(L"ntdll.dll"); - DetourRestoreAfterWith(); - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - if (hNtdll) - { - oNtCreateFile = (pNtCreateFile)GetProcAddress(hNtdll, "NtCreateFile"); - if (oNtCreateFile) - { - DetourAttach(&(PVOID&)oNtCreateFile, NtCreateFileHook); - auto Error = DetourTransactionCommit(); - if (Error == NO_ERROR) - PrintLog("Hooked NtCreateFile"); - else - PrintLog("NtCreateFile Hook Failed. Error: " + std::to_string(Error)); - } - else - { - PrintLog("NtCreateFile Hook Failed. Error: Failed to get NtCreateFile address."); - } - } - - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - if (hNtdll) - { - oNtOpenFile = (pNtOpenFile)GetProcAddress(hNtdll, "NtOpenFile"); - if (oNtOpenFile) - { - DetourAttach(&(PVOID&)oNtOpenFile, NtOpenFileHook); - auto Error = DetourTransactionCommit(); - if (Error == NO_ERROR) - PrintLog("Hooked NtOpenFile"); - else - PrintLog("NtOpenFile Hook Failed. Error: " + std::to_string(Error)); - } - else - { - PrintLog("NtOpenFile Hook Failed. Error: Failed to get NtOpenFile address."); - } - } -} - -bool readReplacesFromIni(const std::wstring& filename, std::vector& replaceList) { - std::ifstream iniFile(filename); - std::string line; - std::map replaceMap; - std::map afterFirstTimeMap; - if (!iniFile.is_open()) { - PrintLog("Unable to open ini file."); - return false; - } - - - while (std::getline(iniFile, line) && line != "[AfterFirstTime]") { - if (line[0] == '[') continue; - std::istringstream is_line(line); - std::string key; - if (std::getline(is_line, key, '=')) { - std::string value; - if (std::getline(is_line, value)) { - replaceMap[key] = value; - } - } - } - - while (std::getline(iniFile, line)) { - if (line[0] == '[') continue; - std::istringstream is_line(line); - std::string key; - if (std::getline(is_line, key, '=')) { - std::string value; - if (std::getline(is_line, value)) { - if (value == "1") - { - afterFirstTimeMap[key] = true; - } - else afterFirstTimeMap[key] = false; - } - } - } - - - for (const auto& entry : replaceMap) { - Replace replace; - replace.origname = utf8ToUtf16(entry.first); - replace.replacename = utf8ToUtf16(entry.second); - replace.replaceafterfirsttime = afterFirstTimeMap[entry.first]; - replaceList.push_back(replace); - } - - iniFile.close(); - return true; -} - -void Checkfile(std::vector& replaceList) -{ - PrintLog("Checking Original Steam_API(64) files..."); - - wchar_t exePath[MAX_PATH]; - GetModuleFileNameW(NULL, exePath, MAX_PATH); - - // Extract the directory from the full path - wchar_t* lastBackslash = wcsrchr(exePath, L'\\'); - if (lastBackslash != nullptr) { - *lastBackslash = L'\0'; // Null-terminate to get the directory path - } - - // Construct file paths based on the executable path - wchar_t steamAPI_ORG[MAX_PATH]; - wcscpy_s(steamAPI_ORG, MAX_PATH, exePath); - wcscat_s(steamAPI_ORG, MAX_PATH, L"\\steam_api.org"); - - wchar_t steamAPI_BAK[MAX_PATH]; - wcscpy_s(steamAPI_BAK, MAX_PATH, exePath); - wcscat_s(steamAPI_BAK, MAX_PATH, L"\\steam_api.dll.bak"); - - wchar_t steamAPI_O[MAX_PATH]; - wcscpy_s(steamAPI_O, MAX_PATH, exePath); - wcscat_s(steamAPI_O, MAX_PATH, L"\\steam_api_o.dll"); - - wchar_t steamAPI64_ORG[MAX_PATH]; - wcscpy_s(steamAPI64_ORG, MAX_PATH, exePath); - wcscat_s(steamAPI64_ORG, MAX_PATH, L"\\steam_api64.org"); - - wchar_t steamAPI64_BAK[MAX_PATH]; - wcscpy_s(steamAPI64_BAK, MAX_PATH, exePath); - wcscat_s(steamAPI64_BAK, MAX_PATH, L"\\steam_api64.dll.bak"); - - wchar_t steamAPI64_O[MAX_PATH]; - wcscpy_s(steamAPI64_O, MAX_PATH, exePath); - wcscat_s(steamAPI64_O, MAX_PATH, L"\\steam_api64_o.dll"); - - - bool SteamAPI_ORG = isFileExist(steamAPI_ORG); - bool SteamAPI_BAK = isFileExist(steamAPI_BAK); - bool SteamAPI_O = isFileExist(steamAPI_O); - bool SteamAPI64_ORG = isFileExist(steamAPI64_ORG); - bool SteamAPI64_BAK = isFileExist(steamAPI64_BAK); - bool SteamAPI64_O = isFileExist(steamAPI64_O); - - if (SteamAPI_BAK) - { - PrintLog("Found steam_api.dll.bak."); - replaceList.push_back({ L"steam_api.dll",L"steam_api.dll.bak",false }); - } - else if (SteamAPI_ORG) - { - PrintLog("Found steam_api.org."); - replaceList.push_back({ L"steam_api.dll",L"steam_api.org",false }); - } - else if (SteamAPI_O) - { - PrintLog("Found steam_api_o.dll."); - replaceList.push_back({ L"steam_api.dll",L"steam_api_o.dll",false }); - } - - if (SteamAPI64_BAK) - { - PrintLog("Found steam_api64.dll.bak."); - replaceList.push_back({ L"steam_api.dll",L"steam_api64.dll.bak",false }); - } - else if (SteamAPI64_ORG) - { - PrintLog("Found steam_api64.org."); - replaceList.push_back({ L"steam_api.dll",L"steam_api64.org",false }); - } - else if (SteamAPI64_O) - { - PrintLog("Found steam_api64_o.dll."); - replaceList.push_back({ L"steam_api.dll",L"steam_api64_o.dll",false }); - } -} - -void GetReplaceList() -{ - wchar_t iniPath[MAX_PATH]; - wcscpy_s(iniPath, MAX_PATH, GetCurrentPath()); - wcscat_s(iniPath, MAX_PATH, inifilename.c_str()); +std::vector initial_files = { + // the current name of the dll is added here as a first entry + L"steamapicheckbypass.json", + L"steamapicheckbypass_config.json", + L"SteamAPICheckBypass.json", + L"SteamAPICheckBypass_congig.json", + L"nt_file_dupe.json", + L"nt_file_dupe_config.json", + L"nt_fs_dupe.json", + L"nt_fs_dupe_config.json", + L"nt_dupe.json", + L"nt_dupe_config.json", +}; - if (useinternallist) - { - replaceList = internalreplaceList; - } +void add_original_entries(const std::wstring& original_path) { + std::wstring _original_path = ntfsdupe::helpers::to_absolute(original_path, ntfsdupe::cfgs::get_exe_dir()); + std::filesystem::path path(_original_path); + std::wstring extension = path.extension().wstring(); + std::wstring stem = path.stem().wstring(); + std::wstring parent_path = path.parent_path().wstring(); - if (readReplacesFromIni(iniPath, replaceList)) - { - PrintLog("Successfully get ini replace infos."); - } - else - { - PrintLog("Failed to get ini replace infos, detecting files..."); - Checkfile(replaceList); - } - PrintLog("-----------------"); - PrintLog("Replace List:"); - { - for (const auto& replace : replaceList) { - PrintLog(utf16ToUtf8(replace.origname) + "," + utf16ToUtf8(replace.replacename)); - } - } - PrintLog("-----------------"); + ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_redirect, _original_path, parent_path + L"\\" + stem + extension + L".bak", true); + ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_redirect, _original_path, parent_path + L"\\" + stem + L".org", true); + ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_redirect, _original_path, parent_path + L"\\" + stem + L"_o" + extension, true); } -void Init() -{ - PrintLog("SteamAPICheckBypass Init"); - GetReplaceList(); - std::thread([]() { - DisableVMP(); - LoadHook(); - }).detach(); -} - -BOOL APIENTRY DllMain(HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved +BOOL APIENTRY DllMain( + HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved ) { -#ifdef _DEBUG - Console::Attach(); -#endif - - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - PrintLog("Steam API Check Bypass dll Loaded."); - Init(); - case DLL_PROCESS_DETACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - break; - } - return TRUE; + switch (ul_reason_for_call) { + case DLL_PROCESS_ATTACH: { + if (!ntfsdupe::init()) return FALSE; + + std::wstring my_path_str(ntfsdupe::helpers::get_module_fullpath(hModule)); + if (my_path_str.empty()) return FALSE; + + // hide ourself (on disk) + if (!ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_hide, my_path_str)) return FALSE; + + auto my_path = std::filesystem::path(my_path_str); + + // hide ourself (in memory) + //if (!ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::module_hide_handle, my_path.filename().wstring())) return FALSE; + + std::wstring stem_lower = my_path.stem().wstring(); + std::transform(stem_lower.begin(), stem_lower.end(), stem_lower.begin(), ::towlower); + if (stem_lower != L"version") { + if (!ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::module_hide_handle, stem_lower)) return FALSE; + } + + // add .json to the list + initial_files.insert(initial_files.begin(), my_path.stem().wstring() + L".json"); + + // try to load some files by default + auto my_dir = my_path.parent_path(); + bool found_cfg_file = false; + for (const auto &file : initial_files) { + auto cfg_file = (my_dir / file).wstring(); + if (ntfsdupe::cfgs::load_file(cfg_file.c_str())) { + found_cfg_file = true; + // hiding this file isn't really critical, right? + ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_hide, cfg_file); + break; + } + } + + if (!found_cfg_file) + { + // hide exe + add_original_entries(ntfsdupe::helpers::get_module_fullpath(nullptr)); + + // hide steam_api.dll + add_original_entries(L"steam_api.dll"); + + // hide steam_api64.dll + add_original_entries(L"steam_api64.dll"); + } + + } + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + ntfsdupe::deinit(); + break; + } + + return TRUE; } -extern "C" __declspec(dllexport) void SteamAPICheckBypass() {}; \ No newline at end of file diff --git a/SteamAPICheckBypass/exports.def b/SteamAPICheckBypass/exports.def new file mode 100644 index 0000000..d2135c4 --- /dev/null +++ b/SteamAPICheckBypass/exports.def @@ -0,0 +1,5 @@ +LIBRARY nt_file_dupe +EXPORTS + ntfsdupe_add_entry + ntfsdupe_load_file + ntfsdupe_deinit diff --git a/SteamAPICheckBypass/framework.h b/SteamAPICheckBypass/framework.h deleted file mode 100644 index 80cbbc9..0000000 --- a/SteamAPICheckBypass/framework.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 -// Windows 头文件 -#include diff --git a/SteamAPICheckBypass/include/nt_file_dupe.hpp b/SteamAPICheckBypass/include/nt_file_dupe.hpp new file mode 100644 index 0000000..71afa88 --- /dev/null +++ b/SteamAPICheckBypass/include/nt_file_dupe.hpp @@ -0,0 +1,42 @@ +#pragma once + + +#if defined(NTFSDUPE_EXPORTS) + #define NTFSDUPE_API extern "C" +#else + #define NTFSDUPE_API extern "C" __declspec(dllimport) +#endif + +#if defined(_WIN32) + #define NTFSDUPE_DECL __stdcall +#else + #define NTFSDUPE_DECL __fastcall +#endif + + +// ntfsdupe::itf works for language >= C++17 +namespace ntfsdupe { +namespace itf { + // mirror of ntfsdupe::cfgs::Mode + // to avoid including .hpp files from the static lib + enum class Mode : char { + file_redirect, + file_hide, + + module_prevent_load, + module_redirect, + module_hide_handle, + }; +} +} + +NTFSDUPE_API bool NTFSDUPE_DECL ntfsdupe_add_entry( + ntfsdupe::itf::Mode mode, + const wchar_t *original, + const wchar_t *target, + bool file_must_exist +); + +NTFSDUPE_API bool NTFSDUPE_DECL ntfsdupe_load_file(const wchar_t *file); + +NTFSDUPE_API void NTFSDUPE_DECL ntfsdupe_deinit(); diff --git a/SteamAPICheckBypass/pch.cpp b/SteamAPICheckBypass/pch.cpp deleted file mode 100644 index 331e647..0000000 --- a/SteamAPICheckBypass/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/SteamAPICheckBypass/pch.h b/SteamAPICheckBypass/pch.h deleted file mode 100644 index f693f3b..0000000 --- a/SteamAPICheckBypass/pch.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PCH_H -#define PCH_H - -#include "framework.h" - -#endif //PCH_H diff --git a/SteamAPICheckBypass/src/nt_file_dupe.cpp b/SteamAPICheckBypass/src/nt_file_dupe.cpp new file mode 100644 index 0000000..38db1a4 --- /dev/null +++ b/SteamAPICheckBypass/src/nt_file_dupe.cpp @@ -0,0 +1,35 @@ +#include "nt_file_dupe.hpp" + +#include "lib_main/lib_main.hpp" +#include "Configs/Configs.hpp" +#include "Helpers/Helpers.hpp" +#include "Hooks/Hooks.hpp" + +#include +#include + + +bool NTFSDUPE_DECL ntfsdupe_add_entry( + ntfsdupe::itf::Mode mode, + const wchar_t *original, + const wchar_t *target, + bool file_must_exist +) +{ + if (!original || !original[0]) return false; + + std::wstring _target = target && target[0] + ? std::wstring(target) + : std::wstring(); + return ntfsdupe::cfgs::add_entry((ntfsdupe::cfgs::Mode)mode, original, _target, file_must_exist); +} + +bool NTFSDUPE_DECL ntfsdupe_load_file(const wchar_t *file) +{ + return ntfsdupe::cfgs::load_file(file); +} + +void NTFSDUPE_DECL ntfsdupe_deinit() +{ + ntfsdupe::deinit(); +} diff --git a/SteamAPICheckBypass_x32/Console.cpp b/SteamAPICheckBypass_x32/Console.cpp deleted file mode 100644 index 1234436..0000000 --- a/SteamAPICheckBypass_x32/Console.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "pch.h" -#include "Console.h" - -HANDLE _out = NULL, _old_out = NULL; -HANDLE _err = NULL, _old_err = NULL; -HANDLE _in = NULL, _old_in = NULL; - -void Console::Attach() -{ - _old_out = GetStdHandle(STD_OUTPUT_HANDLE); - _old_err = GetStdHandle(STD_ERROR_HANDLE); - _old_in = GetStdHandle(STD_INPUT_HANDLE); - - ::AllocConsole() && ::AttachConsole(GetCurrentProcessId()); - - _out = GetStdHandle(STD_OUTPUT_HANDLE); - _err = GetStdHandle(STD_ERROR_HANDLE); - _in = GetStdHandle(STD_INPUT_HANDLE); - - SetConsoleMode(_out, - ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); - - SetConsoleMode(_in, - ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS | - ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE); -} - -void Console::Detach() -{ - if (_out && _err && _in) { - FreeConsole(); - - if (_old_out) - SetStdHandle(STD_OUTPUT_HANDLE, _old_out); - if (_old_err) - SetStdHandle(STD_ERROR_HANDLE, _old_err); - if (_old_in) - SetStdHandle(STD_INPUT_HANDLE, _old_in); - } -} - -bool Console::Print(const char* fmt, ...) -{ - if (!_out) - return false; - - char buf[1024]; - va_list va; - - va_start(va, fmt); - _vsnprintf_s(buf, 1024, fmt, va); - va_end(va); - - return !!WriteConsoleA(_out, buf, static_cast(strlen(buf)), nullptr, nullptr); -} \ No newline at end of file diff --git a/SteamAPICheckBypass_x32/Console.h b/SteamAPICheckBypass_x32/Console.h deleted file mode 100644 index 131a4fa..0000000 --- a/SteamAPICheckBypass_x32/Console.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CONSOLE_H -#define CONSOLE_H - -#include -#include - -namespace Console -{ - void Attach(); - void Detach(); - bool Print(const char* fmt, ...); -} - -#endif \ No newline at end of file diff --git a/SteamAPICheckBypass_x32/SteamAPICheckBypass_x32.vcxproj b/SteamAPICheckBypass_x32/SteamAPICheckBypass_x32.vcxproj deleted file mode 100644 index 2d21056..0000000 --- a/SteamAPICheckBypass_x32/SteamAPICheckBypass_x32.vcxproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - 17.0 - Win32Proj - {8A0F57D1-0E26-435B-9CFA-084D4424DD87} - SteamAPICheckBypass - 10.0 - - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - - - - - - - - - - - - - - Level3 - true - WIN32;_DEBUG;STEAMAPICHECKBYPASS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - Async - - - Windows - true - false - - - - - Level3 - true - true - true - WIN32;NDEBUG;STEAMAPICHECKBYPASS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - Async - - - Windows - true - true - true - false - - - - - - - - - - - - - Create - Create - - - - - - \ No newline at end of file diff --git a/SteamAPICheckBypass_x32/SteamAPICheckBypass_x32.vcxproj.filters b/SteamAPICheckBypass_x32/SteamAPICheckBypass_x32.vcxproj.filters deleted file mode 100644 index 3175590..0000000 --- a/SteamAPICheckBypass_x32/SteamAPICheckBypass_x32.vcxproj.filters +++ /dev/null @@ -1,42 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - - - 源文件 - - - 源文件 - - - 源文件 - - - \ No newline at end of file diff --git a/SteamAPICheckBypass_x32/detours.h b/SteamAPICheckBypass_x32/detours.h deleted file mode 100644 index bf50e8c..0000000 --- a/SteamAPICheckBypass_x32/detours.h +++ /dev/null @@ -1,1059 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// -// Core Detours Functionality (detours.h of detours.lib) -// -// Microsoft Research Detours Package, Version 4.0.1 -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -#pragma once -#ifndef _DETOURS_H_ -#define _DETOURS_H_ - -#define DETOURS_VERSION 0x4c0c1 // 0xMAJORcMINORcPATCH - -////////////////////////////////////////////////////////////////////////////// -// - -#undef DETOURS_X64 -#undef DETOURS_X86 -#undef DETOURS_IA64 -#undef DETOURS_ARM -#undef DETOURS_ARM64 -#undef DETOURS_BITS -#undef DETOURS_32BIT -#undef DETOURS_64BIT - -#if defined(_X86_) -#define DETOURS_X86 -#define DETOURS_OPTION_BITS 64 - -#elif defined(_AMD64_) -#define DETOURS_X64 -#define DETOURS_OPTION_BITS 32 - -#elif defined(_IA64_) -#define DETOURS_IA64 -#define DETOURS_OPTION_BITS 32 - -#elif defined(_ARM_) -#define DETOURS_ARM - -#elif defined(_ARM64_) -#define DETOURS_ARM64 - -#else -#error Unknown architecture (x86, amd64, ia64, arm, arm64) -#endif - -#ifdef _WIN64 -#undef DETOURS_32BIT -#define DETOURS_64BIT 1 -#define DETOURS_BITS 64 -// If all 64bit kernels can run one and only one 32bit architecture. -//#define DETOURS_OPTION_BITS 32 -#else -#define DETOURS_32BIT 1 -#undef DETOURS_64BIT -#define DETOURS_BITS 32 -// If all 64bit kernels can run one and only one 32bit architecture. -//#define DETOURS_OPTION_BITS 32 -#endif - -#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS) - -////////////////////////////////////////////////////////////////////////////// -// - -#if (_MSC_VER < 1299) -typedef LONG LONG_PTR; -typedef ULONG ULONG_PTR; -#endif - -///////////////////////////////////////////////// SAL 2.0 Annotations w/o SAL. -// -// These definitions are include so that Detours will build even if the -// compiler doesn't have full SAL 2.0 support. -// -#ifndef DETOURS_DONT_REMOVE_SAL_20 - -#ifdef DETOURS_TEST_REMOVE_SAL_20 -#undef _Analysis_assume_ -#undef _Benign_race_begin_ -#undef _Benign_race_end_ -#undef _Field_range_ -#undef _Field_size_ -#undef _In_ -#undef _In_bytecount_ -#undef _In_count_ -#undef _In_opt_ -#undef _In_opt_bytecount_ -#undef _In_opt_count_ -#undef _In_opt_z_ -#undef _In_range_ -#undef _In_reads_ -#undef _In_reads_bytes_ -#undef _In_reads_opt_ -#undef _In_reads_opt_bytes_ -#undef _In_reads_or_z_ -#undef _In_z_ -#undef _Inout_ -#undef _Inout_opt_ -#undef _Inout_z_count_ -#undef _Out_ -#undef _Out_opt_ -#undef _Out_writes_ -#undef _Outptr_result_maybenull_ -#undef _Readable_bytes_ -#undef _Success_ -#undef _Writable_bytes_ -#undef _Pre_notnull_ -#endif - -#if defined(_Deref_out_opt_z_) && !defined(_Outptr_result_maybenull_) -#define _Outptr_result_maybenull_ _Deref_out_opt_z_ -#endif - -#if defined(_In_count_) && !defined(_In_reads_) -#define _In_reads_(x) _In_count_(x) -#endif - -#if defined(_In_opt_count_) && !defined(_In_reads_opt_) -#define _In_reads_opt_(x) _In_opt_count_(x) -#endif - -#if defined(_In_opt_bytecount_) && !defined(_In_reads_opt_bytes_) -#define _In_reads_opt_bytes_(x) _In_opt_bytecount_(x) -#endif - -#if defined(_In_bytecount_) && !defined(_In_reads_bytes_) -#define _In_reads_bytes_(x) _In_bytecount_(x) -#endif - -#ifndef _In_ -#define _In_ -#endif - -#ifndef _In_bytecount_ -#define _In_bytecount_(x) -#endif - -#ifndef _In_count_ -#define _In_count_(x) -#endif - -#ifndef _In_opt_ -#define _In_opt_ -#endif - -#ifndef _In_opt_bytecount_ -#define _In_opt_bytecount_(x) -#endif - -#ifndef _In_opt_count_ -#define _In_opt_count_(x) -#endif - -#ifndef _In_opt_z_ -#define _In_opt_z_ -#endif - -#ifndef _In_range_ -#define _In_range_(x,y) -#endif - -#ifndef _In_reads_ -#define _In_reads_(x) -#endif - -#ifndef _In_reads_bytes_ -#define _In_reads_bytes_(x) -#endif - -#ifndef _In_reads_opt_ -#define _In_reads_opt_(x) -#endif - -#ifndef _In_reads_opt_bytes_ -#define _In_reads_opt_bytes_(x) -#endif - -#ifndef _In_reads_or_z_ -#define _In_reads_or_z_ -#endif - -#ifndef _In_z_ -#define _In_z_ -#endif - -#ifndef _Inout_ -#define _Inout_ -#endif - -#ifndef _Inout_opt_ -#define _Inout_opt_ -#endif - -#ifndef _Inout_z_count_ -#define _Inout_z_count_(x) -#endif - -#ifndef _Out_ -#define _Out_ -#endif - -#ifndef _Out_opt_ -#define _Out_opt_ -#endif - -#ifndef _Out_writes_ -#define _Out_writes_(x) -#endif - -#ifndef _Outptr_result_maybenull_ -#define _Outptr_result_maybenull_ -#endif - -#ifndef _Writable_bytes_ -#define _Writable_bytes_(x) -#endif - -#ifndef _Readable_bytes_ -#define _Readable_bytes_(x) -#endif - -#ifndef _Success_ -#define _Success_(x) -#endif - -#ifndef _Pre_notnull_ -#define _Pre_notnull_ -#endif - -#ifdef DETOURS_INTERNAL - -#pragma warning(disable:4615) // unknown warning type (suppress with older compilers) - -#ifndef _Benign_race_begin_ -#define _Benign_race_begin_ -#endif - -#ifndef _Benign_race_end_ -#define _Benign_race_end_ -#endif - -#ifndef _Field_size_ -#define _Field_size_(x) -#endif - -#ifndef _Field_range_ -#define _Field_range_(x,y) -#endif - -#ifndef _Analysis_assume_ -#define _Analysis_assume_(x) -#endif - -#endif // DETOURS_INTERNAL -#endif // DETOURS_DONT_REMOVE_SAL_20 - -////////////////////////////////////////////////////////////////////////////// -// -#ifndef GUID_DEFINED -#define GUID_DEFINED -typedef struct _GUID -{ - DWORD Data1; - WORD Data2; - WORD Data3; - BYTE Data4[8]; -} GUID; - -#ifdef INITGUID -#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - const GUID name \ - = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } -#else -#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - const GUID name -#endif // INITGUID -#endif // !GUID_DEFINED - -#if defined(__cplusplus) -#ifndef _REFGUID_DEFINED -#define _REFGUID_DEFINED -#define REFGUID const GUID & -#endif // !_REFGUID_DEFINED -#else // !__cplusplus -#ifndef _REFGUID_DEFINED -#define _REFGUID_DEFINED -#define REFGUID const GUID * const -#endif // !_REFGUID_DEFINED -#endif // !__cplusplus - -#ifndef ARRAYSIZE -#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) -#endif - -// -////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - - /////////////////////////////////////////////////// Instruction Target Macros. - // -#define DETOUR_INSTRUCTION_TARGET_NONE ((PVOID)0) -#define DETOUR_INSTRUCTION_TARGET_DYNAMIC ((PVOID)(LONG_PTR)-1) -#define DETOUR_SECTION_HEADER_SIGNATURE 0x00727444 // "Dtr\0" - - extern const GUID DETOUR_EXE_RESTORE_GUID; - extern const GUID DETOUR_EXE_HELPER_GUID; - -#define DETOUR_TRAMPOLINE_SIGNATURE 0x21727444 // Dtr! - typedef struct _DETOUR_TRAMPOLINE DETOUR_TRAMPOLINE, * PDETOUR_TRAMPOLINE; - - /////////////////////////////////////////////////////////// Binary Structures. - // -#pragma pack(push, 8) - typedef struct _DETOUR_SECTION_HEADER - { - DWORD cbHeaderSize; - DWORD nSignature; - DWORD nDataOffset; - DWORD cbDataSize; - - DWORD nOriginalImportVirtualAddress; - DWORD nOriginalImportSize; - DWORD nOriginalBoundImportVirtualAddress; - DWORD nOriginalBoundImportSize; - - DWORD nOriginalIatVirtualAddress; - DWORD nOriginalIatSize; - DWORD nOriginalSizeOfImage; - DWORD cbPrePE; - - DWORD nOriginalClrFlags; - DWORD reserved1; - DWORD reserved2; - DWORD reserved3; - - // Followed by cbPrePE bytes of data. - } DETOUR_SECTION_HEADER, * PDETOUR_SECTION_HEADER; - - typedef struct _DETOUR_SECTION_RECORD - { - DWORD cbBytes; - DWORD nReserved; - GUID guid; - } DETOUR_SECTION_RECORD, * PDETOUR_SECTION_RECORD; - - typedef struct _DETOUR_CLR_HEADER - { - // Header versioning - ULONG cb; - USHORT MajorRuntimeVersion; - USHORT MinorRuntimeVersion; - - // Symbol table and startup information - IMAGE_DATA_DIRECTORY MetaData; - ULONG Flags; - - // Followed by the rest of the IMAGE_COR20_HEADER - } DETOUR_CLR_HEADER, * PDETOUR_CLR_HEADER; - - typedef struct _DETOUR_EXE_RESTORE - { - DWORD cb; - DWORD cbidh; - DWORD cbinh; - DWORD cbclr; - - PBYTE pidh; - PBYTE pinh; - PBYTE pclr; - - IMAGE_DOS_HEADER idh; - union { - IMAGE_NT_HEADERS inh; - IMAGE_NT_HEADERS32 inh32; - IMAGE_NT_HEADERS64 inh64; - BYTE raw[sizeof(IMAGE_NT_HEADERS64) + - sizeof(IMAGE_SECTION_HEADER) * 32]; - }; - DETOUR_CLR_HEADER clr; - - } DETOUR_EXE_RESTORE, * PDETOUR_EXE_RESTORE; - - typedef struct _DETOUR_EXE_HELPER - { - DWORD cb; - DWORD pid; - DWORD nDlls; - CHAR rDlls[4]; - } DETOUR_EXE_HELPER, * PDETOUR_EXE_HELPER; - -#pragma pack(pop) - -#define DETOUR_SECTION_HEADER_DECLARE(cbSectionSize) \ -{ \ - sizeof(DETOUR_SECTION_HEADER),\ - DETOUR_SECTION_HEADER_SIGNATURE,\ - sizeof(DETOUR_SECTION_HEADER),\ - (cbSectionSize),\ - \ - 0,\ - 0,\ - 0,\ - 0,\ - \ - 0,\ - 0,\ - 0,\ - 0,\ -} - - /////////////////////////////////////////////////////////////// Helper Macros. - // -#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) -#define DETOURS_STRINGIFY_(x) #x - -///////////////////////////////////////////////////////////// Binary Typedefs. -// - typedef BOOL(CALLBACK* PF_DETOUR_BINARY_BYWAY_CALLBACK)( - _In_opt_ PVOID pContext, - _In_opt_ LPCSTR pszFile, - _Outptr_result_maybenull_ LPCSTR* ppszOutFile); - - typedef BOOL(CALLBACK* PF_DETOUR_BINARY_FILE_CALLBACK)( - _In_opt_ PVOID pContext, - _In_ LPCSTR pszOrigFile, - _In_ LPCSTR pszFile, - _Outptr_result_maybenull_ LPCSTR* ppszOutFile); - - typedef BOOL(CALLBACK* PF_DETOUR_BINARY_SYMBOL_CALLBACK)( - _In_opt_ PVOID pContext, - _In_ ULONG nOrigOrdinal, - _In_ ULONG nOrdinal, - _Out_ ULONG* pnOutOrdinal, - _In_opt_ LPCSTR pszOrigSymbol, - _In_opt_ LPCSTR pszSymbol, - _Outptr_result_maybenull_ LPCSTR* ppszOutSymbol); - - typedef BOOL(CALLBACK* PF_DETOUR_BINARY_COMMIT_CALLBACK)( - _In_opt_ PVOID pContext); - - typedef BOOL(CALLBACK* PF_DETOUR_ENUMERATE_EXPORT_CALLBACK)(_In_opt_ PVOID pContext, - _In_ ULONG nOrdinal, - _In_opt_ LPCSTR pszName, - _In_opt_ PVOID pCode); - - typedef BOOL(CALLBACK* PF_DETOUR_IMPORT_FILE_CALLBACK)(_In_opt_ PVOID pContext, - _In_opt_ HMODULE hModule, - _In_opt_ LPCSTR pszFile); - - typedef BOOL(CALLBACK* PF_DETOUR_IMPORT_FUNC_CALLBACK)(_In_opt_ PVOID pContext, - _In_ DWORD nOrdinal, - _In_opt_ LPCSTR pszFunc, - _In_opt_ PVOID pvFunc); - - // Same as PF_DETOUR_IMPORT_FUNC_CALLBACK but extra indirection on last parameter. - typedef BOOL(CALLBACK* PF_DETOUR_IMPORT_FUNC_CALLBACK_EX)(_In_opt_ PVOID pContext, - _In_ DWORD nOrdinal, - _In_opt_ LPCSTR pszFunc, - _In_opt_ PVOID* ppvFunc); - - typedef VOID* PDETOUR_BINARY; - typedef VOID* PDETOUR_LOADED_BINARY; - - //////////////////////////////////////////////////////////// Transaction APIs. - // - LONG WINAPI DetourTransactionBegin(VOID); - LONG WINAPI DetourTransactionAbort(VOID); - LONG WINAPI DetourTransactionCommit(VOID); - LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID** pppFailedPointer); - - LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread); - - LONG WINAPI DetourAttach(_Inout_ PVOID* ppPointer, - _In_ PVOID pDetour); - - LONG WINAPI DetourAttachEx(_Inout_ PVOID* ppPointer, - _In_ PVOID pDetour, - _Out_opt_ PDETOUR_TRAMPOLINE* ppRealTrampoline, - _Out_opt_ PVOID* ppRealTarget, - _Out_opt_ PVOID* ppRealDetour); - - LONG WINAPI DetourDetach(_Inout_ PVOID* ppPointer, - _In_ PVOID pDetour); - - BOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore); - BOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain); - PVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound); - PVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound); - - ////////////////////////////////////////////////////////////// Code Functions. - // - PVOID WINAPI DetourFindFunction(_In_ LPCSTR pszModule, - _In_ LPCSTR pszFunction); - PVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer, - _Out_opt_ PVOID* ppGlobals); - PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, - _Inout_opt_ PVOID* ppDstPool, - _In_ PVOID pSrc, - _Out_opt_ PVOID* ppTarget, - _Out_opt_ LONG* plExtra); - BOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule, - _In_ BOOL fLimitReferencesToModule); - - ///////////////////////////////////////////////////// Loaded Binary Functions. - // - HMODULE WINAPI DetourGetContainingModule(_In_ PVOID pvAddr); - HMODULE WINAPI DetourEnumerateModules(_In_opt_ HMODULE hModuleLast); - PVOID WINAPI DetourGetEntryPoint(_In_opt_ HMODULE hModule); - ULONG WINAPI DetourGetModuleSize(_In_opt_ HMODULE hModule); - BOOL WINAPI DetourEnumerateExports(_In_ HMODULE hModule, - _In_opt_ PVOID pContext, - _In_ PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport); - BOOL WINAPI DetourEnumerateImports(_In_opt_ HMODULE hModule, - _In_opt_ PVOID pContext, - _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, - _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc); - - BOOL WINAPI DetourEnumerateImportsEx(_In_opt_ HMODULE hModule, - _In_opt_ PVOID pContext, - _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, - _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFuncEx); - - _Writable_bytes_(*pcbData) - _Readable_bytes_(*pcbData) - _Success_(return != NULL) - PVOID WINAPI DetourFindPayload(_In_opt_ HMODULE hModule, - _In_ REFGUID rguid, - _Out_ DWORD* pcbData); - - _Writable_bytes_(*pcbData) - _Readable_bytes_(*pcbData) - _Success_(return != NULL) - PVOID WINAPI DetourFindPayloadEx(_In_ REFGUID rguid, - _Out_ DWORD* pcbData); - - DWORD WINAPI DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule); - - ///////////////////////////////////////////////// Persistent Binary Functions. - // - - PDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE hFile); - - _Writable_bytes_(*pcbData) - _Readable_bytes_(*pcbData) - _Success_(return != NULL) - PVOID WINAPI DetourBinaryEnumeratePayloads(_In_ PDETOUR_BINARY pBinary, - _Out_opt_ GUID* pGuid, - _Out_ DWORD* pcbData, - _Inout_ DWORD* pnIterator); - - _Writable_bytes_(*pcbData) - _Readable_bytes_(*pcbData) - _Success_(return != NULL) - PVOID WINAPI DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary, - _In_ REFGUID rguid, - _Out_ DWORD* pcbData); - - PVOID WINAPI DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary, - _In_ REFGUID rguid, - _In_reads_opt_(cbData) PVOID pData, - _In_ DWORD cbData); - BOOL WINAPI DetourBinaryDeletePayload(_In_ PDETOUR_BINARY pBinary, _In_ REFGUID rguid); - BOOL WINAPI DetourBinaryPurgePayloads(_In_ PDETOUR_BINARY pBinary); - BOOL WINAPI DetourBinaryResetImports(_In_ PDETOUR_BINARY pBinary); - BOOL WINAPI DetourBinaryEditImports(_In_ PDETOUR_BINARY pBinary, - _In_opt_ PVOID pContext, - _In_opt_ PF_DETOUR_BINARY_BYWAY_CALLBACK pfByway, - _In_opt_ PF_DETOUR_BINARY_FILE_CALLBACK pfFile, - _In_opt_ PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbol, - _In_opt_ PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommit); - BOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pBinary, _In_ HANDLE hFile); - BOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary); - - /////////////////////////////////////////////////// Create Process & Load Dll. - // - typedef BOOL(WINAPI* PDETOUR_CREATE_PROCESS_ROUTINEA)( - _In_opt_ LPCSTR lpApplicationName, - _Inout_opt_ LPSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOA lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation); - - typedef BOOL(WINAPI* PDETOUR_CREATE_PROCESS_ROUTINEW)( - _In_opt_ LPCWSTR lpApplicationName, - _Inout_opt_ LPWSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCWSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOW lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation); - - BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName, - _Inout_opt_ LPSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOA lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ LPCSTR lpDllName, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName, - _Inout_opt_ LPWSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCWSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOW lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ LPCSTR lpDllName, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourCreateProcessWithDll DetourCreateProcessWithDllW -#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEW -#else -#define DetourCreateProcessWithDll DetourCreateProcessWithDllA -#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEA -#endif // !UNICODE - - BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName, - _Inout_opt_ LPSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOA lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ LPCSTR lpDllName, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName, - _Inout_opt_ LPWSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCWSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOW lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ LPCSTR lpDllName, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExW -#else -#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExA -#endif // !UNICODE - - BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName, - _Inout_opt_ LPSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOA lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ DWORD nDlls, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName, - _Inout_opt_ LPWSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCWSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOW lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation, - _In_ DWORD nDlls, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsW -#else -#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsA -#endif // !UNICODE - - BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid, - _In_ LPCSTR lpDllName, - _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid, - _In_ LPCSTR lpDllName, - _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourProcessViaHelper DetourProcessViaHelperW -#else -#define DetourProcessViaHelper DetourProcessViaHelperA -#endif // !UNICODE - - BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid, - _In_ DWORD nDlls, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); - - BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid, - _In_ DWORD nDlls, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); - -#ifdef UNICODE -#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsW -#else -#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsA -#endif // !UNICODE - - BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_ DWORD nDlls); - - BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess, - _In_ HMODULE hImage, - _In_ BOOL bIs32Bit, - _In_reads_(nDlls) LPCSTR* rlpDlls, - _In_ DWORD nDlls); - - BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess, - _In_ REFGUID rguid, - _In_reads_bytes_(cbData) PVOID pvData, - _In_ DWORD cbData); - BOOL WINAPI DetourRestoreAfterWith(VOID); - BOOL WINAPI DetourRestoreAfterWithEx(_In_reads_bytes_(cbData) PVOID pvData, - _In_ DWORD cbData); - BOOL WINAPI DetourIsHelperProcess(VOID); - VOID CALLBACK DetourFinishHelperProcess(_In_ HWND, - _In_ HINSTANCE, - _In_ LPSTR, - _In_ INT); - - // - ////////////////////////////////////////////////////////////////////////////// -#ifdef __cplusplus -} -#endif // __cplusplus - -//////////////////////////////////////////////// Detours Internal Definitions. -// -#ifdef __cplusplus -#ifdef DETOURS_INTERNAL - -#define NOTHROW -// #define NOTHROW (nothrow) - -////////////////////////////////////////////////////////////////////////////// -// -#if (_MSC_VER < 1299) -#include -typedef IMAGEHLP_MODULE IMAGEHLP_MODULE64; -typedef PIMAGEHLP_MODULE PIMAGEHLP_MODULE64; -typedef IMAGEHLP_SYMBOL SYMBOL_INFO; -typedef PIMAGEHLP_SYMBOL PSYMBOL_INFO; - -static inline -LONG InterlockedCompareExchange(_Inout_ LONG* ptr, _In_ LONG nval, _In_ LONG oval) -{ - return (LONG)::InterlockedCompareExchange((PVOID*)ptr, (PVOID)nval, (PVOID)oval); -} -#else -#pragma warning(push) -#pragma warning(disable:4091) // empty typedef -#include -#pragma warning(pop) -#endif - -#ifdef IMAGEAPI // defined by DBGHELP.H -typedef LPAPI_VERSION(NTAPI* PF_ImagehlpApiVersionEx)(_In_ LPAPI_VERSION AppVersion); - -typedef BOOL(NTAPI* PF_SymInitialize)(_In_ HANDLE hProcess, - _In_opt_ LPCSTR UserSearchPath, - _In_ BOOL fInvadeProcess); -typedef DWORD(NTAPI* PF_SymSetOptions)(_In_ DWORD SymOptions); -typedef DWORD(NTAPI* PF_SymGetOptions)(VOID); -typedef DWORD64(NTAPI* PF_SymLoadModule64)(_In_ HANDLE hProcess, - _In_opt_ HANDLE hFile, - _In_ LPSTR ImageName, - _In_opt_ LPSTR ModuleName, - _In_ DWORD64 BaseOfDll, - _In_opt_ DWORD SizeOfDll); -typedef BOOL(NTAPI* PF_SymGetModuleInfo64)(_In_ HANDLE hProcess, - _In_ DWORD64 qwAddr, - _Out_ PIMAGEHLP_MODULE64 ModuleInfo); -typedef BOOL(NTAPI* PF_SymFromName)(_In_ HANDLE hProcess, - _In_ LPSTR Name, - _Out_ PSYMBOL_INFO Symbol); - -typedef struct _DETOUR_SYM_INFO -{ - HANDLE hProcess; - HMODULE hDbgHelp; - PF_ImagehlpApiVersionEx pfImagehlpApiVersionEx; - PF_SymInitialize pfSymInitialize; - PF_SymSetOptions pfSymSetOptions; - PF_SymGetOptions pfSymGetOptions; - PF_SymLoadModule64 pfSymLoadModule64; - PF_SymGetModuleInfo64 pfSymGetModuleInfo64; - PF_SymFromName pfSymFromName; -} DETOUR_SYM_INFO, * PDETOUR_SYM_INFO; - -PDETOUR_SYM_INFO DetourLoadImageHlp(VOID); - -#endif // IMAGEAPI - -#if defined(_INC_STDIO) && !defined(_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS) -#error detours.h must be included before stdio.h (or at least define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS earlier) -#endif -#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 - -#ifndef DETOUR_TRACE -#if DETOUR_DEBUG -#define DETOUR_TRACE(x) printf x -#define DETOUR_BREAK() __debugbreak() -#include -#include -#else -#define DETOUR_TRACE(x) -#define DETOUR_BREAK() -#endif -#endif - -#if 1 || defined(DETOURS_IA64) - -// -// IA64 instructions are 41 bits, 3 per bundle, plus 5 bit bundle template => 128 bits per bundle. -// - -#define DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE (3) - -#define DETOUR_IA64_TEMPLATE_OFFSET (0) -#define DETOUR_IA64_TEMPLATE_SIZE (5) - -#define DETOUR_IA64_INSTRUCTION_SIZE (41) -#define DETOUR_IA64_INSTRUCTION0_OFFSET (DETOUR_IA64_TEMPLATE_SIZE) -#define DETOUR_IA64_INSTRUCTION1_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE) -#define DETOUR_IA64_INSTRUCTION2_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE + DETOUR_IA64_INSTRUCTION_SIZE) - -C_ASSERT(DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * DETOUR_IA64_INSTRUCTION_SIZE == 128); - -__declspec(align(16)) struct DETOUR_IA64_BUNDLE -{ -public: - union - { - BYTE data[16]; - UINT64 wide[2]; - }; - - enum { - A_UNIT = 1u, - I_UNIT = 2u, - M_UNIT = 3u, - B_UNIT = 4u, - F_UNIT = 5u, - L_UNIT = 6u, - X_UNIT = 7u, - }; - struct DETOUR_IA64_METADATA - { - ULONG nTemplate : 8; // Instruction template. - ULONG nUnit0 : 4; // Unit for slot 0 - ULONG nUnit1 : 4; // Unit for slot 1 - ULONG nUnit2 : 4; // Unit for slot 2 - }; - -protected: - static const DETOUR_IA64_METADATA s_rceCopyTable[33]; - - UINT RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const; - - bool RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst, - _In_ BYTE slot, - _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const; - - // 120 112 104 96 88 80 72 64 56 48 40 32 24 16 8 0 - // f. e. d. c. b. a. 9. 8. 7. 6. 5. 4. 3. 2. 1. 0. - - // 00 - // f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0. - // 0000 0000 0000 0000 0000 0000 0000 001f : Template [4..0] - // 0000 0000 0000 0000 0000 03ff ffff ffe0 : Zero [ 41.. 5] - // 0000 0000 0000 0000 0000 3c00 0000 0000 : Zero [ 45.. 42] - // 0000 0000 0007 ffff ffff c000 0000 0000 : One [ 82.. 46] - // 0000 0000 0078 0000 0000 0000 0000 0000 : One [ 86.. 83] - // 0fff ffff ff80 0000 0000 0000 0000 0000 : Two [123.. 87] - // f000 0000 0000 0000 0000 0000 0000 0000 : Two [127..124] - BYTE GetTemplate() const; - // Get 4 bit opcodes. - BYTE GetInst0() const; - BYTE GetInst1() const; - BYTE GetInst2() const; - BYTE GetUnit(BYTE slot) const; - BYTE GetUnit0() const; - BYTE GetUnit1() const; - BYTE GetUnit2() const; - // Get 37 bit data. - UINT64 GetData0() const; - UINT64 GetData1() const; - UINT64 GetData2() const; - - // Get/set the full 41 bit instructions. - UINT64 GetInstruction(BYTE slot) const; - UINT64 GetInstruction0() const; - UINT64 GetInstruction1() const; - UINT64 GetInstruction2() const; - void SetInstruction(BYTE slot, UINT64 instruction); - void SetInstruction0(UINT64 instruction); - void SetInstruction1(UINT64 instruction); - void SetInstruction2(UINT64 instruction); - - // Get/set bitfields. - static UINT64 GetBits(UINT64 Value, UINT64 Offset, UINT64 Count); - static UINT64 SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field); - - // Get specific read-only fields. - static UINT64 GetOpcode(UINT64 instruction); // 4bit opcode - static UINT64 GetX(UINT64 instruction); // 1bit opcode extension - static UINT64 GetX3(UINT64 instruction); // 3bit opcode extension - static UINT64 GetX6(UINT64 instruction); // 6bit opcode extension - - // Get/set specific fields. - static UINT64 GetImm7a(UINT64 instruction); - static UINT64 SetImm7a(UINT64 instruction, UINT64 imm7a); - static UINT64 GetImm13c(UINT64 instruction); - static UINT64 SetImm13c(UINT64 instruction, UINT64 imm13c); - static UINT64 GetSignBit(UINT64 instruction); - static UINT64 SetSignBit(UINT64 instruction, UINT64 signBit); - static UINT64 GetImm20a(UINT64 instruction); - static UINT64 SetImm20a(UINT64 instruction, UINT64 imm20a); - static UINT64 GetImm20b(UINT64 instruction); - static UINT64 SetImm20b(UINT64 instruction, UINT64 imm20b); - - static UINT64 SignExtend(UINT64 Value, UINT64 Offset); - - BOOL IsMovlGp() const; - - VOID SetInst(BYTE Slot, BYTE nInst); - VOID SetInst0(BYTE nInst); - VOID SetInst1(BYTE nInst); - VOID SetInst2(BYTE nInst); - VOID SetData(BYTE Slot, UINT64 nData); - VOID SetData0(UINT64 nData); - VOID SetData1(UINT64 nData); - VOID SetData2(UINT64 nData); - BOOL SetNop(BYTE Slot); - BOOL SetNop0(); - BOOL SetNop1(); - BOOL SetNop2(); - -public: - BOOL IsBrl() const; - VOID SetBrl(); - VOID SetBrl(UINT64 target); - UINT64 GetBrlTarget() const; - VOID SetBrlTarget(UINT64 target); - VOID SetBrlImm(UINT64 imm); - UINT64 GetBrlImm() const; - - UINT64 GetMovlGp() const; - VOID SetMovlGp(UINT64 gp); - - VOID SetStop(); - - UINT Copy(_Out_ DETOUR_IA64_BUNDLE* pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra = NULL) const; -}; -#endif // DETOURS_IA64 - -#ifdef DETOURS_ARM - -#define DETOURS_PFUNC_TO_PBYTE(p) ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1)) -#define DETOURS_PBYTE_TO_PFUNC(p) ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1)) - -#endif // DETOURS_ARM - -////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -#define DETOUR_OFFLINE_LIBRARY(x) \ -PVOID WINAPI DetourCopyInstruction##x(_In_opt_ PVOID pDst, \ - _Inout_opt_ PVOID *ppDstPool, \ - _In_ PVOID pSrc, \ - _Out_opt_ PVOID *ppTarget, \ - _Out_opt_ LONG *plExtra); \ - \ -BOOL WINAPI DetourSetCodeModule##x(_In_ HMODULE hModule, \ - _In_ BOOL fLimitReferencesToModule); \ - - DETOUR_OFFLINE_LIBRARY(X86) - DETOUR_OFFLINE_LIBRARY(X64) - DETOUR_OFFLINE_LIBRARY(ARM) - DETOUR_OFFLINE_LIBRARY(ARM64) - DETOUR_OFFLINE_LIBRARY(IA64) - -#undef DETOUR_OFFLINE_LIBRARY - - ////////////////////////////////////////////////////////////////////////////// - // - // Helpers for manipulating page protection. - // - - _Success_(return != FALSE) - BOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_ HANDLE hProcess, - _In_ PVOID pAddress, - _In_ SIZE_T nSize, - _In_ DWORD dwNewProtect, - _Out_ PDWORD pdwOldProtect); - - _Success_(return != FALSE) - BOOL WINAPI DetourVirtualProtectSameExecute(_In_ PVOID pAddress, - _In_ SIZE_T nSize, - _In_ DWORD dwNewProtect, - _Out_ PDWORD pdwOldProtect); -#ifdef __cplusplus -} -#endif // __cplusplus - -////////////////////////////////////////////////////////////////////////////// - -#define MM_ALLOCATION_GRANULARITY 0x10000 - -////////////////////////////////////////////////////////////////////////////// - -#endif // DETOURS_INTERNAL -#endif // __cplusplus - -#endif // _DETOURS_H_ -// -//////////////////////////////////////////////////////////////// End of File. diff --git a/SteamAPICheckBypass_x32/detours.lib b/SteamAPICheckBypass_x32/detours.lib deleted file mode 100644 index 0783342..0000000 Binary files a/SteamAPICheckBypass_x32/detours.lib and /dev/null differ diff --git a/SteamAPICheckBypass_x32/dllmain.cpp b/SteamAPICheckBypass_x32/dllmain.cpp deleted file mode 100644 index 3654a2b..0000000 --- a/SteamAPICheckBypass_x32/dllmain.cpp +++ /dev/null @@ -1,528 +0,0 @@ -#include "pch.h" - -#include - -#include "Console.h" -#include "detours.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#pragma comment(lib, "detours.lib") -#pragma comment(lib, "ntdll.lib") - -struct Replace -{ - std::wstring origname; - std::wstring replacename; - bool replaceafterfirsttime; // Replace reading request after reading for first time - bool firstime = false; // first time read indicator, should always be false -}; - -//----------Configuration start--------------- - -bool useinternallist = false; //Use built-in replace list without reading .ini file - -bool debugprintpath = false; //Print the path of the file being read - -bool enabledebuglogfile = false; //Enable debug log file - -std::string logfilename = "SteamAPICheckBypass.log"; //Log file name - -std::wstring inifilename = L"\\SteamAPICheckBypass.ini"; //Ini file name - -std::vector internalreplaceList = { - {L"steam_api.dll", L"steam_api.org", false, false}, - {L"steam_api64.dll", L"steam_api64.org", false, false}, -};//internal replace list example - -//----------Configuration end----------------- - - -#pragma region Utils -bool isFileExist(const wchar_t* fileName) { - HANDLE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile != INVALID_HANDLE_VALUE) { - CloseHandle(hFile); - return true; - } - return false; -} - - -void PrintLog(std::string str) -{ - std::string logstr = "[SteamAPICheckBypass] " + str + "\n"; -#ifdef _DEBUG - Console::Print(logstr.c_str()); - if (enabledebuglogfile) - { - std::ofstream logfile; - logfile.open(logfilename, std::ios_base::app); - logfile << logstr; - } -#endif - int wideStrLength = MultiByteToWideChar(CP_UTF8, 0, logstr.c_str(), -1, nullptr, 0); - wchar_t* wideString = new wchar_t[wideStrLength]; - MultiByteToWideChar(CP_UTF8, 0, logstr.c_str(), -1, wideString, wideStrLength); - OutputDebugString(wideString); - delete[] wideString; -} - -wchar_t const* GetCurrentPath() -{ - wchar_t exePath[MAX_PATH]; - GetModuleFileNameW(NULL, exePath, MAX_PATH); - wchar_t* lastBackslash = wcsrchr(exePath, L'\\'); - if (lastBackslash != nullptr) { - *lastBackslash = L'\0'; // Null-terminate to get the directory path - } - return exePath; -} - -std::wstring utf8ToUtf16(const std::string& utf8Str) -{ - std::wstring_convert> conv; - return conv.from_bytes(utf8Str); -} - -std::string utf16ToUtf8(const std::wstring& utf16Str) -{ - std::wstring_convert> conv; - return conv.to_bytes(utf16Str); -} - -typedef enum _SECTION_INFORMATION_CLASS { - SectionBasicInformation, - SectionImageInformation -} SECTION_INFORMATION_CLASS, * PSECTION_INFORMATION_CLASS; -EXTERN_C NTSTATUS __stdcall NtQuerySection(HANDLE SectionHandle, SECTION_INFORMATION_CLASS InformationClass, PVOID InformationBuffer, ULONG InformationBufferSize, PULONG ResultLength); -EXTERN_C NTSTATUS __stdcall NtProtectVirtualMemory(HANDLE ProcessHandle, PVOID* BaseAddress, PULONG NumberOfBytesToProtect, ULONG NewAccessProtection, PULONG OldAccessProtection); -EXTERN_C NTSTATUS __stdcall NtPulseEvent(HANDLE EventHandle, PULONG PreviousState); - -void DisableVMP() -{ - // restore hook at NtProtectVirtualMemory - auto ntdll = GetModuleHandleA("ntdll.dll"); - if (ntdll == NULL) return; - - bool linux = GetProcAddress(ntdll, "wine_get_version") != nullptr; - void* routine = linux ? (void*)NtPulseEvent : (void*)NtQuerySection; - DWORD old; - VirtualProtect(NtProtectVirtualMemory, 1, PAGE_EXECUTE_READWRITE, &old); - *(uintptr_t*)NtProtectVirtualMemory = *(uintptr_t*)routine & ~(0xFFui64 << 32) | (uintptr_t)(*(uint32_t*)((uintptr_t)routine + 4) - 1) << 32; - VirtualProtect(NtProtectVirtualMemory, 1, old, &old); -} -#pragma endregion - -std::vector replaceList; - -typedef NTSTATUS(WINAPI* pNtCreateFile)( - PHANDLE FileHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - PLARGE_INTEGER AllocationSize, - ULONG FileAttributes, - ULONG ShareAccess, - ULONG CreateDisposition, - ULONG CreateOptions, - PVOID EaBuffer, - ULONG EaLength); - -pNtCreateFile oNtCreateFile = nullptr; - -typedef NTSTATUS(WINAPI* pNtOpenFile)( - PHANDLE FileHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - ULONG ShareAccess, - ULONG OpenOptions); - -pNtOpenFile oNtOpenFile = nullptr; - -std::wstring GetReplacedPath(std::wstring path) -{ - // Get the file name from the path - size_t lastSlash = path.find_last_of('/'); - size_t lastBackslash = path.find_last_of('\\'); - size_t lastSeparator = (lastSlash > lastBackslash) ? lastSlash : lastBackslash; - std::wstring filename = path.substr(lastSeparator + 1); - if (filename.find(utf8ToUtf16(logfilename)) == std::string::npos && debugprintpath) - { - PrintLog("Reading Path:" + utf16ToUtf8(path)); - } - // Check if the file name matches any entry in the replaceList - for (Replace& replace : replaceList) - { - if (filename.find(replace.origname) != std::wstring::npos) - { - - replace.firstime = true; - - if (replace.replaceafterfirsttime && !replace.firstime) - { - PrintLog("Reading " + utf16ToUtf8(replace.origname) + "for first time."); - break; - } - PrintLog("Reading " + utf16ToUtf8(replace.origname) + ",Replacing..."); - // Replace the path's filename with replacename - size_t pos = path.find_last_of(L"/\\"); - path = path.substr(0, pos + 1) + replace.replacename; - PrintLog("Replaced Path:" + utf16ToUtf8(path)); - // Set firstime to true if replaceafterfirsttime is true and firstime is false - - - break; // No need to check further once a replacement is made - } - } - - return path; -} - -NTSTATUS WINAPI NtCreateFileHook( - PHANDLE FileHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - PLARGE_INTEGER AllocationSize, - ULONG FileAttributes, - ULONG ShareAccess, - ULONG CreateDisposition, - ULONG CreateOptions, - PVOID EaBuffer, - ULONG EaLength) -{ - try - { - if (ObjectAttributes != nullptr && ObjectAttributes->ObjectName && - ObjectAttributes->ObjectName->Length && - ObjectAttributes->ObjectName->Buffer != nullptr && !IsBadReadPtr(ObjectAttributes->ObjectName->Buffer, sizeof(WCHAR)) && ObjectAttributes->ObjectName->Buffer[0]) { - std::wstring originalPath(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(WCHAR)); - std::wstring replacedPathStr = GetReplacedPath(originalPath); - UNICODE_STRING replacedPathUnicode; - RtlInitUnicodeString(&replacedPathUnicode, replacedPathStr.c_str()); - ObjectAttributes->ObjectName = &replacedPathUnicode; - return oNtCreateFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - AllocationSize, - FileAttributes, - ShareAccess, - CreateDisposition, - CreateOptions, - EaBuffer, - EaLength); - } - } - catch (...) - { - PrintLog("Error in NtCreateFileHook"); - } - - return oNtCreateFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - AllocationSize, - FileAttributes, - ShareAccess, - CreateDisposition, - CreateOptions, - EaBuffer, - EaLength); -} - -NTSTATUS WINAPI NtOpenFileHook( - PHANDLE FileHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - ULONG ShareAccess, - ULONG OpenOptions) -{ - try - { - if (ObjectAttributes != nullptr && ObjectAttributes->ObjectName && - ObjectAttributes->ObjectName->Length && - ObjectAttributes->ObjectName->Buffer != nullptr && !IsBadReadPtr(ObjectAttributes->ObjectName->Buffer, sizeof(WCHAR)) && ObjectAttributes->ObjectName->Buffer[0]) { - std::wstring originalPath(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(WCHAR)); - std::wstring replacedPathStr = GetReplacedPath(originalPath); - UNICODE_STRING replacedPathUnicode; - RtlInitUnicodeString(&replacedPathUnicode, replacedPathStr.c_str()); - ObjectAttributes->ObjectName = &replacedPathUnicode; - return oNtOpenFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - ShareAccess, - OpenOptions); - } - } - catch (...) - { - PrintLog("Error in NtOpenFileHook"); - } - - return oNtOpenFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - ShareAccess, - OpenOptions); -} - -void LoadHook() -{ - PrintLog("Starting to hook File APIs..."); - HMODULE hNtdll = GetModuleHandle(L"ntdll.dll"); - DetourRestoreAfterWith(); - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - if (hNtdll) - { - oNtCreateFile = (pNtCreateFile)GetProcAddress(hNtdll, "NtCreateFile"); - if (oNtCreateFile) - { - DetourAttach(&(PVOID&)oNtCreateFile, NtCreateFileHook); - auto Error = DetourTransactionCommit(); - if (Error == NO_ERROR) - PrintLog("Hooked NtCreateFile"); - else - PrintLog("NtCreateFile Hook Failed. Error: " + std::to_string(Error)); - } - else - { - PrintLog("NtCreateFile Hook Failed. Error: Failed to get NtCreateFile address."); - } - } - - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - if (hNtdll) - { - oNtOpenFile = (pNtOpenFile)GetProcAddress(hNtdll, "NtOpenFile"); - if (oNtOpenFile) - { - DetourAttach(&(PVOID&)oNtOpenFile, NtOpenFileHook); - auto Error = DetourTransactionCommit(); - if (Error == NO_ERROR) - PrintLog("Hooked NtOpenFile"); - else - PrintLog("NtOpenFile Hook Failed. Error: " + std::to_string(Error)); - } - else - { - PrintLog("NtOpenFile Hook Failed. Error: Failed to get NtOpenFile address."); - } - } -} - -bool readReplacesFromIni(const std::wstring& filename, std::vector& replaceList) { - std::ifstream iniFile(filename); - std::string line; - std::map replaceMap; - std::map afterFirstTimeMap; - if (!iniFile.is_open()) { - PrintLog("Unable to open ini file."); - return false; - } - - - while (std::getline(iniFile, line) && line != "[AfterFirstTime]") { - if (line[0] == '[') continue; - std::istringstream is_line(line); - std::string key; - if (std::getline(is_line, key, '=')) { - std::string value; - if (std::getline(is_line, value)) { - replaceMap[key] = value; - } - } - } - - while (std::getline(iniFile, line)) { - if (line[0] == '[') continue; - std::istringstream is_line(line); - std::string key; - if (std::getline(is_line, key, '=')) { - std::string value; - if (std::getline(is_line, value)) { - if (value == "1") - { - afterFirstTimeMap[key] = true; - } - else afterFirstTimeMap[key] = false; - } - } - } - - - for (const auto& entry : replaceMap) { - Replace replace; - replace.origname = utf8ToUtf16(entry.first); - replace.replacename = utf8ToUtf16(entry.second); - replace.replaceafterfirsttime = afterFirstTimeMap[entry.first]; - replaceList.push_back(replace); - } - - iniFile.close(); - return true; -} - -void Checkfile(std::vector& replaceList) -{ - PrintLog("Checking Original Steam_API(64) files..."); - - wchar_t exePath[MAX_PATH]; - GetModuleFileNameW(NULL, exePath, MAX_PATH); - - // Extract the directory from the full path - wchar_t* lastBackslash = wcsrchr(exePath, L'\\'); - if (lastBackslash != nullptr) { - *lastBackslash = L'\0'; // Null-terminate to get the directory path - } - - // Construct file paths based on the executable path - wchar_t steamAPI_ORG[MAX_PATH]; - wcscpy_s(steamAPI_ORG, MAX_PATH, exePath); - wcscat_s(steamAPI_ORG, MAX_PATH, L"\\steam_api.org"); - - wchar_t steamAPI_BAK[MAX_PATH]; - wcscpy_s(steamAPI_BAK, MAX_PATH, exePath); - wcscat_s(steamAPI_BAK, MAX_PATH, L"\\steam_api.dll.bak"); - - wchar_t steamAPI_O[MAX_PATH]; - wcscpy_s(steamAPI_O, MAX_PATH, exePath); - wcscat_s(steamAPI_O, MAX_PATH, L"\\steam_api_o.dll"); - - wchar_t steamAPI64_ORG[MAX_PATH]; - wcscpy_s(steamAPI64_ORG, MAX_PATH, exePath); - wcscat_s(steamAPI64_ORG, MAX_PATH, L"\\steam_api64.org"); - - wchar_t steamAPI64_BAK[MAX_PATH]; - wcscpy_s(steamAPI64_BAK, MAX_PATH, exePath); - wcscat_s(steamAPI64_BAK, MAX_PATH, L"\\steam_api64.dll.bak"); - - wchar_t steamAPI64_O[MAX_PATH]; - wcscpy_s(steamAPI64_O, MAX_PATH, exePath); - wcscat_s(steamAPI64_O, MAX_PATH, L"\\steam_api64_o.dll"); - - - bool SteamAPI_ORG = isFileExist(steamAPI_ORG); - bool SteamAPI_BAK = isFileExist(steamAPI_BAK); - bool SteamAPI_O = isFileExist(steamAPI_O); - bool SteamAPI64_ORG = isFileExist(steamAPI64_ORG); - bool SteamAPI64_BAK = isFileExist(steamAPI64_BAK); - bool SteamAPI64_O = isFileExist(steamAPI64_O); - - if (SteamAPI_BAK) - { - PrintLog("Found steam_api.dll.bak."); - replaceList.push_back({ L"steam_api.dll",L"steam_api.dll.bak",false }); - } - else if (SteamAPI_ORG) - { - PrintLog("Found steam_api.org."); - replaceList.push_back({ L"steam_api.dll",L"steam_api.org",false }); - } - else if (SteamAPI_O) - { - PrintLog("Found steam_api_o.dll."); - replaceList.push_back({ L"steam_api.dll",L"steam_api_o.dll",false }); - } - - if (SteamAPI64_BAK) - { - PrintLog("Found steam_api64.dll.bak."); - replaceList.push_back({ L"steam_api.dll",L"steam_api64.dll.bak",false }); - } - else if (SteamAPI64_ORG) - { - PrintLog("Found steam_api64.org."); - replaceList.push_back({ L"steam_api.dll",L"steam_api64.org",false }); - } - else if (SteamAPI64_O) - { - PrintLog("Found steam_api64_o.dll."); - replaceList.push_back({ L"steam_api.dll",L"steam_api64_o.dll",false }); - } -} - -void GetReplaceList() -{ - wchar_t iniPath[MAX_PATH]; - wcscpy_s(iniPath, MAX_PATH, GetCurrentPath()); - wcscat_s(iniPath, MAX_PATH, inifilename.c_str()); - - if (useinternallist) - { - replaceList = internalreplaceList; - } - - if (readReplacesFromIni(iniPath, replaceList)) - { - PrintLog("Successfully get ini replace infos."); - } - else - { - PrintLog("Failed to get ini replace infos, detecting files..."); - Checkfile(replaceList); - } - PrintLog("-----------------"); - PrintLog("Replace List:"); - { - for (const auto& replace : replaceList) { - PrintLog(utf16ToUtf8(replace.origname) + "," + utf16ToUtf8(replace.replacename)); - } - } - PrintLog("-----------------"); -} - -void Init() -{ - PrintLog("SteamAPICheckBypass Init"); - GetReplaceList(); - std::thread([]() { - DisableVMP(); - LoadHook(); - }).detach(); -} - -BOOL APIENTRY DllMain(HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved -) -{ -#ifdef _DEBUG - Console::Attach(); -#endif - - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - PrintLog("Steam API Check Bypass dll Loaded."); - Init(); - case DLL_PROCESS_DETACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - break; - } - return TRUE; -} - -extern "C" __declspec(dllexport) void SteamAPICheckBypass() {}; \ No newline at end of file diff --git a/SteamAPICheckBypass_x32/framework.h b/SteamAPICheckBypass_x32/framework.h deleted file mode 100644 index 80cbbc9..0000000 --- a/SteamAPICheckBypass_x32/framework.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 -// Windows 头文件 -#include diff --git a/SteamAPICheckBypass_x32/pch.cpp b/SteamAPICheckBypass_x32/pch.cpp deleted file mode 100644 index 331e647..0000000 --- a/SteamAPICheckBypass_x32/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/SteamAPICheckBypass_x32/pch.h b/SteamAPICheckBypass_x32/pch.h deleted file mode 100644 index f693f3b..0000000 --- a/SteamAPICheckBypass_x32/pch.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PCH_H -#define PCH_H - -#include "framework.h" - -#endif //PCH_H diff --git a/VersionShim/.gitignore b/VersionShim/.gitignore deleted file mode 100644 index b43ba3e..0000000 --- a/VersionShim/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.vs/ -.idea/ -x64/ -*.user \ No newline at end of file diff --git a/VersionShim/LICENSE b/VersionShim/LICENSE deleted file mode 100644 index a612ad9..0000000 --- a/VersionShim/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/VersionShim/README.md b/VersionShim/README.md deleted file mode 100644 index 060e610..0000000 --- a/VersionShim/README.md +++ /dev/null @@ -1,19 +0,0 @@ -VersionShim -=========== -A Dynamic-Link Library which exports the same methods as version.dll (winver.h) in addition to chainloading additional user defined libraries. - -Usage ------ -Rename VersionShim.dll to `version.dll` and place it in the target process's search location, -then create a file named `libraries.txt` in the same directory and list the paths to all the libraries you'd like to load, -each on a new line. - -You can optionally select the target process(es) by listing its path or filename, each on a new line preceded by `*`. These must come before the library paths. - -Lines beginning with `#` will be skipped. - -libraries.txt *MUST* be encoded in UTF-8. - -License -------- -This project is subject to the terms of the [Mozilla Public License, v. 2.0](./LICENSE). \ No newline at end of file diff --git a/VersionShim/VersionShim.sln b/VersionShim/VersionShim.sln deleted file mode 100644 index 57f3bd8..0000000 --- a/VersionShim/VersionShim.sln +++ /dev/null @@ -1,32 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.33209.295 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VersionShim", "VersionShim\VersionShim.vcxproj", "{7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{39DCA191-5807-42D8-A66A-373FBE7F5EA9}" - ProjectSection(SolutionItems) = preProject - .gitignore = .gitignore - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Debug|x64.ActiveCfg = Debug|x64 - {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Debug|x64.Build.0 = Debug|x64 - {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Release|x64.ActiveCfg = Release|x64 - {7D2E1375-BF0B-49EF-BBA8-C134A347F1C7}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {577CC093-ACE1-4DFE-BC2F-AAD2A3A7FF00} - EndGlobalSection -EndGlobal diff --git a/VersionShim/VersionShim/VersionShim.vcxproj b/VersionShim/VersionShim.vcxproj similarity index 98% rename from VersionShim/VersionShim/VersionShim.vcxproj rename to VersionShim/VersionShim.vcxproj index fb61af6..93bdbf0 100644 --- a/VersionShim/VersionShim/VersionShim.vcxproj +++ b/VersionShim/VersionShim.vcxproj @@ -61,7 +61,7 @@ true NDEBUG;VERSIONSHIM_EXPORTS;_WINDOWS;_USRDLL;PROJECT_NAME="$(ProjectName)";%(PreprocessorDefinitions) true - Create + NotUsing pch.h diff --git a/VersionShim/VersionShim/VersionShim.vcxproj.filters b/VersionShim/VersionShim.vcxproj.filters similarity index 100% rename from VersionShim/VersionShim/VersionShim.vcxproj.filters rename to VersionShim/VersionShim.vcxproj.filters diff --git a/VersionShim/VersionShim/dllmain.c b/VersionShim/dllmain.c similarity index 89% rename from VersionShim/VersionShim/dllmain.c rename to VersionShim/dllmain.c index 6c509b5..8e430f2 100644 --- a/VersionShim/VersionShim/dllmain.c +++ b/VersionShim/dllmain.c @@ -35,12 +35,21 @@ BOOL APIENTRY DllMain(CONST HMODULE hModule, CONST DWORD fdwReason, CONST LPVOID LPSTR fileStr = NULL; DWORD fileLen = 0; - if (!OpenReadFileUtf8("libraries.txt", &fileStr, &fileLen)) + if (GetFileAttributesA("libraries.txt") == INVALID_FILE_ATTRIBUTES) { - MessageBoxA(NULL, "Failed to read libraries.txt", PROJECT_NAME, ErrBoxType); + if (!LoadLibraryA("SteamAPICheckBypass.dll")) + { + MessageBoxA(NULL, "Failed to load SteamAPICheckBypass.dll", PROJECT_NAME, ErrBoxType); + } return TRUE; } + if (!OpenReadFileUtf8("libraries.txt", &fileStr, &fileLen)) + { + MessageBoxA(NULL, "Failed to open libraries.txt", PROJECT_NAME, ErrBoxType); + return TRUE; + } + LoadLibraryA("SteamAPICheckBypass.dll"); LPSTR callerPath[MAX_PATH + 1]; if (!GetModuleFileNameA(NULL, callerPath, MAX_PATH + 1)) // NOLINT(clang-diagnostic-incompatible-pointer-types) { diff --git a/VersionShim/VersionShim/pch.c b/VersionShim/pch.c similarity index 100% rename from VersionShim/VersionShim/pch.c rename to VersionShim/pch.c diff --git a/VersionShim/VersionShim/pch.h b/VersionShim/pch.h similarity index 100% rename from VersionShim/VersionShim/pch.h rename to VersionShim/pch.h diff --git a/VersionShim/VersionShim/util.c b/VersionShim/util.c similarity index 100% rename from VersionShim/VersionShim/util.c rename to VersionShim/util.c diff --git a/VersionShim/VersionShim/util.h b/VersionShim/util.h similarity index 100% rename from VersionShim/VersionShim/util.h rename to VersionShim/util.h diff --git a/nt_file_dupe/README.md b/nt_file_dupe/README.md new file mode 100644 index 0000000..7cdc6bf --- /dev/null +++ b/nt_file_dupe/README.md @@ -0,0 +1,164 @@ +# Nt Filesystem Dupe +A library for file & module redirection and hiding, by hooking various Nt APIs. +This project is inspired by `CODEX` Steam emu and based on reversing it. Credits to them. + +--- + +## Solution structure +The solution is divided into 3 projects: +1. `nt_file_dupe`: The actual library, this is a static library (`.lib`) +2. `dll_interface`: A thin wrapper `.dll` project around the static library, exporting the necessary functions +3. `testxxx`: A simple console app to test the library + helpers + +## JSON file format: +```json +{ + "myfile.txt": { + "mode": "file_redirect", + "to": "myfile.org", + "file_must_exist": true + }, + "path/myfile_22.txt": { + "mode": "file_redirect", + "to": "path/myfile_22.org", + }, + "../../folder/some_file.txt": { + "mode": "file_redirect", + "to": "../../folder/some_file.org", + }, + + "hideme.txt": { + "mode": "file_hide" + }, + "../hideme_22.txt": { + "mode": "file_hide" + }, + + "prevent_me.dll": { + "mode": "module_prevent_load" + }, + "prevent_me": { + "mode": "module_prevent_load" + }, + + "my_module_org.dll": { + "mode": "module_redirect", + "to": "my_module_mod.dll" + }, + "my_module_org": { + "mode": "module_redirect", + "to": "my_module_mod" + } +} +``` +Each JSON key is considered the *original* file, the value/object for that key defines the action for the original file. +The entry type is determined by the `mode` JSON key. +* `mode` + + --- + + - `file_redirect` + Redirect original file creation/opening `to` a target file. + Target files are always hidden. + - `file_hide` + Hide the file, as if it doesn't exist on disk. + + --- + + - `module_prevent_load` + Prevent loading the module via `LoadLibrary()` and its variants. + - `module_redirect` + Redirect the loading operation `to` another target module. + Target modules are always hidden and cannot be loaded by the process. + - `module_hide_handle` + Prevent `GetModuleHandle()` from succeeding, this won't affect `LoadLibrary()` and its variants. + As if the module doesn't exist in the current process memory. + +* `to` + Defines which target file/module to redirect the original file/module to. + Only useful when `mode` is: + - `file_redirect` + - `module_redirect` + +* `file_must_exist` (default = `false`) + When set to `true`, the JSON entry will be skipped if the original file doesn't exist, or the target file doesn't exist in `file_redirect` mode + +Check the example [sample.json](./example/sample.json) + +## Behavior +In case this is a **file** entry, the paths to the original or target files could be absolute or relative. +Relative paths will be relative to the **location of the current `.exe`**, not the current directory. + +In case this is a **module** entry, the paths to the original or target modules must be just their filenames. + +Both the original and target files/modules must have the same filename length. +Additionally, if this is a **file** entry, they must exist in the same dir. + +Target files/modules are hidden by default, for example, in the JSON file defined above: +* `myfile.org` Will be hidden and cannot be opened/created +* `my_module_mod.dll` Will be hidden and cannot be loaded + +If `file_must_exist` = `true`, then this JSON entry will be ignored without an error if: +* The original file was missing +* The `mode` = `file_redirect` and the target file, defined by the JSON key `to`, is missing + +The dll will try to load only one of the following files upon startup in that order: +* A JSON file with the same name as the `.dll` itself +* `nt_file_dupe.json` +* `nt_file_dupe_config.json` +* `nt_fs_dupe.json` +* `nt_fs_dupe_config.json` +* `nt_dupe.json` +* `nt_dupe_config.json` + +Any of these files must exist **beside** the `.dll`, not the current running `.exe` + +Upon startup, the dll will try to hide itself, both on disk and in memory. +Additionally in the debug build, it will try to hide the log file on disk. + +## How use the pre-built .dll: +1. Create a `.json` file with some entries as shown above +2. Rename the `.json` file to the same name of the `.dll`, for example if the `.dll` is called `nt_file_dupe.dll`, then the `.json` file must be named `nt_file_dupe.json` +3. Place both the `.dll` and the `.json` files beside each other in the same folder +4. Load the `.dll` inside your target, either modify the imports table with something like `CFF Explorer`, or use any dll loader/injector + + +Note that the functions to add entries are not thread safe. + + +## How to link and use as a static lib (.lib): +1. Open the Visual Studio solution file `nt_file_dupe.sln` and build the project `nt_file_dupe`. + Make sure to select the right architecture (`x64` or `x86`) and build type (`release` or `debug`) +2. Assuming for example you've selected `Debug | x64`, the library will be built in + ```batch + bin\x64\nt_file_dupe\nt_file_dupe_static.lib + ``` +3. In your own Visual Studio project, you must use C++ language version >= `C++17`, + add the static `.lib` file as an input to the linker: [.lib files as linker input](https://learn.microsoft.com/en-us/cpp/build/reference/dot-lib-files-as-linker-input#to-add-lib-files-as-linker-input-in-the-development-environment) +4. Finally, add the folder `nt_file_dupe\include` as an extra include directory: [Additional include directories](https://learn.microsoft.com/en-us/cpp/build/reference/i-additional-include-directories#to-set-this-compiler-option-in-the-visual-studio-development-environment) +5. Everything will be under the namespace `ntfsdupe::` +6. Check how the `.dll` wrapper project is importing the required `.hpp` files, and using the library in its [DllMain](./dll_interface/dllmain.cpp) + +## How to link and use as a dynamic lib (.dll): +1. Open the Visual Studio solution file `nt_file_dupe.sln` and build the project `dll_interface`. + Make sure to select the right architecture (`x64` or `x86`) and build type (`release` or `debug`) +2. Assuming for example you've selected `Debug | x64`, the library will be built as 2 parts in + ```batch + bin\Debug\x64\dll_interface\nt_file_dupe.dll + bin\Debug\x64\dll_interface\nt_file_dupe.lib + ``` + Notice that the `.dll` file must be accompanied by a small-sized `.lib` file which we'll use next +3. In your own Visual Studio project, you must use C++ language version >= `C++17`, + add the static `.lib` file as an input to the linker: [.lib files as linker input](https://learn.microsoft.com/en-us/cpp/build/reference/dot-lib-files-as-linker-input#to-add-lib-files-as-linker-input-in-the-development-environment) +4. Finally, add the folder `dll_interface\include` as an extra include directory: [Additional include directories](https://learn.microsoft.com/en-us/cpp/build/reference/i-additional-include-directories#to-set-this-compiler-option-in-the-visual-studio-development-environment) +5. All available exports will have this prefix `ntfsdupe_` + ```c++ + #include "nt_file_dupe.hpp" + + int main() { + ntfsdupe_load_file(L"myfile.json"); + ntfsdupe_add_entry(ntfsdupe::itf::Mode::file_hide, L"some_file.dll", nullptr); + return 0; + } + ``` +6. The file `nt_file_dupe.dll` will be added to your imports table, so make sure to copy this `.dll` file beside your project's build output diff --git a/nt_file_dupe/include/Configs/Configs.hpp b/nt_file_dupe/include/Configs/Configs.hpp new file mode 100644 index 0000000..f262131 --- /dev/null +++ b/nt_file_dupe/include/Configs/Configs.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include +#include + +namespace ntfsdupe::cfgs { + // input from file + enum class Mode : char { + file_redirect, + file_hide, + + module_prevent_load, + module_redirect, + module_hide_handle, + }; + + enum class HookTimesMode : char { + all, + nth_time_only, + not_nth_time_only, + }; + + struct HookTimes { + // only works when mode = file_redirect, and ntcreate/openfile or create/opendirectoryobject(ex) + HookTimesMode mode{ HookTimesMode::all }; + uint64_t hook_time_n{ 0 }; + uint64_t hook_times{ 0 }; + }; + + // file operation/action type + enum class FileType : char { + original, // current file to redirect (file.dll) + target, // target file after redirection (file.org) + + hide, // hide this file + }; + + struct FileCfgEntry { + FileType mode{}; + + // strong ref holder + std::wstring original{}; + const wchar_t *original_filename{}; + + // strong ref holder + std::wstring target{}; + const wchar_t *target_filename{}; + + unsigned short filename_bytes{}; + + HookTimes hook_times{ }; + }; + + // module operation/action type + enum class ModuleType : char { + prevent_load, // prevent dynamic load via LoadLibrary() + + // --- both used when mode = redirect + original, // current file to redirect (file.dll) + target, // target file after redirection (file.org) + // --- + + hide_handle, // prevent GetModuleHandle(), but allow LoadLibrary() + }; + + struct ModuleCfgEntry { + ModuleType mode{}; + + std::wstring original_filename{}; + std::wstring target_filename{}; + + unsigned short filename_bytes{}; + }; + + + bool init(); + + void deinit(); + + const std::wstring& get_exe_dir() noexcept; + + bool add_entry(Mode mode, const std::wstring &original, const std::wstring &target = std::wstring(), bool file_must_exist = false, HookTimesMode hook_times_cfg = HookTimesMode::all, int hook_time_n = 0); + + bool load_file(const wchar_t *file); + + FileCfgEntry* find_file_entry(const std::wstring_view &str) noexcept; + + const ModuleCfgEntry* find_module_entry(const std::wstring_view &str) noexcept; + + void add_bypass(const std::wstring_view &str) noexcept; + + void remove_bypass(const std::wstring_view &str) noexcept; + + bool is_bypassed(const std::wstring_view &str) noexcept; + +} + diff --git a/nt_file_dupe/include/Helpers/Helpers.hpp b/nt_file_dupe/include/Helpers/Helpers.hpp new file mode 100644 index 0000000..cae5aa5 --- /dev/null +++ b/nt_file_dupe/include/Helpers/Helpers.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#define unique_ptr_stack(Type, size) \ + std::unique_ptr( \ + (Type*)_malloca(size), \ + [](void* const p) -> void { _freea(p); }) + +#define null_unique_ptr_stack(Type) \ + std::unique_ptr(nullptr, nullptr) + +namespace ntfsdupe::helpers +{ + __forceinline static std::wstring upper(const std::wstring& wstr) + { + std::wstring _wstr(wstr); + std::transform( + _wstr.begin(), _wstr.end(), _wstr.begin(), + [](auto c) { return c == L'/' ? L'\\' : std::toupper(c); } + ); + + return _wstr; + } + + __forceinline static std::wstring str_to_wstr(const std::string &str) + { + if (str.empty()) return {}; + + int chars = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), nullptr, 0); + if (!chars) return {}; + + std::wstring wstr(chars, 0); + chars = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &wstr[0], (int)wstr.size()); + if (!chars) return {}; + + return wstr; + } + + __forceinline static bool file_exist(const std::filesystem::path &filepath) + { + if (std::filesystem::is_directory(filepath)) return false; + else if (std::filesystem::exists(filepath)) return true; + + return false; + } + + __forceinline static bool file_exist(const std::string &filepath) + { + if (filepath.empty()) return false; + + return file_exist(std::filesystem::u8path(filepath)); + } + + __forceinline static bool file_exist(const std::wstring &filepath) + { + if (filepath.empty()) return false; + + return file_exist(std::filesystem::path(filepath)); + } + + __forceinline static std::filesystem::path to_absolute(const std::filesystem::path &path, const std::filesystem::path &base) + { + if (path.is_absolute()) return path; + + return std::filesystem::absolute(base / path); + } + + __forceinline static std::wstring to_absolute(const std::wstring& path, const std::wstring& base) + { + if (path.empty()) return path; + auto path_abs = to_absolute( + std::filesystem::path(path), + base.empty() ? std::filesystem::current_path() : std::filesystem::path(base) + ); + return path_abs.wstring(); + } + + __forceinline static wchar_t* upper(wchar_t* wstr, int count) + { + if (!wstr || !wstr[0] || !count) return wstr; + + while (count) { + *wstr = (wchar_t)std::toupper(*wstr); + wstr++; + count--; + } + return wstr; + } + + __forceinline static std::wstring get_module_fullpath(HMODULE hModule) + { + size_t chars = 512; + std::wstring mod_path{}; + do { + chars *= 2; + mod_path.resize(chars); + // from docs: + // The string returned will use the same format that was specified when the module was loaded. + // Therefore, the path can be a long or short file name, and can use the prefix "\?" + chars = GetModuleFileNameW(hModule, &mod_path[0], (DWORD)mod_path.size()); + if (!chars) return {}; + } while (chars == mod_path.size()); + if (!chars) return {}; + + return mod_path.substr(0, chars); + } + + __forceinline static std::wstring get_current_module_fullpath() + { + HMODULE hModule = GetModuleHandleW(nullptr); + if (!hModule) return {}; + return get_module_fullpath(hModule); + } + + __forceinline static std::wstring get_module_name(HMODULE hModule) + { + std::wstring full_path = get_module_fullpath(hModule); + if (full_path.empty()) return {}; + + std::filesystem::path path(full_path); + return path.filename().wstring(); + } +} diff --git a/nt_file_dupe/include/Helpers/dbglog.hpp b/nt_file_dupe/include/Helpers/dbglog.hpp new file mode 100644 index 0000000..64d6d5f --- /dev/null +++ b/nt_file_dupe/include/Helpers/dbglog.hpp @@ -0,0 +1,29 @@ +#pragma once + +#if !defined(NT_FS_DUPE_RELEASE) + +#include + +namespace ntfsdupe::helpers::dbglog { + + bool init(); + + void write(const wchar_t *fmt, ...); + void write(const std::wstring &str); + void write(const std::string &str); + + void close(); + +} + +#define NTFSDUPE_DBG_INIT() ntfsdupe::helpers::dbglog::init() +#define NTFSDUPE_DBG_CLOSE() ntfsdupe::helpers::dbglog::close() +#define NTFSDUPE_DBG(fmt, ...) ntfsdupe::helpers::dbglog::write(fmt, __VA_ARGS__) + +#else + +#define NTFSDUPE_DBG_INIT() +#define NTFSDUPE_DBG_CLOSE() +#define NTFSDUPE_DBG(...) + +#endif // NT_FS_DUPE_RELEASE diff --git a/nt_file_dupe/include/Hooks/Hooks.hpp b/nt_file_dupe/include/Hooks/Hooks.hpp new file mode 100644 index 0000000..f223577 --- /dev/null +++ b/nt_file_dupe/include/Hooks/Hooks.hpp @@ -0,0 +1,369 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#if !defined(STATUS_OBJECT_NAME_NOT_FOUND) + #define STATUS_OBJECT_NAME_NOT_FOUND 0xC0000034 +#endif + +#if !defined(STATUS_BUFFER_OVERFLOW) + #define STATUS_BUFFER_OVERFLOW 0x80000005 +#endif + +#include "NtApis/NtApis.hpp" +#include "Configs/Configs.hpp" +#include "Helpers/Helpers.hpp" +#include "Helpers/dbglog.hpp" + +#include "Hooks/NtOpenFile_hook/NtOpenFile_hook.hpp" +#include "Hooks/NtCreateFile_hook/NtCreateFile_hook.hpp" +#include "Hooks/NtDeleteFile_hook/NtDeleteFile_hook.hpp" +#include "Hooks/NtOpenDirectoryObject_hook/NtOpenDirectoryObject_hook.hpp" +#include "Hooks/NtCreateDirectoryObject_hook/NtCreateDirectoryObject_hook.hpp" +#include "Hooks/NtCreateDirectoryObjectEx_hook/NtCreateDirectoryObjectEx_hook.hpp" +#include "Hooks/NtQueryAttributesFile_hook/NtQueryAttributesFile_hook.hpp" +#include "Hooks/NtQueryFullAttributesFile_hook/NtQueryFullAttributesFile_hook.hpp" +#include "Hooks/NtQueryInformationFile_hook/NtQueryInformationFile_hook.hpp" + +#include "Hooks/NtQueryInformationByName_hook/NtQueryInformationByName_hook.hpp" + +#include "Hooks/NtQueryDirectoryFile_hook/NtQueryDirectoryFile_hook.hpp" +#include "Hooks/NtQueryDirectoryFileEx_hook/NtQueryDirectoryFileEx_hook.hpp" + +#include "Hooks/LdrLoadDll_hook/LdrLoadDll_hook.hpp" +#include "Hooks/LdrGetDllHandle_hook/LdrGetDllHandle_hook.hpp" + + +namespace ntfsdupe::hooks { + // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntqueryinformationfile + // the one that comes with Visual Studio has only 1 entry + typedef enum { + FileDirectoryInformation = 1, + FileFullDirectoryInformation, // 2 + FileBothDirectoryInformation, // 3 + FileBasicInformation, // 4 + FileStandardInformation, // 5 + FileInternalInformation, // 6 + FileEaInformation, // 7 + FileAccessInformation, // 8 + FileNameInformation, // 9 + FileRenameInformation, // 10 + FileLinkInformation, // 11 + FileNamesInformation, // 12 + FileDispositionInformation, // 13 + FilePositionInformation, // 14 + FileFullEaInformation, // 15 + FileModeInformation, // 16 + FileAlignmentInformation, // 17 + FileAllInformation, // 18 + FileAllocationInformation, // 19 + FileEndOfFileInformation, // 20 + FileAlternateNameInformation, // 21 + FileStreamInformation, // 22 + FilePipeInformation, // 23 + FilePipeLocalInformation, // 24 + FilePipeRemoteInformation, // 25 + FileMailslotQueryInformation, // 26 + FileMailslotSetInformation, // 27 + FileCompressionInformation, // 28 + FileObjectIdInformation, // 29 + FileCompletionInformation, // 30 + FileMoveClusterInformation, // 31 + FileQuotaInformation, // 32 + FileReparsePointInformation, // 33 + FileNetworkOpenInformation, // 34 + FileAttributeTagInformation, // 35 + FileTrackingInformation, // 36 + FileIdBothDirectoryInformation, // 37 + FileIdFullDirectoryInformation, // 38 + FileValidDataLengthInformation, // 39 + FileShortNameInformation, // 40 + FileIoCompletionNotificationInformation, // 41 + FileIoStatusBlockRangeInformation, // 42 + FileIoPriorityHintInformation, // 43 + FileSfioReserveInformation, // 44 + FileSfioVolumeInformation, // 45 + FileHardLinkInformation, // 46 + FileProcessIdsUsingFileInformation, // 47 + FileNormalizedNameInformation, // 48 + FileNetworkPhysicalNameInformation, // 49 + FileIdGlobalTxDirectoryInformation, // 50 + FileIsRemoteDeviceInformation, // 51 + FileUnusedInformation, // 52 + FileNumaNodeInformation, // 53 + FileStandardLinkInformation, // 54 + FileRemoteProtocolInformation, // 55 + + // + // These are special versions of these operations (defined earlier) + // which can be used by kernel mode drivers only to bypass security + // access checks for Rename and HardLink operations. These operations + // are only recognized by the IOManager, a file system should never + // receive these. + // + + FileRenameInformationBypassAccessCheck, // 56 + FileLinkInformationBypassAccessCheck, // 57 + + // + // End of special information classes reserved for IOManager. + // + + FileVolumeNameInformation, // 58 + FileIdInformation, // 59 + FileIdExtdDirectoryInformation, // 60 + FileReplaceCompletionInformation, // 61 + FileHardLinkFullIdInformation, // 62 + FileIdExtdBothDirectoryInformation, // 63 + FileDispositionInformationEx, // 64 + FileRenameInformationEx, // 65 + FileRenameInformationExBypassAccessCheck, // 66 + FileDesiredStorageClassInformation, // 67 + FileStatInformation, // 68 + FileMemoryPartitionInformation, // 69 + FileStatLxInformation, // 70 + FileCaseSensitiveInformation, // 71 + FileLinkInformationEx, // 72 + FileLinkInformationExBypassAccessCheck, // 73 + FileStorageReserveIdInformation, // 74 + FileCaseSensitiveInformationForceAccessCheck, // 75 + FileKnownFolderInformation, // 76 + + FileMaximumInformation + } FILE_INFORMATION_CLASS_ACTUAL, * PFILE_INFORMATION_CLASS_ACTUAL; +} + + +namespace ntfsdupe::hooks { + typedef struct _FILE_NAME_INFORMATION { + ULONG FileNameLength; + WCHAR FileName[1]; + } FILE_NAME_INFORMATION, * PFILE_NAME_INFORMATION; + + typedef struct _FILE_STANDARD_INFORMATION { + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG NumberOfLinks; + BOOLEAN DeletePending; + BOOLEAN Directory; + } FILE_STANDARD_INFORMATION, * PFILE_STANDARD_INFORMATION; + + typedef struct _FILE_INTERNAL_INFORMATION { + LARGE_INTEGER IndexNumber; + } FILE_INTERNAL_INFORMATION, * PFILE_INTERNAL_INFORMATION; + + typedef struct _FILE_EA_INFORMATION { + ULONG EaSize; + } FILE_EA_INFORMATION, * PFILE_EA_INFORMATION; + + typedef struct _FILE_ACCESS_INFORMATION { + ACCESS_MASK AccessFlags; + } FILE_ACCESS_INFORMATION, * PFILE_ACCESS_INFORMATION; + + typedef struct _FILE_POSITION_INFORMATION { + LARGE_INTEGER CurrentByteOffset; + } FILE_POSITION_INFORMATION, * PFILE_POSITION_INFORMATION; + + typedef struct _FILE_MODE_INFORMATION { + ULONG Mode; + } FILE_MODE_INFORMATION, * PFILE_MODE_INFORMATION; + + typedef struct _FILE_ALIGNMENT_INFORMATION { + ULONG AlignmentRequirement; + } FILE_ALIGNMENT_INFORMATION, * PFILE_ALIGNMENT_INFORMATION; + + typedef struct _FILE_ALL_INFORMATION { + FILE_BASIC_INFORMATION BasicInformation; + FILE_STANDARD_INFORMATION StandardInformation; + FILE_INTERNAL_INFORMATION InternalInformation; + FILE_EA_INFORMATION EaInformation; + FILE_ACCESS_INFORMATION AccessInformation; + FILE_POSITION_INFORMATION PositionInformation; + FILE_MODE_INFORMATION ModeInformation; + FILE_ALIGNMENT_INFORMATION AlignmentInformation; + FILE_NAME_INFORMATION NameInformation; + } FILE_ALL_INFORMATION, * PFILE_ALL_INFORMATION; + + + // --- query types --- // + typedef struct _FILE_DIRECTORY_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + WCHAR FileName[1]; + } FILE_DIRECTORY_INFORMATION, * PFILE_DIRECTORY_INFORMATION; + + typedef struct _FILE_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + WCHAR FileName[1]; + } FILE_FULL_DIR_INFORMATION, * PFILE_FULL_DIR_INFORMATION; + + typedef struct _FILE_BOTH_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + WCHAR FileName[1]; + } FILE_BOTH_DIR_INFORMATION, * PFILE_BOTH_DIR_INFORMATION; + + typedef struct _FILE_NAMES_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + ULONG FileNameLength; + WCHAR FileName[1]; + } FILE_NAMES_INFORMATION, * PFILE_NAMES_INFORMATION; + + typedef struct _FILE_ID_BOTH_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + LARGE_INTEGER FileId; + WCHAR FileName[1]; + } FILE_ID_BOTH_DIR_INFORMATION, * PFILE_ID_BOTH_DIR_INFORMATION; + + typedef struct _FILE_ID_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + LARGE_INTEGER FileId; + WCHAR FileName[1]; + } FILE_ID_FULL_DIR_INFORMATION, * PFILE_ID_FULL_DIR_INFORMATION; + + typedef struct _FILE_ID_GLOBAL_TX_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + LARGE_INTEGER FileId; + GUID LockingTransactionId; + ULONG TxInfoFlags; + WCHAR FileName[1]; + } FILE_ID_GLOBAL_TX_DIR_INFORMATION, * PFILE_ID_GLOBAL_TX_DIR_INFORMATION; + + // defined on win 10 + typedef struct _FILE_ID_128 { + BYTE Identifier[16]; + } FILE_ID_128, * PFILE_ID_128; + + typedef struct _FILE_ID_EXTD_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + ULONG ReparsePointTag; + FILE_ID_128 FileId; + WCHAR FileName[1]; + } FILE_ID_EXTD_DIR_INFORMATION, * PFILE_ID_EXTD_DIR_INFORMATION; + + typedef struct _FILE_ID_EXTD_BOTH_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + ULONG ReparsePointTag; + FILE_ID_128 FileId; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + WCHAR FileName[1]; + } FILE_ID_EXTD_BOTH_DIR_INFORMATION, * PFILE_ID_EXTD_BOTH_DIR_INFORMATION; +} + + +namespace ntfsdupe::hooks { + typedef struct { + unsigned long node_next_entry_offset; + unsigned long node_filename_offset; + unsigned long node_filename_bytes_offset; + } MultiQueryOffsets_t; +} + +namespace ntfsdupe::hooks { + void copy_new_file_target(wchar_t *object_name_new, const PUNICODE_STRING ObjectName, const ntfsdupe::cfgs::FileCfgEntry *cfg); + void copy_new_module_target(wchar_t *dllname_new, const PUNICODE_STRING DllName, const ntfsdupe::cfgs::ModuleCfgEntry *cfg); + + ntfsdupe::cfgs::FileCfgEntry* find_single_file_obj_dospath(wchar_t* dos_path); // dos path has to be null terminated + ntfsdupe::cfgs::FileCfgEntry* find_single_file_obj_ntpath(PCWSTR ntpath, ULONG ntpath_bytes); + ntfsdupe::cfgs::FileCfgEntry* find_single_file_obj_ntpath(POBJECT_ATTRIBUTES ObjectAttributes); + ntfsdupe::cfgs::FileCfgEntry* find_single_file_obj_base_handle(MultiQueryOffsets_t& query_info, HANDLE FileHandle, PVOID FileInformation); + + const ntfsdupe::cfgs::ModuleCfgEntry* find_module_obj_filename(PUNICODE_STRING DllName); + + NTSTATUS handle_file_multi_query( + MultiQueryOffsets_t& query_info, + HANDLE FileHandle, + PVOID FileInformation, + PIO_STATUS_BLOCK IoStatusBlock, + NTSTATUS result + ); + + bool init(void); + + void deinit(void); + +} diff --git a/nt_file_dupe/include/Hooks/LdrGetDllHandle_hook/LdrGetDllHandle_hook.hpp b/nt_file_dupe/include/Hooks/LdrGetDllHandle_hook/LdrGetDllHandle_hook.hpp new file mode 100644 index 0000000..d62f434 --- /dev/null +++ b/nt_file_dupe/include/Hooks/LdrGetDllHandle_hook/LdrGetDllHandle_hook.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace ntfsdupe::hooks { + // https://doxygen.reactos.org/d7/d55/ldrapi_8c.html#a048f061547bbe72e1b884b368cf526a2 + // https://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FExecutable%20Images%2FLdrGetDllHandle.html + NTSTATUS NTAPI LdrGetDllHandle_hook( + _In_opt_ PWSTR DllPath, + _In_opt_ PULONG DllCharacteristics, + _In_ PUNICODE_STRING DllName, + _Out_ PHANDLE DllHandle + ); + + extern decltype(LdrGetDllHandle_hook) *LdrGetDllHandle_original; +} diff --git a/nt_file_dupe/include/Hooks/LdrLoadDll_hook/LdrLoadDll_hook.hpp b/nt_file_dupe/include/Hooks/LdrLoadDll_hook/LdrLoadDll_hook.hpp new file mode 100644 index 0000000..806b7e6 --- /dev/null +++ b/nt_file_dupe/include/Hooks/LdrLoadDll_hook/LdrLoadDll_hook.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace ntfsdupe::hooks { + // https://doxygen.reactos.org/d7/d55/ldrapi_8c.html#a8838a6bd5ee2987045215ee7129c3a2c + // https://github.com/wine-mirror/wine/blob/86557b9e0ba8a783f1b0d0918b1ddec7e0a7749e/dlls/ntdll/loader.c#L3372 + // Note: both docs seem incorrect, the first arg is the dll flags from LoadLibraryEx() | 1 + // ex: LOAD_LIBRARY_SEARCH_SYSTEM32 | 1 + // also on x64, first arg is sometimes used as ecx (4 bytes) and sometimes rcx (8 bytes) + NTSTATUS NTAPI LdrLoadDll_hook( + __in_opt LPVOID DllCharacteristics, + __in_opt LPDWORD Unknown, + __in PUNICODE_STRING DllName, + _Out_ HMODULE* BaseAddress + ); + + extern decltype(LdrLoadDll_hook) *LdrLoadDll_original; + +} diff --git a/nt_file_dupe/include/Hooks/NtCreateDirectoryObjectEx_hook/NtCreateDirectoryObjectEx_hook.hpp b/nt_file_dupe/include/Hooks/NtCreateDirectoryObjectEx_hook/NtCreateDirectoryObjectEx_hook.hpp new file mode 100644 index 0000000..32a0125 --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtCreateDirectoryObjectEx_hook/NtCreateDirectoryObjectEx_hook.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace ntfsdupe::hooks +{ + // https://googleprojectzero.blogspot.com/2016/08/ + // https://docs.rs/ntapi/latest/ntapi/ntobapi/fn.NtCreateDirectoryObjectEx.html + NTSTATUS NTAPI NtCreateDirectoryObjectEx_hook( + __out PHANDLE DirectoryHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in_opt HANDLE ShadowDirectoryHandle, + __in ULONG Flags + ); + + extern decltype(NtCreateDirectoryObjectEx_hook) *NtCreateDirectoryObjectEx_original; + +} diff --git a/nt_file_dupe/include/Hooks/NtCreateDirectoryObject_hook/NtCreateDirectoryObject_hook.hpp b/nt_file_dupe/include/Hooks/NtCreateDirectoryObject_hook/NtCreateDirectoryObject_hook.hpp new file mode 100644 index 0000000..2a09cbf --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtCreateDirectoryObject_hook/NtCreateDirectoryObject_hook.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace ntfsdupe::hooks { + NTSTATUS NTAPI NtCreateDirectoryObject_hook( + __out PHANDLE DirectoryHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes + ); + + extern decltype(NtCreateDirectoryObject_hook) *NtCreateDirectoryObject_original; +} + diff --git a/nt_file_dupe/include/Hooks/NtCreateFile_hook/NtCreateFile_hook.hpp b/nt_file_dupe/include/Hooks/NtCreateFile_hook/NtCreateFile_hook.hpp new file mode 100644 index 0000000..7c8963a --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtCreateFile_hook/NtCreateFile_hook.hpp @@ -0,0 +1,20 @@ +#pragma once + +namespace ntfsdupe::hooks { + NTSTATUS NTAPI NtCreateFile_hook( + __out PHANDLE FileHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __out PIO_STATUS_BLOCK IoStatusBlock, + __in_opt PLARGE_INTEGER AllocationSize, + __in ULONG FileAttributes, + __in ULONG ShareAccess, + __in ULONG CreateDisposition, + __in ULONG CreateOptions, + __in_opt PVOID EaBuffer, + __in ULONG EaLength + ); + + extern decltype(NtCreateFile_hook) *NtCreateFile_original; +} + diff --git a/nt_file_dupe/include/Hooks/NtDeleteFile_hook/NtDeleteFile_hook.hpp b/nt_file_dupe/include/Hooks/NtDeleteFile_hook/NtDeleteFile_hook.hpp new file mode 100644 index 0000000..8e6a399 --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtDeleteFile_hook/NtDeleteFile_hook.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace ntfsdupe::hooks { + NTSTATUS NTAPI NtDeleteFile_hook( + __in POBJECT_ATTRIBUTES ObjectAttributes + ); + + extern decltype(NtDeleteFile_hook) *NtDeleteFile_original; +} diff --git a/nt_file_dupe/include/Hooks/NtOpenDirectoryObject_hook/NtOpenDirectoryObject_hook.hpp b/nt_file_dupe/include/Hooks/NtOpenDirectoryObject_hook/NtOpenDirectoryObject_hook.hpp new file mode 100644 index 0000000..929b458 --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtOpenDirectoryObject_hook/NtOpenDirectoryObject_hook.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace ntfsdupe::hooks { + NTSTATUS NTAPI NtOpenDirectoryObject_hook( + __out PHANDLE DirectoryHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes + ); + + extern decltype(NtOpenDirectoryObject_hook) *NtOpenDirectoryObject_original; + +} diff --git a/nt_file_dupe/include/Hooks/NtOpenFile_hook/NtOpenFile_hook.hpp b/nt_file_dupe/include/Hooks/NtOpenFile_hook/NtOpenFile_hook.hpp new file mode 100644 index 0000000..215deca --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtOpenFile_hook/NtOpenFile_hook.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace ntfsdupe::hooks { + NTSTATUS NTAPI NtOpenFile_hook( + __out PHANDLE FileHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __out PIO_STATUS_BLOCK IoStatusBlock, + __in ULONG ShareAccess, + __in ULONG OpenOptions + ); + + extern decltype(NtOpenFile_hook) *NtOpenFile_original; + +} diff --git a/nt_file_dupe/include/Hooks/NtQueryAttributesFile_hook/NtQueryAttributesFile_hook.hpp b/nt_file_dupe/include/Hooks/NtQueryAttributesFile_hook/NtQueryAttributesFile_hook.hpp new file mode 100644 index 0000000..2581f3d --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtQueryAttributesFile_hook/NtQueryAttributesFile_hook.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace ntfsdupe::hooks { + // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_file_basic_information + typedef struct _FILE_BASIC_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + ULONG FileAttributes; + } FILE_BASIC_INFORMATION, * PFILE_BASIC_INFORMATION; + + NTSTATUS NTAPI NtQueryAttributesFile_hook( + __in POBJECT_ATTRIBUTES ObjectAttributes, + __out PFILE_BASIC_INFORMATION FileInformation + ); + + extern decltype(NtQueryAttributesFile_hook) *NtQueryAttributesFile_original; + +} + diff --git a/nt_file_dupe/include/Hooks/NtQueryDirectoryFileEx_hook/NtQueryDirectoryFileEx_hook.hpp b/nt_file_dupe/include/Hooks/NtQueryDirectoryFileEx_hook/NtQueryDirectoryFileEx_hook.hpp new file mode 100644 index 0000000..4b90c3a --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtQueryDirectoryFileEx_hook/NtQueryDirectoryFileEx_hook.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace ntfsdupe::hooks { + NTSTATUS NTAPI NtQueryDirectoryFileEx_hook( + __in HANDLE FileHandle, + __in_opt HANDLE Event, + __in_opt PIO_APC_ROUTINE ApcRoutine, + __in_opt PVOID ApcContext, + __out PIO_STATUS_BLOCK IoStatusBlock, + __out PVOID FileInformation, + __in ULONG Length, + __in FILE_INFORMATION_CLASS FileInformationClass, + __in ULONG QueryFlags, + __in_opt PUNICODE_STRING FileName + ); + + extern decltype(NtQueryDirectoryFileEx_hook) *NtQueryDirectoryFileEx_original; +} diff --git a/nt_file_dupe/include/Hooks/NtQueryDirectoryFile_hook/NtQueryDirectoryFile_hook.hpp b/nt_file_dupe/include/Hooks/NtQueryDirectoryFile_hook/NtQueryDirectoryFile_hook.hpp new file mode 100644 index 0000000..c38da7a --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtQueryDirectoryFile_hook/NtQueryDirectoryFile_hook.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace ntfsdupe::hooks { + NTSTATUS NTAPI NtQueryDirectoryFile_hook( + __in HANDLE FileHandle, + __in_opt HANDLE Event, + __in_opt PIO_APC_ROUTINE ApcRoutine, + __in_opt PVOID ApcContext, + __out PIO_STATUS_BLOCK IoStatusBlock, + __out PVOID FileInformation, + __in ULONG Length, + __in FILE_INFORMATION_CLASS FileInformationClass, + __in BOOLEAN ReturnSingleEntry, + __in_opt PUNICODE_STRING FileName, + __in BOOLEAN RestartScan + ); + + extern decltype(NtQueryDirectoryFile_hook) *NtQueryDirectoryFile_original; + +} + diff --git a/nt_file_dupe/include/Hooks/NtQueryFullAttributesFile_hook/NtQueryFullAttributesFile_hook.hpp b/nt_file_dupe/include/Hooks/NtQueryFullAttributesFile_hook/NtQueryFullAttributesFile_hook.hpp new file mode 100644 index 0000000..b791022 --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtQueryFullAttributesFile_hook/NtQueryFullAttributesFile_hook.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace ntfsdupe::hooks { + // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_file_network_open_information + typedef struct _FILE_NETWORK_OPEN_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; + } FILE_NETWORK_OPEN_INFORMATION, * PFILE_NETWORK_OPEN_INFORMATION; + + NTSTATUS NTAPI NtQueryFullAttributesFile_hook( + __in POBJECT_ATTRIBUTES ObjectAttributes, + __out PFILE_NETWORK_OPEN_INFORMATION FileInformation + ); + + extern decltype(NtQueryFullAttributesFile_hook) *NtQueryFullAttributesFile_original; +} diff --git a/nt_file_dupe/include/Hooks/NtQueryInformationByName_hook/NtQueryInformationByName_hook.hpp b/nt_file_dupe/include/Hooks/NtQueryInformationByName_hook/NtQueryInformationByName_hook.hpp new file mode 100644 index 0000000..7d832da --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtQueryInformationByName_hook/NtQueryInformationByName_hook.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace ntfsdupe::hooks { + NTSTATUS NTAPI NtQueryInformationByName_hook( + __in POBJECT_ATTRIBUTES ObjectAttributes, + __out PIO_STATUS_BLOCK IoStatusBlock, + __out PVOID FileInformation, + __in ULONG Length, + __in FILE_INFORMATION_CLASS FileInformationClass + ); + + extern decltype(NtQueryInformationByName_hook) *NtQueryInformationByName_original; +} diff --git a/nt_file_dupe/include/Hooks/NtQueryInformationFile_hook/NtQueryInformationFile_hook.hpp b/nt_file_dupe/include/Hooks/NtQueryInformationFile_hook/NtQueryInformationFile_hook.hpp new file mode 100644 index 0000000..6b936c2 --- /dev/null +++ b/nt_file_dupe/include/Hooks/NtQueryInformationFile_hook/NtQueryInformationFile_hook.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace ntfsdupe::hooks { + NTSTATUS NTAPI NtQueryInformationFile_hook( + __in HANDLE FileHandle, + __out PIO_STATUS_BLOCK IoStatusBlock, + __out PVOID FileInformation, + __in ULONG Length, + __in FILE_INFORMATION_CLASS FileInformationClass + ); + + extern decltype(NtQueryInformationFile_hook) *NtQueryInformationFile_original; + +} diff --git a/nt_file_dupe/include/NtApis/NtApis.hpp b/nt_file_dupe/include/NtApis/NtApis.hpp new file mode 100644 index 0000000..a21af3a --- /dev/null +++ b/nt_file_dupe/include/NtApis/NtApis.hpp @@ -0,0 +1,27 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#ifndef NT_STATUS + #define NT_STATUS(s) (s >= 0) +#endif + +namespace ntfsdupe::ntapis { + bool init(void); + + bool NtPathToDosPath( + PWSTR dosPath, USHORT* dosPathBytes, + PCWSTR ntPath, const USHORT ntPathBytes + ); + + bool GetFullDosPath(PWSTR fullPath, USHORT* fullPathBytes, PWSTR path); + + HANDLE DuplicateHandle(HANDLE original); + + NTSTATUS CloseHandle(HANDLE handle); + + void LockPeb(); + void ReleasePeb(); +} diff --git a/nt_file_dupe/include/NtApis/peb_helper.hpp b/nt_file_dupe/include/NtApis/peb_helper.hpp new file mode 100644 index 0000000..24f9f72 --- /dev/null +++ b/nt_file_dupe/include/NtApis/peb_helper.hpp @@ -0,0 +1,8 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include + +namespace ntfsdupe::ntapis::peb_helper { + bool remove_from_peb(HMODULE hModule); +} diff --git a/nt_file_dupe/include/lib_main/lib_main.hpp b/nt_file_dupe/include/lib_main/lib_main.hpp new file mode 100644 index 0000000..417192b --- /dev/null +++ b/nt_file_dupe/include/lib_main/lib_main.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace ntfsdupe { + bool init(); + void deinit(); +} diff --git a/nt_file_dupe/libs/Detours/CREDITS.md b/nt_file_dupe/libs/Detours/CREDITS.md new file mode 100644 index 0000000..7c7c815 --- /dev/null +++ b/nt_file_dupe/libs/Detours/CREDITS.md @@ -0,0 +1,118 @@ +# Detours Contributor Credits + +The following individuals have helped identify specific bugs and improvements +in Detours. The entire Detours community has benefited from their help. + +* Jay Krell: Identified error in DetourFindPayload that caused a + incorrect failure when pcbData is NULL. (Build_342) + +* Jay Krell: Identified issue with VirtualSize == 0 files created in + NT 3.1 images. (Build_339) + +* Igor Odnovorov: Identified an issue with the placement of the trampoline + region when a function is detoured twice and the second + trampoline region is outside of the +/- 2GB range of + the target. (Build_337) + +* Jay Krell: Identified need for some programs to enumerate the + address of IAT entries. (Build_336) + +* Calvin Hsia: Identified need for some program to change the excluded + system region. (Build_336) + +* Adam Smith: Identified error in failure handling when VirtualProect + cannot make pages executable because the Prohibit + Dynamic Code Generation mitigation policy has been + applied to a process. (Build_335) + +* Ben Faull: Identified fix to detour_alloc_region_from_lo and + detour_alloc_region_from_hi that preserves ASLR entropy. + (Build_334) + +* Shaoxiang Su: Reported errors building with Visual Studio 2015. + (Build_332) + +* Jay Krell: Identified and resolved significant gaps in the X86, X64 + and IA64 disassemblers for instruction found in code, + but seldom found in function prologues. (Build_331) + +* Allan Murphy: Identify error in rep and jmp ds: encodings. (Build_331) + +* Philip Bacon: Identified incorrect entry point return for pure + resource-only binaries. (Build_330) + +* Jay Krell: Identified failure in DetourAttachEx to update nAlign. + (Build_330) + +* Sumit Sarin: Helped debug error with packed binaries. + (Build_329) + +* Nitya Kumar Sharma: Reported bug in DetourAfterWithDll for 32/64 agnostic + EXEs. + (Build_327) + +* Richard Black: Identified a large number of typos in documentation. + (Build_326) + +* Michael Bilodeau: Identified bug in DetourUpdateProcessWithDll when the + target process contains a Detours payload *after* all + valid PE binaries. + (Build_324) + +* Meera Jindal: Reported bug in identification of target address in + DetourCopyInstruction for jmp[] and call[] on x86 & x64, + the ff15 and ff25 opcodes. + (Build_323) + +* Ken Johnson: Assistance with SAL 2.0 annotations. + (Build_319) + +* Nick Wood: Identified bug in DetourFindFunction on ARM. + (Build_314) + +* Mark Russinovich: Helped debug DetourCreateProcessWithDllEx. + (Build_314) + +* John Lin: Implementation idea for DetoursCreateProcessWithDllEx. + (Build_314) + +* Andrew Zawadowskiy Reported an improper memory page permissions + vulnerability in Detours 2.1. (Vulnerability does not + exist in versions later than Detours 2.1.) + (Build_223) + +* Nightxie: Identified bug in detour_alloc_round_up_to_region. + (Build_310) + +* Diana Milirud: Identified bug in B* instructions on ARM. + (Build_309) + +* Juan Carlos Identified correct MSIL entry point for unsigned MSIL. + Luciani: (Build_308) + +* Lee Hunt Suggested improvements in algorithm for allocation of + Lawrence Landauer trampoline regions on x64 to avoid collisions with + Joe Laughlin: system DLLs. + (Build_307) + +* Tyler Sims Identified bug in handling of "anycpu" MSIL binaries + Darren Kennedy: on x64. + (Build_307) + +* Andre Vachon: Help with optimized binaries. + (Build 301) + +* Chris Mann: Identified fix not forward ported from 2.2 to 3.0. + (Build_301) + +* Mark Irving: Identified bug with EXEs missing second import table. + (Build_300) + +* Ben Schwarz: Identified bug in handling of multi-byte NOPs. + (Build_300) + +* Aaron Giles Coded initial ARM/Thumb2 disassembler. + Jared Henderson: (Build_300) + +* Doug Brubacher: Coded initial x86 disassembler. + (Build_100) diff --git a/nt_file_dupe/libs/Detours/LICENSE b/nt_file_dupe/libs/Detours/LICENSE new file mode 100644 index 0000000..b2f52a2 --- /dev/null +++ b/nt_file_dupe/libs/Detours/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) Microsoft Corporation. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/nt_file_dupe/libs/Detours/LICENSE.md b/nt_file_dupe/libs/Detours/LICENSE.md new file mode 100644 index 0000000..e6a4c56 --- /dev/null +++ b/nt_file_dupe/libs/Detours/LICENSE.md @@ -0,0 +1,23 @@ +# Copyright (c) Microsoft Corporation + +All rights reserved. + +# MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/nt_file_dupe/libs/Detours/README.md b/nt_file_dupe/libs/Detours/README.md new file mode 100644 index 0000000..84a4d14 --- /dev/null +++ b/nt_file_dupe/libs/Detours/README.md @@ -0,0 +1,56 @@ +# Microsoft Research Detours Package + +Detours is a software package for monitoring and instrumenting API calls on Windows. Detours +has been used by many ISVs and is also used by product teams at Microsoft. Detours is now available under +a standard open source license ([MIT](https://github.com/microsoft/Detours/blob/master/LICENSE.md)). This simplifies licensing for programmers using Detours +and allows the community to support Detours using open source tools and processes. + +Detours is compatible with the Windows NT family of +operating systems: Windows NT, Windows XP, Windows Server 2003, Windows 7, +Windows 8, and Windows 10. It cannot be used by Windows Store apps +because Detours requires APIs not available to those applications. +This repo contains the source code for version 4.0.1 of Detours. + +For technical documentation on Detours, see the [Detours Wiki](https://github.com/microsoft/Detours/wiki). +For directions on how to build and run samples, see the +samples [README.txt](https://github.com/Microsoft/Detours/blob/master/samples/README.TXT) file. + +## Contributing + +The [`Detours`](https://github.com/microsoft/detours) repository is where development is done. +Here are some ways you can participate in the project: + +* [Answer questions](https://github.com/microsoft/detours/issues) about using Detours. +* [Improve the Wiki](https://github.com/microsoft/detours/wiki). +* [Submit bugs](https://github.com/microsoft/detours/issues) and help us verify fixes and changes as they are checked in. +* Review [source code changes](https://github.com/microsoft/detours/pulls). + +Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that +you have the right to, and actually do, grant us the rights to use your contribution. +For details, visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Issues, questions, and feedback + +* Open an issue on [GitHub Issues](https://github.com/Microsoft/detours/issues). + +## Mailing list for announcements + +The detours-announce mailing list is a low-traffic email list for important announcements +about the project, such as the availability of new versions of Detours. To join it, send +an email to listserv@lists.research.microsoft.com with a +message body containing only the text SUBSCRIBE DETOURS-ANNOUNCE. +To leave it, send an email to listserv@lists.research.microsoft.com with a +message body containing only the text UNSUBSCRIBE DETOURS-ANNOUNCE. + + +## License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the [MIT](LICENSE.md) License. diff --git a/nt_file_dupe/libs/Detours/SECURITY.md b/nt_file_dupe/libs/Detours/SECURITY.md new file mode 100644 index 0000000..e138ec5 --- /dev/null +++ b/nt_file_dupe/libs/Detours/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/nt_file_dupe/libs/Detours/creatwth.cpp b/nt_file_dupe/libs/Detours/creatwth.cpp new file mode 100644 index 0000000..c2819af --- /dev/null +++ b/nt_file_dupe/libs/Detours/creatwth.cpp @@ -0,0 +1,1783 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Create a process with a DLL (creatwth.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// #define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL +#include "detours.h" +#include + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +#define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] +#define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] +#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] +#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT] + +////////////////////////////////////////////////////////////////////////////// +// +const GUID DETOUR_EXE_HELPER_GUID = { /* ea0251b9-5cde-41b5-98d0-2af4a26b0fee */ + 0xea0251b9, 0x5cde, 0x41b5, + { 0x98, 0xd0, 0x2a, 0xf4, 0xa2, 0x6b, 0x0f, 0xee }}; + +////////////////////////////////////////////////////////////////////////////// +// +// Enumerate through modules in the target process. +// +static PVOID LoadNtHeaderFromProcess(_In_ HANDLE hProcess, + _In_ HMODULE hModule, + _Out_ PIMAGE_NT_HEADERS32 pNtHeader) +{ + ZeroMemory(pNtHeader, sizeof(*pNtHeader)); + PBYTE pbModule = (PBYTE)hModule; + + if (pbModule == NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + if (VirtualQueryEx(hProcess, hModule, &mbi, sizeof(mbi)) == 0) { + return NULL; + } + + IMAGE_DOS_HEADER idh; + if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n", + pbModule, pbModule + sizeof(idh), GetLastError())); + return NULL; + } + + if (idh.e_magic != IMAGE_DOS_SIGNATURE || + (DWORD)idh.e_lfanew > mbi.RegionSize || + (DWORD)idh.e_lfanew < sizeof(idh)) { + + SetLastError(ERROR_BAD_EXE_FORMAT); + return NULL; + } + + if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, + pNtHeader, sizeof(*pNtHeader), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p:%p) failed: %lu\n", + pbModule + idh.e_lfanew, + pbModule + idh.e_lfanew + sizeof(*pNtHeader), + pbModule, + GetLastError())); + return NULL; + } + + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return NULL; + } + + return pbModule + idh.e_lfanew; +} + +static HMODULE EnumerateModulesInProcess(_In_ HANDLE hProcess, + _In_opt_ HMODULE hModuleLast, + _Out_ PIMAGE_NT_HEADERS32 pNtHeader, + _Out_opt_ PVOID *pRemoteNtHeader) +{ + ZeroMemory(pNtHeader, sizeof(*pNtHeader)); + if (pRemoteNtHeader) { + *pRemoteNtHeader = NULL; + } + + PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY; + + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + // Find the next memory region that contains a mapped PE image. + // + + for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) { + if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) { + break; + } + + // Usermode address space has such an unaligned region size always at the + // end and only at the end. + // + if ((mbi.RegionSize & 0xfff) == 0xfff) { + break; + } + if (((PBYTE)mbi.BaseAddress + mbi.RegionSize) < pbLast) { + break; + } + + // Skip uncommitted regions and guard pages. + // + if ((mbi.State != MEM_COMMIT) || + ((mbi.Protect & 0xff) == PAGE_NOACCESS) || + (mbi.Protect & PAGE_GUARD)) { + continue; + } + + PVOID remoteHeader + = LoadNtHeaderFromProcess(hProcess, (HMODULE)pbLast, pNtHeader); + if (remoteHeader) { + if (pRemoteNtHeader) { + *pRemoteNtHeader = remoteHeader; + } + + return (HMODULE)pbLast; + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Find payloads in target process. +// + +static PVOID FindDetourSectionInRemoteModule(_In_ HANDLE hProcess, + _In_ HMODULE hModule, + _In_ const IMAGE_NT_HEADERS32 *pNtHeader, + _In_ PVOID pRemoteNtHeader) +{ + if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } + + PIMAGE_SECTION_HEADER pRemoteSectionHeaders + = (PIMAGE_SECTION_HEADER)((PBYTE)pRemoteNtHeader + + sizeof(pNtHeader->Signature) + + sizeof(pNtHeader->FileHeader) + + pNtHeader->FileHeader.SizeOfOptionalHeader); + + IMAGE_SECTION_HEADER header; + for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; ++n) { + if (!ReadProcessMemory(hProcess, pRemoteSectionHeaders + n, &header, sizeof(header), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n", + pRemoteSectionHeaders + n, + (PBYTE)(pRemoteSectionHeaders + n) + sizeof(header), + GetLastError())); + + return NULL; + } + + if (strcmp((PCHAR)header.Name, ".detour") == 0) { + if (header.VirtualAddress == 0 || + header.SizeOfRawData == 0) { + + break; + } + + SetLastError(NO_ERROR); + return (PBYTE)hModule + header.VirtualAddress; + } + } + + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; +} + +static PVOID FindPayloadInRemoteDetourSection(_In_ HANDLE hProcess, + _In_ REFGUID rguid, + _Out_opt_ DWORD *pcbData, + _In_ PVOID pvRemoteDetoursSection) +{ + if (pcbData) { + *pcbData = 0; + } + + PBYTE pbData = (PBYTE)pvRemoteDetoursSection; + + DETOUR_SECTION_HEADER header; + if (!ReadProcessMemory(hProcess, pbData, &header, sizeof(header), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(dsh@%p..%p) failed: %lu\n", + pbData, + pbData + sizeof(header), + GetLastError())); + return NULL; + } + + if (header.cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) || + header.nSignature != DETOUR_SECTION_HEADER_SIGNATURE) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } + + if (header.nDataOffset == 0) { + header.nDataOffset = header.cbHeaderSize; + } + + for (PVOID pvSection = pbData + header.nDataOffset; pvSection < pbData + header.cbDataSize;) { + DETOUR_SECTION_RECORD section; + if (!ReadProcessMemory(hProcess, pvSection, §ion, sizeof(section), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(dsr@%p..%p) failed: %lu\n", + pvSection, + (PBYTE)pvSection + sizeof(section), + GetLastError())); + return NULL; + } + + if (DetourAreSameGuid(section.guid, rguid)) { + if (pcbData) { + *pcbData = section.cbBytes - sizeof(section); + } + SetLastError(NO_ERROR); + return (DETOUR_SECTION_RECORD *)pvSection + 1; + } + + pvSection = (PBYTE)pvSection + section.cbBytes; + } + + return NULL; +} + +_Success_(return != NULL) +PVOID WINAPI DetourFindRemotePayload(_In_ HANDLE hProcess, + _In_ REFGUID rguid, + _Out_opt_ DWORD *pcbData) +{ + if (hProcess == NULL) { + SetLastError(ERROR_INVALID_HANDLE); + return NULL; + } + + IMAGE_NT_HEADERS32 header; + PVOID pvRemoteHeader; + for (HMODULE hMod = NULL; (hMod = EnumerateModulesInProcess(hProcess, hMod, &header, &pvRemoteHeader)) != NULL;) { + PVOID pvData = FindDetourSectionInRemoteModule(hProcess, hMod, &header, pvRemoteHeader); + if (pvData != NULL) { + pvData = FindPayloadInRemoteDetourSection(hProcess, rguid, pcbData, pvData); + if (pvData != NULL) { + return pvData; + } + } + } + + SetLastError(ERROR_MOD_NOT_FOUND); + return NULL; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Find a region of memory in which we can create a replacement import table. +// +static PBYTE FindAndAllocateNearBase(HANDLE hProcess, PBYTE pbModule, PBYTE pbBase, DWORD cbAlloc) +{ + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + PBYTE pbLast = pbBase; + for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) { + + ZeroMemory(&mbi, sizeof(mbi)); + if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + break; + } + DETOUR_TRACE(("VirtualQueryEx(%p) failed: %lu\n", + pbLast, GetLastError())); + break; + } + // Usermode address space has such an unaligned region size always at the + // end and only at the end. + // + if ((mbi.RegionSize & 0xfff) == 0xfff) { + break; + } + + // Skip anything other than a pure free region. + // + if (mbi.State != MEM_FREE) { + continue; + } + + // Use the max of mbi.BaseAddress and pbBase, in case mbi.BaseAddress < pbBase. + PBYTE pbAddress = (PBYTE)mbi.BaseAddress > pbBase ? (PBYTE)mbi.BaseAddress : pbBase; + + // Round pbAddress up to the nearest MM allocation boundary. + const DWORD_PTR mmGranularityMinusOne = (DWORD_PTR)(MM_ALLOCATION_GRANULARITY -1); + pbAddress = (PBYTE)(((DWORD_PTR)pbAddress + mmGranularityMinusOne) & ~mmGranularityMinusOne); + +#ifdef _WIN64 + // The offset from pbModule to any replacement import must fit into 32 bits. + // For simplicity, we check that the offset to the last byte fits into 32 bits, + // instead of the largest offset we'll actually use. The values are very similar. + const size_t GB4 = ((((size_t)1) << 32) - 1); + if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) { + DETOUR_TRACE(("FindAndAllocateNearBase(1) failing due to distance >4GB %p\n", pbAddress)); + return NULL; + } +#else + UNREFERENCED_PARAMETER(pbModule); +#endif + + DETOUR_TRACE(("Free region %p..%p\n", + mbi.BaseAddress, + (PBYTE)mbi.BaseAddress + mbi.RegionSize)); + + for (; pbAddress < (PBYTE)mbi.BaseAddress + mbi.RegionSize; pbAddress += MM_ALLOCATION_GRANULARITY) { + PBYTE pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (pbAlloc == NULL) { + DETOUR_TRACE(("VirtualAllocEx(%p) failed: %lu\n", pbAddress, GetLastError())); + continue; + } +#ifdef _WIN64 + // The offset from pbModule to any replacement import must fit into 32 bits. + if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) { + DETOUR_TRACE(("FindAndAllocateNearBase(2) failing due to distance >4GB %p\n", pbAddress)); + return NULL; + } +#endif + DETOUR_TRACE(("[%p..%p] Allocated for import table.\n", + pbAlloc, pbAlloc + cbAlloc)); + return pbAlloc; + } + } + return NULL; +} + +static inline DWORD PadToDword(DWORD dw) +{ + return (dw + 3) & ~3u; +} + +static inline DWORD PadToDwordPtr(DWORD dw) +{ + return (dw + 7) & ~7u; +} + +static inline HRESULT ReplaceOptionalSizeA(_Inout_z_count_(cchDest) LPSTR pszDest, + _In_ size_t cchDest, + _In_z_ LPCSTR pszSize) +{ + if (cchDest == 0 || pszDest == NULL || pszSize == NULL || + pszSize[0] == '\0' || pszSize[1] == '\0' || pszSize[2] != '\0') { + + // can not write into empty buffer or with string other than two chars. + return ERROR_INVALID_PARAMETER; + } + + for (; cchDest >= 2; cchDest--, pszDest++) { + if (pszDest[0] == '?' && pszDest[1] == '?') { + pszDest[0] = pszSize[0]; + pszDest[1] = pszSize[1]; + break; + } + } + + return S_OK; +} + +static BOOL RecordExeRestore(HANDLE hProcess, HMODULE hModule, DETOUR_EXE_RESTORE& der) +{ + // Save the various headers for DetourRestoreAfterWith. + ZeroMemory(&der, sizeof(der)); + der.cb = sizeof(der); + + der.pidh = (PBYTE)hModule; + der.cbidh = sizeof(der.idh); + if (!ReadProcessMemory(hProcess, der.pidh, &der.idh, sizeof(der.idh), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n", + der.pidh, der.pidh + der.cbidh, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("IDH: %p..%p\n", der.pidh, der.pidh + der.cbidh)); + + // We read the NT header in two passes to get the full size. + // First we read just the Signature and FileHeader. + der.pinh = der.pidh + der.idh.e_lfanew; + der.cbinh = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader); + if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n", + der.pinh, der.pinh + der.cbinh, GetLastError())); + return FALSE; + } + + // Second we read the OptionalHeader and Section headers. + der.cbinh = (FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + der.inh.FileHeader.SizeOfOptionalHeader + + der.inh.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)); + + if (der.cbinh > sizeof(der.raw)) { + return FALSE; + } + + if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n", + der.pinh, der.pinh + der.cbinh, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("INH: %p..%p\n", der.pinh, der.pinh + der.cbinh)); + + // Third, we read the CLR header + + if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (der.inh32.CLR_DIRECTORY.VirtualAddress != 0 && + der.inh32.CLR_DIRECTORY.Size != 0) { + + DETOUR_TRACE(("CLR32.VirtAddr=%08lx, CLR.Size=%lu\n", + der.inh32.CLR_DIRECTORY.VirtualAddress, + der.inh32.CLR_DIRECTORY.Size)); + + der.pclr = ((PBYTE)hModule) + der.inh32.CLR_DIRECTORY.VirtualAddress; + } + } + else if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + if (der.inh64.CLR_DIRECTORY.VirtualAddress != 0 && + der.inh64.CLR_DIRECTORY.Size != 0) { + + DETOUR_TRACE(("CLR64.VirtAddr=%08lx, CLR.Size=%lu\n", + der.inh64.CLR_DIRECTORY.VirtualAddress, + der.inh64.CLR_DIRECTORY.Size)); + + der.pclr = ((PBYTE)hModule) + der.inh64.CLR_DIRECTORY.VirtualAddress; + } + } + + if (der.pclr != 0) { + der.cbclr = sizeof(der.clr); + if (!ReadProcessMemory(hProcess, der.pclr, &der.clr, der.cbclr, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(clr@%p..%p) failed: %lu\n", + der.pclr, der.pclr + der.cbclr, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); + } + + return TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// +#if DETOURS_32BIT +#define DWORD_XX DWORD32 +#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS32 +#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR32_MAGIC +#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG32 +#define IMAGE_THUNK_DATAXX IMAGE_THUNK_DATA32 +#define UPDATE_IMPORTS_XX UpdateImports32 +#define DETOURS_BITS_XX 32 +#include "uimports.cpp" +#undef DETOUR_EXE_RESTORE_FIELD_XX +#undef DWORD_XX +#undef IMAGE_NT_HEADERS_XX +#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX +#undef IMAGE_ORDINAL_FLAG_XX +#undef UPDATE_IMPORTS_XX +#endif // DETOURS_32BIT + +#if DETOURS_64BIT +#define DWORD_XX DWORD64 +#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS64 +#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR64_MAGIC +#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG64 +#define IMAGE_THUNK_DATAXX IMAGE_THUNK_DATA64 +#define UPDATE_IMPORTS_XX UpdateImports64 +#define DETOURS_BITS_XX 64 +#include "uimports.cpp" +#undef DETOUR_EXE_RESTORE_FIELD_XX +#undef DWORD_XX +#undef IMAGE_NT_HEADERS_XX +#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX +#undef IMAGE_ORDINAL_FLAG_XX +#undef UPDATE_IMPORTS_XX +#endif // DETOURS_64BIT + +////////////////////////////////////////////////////////////////////////////// +// +#if DETOURS_64BIT + +C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == sizeof(IMAGE_NT_HEADERS32) + 16); + +static BOOL UpdateFrom32To64(HANDLE hProcess, HMODULE hModule, WORD machine, + DETOUR_EXE_RESTORE& der) +{ + IMAGE_DOS_HEADER idh; + IMAGE_NT_HEADERS32 inh32; + IMAGE_NT_HEADERS64 inh64; + IMAGE_SECTION_HEADER sects[32]; + PBYTE pbModule = (PBYTE)hModule; + DWORD n; + + ZeroMemory(&inh32, sizeof(inh32)); + ZeroMemory(&inh64, sizeof(inh64)); + ZeroMemory(sects, sizeof(sects)); + + DETOUR_TRACE(("UpdateFrom32To64(%04x)\n", machine)); + //////////////////////////////////////////////////////// Read old headers. + // + if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n", + pbModule, pbModule + sizeof(idh), GetLastError())); + return FALSE; + } + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p)\n", + pbModule, pbModule + sizeof(idh))); + + PBYTE pnh = pbModule + idh.e_lfanew; + if (!ReadProcessMemory(hProcess, pnh, &inh32, sizeof(inh32), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n", + pnh, pnh + sizeof(inh32), GetLastError())); + return FALSE; + } + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh32))); + + if (inh32.FileHeader.NumberOfSections > (sizeof(sects)/sizeof(sects[0]))) { + return FALSE; + } + + PBYTE psects = pnh + + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + inh32.FileHeader.SizeOfOptionalHeader; + ULONG cb = inh32.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + if (!ReadProcessMemory(hProcess, psects, §s, cb, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n", + psects, psects + cb, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p)\n", psects, psects + cb)); + + ////////////////////////////////////////////////////////// Convert header. + // + inh64.Signature = inh32.Signature; + inh64.FileHeader = inh32.FileHeader; + inh64.FileHeader.Machine = machine; + inh64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64); + + inh64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC; + inh64.OptionalHeader.MajorLinkerVersion = inh32.OptionalHeader.MajorLinkerVersion; + inh64.OptionalHeader.MinorLinkerVersion = inh32.OptionalHeader.MinorLinkerVersion; + inh64.OptionalHeader.SizeOfCode = inh32.OptionalHeader.SizeOfCode; + inh64.OptionalHeader.SizeOfInitializedData = inh32.OptionalHeader.SizeOfInitializedData; + inh64.OptionalHeader.SizeOfUninitializedData = inh32.OptionalHeader.SizeOfUninitializedData; + inh64.OptionalHeader.AddressOfEntryPoint = inh32.OptionalHeader.AddressOfEntryPoint; + inh64.OptionalHeader.BaseOfCode = inh32.OptionalHeader.BaseOfCode; + inh64.OptionalHeader.ImageBase = inh32.OptionalHeader.ImageBase; + inh64.OptionalHeader.SectionAlignment = inh32.OptionalHeader.SectionAlignment; + inh64.OptionalHeader.FileAlignment = inh32.OptionalHeader.FileAlignment; + inh64.OptionalHeader.MajorOperatingSystemVersion + = inh32.OptionalHeader.MajorOperatingSystemVersion; + inh64.OptionalHeader.MinorOperatingSystemVersion + = inh32.OptionalHeader.MinorOperatingSystemVersion; + inh64.OptionalHeader.MajorImageVersion = inh32.OptionalHeader.MajorImageVersion; + inh64.OptionalHeader.MinorImageVersion = inh32.OptionalHeader.MinorImageVersion; + inh64.OptionalHeader.MajorSubsystemVersion = inh32.OptionalHeader.MajorSubsystemVersion; + inh64.OptionalHeader.MinorSubsystemVersion = inh32.OptionalHeader.MinorSubsystemVersion; + inh64.OptionalHeader.Win32VersionValue = inh32.OptionalHeader.Win32VersionValue; + inh64.OptionalHeader.SizeOfImage = inh32.OptionalHeader.SizeOfImage; + inh64.OptionalHeader.SizeOfHeaders = inh32.OptionalHeader.SizeOfHeaders; + inh64.OptionalHeader.CheckSum = inh32.OptionalHeader.CheckSum; + inh64.OptionalHeader.Subsystem = inh32.OptionalHeader.Subsystem; + inh64.OptionalHeader.DllCharacteristics = inh32.OptionalHeader.DllCharacteristics; + inh64.OptionalHeader.SizeOfStackReserve = inh32.OptionalHeader.SizeOfStackReserve; + inh64.OptionalHeader.SizeOfStackCommit = inh32.OptionalHeader.SizeOfStackCommit; + inh64.OptionalHeader.SizeOfHeapReserve = inh32.OptionalHeader.SizeOfHeapReserve; + inh64.OptionalHeader.SizeOfHeapCommit = inh32.OptionalHeader.SizeOfHeapCommit; + inh64.OptionalHeader.LoaderFlags = inh32.OptionalHeader.LoaderFlags; + inh64.OptionalHeader.NumberOfRvaAndSizes = inh32.OptionalHeader.NumberOfRvaAndSizes; + for (n = 0; n < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; n++) { + inh64.OptionalHeader.DataDirectory[n] = inh32.OptionalHeader.DataDirectory[n]; + } + + /////////////////////////////////////////////////////// Write new headers. + // + DWORD dwProtect = 0; + if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders, + PAGE_EXECUTE_READWRITE, &dwProtect)) { + return FALSE; + } + + if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %lu\n", + pnh, pnh + sizeof(inh64), GetLastError())); + return FALSE; + } + DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh64))); + + psects = pnh + + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + inh64.FileHeader.SizeOfOptionalHeader; + cb = inh64.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + if (!WriteProcessMemory(hProcess, psects, §s, cb, NULL)) { + DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p) failed: %lu\n", + psects, psects + cb, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p)\n", psects, psects + cb)); + + // Record the updated headers. + if (!RecordExeRestore(hProcess, hModule, der)) { + return FALSE; + } + + // Remove the import table. + if (der.pclr != NULL && (der.clr.Flags & COMIMAGE_FLAGS_ILONLY)) { + inh64.IMPORT_DIRECTORY.VirtualAddress = 0; + inh64.IMPORT_DIRECTORY.Size = 0; + + if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %lu\n", + pnh, pnh + sizeof(inh64), GetLastError())); + return FALSE; + } + } + + DWORD dwOld = 0; + if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders, + dwProtect, &dwOld)) { + return FALSE; + } + + return TRUE; +} +#endif // DETOURS_64BIT + +typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + +static BOOL IsWow64ProcessHelper(HANDLE hProcess, + PBOOL Wow64Process) +{ +#ifdef _X86_ + if (Wow64Process == NULL) { + return FALSE; + } + + // IsWow64Process is not available on all supported versions of Windows. + // + HMODULE hKernel32 = LoadLibraryW(L"KERNEL32.DLL"); + if (hKernel32 == NULL) { + DETOUR_TRACE(("LoadLibraryW failed: %lu\n", GetLastError())); + return FALSE; + } + + LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( + hKernel32, "IsWow64Process"); + + if (pfnIsWow64Process == NULL) { + DETOUR_TRACE(("GetProcAddress failed: %lu\n", GetLastError())); + return FALSE; + } + return pfnIsWow64Process(hProcess, Wow64Process); +#else + return IsWow64Process(hProcess, Wow64Process); +#endif +} + +////////////////////////////////////////////////////////////////////////////// +// +BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls) +{ + // Find the next memory region that contains a mapped PE image. + // + BOOL bIs32BitProcess; + BOOL bIs64BitOS = FALSE; + HMODULE hModule = NULL; + HMODULE hLast = NULL; + + DETOUR_TRACE(("DetourUpdateProcessWithDll(%p,dlls=%lu)\n", hProcess, nDlls)); + + for (;;) { + IMAGE_NT_HEADERS32 inh; + + if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh, NULL)) == NULL) { + break; + } + + DETOUR_TRACE(("%p machine=%04x magic=%04x\n", + hLast, inh.FileHeader.Machine, inh.OptionalHeader.Magic)); + + if ((inh.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) { + hModule = hLast; + DETOUR_TRACE(("%p Found EXE\n", hLast)); + } + } + + if (hModule == NULL) { + SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + // Determine if the target process is 32bit or 64bit. This is a two-stop process: + // + // 1. First, determine if we're running on a 64bit operating system. + // - If we're running 64bit code (i.e. _WIN64 is defined), this is trivially true. + // - If we're running 32bit code (i.e. _WIN64 is not defined), test if + // we're running under Wow64. If so, it implies that the operating system + // is 64bit. + // +#ifdef _WIN64 + bIs64BitOS = TRUE; +#else + if (!IsWow64ProcessHelper(GetCurrentProcess(), &bIs64BitOS)) { + return FALSE; + } +#endif + + // 2. With the operating system bitness known, we can now consider the target process: + // - If we're running on a 64bit OS, the target process is 32bit in case + // it is running under Wow64. Otherwise, it's 64bit, running natively + // (without Wow64). + // - If we're running on a 32bit OS, the target process must be 32bit, too. + // + if (bIs64BitOS) { + if (!IsWow64ProcessHelper(hProcess, &bIs32BitProcess)) { + return FALSE; + } + } else { + bIs32BitProcess = TRUE; + } + + DETOUR_TRACE((" 32BitProcess=%d\n", bIs32BitProcess)); + + return DetourUpdateProcessWithDllEx(hProcess, + hModule, + bIs32BitProcess, + rlpDlls, + nDlls); +} + +BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess, + _In_ HMODULE hModule, + _In_ BOOL bIs32BitProcess, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls) +{ + // Find the next memory region that contains a mapped PE image. + // + BOOL bIs32BitExe = FALSE; + + DETOUR_TRACE(("DetourUpdateProcessWithDllEx(%p,%p,dlls=%lu)\n", hProcess, hModule, nDlls)); + + IMAGE_NT_HEADERS32 inh; + + if (hModule == NULL || !LoadNtHeaderFromProcess(hProcess, hModule, &inh)) { + SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC + && inh.FileHeader.Machine != 0) { + + bIs32BitExe = TRUE; + } + + DETOUR_TRACE((" 32BitExe=%d\n", bIs32BitExe)); + + if (hModule == NULL) { + SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + // Save the various headers for DetourRestoreAfterWith. + // + DETOUR_EXE_RESTORE der; + + if (!RecordExeRestore(hProcess, hModule, der)) { + return FALSE; + } + +#if defined(DETOURS_64BIT) + // Try to convert a neutral 32-bit managed binary to a 64-bit managed binary. + if (bIs32BitExe && !bIs32BitProcess) { + if (!der.pclr // Native binary + || (der.clr.Flags & COMIMAGE_FLAGS_ILONLY) == 0 // Or mixed-mode MSIL + || (der.clr.Flags & COMIMAGE_FLAGS_32BITREQUIRED) != 0) { // Or 32BIT Required MSIL + + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + if (!UpdateFrom32To64(hProcess, hModule, +#if defined(DETOURS_X64) + IMAGE_FILE_MACHINE_AMD64, +#elif defined(DETOURS_IA64) + IMAGE_FILE_MACHINE_IA64, +#elif defined(DETOURS_ARM64) + IMAGE_FILE_MACHINE_ARM64, +#else +#error Must define one of DETOURS_X64 or DETOURS_IA64 or DETOURS_ARM64 on 64-bit. +#endif + der)) { + return FALSE; + } + bIs32BitExe = FALSE; + } +#endif // DETOURS_64BIT + + // Now decide if we can insert the detour. + +#if defined(DETOURS_32BIT) + if (bIs32BitProcess) { + // 32-bit native or 32-bit managed process on any platform. + if (!UpdateImports32(hProcess, hModule, rlpDlls, nDlls)) { + return FALSE; + } + } + else { + // 64-bit native or 64-bit managed process. + // + // Can't detour a 64-bit process with 32-bit code. + // Note: This happens for 32-bit PE binaries containing only + // manage code that have been marked as 64-bit ready. + // + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } +#elif defined(DETOURS_64BIT) + if (bIs32BitProcess || bIs32BitExe) { + // Can't detour a 32-bit process with 64-bit code. + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + else { + // 64-bit native or 64-bit managed process on any platform. + if (!UpdateImports64(hProcess, hModule, rlpDlls, nDlls)) { + return FALSE; + } + } +#else +#pragma Must define one of DETOURS_32BIT or DETOURS_64BIT. +#endif // DETOURS_64BIT + + /////////////////////////////////////////////////// Update the CLR header. + // + if (der.pclr != NULL) { + DETOUR_CLR_HEADER clr; + CopyMemory(&clr, &der.clr, sizeof(clr)); + clr.Flags &= ~COMIMAGE_FLAGS_ILONLY; // Clear the IL_ONLY flag. + + DWORD dwProtect; + if (!DetourVirtualProtectSameExecuteEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) { + DETOUR_TRACE(("VirtualProtectEx(clr) write failed: %lu\n", GetLastError())); + return FALSE; + } + + if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(clr) failed: %lu\n", GetLastError())); + return FALSE; + } + + if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) { + DETOUR_TRACE(("VirtualProtectEx(clr) restore failed: %lu\n", GetLastError())); + return FALSE; + } + DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); + +#if DETOURS_64BIT + if (der.clr.Flags & COMIMAGE_FLAGS_32BITREQUIRED) { // Is the 32BIT Required Flag set? + // X64 never gets here because the process appears as a WOW64 process. + // However, on IA64, it doesn't appear to be a WOW process. + DETOUR_TRACE(("CLR Requires 32-bit\n")); + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } +#endif // DETOURS_64BIT + } + + //////////////////////////////// Save the undo data to the target process. + // + if (!DetourCopyPayloadToProcess(hProcess, DETOUR_EXE_RESTORE_GUID, &der, sizeof(der))) { + DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError())); + return FALSE; + } + return TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// +BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); + PROCESS_INFORMATION pi; + BOOL fResult = FALSE; + + if (pfCreateProcessA == NULL) { + pfCreateProcessA = CreateProcessA; + } + + fResult = pfCreateProcessA(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwMyCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &pi); + + if (lpProcessInformation != NULL) { + CopyMemory(lpProcessInformation, &pi, sizeof(pi)); + } + + if (!fResult) { + return FALSE; + } + + LPCSTR rlpDlls[2]; + DWORD nDlls = 0; + if (lpDllName != NULL) { + rlpDlls[nDlls++] = lpDllName; + } + + if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) { + TerminateProcess(pi.hProcess, ~0u); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(pi.hThread); + } + return TRUE; +} + + +BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); + PROCESS_INFORMATION pi; + + if (pfCreateProcessW == NULL) { + pfCreateProcessW = CreateProcessW; + } + + BOOL fResult = pfCreateProcessW(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwMyCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &pi); + + if (lpProcessInformation) { + CopyMemory(lpProcessInformation, &pi, sizeof(pi)); + } + + if (!fResult) { + return FALSE; + } + + LPCSTR rlpDlls[2]; + DWORD nDlls = 0; + if (lpDllName != NULL) { + rlpDlls[nDlls++] = lpDllName; + } + + if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) { + TerminateProcess(pi.hProcess, ~0u); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(pi.hThread); + } + return TRUE; +} + +BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess, + _In_ REFGUID rguid, + _In_reads_bytes_(cbData) LPCVOID pvData, + _In_ DWORD cbData) +{ + return DetourCopyPayloadToProcessEx(hProcess, rguid, pvData, cbData) != NULL; +} + +_Success_(return != NULL) +PVOID WINAPI DetourCopyPayloadToProcessEx(_In_ HANDLE hProcess, + _In_ REFGUID rguid, + _In_reads_bytes_(cbData) LPCVOID pvData, + _In_ DWORD cbData) +{ + if (hProcess == NULL) { + SetLastError(ERROR_INVALID_HANDLE); + return NULL; + } + + DWORD cbTotal = (sizeof(IMAGE_DOS_HEADER) + + sizeof(IMAGE_NT_HEADERS) + + sizeof(IMAGE_SECTION_HEADER) + + sizeof(DETOUR_SECTION_HEADER) + + sizeof(DETOUR_SECTION_RECORD) + + cbData); + + PBYTE pbBase = (PBYTE)VirtualAllocEx(hProcess, NULL, cbTotal, + MEM_COMMIT, PAGE_READWRITE); + if (pbBase == NULL) { + DETOUR_TRACE(("VirtualAllocEx(%lu) failed: %lu\n", cbTotal, GetLastError())); + return NULL; + } + + // As you can see in the following code, + // the memory layout of the payload range "[pbBase, pbBase+cbTotal]" is a PE executable file, + // so DetourFreePayload can use "DetourGetContainingModule(Payload pointer)" to get the above "pbBase" pointer, + // pbBase: the memory block allocated by VirtualAllocEx will be released in DetourFreePayload by VirtualFree. + + PBYTE pbTarget = pbBase; + IMAGE_DOS_HEADER idh; + IMAGE_NT_HEADERS inh; + IMAGE_SECTION_HEADER ish; + DETOUR_SECTION_HEADER dsh; + DETOUR_SECTION_RECORD dsr; + SIZE_T cbWrote = 0; + + ZeroMemory(&idh, sizeof(idh)); + idh.e_magic = IMAGE_DOS_SIGNATURE; + idh.e_lfanew = sizeof(idh); + if (!WriteProcessMemory(hProcess, pbTarget, &idh, sizeof(idh), &cbWrote) || + cbWrote != sizeof(idh)) { + DETOUR_TRACE(("WriteProcessMemory(idh) failed: %lu\n", GetLastError())); + return NULL; + } + pbTarget += sizeof(idh); + + ZeroMemory(&inh, sizeof(inh)); + inh.Signature = IMAGE_NT_SIGNATURE; + inh.FileHeader.SizeOfOptionalHeader = sizeof(inh.OptionalHeader); + inh.FileHeader.Characteristics = IMAGE_FILE_DLL; + inh.FileHeader.NumberOfSections = 1; + inh.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC; + if (!WriteProcessMemory(hProcess, pbTarget, &inh, sizeof(inh), &cbWrote) || + cbWrote != sizeof(inh)) { + return NULL; + } + pbTarget += sizeof(inh); + + ZeroMemory(&ish, sizeof(ish)); + memcpy(ish.Name, ".detour", sizeof(ish.Name)); + ish.VirtualAddress = (DWORD)((pbTarget + sizeof(ish)) - pbBase); + ish.SizeOfRawData = (sizeof(DETOUR_SECTION_HEADER) + + sizeof(DETOUR_SECTION_RECORD) + + cbData); + if (!WriteProcessMemory(hProcess, pbTarget, &ish, sizeof(ish), &cbWrote) || + cbWrote != sizeof(ish)) { + return NULL; + } + pbTarget += sizeof(ish); + + ZeroMemory(&dsh, sizeof(dsh)); + dsh.cbHeaderSize = sizeof(dsh); + dsh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE; + dsh.nDataOffset = sizeof(DETOUR_SECTION_HEADER); + dsh.cbDataSize = (sizeof(DETOUR_SECTION_HEADER) + + sizeof(DETOUR_SECTION_RECORD) + + cbData); + if (!WriteProcessMemory(hProcess, pbTarget, &dsh, sizeof(dsh), &cbWrote) || + cbWrote != sizeof(dsh)) { + return NULL; + } + pbTarget += sizeof(dsh); + + ZeroMemory(&dsr, sizeof(dsr)); + dsr.cbBytes = cbData + sizeof(DETOUR_SECTION_RECORD); + dsr.nReserved = 0; + dsr.guid = rguid; + if (!WriteProcessMemory(hProcess, pbTarget, &dsr, sizeof(dsr), &cbWrote) || + cbWrote != sizeof(dsr)) { + return NULL; + } + pbTarget += sizeof(dsr); + + if (!WriteProcessMemory(hProcess, pbTarget, pvData, cbData, &cbWrote) || + cbWrote != cbData) { + return NULL; + } + + DETOUR_TRACE(("Copied %lu byte payload into target process at %p\n", + cbData, pbTarget)); + + SetLastError(NO_ERROR); + return pbTarget; +} + +static BOOL s_fSearchedForHelper = FALSE; +static PDETOUR_EXE_HELPER s_pHelper = NULL; + +VOID CALLBACK DetourFinishHelperProcess(_In_ HWND, + _In_ HINSTANCE, + _In_ LPSTR, + _In_ INT) +{ + LPCSTR * rlpDlls = NULL; + DWORD Result = 9900; + DWORD cOffset = 0; + DWORD cSize = 0; + HANDLE hProcess = NULL; + + if (s_pHelper == NULL) { + DETOUR_TRACE(("DetourFinishHelperProcess called with s_pHelper = NULL.\n")); + Result = 9905; + goto Cleanup; + } + + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, s_pHelper->pid); + if (hProcess == NULL) { + DETOUR_TRACE(("OpenProcess(pid=%lu) failed: %lu\n", + s_pHelper->pid, GetLastError())); + Result = 9901; + goto Cleanup; + } + + rlpDlls = new NOTHROW LPCSTR [s_pHelper->nDlls]; + cSize = s_pHelper->cb - sizeof(DETOUR_EXE_HELPER); + for (DWORD n = 0; n < s_pHelper->nDlls; n++) { + size_t cchDest = 0; + HRESULT hr = StringCchLengthA(&s_pHelper->rDlls[cOffset], cSize - cOffset, &cchDest); + if (!SUCCEEDED(hr)) { + Result = 9902; + goto Cleanup; + } + + rlpDlls[n] = &s_pHelper->rDlls[cOffset]; + cOffset += (DWORD)cchDest + 1; + } + + if (!DetourUpdateProcessWithDll(hProcess, rlpDlls, s_pHelper->nDlls)) { + DETOUR_TRACE(("DetourUpdateProcessWithDll(pid=%lu) failed: %lu\n", + s_pHelper->pid, GetLastError())); + Result = 9903; + goto Cleanup; + } + Result = 0; + + Cleanup: + if (rlpDlls != NULL) { + delete[] rlpDlls; + rlpDlls = NULL; + } + + // Note: s_pHelper is allocated as part of injecting the payload in DetourCopyPayloadToProcess(..), + // it's a fake section and not data allocated by the system PE loader. + + // Delete the payload after execution to release the memory occupied by it + if (s_pHelper != NULL) { + DetourFreePayload(s_pHelper); + s_pHelper = NULL; + } + + ExitProcess(Result); +} + +BOOL WINAPI DetourIsHelperProcess(VOID) +{ + PVOID pvData; + DWORD cbData; + + if (s_fSearchedForHelper) { + return (s_pHelper != NULL); + } + + s_fSearchedForHelper = TRUE; + pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData); + + if (pvData == NULL || cbData < sizeof(DETOUR_EXE_HELPER)) { + return FALSE; + } + + s_pHelper = (PDETOUR_EXE_HELPER)pvData; + if (s_pHelper->cb < sizeof(*s_pHelper)) { + s_pHelper = NULL; + return FALSE; + } + + return TRUE; +} + +static +BOOL WINAPI AllocExeHelper(_Out_ PDETOUR_EXE_HELPER *pHelper, + _In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls) +{ + PDETOUR_EXE_HELPER Helper = NULL; + BOOL Result = FALSE; + _Field_range_(0, cSize - 4) DWORD cOffset = 0; + DWORD cSize = 4; + + if (pHelper == NULL) { + goto Cleanup; + } + *pHelper = NULL; + + if (nDlls < 1 || nDlls > 4096) { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + + for (DWORD n = 0; n < nDlls; n++) { + HRESULT hr; + size_t cchDest = 0; + + hr = StringCchLengthA(rlpDlls[n], 4096, &cchDest); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + cSize += (DWORD)cchDest + 1; + } + + Helper = (PDETOUR_EXE_HELPER) new NOTHROW BYTE[sizeof(DETOUR_EXE_HELPER) + cSize]; + if (Helper == NULL) { + goto Cleanup; + } + + Helper->cb = sizeof(DETOUR_EXE_HELPER) + cSize; + Helper->pid = dwTargetPid; + Helper->nDlls = nDlls; + + for (DWORD n = 0; n < nDlls; n++) { + HRESULT hr; + size_t cchDest = 0; + + if (cOffset > 0x10000 || cSize > 0x10000 || cOffset + 2 >= cSize) { + goto Cleanup; + } + + if (cOffset + 2 >= cSize || cOffset + 65536 < cSize) { + goto Cleanup; + } + + _Analysis_assume_(cOffset + 1 < cSize); + _Analysis_assume_(cOffset < 0x10000); + _Analysis_assume_(cSize < 0x10000); + + PCHAR psz = &Helper->rDlls[cOffset]; + + hr = StringCchCopyA(psz, cSize - cOffset, rlpDlls[n]); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + +// REVIEW 28020 The expression '1<=_Param_(2)& &_Param_(2)<=2147483647' is not true at this call. +// REVIEW 28313 Analysis will not proceed past this point because of annotation evaluation. The annotation expression *_Param_(3)<_Param_(2)&&*_Param_(3)<=stringLength$(_Param_(1)) cannot be true under any assumptions at this point in the program. +#pragma warning(suppress:28020 28313) + hr = StringCchLengthA(psz, cSize - cOffset, &cchDest); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + // Replace "32." with "64." or "64." with "32." + + for (DWORD c = (DWORD)cchDest + 1; c > 3; c--) { +#if DETOURS_32BIT + if (psz[c - 3] == '3' && psz[c - 2] == '2' && psz[c - 1] == '.') { + psz[c - 3] = '6'; psz[c - 2] = '4'; + break; + } +#else + if (psz[c - 3] == '6' && psz[c - 2] == '4' && psz[c - 1] == '.') { + psz[c - 3] = '3'; psz[c - 2] = '2'; + break; + } +#endif + } + + cOffset += (DWORD)cchDest + 1; + } + + *pHelper = Helper; + Helper = NULL; + Result = TRUE; + + Cleanup: + if (Helper != NULL) { + delete[] (PBYTE)Helper; + Helper = NULL; + } + return Result; +} + +static +VOID WINAPI FreeExeHelper(PDETOUR_EXE_HELPER *pHelper) +{ + if (*pHelper != NULL) { + delete[] (PBYTE)*pHelper; + *pHelper = NULL; + } +} + +BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + return DetourProcessViaHelperDllsA(dwTargetPid, 1, &lpDllName, pfCreateProcessA); +} + + +BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + BOOL Result = FALSE; + PROCESS_INFORMATION pi; + STARTUPINFOA si; + CHAR szExe[MAX_PATH]; + CHAR szCommand[MAX_PATH]; + PDETOUR_EXE_HELPER helper = NULL; + HRESULT hr; + DWORD nLen = GetEnvironmentVariableA("WINDIR", szExe, ARRAYSIZE(szExe)); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls)); + if (nDlls < 1 || nDlls > 4096) { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) { + goto Cleanup; + } + + if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { + goto Cleanup; + } + +#if DETOURS_OPTION_BITS +#if DETOURS_32BIT + hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\sysnative\\rundll32.exe"); +#else // !DETOURS_32BIT + hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\syswow64\\rundll32.exe"); +#endif // !DETOURS_32BIT +#else // DETOURS_OPTIONS_BITS + hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\system32\\rundll32.exe"); +#endif // DETOURS_OPTIONS_BITS + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + //for East Asia languages and so on, like Chinese, print format with "%hs" can not work fine before user call _tsetlocale(LC_ALL,_T(".ACP")); + //so we can't use "%hs" in format string, because the dll that contain this code would inject to any process, even not call _tsetlocale(LC_ALL,_T(".ACP")) before + hr = StringCchPrintfA(szCommand, ARRAYSIZE(szCommand), + "rundll32.exe \"%s\",#1", &helper->rDlls[0]); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%hs\", \"%hs\")\n", szExe, szCommand)); + if (pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, + NULL, NULL, &si, &pi)) { + + if (!DetourCopyPayloadToProcess(pi.hProcess, + DETOUR_EXE_HELPER_GUID, + helper, helper->cb)) { + DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError())); + TerminateProcess(pi.hProcess, ~0u); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + goto Cleanup; + } + + ResumeThread(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD dwResult = 500; + GetExitCodeProcess(pi.hProcess, &dwResult); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (dwResult != 0) { + DETOUR_TRACE(("Rundll32.exe failed: result=%lu\n", dwResult)); + goto Cleanup; + } + Result = TRUE; + } + else { + DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError())); + goto Cleanup; + } + + Cleanup: + FreeExeHelper(&helper); + return Result; +} + +BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + return DetourProcessViaHelperDllsW(dwTargetPid, 1, &lpDllName, pfCreateProcessW); +} + +BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + BOOL Result = FALSE; + PROCESS_INFORMATION pi; + STARTUPINFOW si; + WCHAR szExe[MAX_PATH]; + WCHAR szCommand[MAX_PATH]; + PDETOUR_EXE_HELPER helper = NULL; + HRESULT hr; + WCHAR szDllName[MAX_PATH]; + int cchWrittenWideChar; + DWORD nLen = GetEnvironmentVariableW(L"WINDIR", szExe, ARRAYSIZE(szExe)); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls)); + if (nDlls < 1 || nDlls > 4096) { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) { + goto Cleanup; + } + + if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { + goto Cleanup; + } + +#if DETOURS_OPTION_BITS +#if DETOURS_32BIT + hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\sysnative\\rundll32.exe"); +#else // !DETOURS_32BIT + hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\syswow64\\rundll32.exe"); +#endif // !DETOURS_32BIT +#else // DETOURS_OPTIONS_BITS + hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\system32\\rundll32.exe"); +#endif // DETOURS_OPTIONS_BITS + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + //for East Asia languages and so on, like Chinese, print format with "%hs" can not work fine before user call _tsetlocale(LC_ALL,_T(".ACP")); + //so we can't use "%hs" in format string, because the dll that contain this code would inject to any process, even not call _tsetlocale(LC_ALL,_T(".ACP")) before + + cchWrittenWideChar = MultiByteToWideChar(CP_ACP, 0, &helper->rDlls[0], -1, szDllName, ARRAYSIZE(szDllName)); + if (cchWrittenWideChar >= ARRAYSIZE(szDllName) || cchWrittenWideChar <= 0) { + goto Cleanup; + } + hr = StringCchPrintfW(szCommand, ARRAYSIZE(szCommand), + L"rundll32.exe \"%s\",#1", szDllName); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%ls\", \"%ls\")\n", szExe, szCommand)); + if (pfCreateProcessW(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, + NULL, NULL, &si, &pi)) { + + if (!DetourCopyPayloadToProcess(pi.hProcess, + DETOUR_EXE_HELPER_GUID, + helper, helper->cb)) { + DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError())); + TerminateProcess(pi.hProcess, ~0u); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + goto Cleanup; + } + + ResumeThread(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD dwResult = 500; + GetExitCodeProcess(pi.hProcess, &dwResult); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (dwResult != 0) { + DETOUR_TRACE(("Rundll32.exe failed: result=%lu\n", dwResult)); + goto Cleanup; + } + Result = TRUE; + } + else { + DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError())); + goto Cleanup; + } + + Cleanup: + FreeExeHelper(&helper); + return Result; +} + +BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + if (pfCreateProcessA == NULL) { + pfCreateProcessA = CreateProcessA; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessA(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + LPCSTR szDll = lpDllName; + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &szDll, 1) && + !DetourProcessViaHelperA(lpProcessInformation->dwProcessId, + lpDllName, + pfCreateProcessA)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + + return TRUE; +} + +BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + if (pfCreateProcessW == NULL) { + pfCreateProcessW = CreateProcessW; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessW(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + + LPCSTR sz = lpDllName; + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &sz, 1) && + !DetourProcessViaHelperW(lpProcessInformation->dwProcessId, + lpDllName, + pfCreateProcessW)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + return TRUE; +} + +BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + if (pfCreateProcessA == NULL) { + pfCreateProcessA = CreateProcessA; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessA(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) && + !DetourProcessViaHelperDllsA(lpProcessInformation->dwProcessId, + nDlls, + rlpDlls, + pfCreateProcessA)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + + return TRUE; +} + +BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + if (pfCreateProcessW == NULL) { + pfCreateProcessW = CreateProcessW; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessW(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) && + !DetourProcessViaHelperDllsW(lpProcessInformation->dwProcessId, + nDlls, + rlpDlls, + pfCreateProcessW)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + return TRUE; +} + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/nt_file_dupe/libs/Detours/detours.cpp b/nt_file_dupe/libs/Detours/detours.cpp new file mode 100644 index 0000000..c1138dc --- /dev/null +++ b/nt_file_dupe/libs/Detours/detours.cpp @@ -0,0 +1,2604 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Core Detours Functionality (detours.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + + +//#define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL +#include "detours.h" + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +#define NOTHROW + +////////////////////////////////////////////////////////////////////////////// +// + +#ifdef _DEBUG +extern "C" IMAGE_DOS_HEADER __ImageBase; +int Detour_AssertExprWithFunctionName(int reportType, const char* filename, int linenumber, const char* FunctionName, const char* msg) +{ + int nRet = 0; + DWORD dwLastError = GetLastError(); + CHAR szModuleNameWithFunctionName[MAX_PATH * 2]; + szModuleNameWithFunctionName[0] = 0; + GetModuleFileNameA((HMODULE)&__ImageBase, szModuleNameWithFunctionName, ARRAYSIZE(szModuleNameWithFunctionName)); + StringCchCatNA(szModuleNameWithFunctionName, ARRAYSIZE(szModuleNameWithFunctionName), ",", ARRAYSIZE(szModuleNameWithFunctionName) - strlen(szModuleNameWithFunctionName) - 1); + StringCchCatNA(szModuleNameWithFunctionName, ARRAYSIZE(szModuleNameWithFunctionName), FunctionName, ARRAYSIZE(szModuleNameWithFunctionName) - strlen(szModuleNameWithFunctionName) - 1); + SetLastError(dwLastError); + nRet = _CrtDbgReport(reportType, filename, linenumber, szModuleNameWithFunctionName, msg); + SetLastError(dwLastError); + return nRet; +} +#endif// _DEBUG + +////////////////////////////////////////////////////////////////////////////// +// +struct _DETOUR_ALIGN +{ + BYTE obTarget : 3; + BYTE obTrampoline : 5; +}; + +C_ASSERT(sizeof(_DETOUR_ALIGN) == 1); + +////////////////////////////////////////////////////////////////////////////// +// +// Region reserved for system DLLs, which cannot be used for trampolines. +// +static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000; +static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000; + +////////////////////////////////////////////////////////////////////////////// +// +static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress) +{ + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi)); + __try { + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + return false; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + return false; + } + + if (pbAddress >= ((PBYTE)pDosHeader + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) && + pbAddress < ((PBYTE)pDosHeader + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) { + return true; + } + } +#pragma prefast(suppress:28940, "A bad pointer means this probably isn't a PE header.") + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + return false; + } + return false; +} + +inline ULONG_PTR detour_2gb_below(ULONG_PTR address) +{ + return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000; +} + +inline ULONG_PTR detour_2gb_above(ULONG_PTR address) +{ +#if defined(DETOURS_64BIT) + return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000; +#else + return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000; +#endif +} + +///////////////////////////////////////////////////////////////////////// X86. +// +#ifdef DETOURS_X86 + +struct _DETOUR_TRAMPOLINE +{ + BYTE rbCode[30]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[22]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 72); + +enum { + SIZE_OF_JMP = 5 +}; + +inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 5; + *pbCode++ = 0xE9; // jmp +imm32 + *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal) +{ + *pbCode++ = 0xff; // jmp [+imm32] + *pbCode++ = 0x25; + *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal); + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + *pbCode++ = 0xcc; // brk; + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + + // Then, skip over a patch jump + if (pbCode[0] == 0xeb) { // jmp +imm8 + PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew)); + pbCode = pbNew; + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + // Finally, skip over a long jump if it is the target of the patch jump. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi)); + + // And, within +/- 2GB of relative jmp targets. + if (pbCode[0] == 0xe9) { // jmp +imm32 + PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] +imm32\n", (PVOID)lo, pbCode, (PVOID)hi)); + } + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + if (pbCode[0] == 0xeb || // jmp +imm8 + pbCode[0] == 0xe9 || // jmp +imm32 + pbCode[0] == 0xe0 || // jmp eax + pbCode[0] == 0xc2 || // ret +imm8 + pbCode[0] == 0xc3 || // ret + pbCode[0] == 0xcc) { // brk + return TRUE; + } + else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret + return TRUE; + } + else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + return TRUE; + } + else if ((pbCode[0] == 0x26 || // jmp es: + pbCode[0] == 0x2e || // jmp cs: + pbCode[0] == 0x36 || // jmp ss: + pbCode[0] == 0x3e || // jmp ds: + pbCode[0] == 0x64 || // jmp fs: + pbCode[0] == 0x65) && // jmp gs: + pbCode[1] == 0xff && // jmp [+imm32] + pbCode[2] == 0x25) { + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // 1-byte through 11-byte NOPs. + if (pbCode[0] == 0x90) { + return 1; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x90) { + return 2; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) { + return 3; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 && + pbCode[3] == 0x00) { + return 4; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 && + pbCode[3] == 0x00 && pbCode[4] == 0x00) { + return 5; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) { + return 6; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00) { + return 7; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00) { + return 8; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) { + return 9; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F && + pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00) { + return 10; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 && + pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00 && pbCode[10] == 0x00) { + return 11; + } + + // int 3. + if (pbCode[0] == 0xcc) { + return 1; + } + return 0; +} + +#endif // DETOURS_X86 + +///////////////////////////////////////////////////////////////////////// X64. +// +#ifdef DETOURS_X64 + +struct _DETOUR_TRAMPOLINE +{ + // An X64 instuction can be 15 bytes long. + // In practice 11 seems to be the limit. + BYTE rbCode[30]; // target code + jmp to pbRemain. + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[30]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + BYTE rbCodeIn[8]; // jmp [pbDetour] +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 96); + +enum { + SIZE_OF_JMP = 5 +}; + +inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 5; + *pbCode++ = 0xE9; // jmp +imm32 + *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 6; + *pbCode++ = 0xff; // jmp [+imm32] + *pbCode++ = 0x25; + *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + *pbCode++ = 0xcc; // brk; + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + + // Then, skip over a patch jump + if (pbCode[0] == 0xeb) { // jmp +imm8 + PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew)); + pbCode = pbNew; + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + // Finally, skip over a long jump if it is the target of the patch jump. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi)); + + // And, within +/- 2GB of relative jmp vectors. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + PBYTE pbNew = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] [+imm32]\n", (PVOID)lo, pbCode, (PVOID)hi)); + } + // And, within +/- 2GB of relative jmp targets. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] +imm32\n", (PVOID)lo, pbCode, (PVOID)hi)); + } + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + if (pbCode[0] == 0xeb || // jmp +imm8 + pbCode[0] == 0xe9 || // jmp +imm32 + pbCode[0] == 0xe0 || // jmp eax + pbCode[0] == 0xc2 || // ret +imm8 + pbCode[0] == 0xc3 || // ret + pbCode[0] == 0xcc) { // brk + return TRUE; + } + else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret + return TRUE; + } + else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + return TRUE; + } + else if ((pbCode[0] == 0x26 || // jmp es: + pbCode[0] == 0x2e || // jmp cs: + pbCode[0] == 0x36 || // jmp ss: + pbCode[0] == 0x3e || // jmp ds: + pbCode[0] == 0x64 || // jmp fs: + pbCode[0] == 0x65) && // jmp gs: + pbCode[1] == 0xff && // jmp [+imm32] + pbCode[2] == 0x25) { + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // 1-byte through 11-byte NOPs. + if (pbCode[0] == 0x90) { + return 1; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x90) { + return 2; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) { + return 3; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 && + pbCode[3] == 0x00) { + return 4; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 && + pbCode[3] == 0x00 && pbCode[4] == 0x00) { + return 5; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) { + return 6; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00) { + return 7; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00) { + return 8; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) { + return 9; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F && + pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00) { + return 10; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 && + pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00 && pbCode[10] == 0x00) { + return 11; + } + + // int 3. + if (pbCode[0] == 0xcc) { + return 1; + } + return 0; +} + +#endif // DETOURS_X64 + +//////////////////////////////////////////////////////////////////////// IA64. +// +#ifdef DETOURS_IA64 + +struct _DETOUR_TRAMPOLINE +{ + // On the IA64, a trampoline is used for both incoming and outgoing calls. + // + // The trampoline contains the following bundles for the outgoing call: + // movl gp=target_gp; + // + // brl target_code; + // + // The trampoline contains the following bundles for the incoming call: + // alloc r41=ar.pfs, b, 0, 8, 0 + // mov r40=rp + // + // adds r50=0, r39 + // adds r49=0, r38 + // adds r48=0, r37 ;; + // + // adds r47=0, r36 + // adds r46=0, r35 + // adds r45=0, r34 + // + // adds r44=0, r33 + // adds r43=0, r32 + // adds r42=0, gp ;; + // + // movl gp=ffffffff`ffffffff ;; + // + // brl.call.sptk.few rp=disas!TestCodes+20e0 (00000000`00404ea0) ;; + // + // adds gp=0, r42 + // mov rp=r40, +0 ;; + // mov.i ar.pfs=r41 + // + // br.ret.sptk.many rp ;; + // + // This way, we only have to relocate a single bundle. + // + // The complicated incoming trampoline is required because we have to + // create an additional stack frame so that we save and restore the gp. + // We must do this because gp is a caller-saved register, but not saved + // if the caller thinks the target is in the same DLL, which changes + // when we insert a detour. + // + DETOUR_IA64_BUNDLE bMovlTargetGp; // Bundle which sets target GP + BYTE rbCode[sizeof(DETOUR_IA64_BUNDLE)]; // moved bundle. + DETOUR_IA64_BUNDLE bBrlRemainEip; // Brl to pbRemain + // This must be adjacent to bBranchIslands. + + // Each instruction in the moved bundle could be a IP-relative chk or branch or call. + // Any such instructions are changed to point to a brl in bBranchIslands. + // This must be adjacent to bBrlRemainEip -- see "pbPool". + DETOUR_IA64_BUNDLE bBranchIslands[DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE]; + + // Target of brl inserted in target function + DETOUR_IA64_BUNDLE bAllocFrame; // alloc frame + DETOUR_IA64_BUNDLE bSave37to39; // save r37, r38, r39. + DETOUR_IA64_BUNDLE bSave34to36; // save r34, r35, r36. + DETOUR_IA64_BUNDLE bSaveGPto33; // save gp, r32, r33. + DETOUR_IA64_BUNDLE bMovlDetourGp; // set detour GP. + DETOUR_IA64_BUNDLE bCallDetour; // call detour. + DETOUR_IA64_BUNDLE bPopFrameGp; // pop frame and restore gp. + DETOUR_IA64_BUNDLE bReturn; // return to caller. + + PLABEL_DESCRIPTOR pldTrampoline; + + BYTE rbRestore[sizeof(DETOUR_IA64_BUNDLE)]; // original target bundle. + BYTE cbRestore; // size of original target code. + BYTE cbCode; // size of moved target code. + _DETOUR_ALIGN rAlign[14]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + PPLABEL_DESCRIPTOR ppldDetour; // [pbDetour,gpDetour] + PPLABEL_DESCRIPTOR ppldTarget; // [pbTarget,gpDetour] +}; + +C_ASSERT(sizeof(DETOUR_IA64_BUNDLE) == 16); +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 256 + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * 16); + +enum { + SIZE_OF_JMP = sizeof(DETOUR_IA64_BUNDLE) +}; + +inline PBYTE detour_skip_jmp(PBYTE pPointer, PVOID *ppGlobals) +{ + PBYTE pGlobals = NULL; + PBYTE pbCode = NULL; + + if (pPointer != NULL) { + PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)pPointer; + pbCode = (PBYTE)ppld->EntryPoint; + pGlobals = (PBYTE)ppld->GlobalPointer; + } + if (ppGlobals != NULL) { + *ppGlobals = pGlobals; + } + if (pbCode == NULL) { + return NULL; + } + + DETOUR_IA64_BUNDLE *pb = (DETOUR_IA64_BUNDLE *)pbCode; + + // IA64 Local Import Jumps look like: + // addl r2=ffffffff`ffe021c0, gp ;; + // ld8 r2=[r2] + // nop.i 0 ;; + // + // ld8 r3=[r2], 8 ;; + // ld8 gp=[r2] + // mov b6=r3, +0 + // + // nop.m 0 + // nop.i 0 + // br.cond.sptk.few b6 + // + + // 002024000200100b + if ((pb[0].wide[0] & 0xfffffc000603ffff) == 0x002024000200100b && + pb[0].wide[1] == 0x0004000000203008 && + pb[1].wide[0] == 0x001014180420180a && + pb[1].wide[1] == 0x07000830c0203008 && + pb[2].wide[0] == 0x0000000100000010 && + pb[2].wide[1] == 0x0080006000000200) { + + ULONG64 offset = + ((pb[0].wide[0] & 0x0000000001fc0000) >> 18) | // imm7b + ((pb[0].wide[0] & 0x000001ff00000000) >> 25) | // imm9d + ((pb[0].wide[0] & 0x00000000f8000000) >> 11); // imm5c + if (pb[0].wide[0] & 0x0000020000000000) { // sign + offset |= 0xffffffffffe00000; + } + PBYTE pbTarget = pGlobals + offset; + DETOUR_TRACE(("%p: potential import jump, target=%p\n", pb, pbTarget)); + + if (detour_is_imported(pbCode, pbTarget) && *(PBYTE*)pbTarget != NULL) { + DETOUR_TRACE(("%p: is import jump, label=%p\n", pb, *(PBYTE *)pbTarget)); + + PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)*(PBYTE *)pbTarget; + pbCode = (PBYTE)ppld->EntryPoint; + pGlobals = (PBYTE)ppld->GlobalPointer; + if (ppGlobals != NULL) { + *ppGlobals = pGlobals; + } + } + } + return pbCode; +} + + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + (void)pbCode; + *ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000; + *ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + // Routine not needed on IA64. + (void)pbCode; + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // Routine not needed on IA64. + (void)pbCode; + return 0; +} + +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM + +struct _DETOUR_TRAMPOLINE +{ + // A Thumb-2 instruction can be 2 or 4 bytes long. + BYTE rbCode[62]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[22]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104); + +enum { + SIZE_OF_JMP = 8 +}; + +inline PBYTE align4(PBYTE pValue) +{ + return (PBYTE)(((ULONG)pValue) & ~(ULONG)3u); +} + +inline ULONG fetch_thumb_opcode(PBYTE pbCode) +{ + ULONG Opcode = *(UINT16 *)&pbCode[0]; + if (Opcode >= 0xe800) { + Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2]; + } + return Opcode; +} + +inline void write_thumb_opcode(PBYTE &pbCode, ULONG Opcode) +{ + if (Opcode >= 0x10000) { + *((UINT16*&)pbCode)++ = Opcode >> 16; + } + *((UINT16*&)pbCode)++ = (UINT16)Opcode; +} + +PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal) +{ + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 4; + pbLiteral = *ppPool; + } + else { + pbLiteral = align4(pbCode + 6); + } + + *((PBYTE*&)pbLiteral) = DETOURS_PBYTE_TO_PFUNC(pbJmpVal); + LONG delta = pbLiteral - align4(pbCode + 4); + + write_thumb_opcode(pbCode, 0xf8dff000 | delta); // LDR PC,[PC+n] + + if (ppPool == NULL) { + if (((ULONG)pbCode & 2) != 0) { + write_thumb_opcode(pbCode, 0xdefe); // BREAK + } + pbCode += 4; + } + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + write_thumb_opcode(pbCode, 0xdefe); + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // Skip over the import jump if there is one. + pbCode = (PBYTE)DETOURS_PFUNC_TO_PBYTE(pbCode); + ULONG Opcode = fetch_thumb_opcode(pbCode); + + if ((Opcode & 0xfbf08f00) == 0xf2400c00) { // movw r12,#xxxx + ULONG Opcode2 = fetch_thumb_opcode(pbCode+4); + + if ((Opcode2 & 0xfbf08f00) == 0xf2c00c00) { // movt r12,#xxxx + ULONG Opcode3 = fetch_thumb_opcode(pbCode+8); + if (Opcode3 == 0xf8dcf000) { // ldr pc,[r12] + PBYTE pbTarget = (PBYTE)(((Opcode2 << 12) & 0xf7000000) | + ((Opcode2 << 1) & 0x08000000) | + ((Opcode2 << 16) & 0x00ff0000) | + ((Opcode >> 4) & 0x0000f700) | + ((Opcode >> 15) & 0x00000800) | + ((Opcode >> 0) & 0x000000ff)); + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(PBYTE *)pbTarget; + pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew); + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + return pbNew; + } + } + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi)); + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + ULONG Opcode = fetch_thumb_opcode(pbCode); + if ((Opcode & 0xffffff87) == 0x4700 || // bx + (Opcode & 0xf800d000) == 0xf0009000) { // b + return TRUE; + } + if ((Opcode & 0xffff8000) == 0xe8bd8000) { // pop {...,pc} + __debugbreak(); + return TRUE; + } + if ((Opcode & 0xffffff00) == 0x0000bd00) { // pop {...,pc} + __debugbreak(); + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + if (pbCode[0] == 0x00 && pbCode[1] == 0xbf) { // nop. + return 2; + } + if (pbCode[0] == 0x00 && pbCode[1] == 0x00) { // zero-filled padding. + return 2; + } + return 0; +} + +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + +struct _DETOUR_TRAMPOLINE +{ + // An ARM64 instruction is 4 bytes long. + // + // The overwrite is always composed of 3 instructions (12 bytes) which perform an indirect jump + // using _DETOUR_TRAMPOLINE::pbDetour as the address holding the target location. + // + // Copied instructions can expand. + // + // The scheme using MovImmediate can cause an instruction + // to grow as much as 6 times. + // That would be Bcc or Tbz with a large address space: + // 4 instructions to form immediate + // inverted tbz/bcc + // br + // + // An expansion of 4 is not uncommon -- bl/blr and small address space: + // 3 instructions to form immediate + // br or brl + // + // A theoretical maximum for rbCode is thefore 4*4*6 + 16 = 112 (another 16 for jmp to pbRemain). + // + // With literals, the maximum expansion is 5, including the literals: 4*4*5 + 16 = 96. + // + // The number is rounded up to 128. m_rbScratchDst should match this. + // + BYTE rbCode[128]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak[3]; // padding to make debugging easier. + BYTE rbRestore[24]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak[3]; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 184); + +enum { + SIZE_OF_JMP = 12 +}; + +inline ULONG fetch_opcode(PBYTE pbCode) +{ + return *(ULONG *)pbCode; +} + +inline void write_opcode(PBYTE &pbCode, ULONG Opcode) +{ + *(ULONG *)pbCode = Opcode; + pbCode += 4; +} + +struct ARM64_INDIRECT_JMP { + struct { + ULONG Rd : 5; + ULONG immhi : 19; + ULONG iop : 5; + ULONG immlo : 2; + ULONG op : 1; + } ardp; + + struct { + ULONG Rt : 5; + ULONG Rn : 5; + ULONG imm : 12; + ULONG opc : 2; + ULONG iop1 : 2; + ULONG V : 1; + ULONG iop2 : 3; + ULONG size : 2; + } ldr; + + ULONG br; +}; + +#pragma warning(push) +#pragma warning(disable:4201) + +union ARM64_INDIRECT_IMM { + struct { + ULONG64 pad : 12; + ULONG64 adrp_immlo : 2; + ULONG64 adrp_immhi : 19; + }; + + LONG64 value; +}; + +#pragma warning(pop) + +PBYTE detour_gen_jmp_indirect(BYTE *pbCode, ULONG64 *pbJmpVal) +{ + // adrp x17, [jmpval] + // ldr x17, [x17, jmpval] + // br x17 + + struct ARM64_INDIRECT_JMP *pIndJmp; + union ARM64_INDIRECT_IMM jmpIndAddr; + + jmpIndAddr.value = (((LONG64)pbJmpVal) & 0xFFFFFFFFFFFFF000) - + (((LONG64)pbCode) & 0xFFFFFFFFFFFFF000); + + pIndJmp = (struct ARM64_INDIRECT_JMP *)pbCode; + pbCode = (BYTE *)(pIndJmp + 1); + + pIndJmp->ardp.Rd = 17; + pIndJmp->ardp.immhi = jmpIndAddr.adrp_immhi; + pIndJmp->ardp.iop = 0x10; + pIndJmp->ardp.immlo = jmpIndAddr.adrp_immlo; + pIndJmp->ardp.op = 1; + + pIndJmp->ldr.Rt = 17; + pIndJmp->ldr.Rn = 17; + pIndJmp->ldr.imm = (((ULONG64)pbJmpVal) & 0xFFF) / 8; + pIndJmp->ldr.opc = 1; + pIndJmp->ldr.iop1 = 1; + pIndJmp->ldr.V = 0; + pIndJmp->ldr.iop2 = 7; + pIndJmp->ldr.size = 3; + + pIndJmp->br = 0xD61F0220; + + return pbCode; +} + +PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal) +{ + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 8; + pbLiteral = *ppPool; + } + else { + pbLiteral = pbCode + 8; + } + + *((PBYTE*&)pbLiteral) = pbJmpVal; + LONG delta = (LONG)(pbLiteral - pbCode); + + write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n] + write_opcode(pbCode, 0xd61f0000 | (17 << 5)); // BR X17 + + if (ppPool == NULL) { + pbCode += 8; + } + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + write_opcode(pbCode, 0xd4100000 | (0xf000 << 5)); + } + return pbCode; +} + +inline INT64 detour_sign_extend(UINT64 value, UINT bits) +{ + const UINT left = 64 - bits; + const INT64 m1 = -1; + const INT64 wide = (INT64)(value << left); + const INT64 sign = (wide < 0) ? (m1 << left) : 0; + return value | sign; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // Skip over the import jump if there is one. + pbCode = (PBYTE)pbCode; + ULONG Opcode = fetch_opcode(pbCode); + + if ((Opcode & 0x9f00001f) == 0x90000010) { // adrp x16, IAT + ULONG Opcode2 = fetch_opcode(pbCode + 4); + + if ((Opcode2 & 0xffe003ff) == 0xf9400210) { // ldr x16, [x16, IAT] + ULONG Opcode3 = fetch_opcode(pbCode + 8); + + if (Opcode3 == 0xd61f0200) { // br x16 + +/* https://static.docs.arm.com/ddi0487/bb/DDI0487B_b_armv8_arm.pdf + The ADRP instruction shifts a signed, 21-bit immediate left by 12 bits, adds it to the value of the program counter with + the bottom 12 bits cleared to zero, and then writes the result to a general-purpose register. This permits the + calculation of the address at a 4KB aligned memory region. In conjunction with an ADD (immediate) instruction, or + a Load/Store instruction with a 12-bit immediate offset, this allows for the calculation of, or access to, any address + within +/- 4GB of the current PC. + +PC-rel. addressing + This section describes the encoding of the PC-rel. addressing instruction class. The encodings in this section are + decoded from Data Processing -- Immediate on page C4-226. + Add/subtract (immediate) + This section describes the encoding of the Add/subtract (immediate) instruction class. The encodings in this section + are decoded from Data Processing -- Immediate on page C4-226. + Decode fields + Instruction page + op + 0 ADR + 1 ADRP + +C6.2.10 ADRP + Form PC-relative address to 4KB page adds an immediate value that is shifted left by 12 bits, to the PC value to + form a PC-relative address, with the bottom 12 bits masked out, and writes the result to the destination register. + ADRP ,