diff --git a/cnc-ddraw.vcxproj b/cnc-ddraw.vcxproj
index 39864d4..3781845 100644
--- a/cnc-ddraw.vcxproj
+++ b/cnc-ddraw.vcxproj
@@ -13,6 +13,19 @@
+
+
+
+
+
+
+
+
+
+
+
+ true
+
@@ -99,7 +112,7 @@
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
MultiThreadedDebug
- inc
+ inc;src\detours
Windows
@@ -124,7 +137,7 @@ if exist "$(LocalDebuggerCommand)" if exist "$(LocalDebuggerWorkingDirectory)"
true
WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
MultiThreaded
- inc
+ inc;src\detours
Windows
diff --git a/cnc-ddraw.vcxproj.filters b/cnc-ddraw.vcxproj.filters
index 2423530..24513d4 100644
--- a/cnc-ddraw.vcxproj.filters
+++ b/cnc-ddraw.vcxproj.filters
@@ -13,6 +13,9 @@
{9b152f9d-a092-42a9-ac47-0594f135a640}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+ {af194dd7-3316-4887-93d6-9f2af2135f94}
+
@@ -60,6 +63,39 @@
Source Files
+
+ Source Files\detours
+
+
+ Source Files\detours
+
+
+ Source Files\detours
+
+
+ Source Files\detours
+
+
+ Source Files\detours
+
+
+ Source Files\detours
+
+
+ Source Files\detours
+
+
+ Source Files\detours
+
+
+ Source Files\detours
+
+
+ Source Files\detours
+
+
+ Source Files\detours
+
diff --git a/ddraw.rc b/ddraw.rc
index e9ed161..fc7e528 100644
--- a/ddraw.rc
+++ b/ddraw.rc
@@ -4,7 +4,7 @@
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define VERSION_BUILD 4
-#define VERSION_REVISION 1
+#define VERSION_REVISION 2
#define VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION
#define VERSION_STRING ver_str(VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION)
@@ -21,7 +21,7 @@ PRODUCTVERSION VERSION
VALUE "FileDescription", "DirectDraw replacement"
VALUE "FileVersion", VERSION_STRING
VALUE "InternalName", "ddraw"
- VALUE "LegalCopyright", "Copyright (c) 2010-2018"
+ VALUE "LegalCopyright", "Copyright (c) 2010-2019"
VALUE "LegalTrademarks", ""
VALUE "OriginalFileName", "ddraw.dll"
VALUE "ProductName", "cnc-ddraw"
diff --git a/inc/hook.h b/inc/hook.h
index 7c6ca81..cfc4127 100644
--- a/inc/hook.h
+++ b/inc/hook.h
@@ -41,11 +41,13 @@ extern MOVEWINDOWPROC real_MoveWindow;
extern SENDMESSAGEAPROC real_SendMessageA;
extern SETWINDOWLONGAPROC real_SetWindowLongA;
+extern int HookingMethod;
extern BOOL Hook_Active;
void Hook_Init();
+void Hook_Exit();
void Hook_PatchIAT(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction);
-PROC Hook_HotPatch(PROC function, PROC newFunction);
void Hook_Create(char *moduleName, char *functionName, PROC newFunction, PROC *function);
+void Hook_Revert(char *moduleName, char *functionName, PROC newFunction, PROC *function);
#endif
diff --git a/inc/main.h b/inc/main.h
index 9dc5c2a..8161349 100644
--- a/inc/main.h
+++ b/inc/main.h
@@ -124,7 +124,6 @@ typedef struct IDirectDrawImpl
BOOL altenter;
BOOL hidecursor;
BOOL accurateTimers;
- BOOL hook;
SpeedLimiter ticksLimiter;
SpeedLimiter flipLimiter;
SpeedLimiter fpsLimiter;
diff --git a/src/detours/CREDITS.TXT b/src/detours/CREDITS.TXT
new file mode 100644
index 0000000..a122676
--- /dev/null
+++ b/src/detours/CREDITS.TXT
@@ -0,0 +1,115 @@
+==============================================================================
+The following individuals have helped identify specific bugs and improvements
+in Detours. The entire Detours community has benefited from their help.
+==============================================================================
+
+* 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/src/detours/LICENSE.md b/src/detours/LICENSE.md
new file mode 100644
index 0000000..e6a4c56
--- /dev/null
+++ b/src/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/src/detours/README.md b/src/detours/README.md
new file mode 100644
index 0000000..b5c5239
--- /dev/null
+++ b/src/detours/README.md
@@ -0,0 +1,48 @@
+# 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). 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 Window 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).
+
+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/src/detours/creatwth.cpp b/src/detours/creatwth.cpp
new file mode 100644
index 0000000..bbe7cc8
--- /dev/null
+++ b/src/detours/creatwth.cpp
@@ -0,0 +1,1586 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// 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.
+//
+
+#if _MSC_VER >= 1900
+#pragma warning(push)
+#pragma warning(disable:4091) // empty typedef
+#endif
+#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
+#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
+#include
+#include
+#pragma warning(push)
+#if _MSC_VER > 1400
+#pragma warning(disable:6102 6103) // /analyze warnings
+#endif
+#include
+#pragma warning(pop)
+
+// #define DETOUR_DEBUG 1
+#define DETOURS_INTERNAL
+
+#include "detours.h"
+
+#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
+#error detours.h version mismatch
+#endif
+
+#if _MSC_VER >= 1900
+#pragma warning(pop)
+#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 }};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Enumate through modules in the target process.
+//
+static BOOL WINAPI LoadNtHeaderFromProcess(HANDLE hProcess,
+ HMODULE hModule,
+ PIMAGE_NT_HEADERS32 pNtHeader)
+{
+ PBYTE pbModule = (PBYTE)hModule;
+
+ if (pbModule == NULL) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ MEMORY_BASIC_INFORMATION mbi;
+ ZeroMemory(&mbi, sizeof(mbi));
+
+ if (VirtualQueryEx(hProcess, hModule, &mbi, sizeof(mbi)) == 0) {
+ return FALSE;
+ }
+
+ IMAGE_DOS_HEADER idh;
+
+ if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n",
+ pbModule, pbModule + sizeof(idh), GetLastError()));
+ return FALSE;
+ }
+
+ 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 FALSE;
+ }
+
+ if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew,
+ pNtHeader, sizeof(*pNtHeader), NULL)) {
+ DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p:%p) failed: %d\n",
+ pbModule + idh.e_lfanew,
+ pbModule + idh.e_lfanew + sizeof(*pNtHeader),
+ pbModule,
+ GetLastError()));
+ return FALSE;
+ }
+
+ if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
+ SetLastError(ERROR_BAD_EXE_FORMAT);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static HMODULE WINAPI EnumerateModulesInProcess(HANDLE hProcess,
+ HMODULE hModuleLast,
+ PIMAGE_NT_HEADERS32 pNtHeader)
+{
+ 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;
+ }
+
+ if (LoadNtHeaderFromProcess(hProcess, (HMODULE)pbLast, pNtHeader)) {
+ return (HMODULE)pbLast;
+ }
+ }
+ 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: %d\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: %d\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: %d\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: %d\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: %d\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=%x, CLR.Size=%x\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=%x, CLR.Size=%x\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: %d\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 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 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: %d\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: %d\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: %d\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: %d\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: %d\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 & 1)) {
+ 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: %d\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
+
+//////////////////////////////////////////////////////////////////////////////
+//
+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 bHas64BitDll = FALSE;
+ BOOL bHas32BitExe = FALSE;
+ BOOL bIs32BitProcess;
+ HMODULE hModule = NULL;
+ HMODULE hLast = NULL;
+
+ DETOUR_TRACE(("DetourUpdateProcessWithDll(%p,dlls=%d)\n", hProcess, nDlls));
+
+ for (;;) {
+ IMAGE_NT_HEADERS32 inh;
+
+ if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh)) == 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;
+ if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
+ && inh.FileHeader.Machine != 0) {
+
+ bHas32BitExe = TRUE;
+ }
+ DETOUR_TRACE(("%p Found EXE\n", hLast));
+ }
+ else {
+ if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC
+ && inh.FileHeader.Machine != 0) {
+
+ bHas64BitDll = TRUE;
+ }
+ }
+ }
+
+ if (hModule == NULL) {
+ SetLastError(ERROR_INVALID_OPERATION);
+ return FALSE;
+ }
+
+ if (!bHas32BitExe) {
+ bIs32BitProcess = FALSE;
+ }
+ else if (!bHas64BitDll) {
+ bIs32BitProcess = TRUE;
+ }
+ else {
+ if (!IsWow64Process(hProcess, &bIs32BitProcess)) {
+ return FALSE;
+ }
+ }
+
+ DETOUR_TRACE((" 32BitExe=%d 32BitProcess\n", bHas32BitExe, 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=%d)\n", hProcess, hModule, nDlls));
+
+ IMAGE_NT_HEADERS32 inh;
+
+ if (hModule == NULL || LoadNtHeaderFromProcess(hProcess, hModule, &inh) == NULL) {
+ 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 32BitProcess\n", bIs32BitExe, bIs32BitProcess));
+
+ 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 & 1) == 0 // Or mixed-mode MSIL
+ || (der.clr.Flags & 2) != 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 &= 0xfffffffe; // Clear the IL_ONLY flag.
+
+ DWORD dwProtect;
+ if (!DetourVirtualProtectSameExecuteEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) {
+ DETOUR_TRACE(("VirtualProtectEx(clr) write failed: %d\n", GetLastError()));
+ return FALSE;
+ }
+
+ if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) {
+ DETOUR_TRACE(("WriteProcessMemory(clr) failed: %d\n", GetLastError()));
+ return FALSE;
+ }
+
+ if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) {
+ DETOUR_TRACE(("VirtualProtectEx(clr) restore failed: %d\n", GetLastError()));
+ return FALSE;
+ }
+ DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr));
+
+#if DETOURS_64BIT
+ if (der.clr.Flags & 0x2) { // 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", der.pclr, der.pclr + der.cbclr));
+ 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: %d\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) PVOID pvData,
+ _In_ DWORD cbData)
+{
+ 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(%d) failed: %d\n", cbTotal, GetLastError()));
+ return FALSE;
+ }
+
+ 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: %d\n", GetLastError()));
+ return FALSE;
+ }
+ 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 FALSE;
+ }
+ 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 FALSE;
+ }
+ 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 FALSE;
+ }
+ 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 FALSE;
+ }
+ pbTarget += sizeof(dsr);
+
+ if (!WriteProcessMemory(hProcess, pbTarget, pvData, cbData, &cbWrote) ||
+ cbWrote != cbData) {
+ return FALSE;
+ }
+ pbTarget += cbData;
+
+ DETOUR_TRACE(("Copied %d byte payload into target process at %p\n",
+ cbTotal, pbTarget - cbTotal));
+ return TRUE;
+}
+
+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=%d) failed: %d\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=%d) failed: %d\n",
+ s_pHelper->pid, GetLastError()));
+ Result = 9903;
+ goto Cleanup;
+ }
+ Result = 0;
+
+ Cleanup:
+ if (rlpDlls != NULL) {
+ delete[] rlpDlls;
+ rlpDlls = 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=%d,dlls=%d)\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;
+ }
+
+ hr = StringCchPrintfA(szCommand, ARRAYSIZE(szCommand),
+ "rundll32.exe \"%hs\",#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: %d\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=%d\n", dwResult));
+ goto Cleanup;
+ }
+ Result = TRUE;
+ }
+ else {
+ DETOUR_TRACE(("CreateProcess failed: %d\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;
+ DWORD nLen = GetEnvironmentVariableW(L"WINDIR", szExe, ARRAYSIZE(szExe));
+
+ DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%d,dlls=%d)\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;
+ }
+
+ hr = StringCchPrintfW(szCommand, ARRAYSIZE(szCommand),
+ L"rundll32.exe \"%hs\",#1", &helper->rDlls[0]);
+ 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: %d\n", GetLastError()));
+ TerminateProcess(pi.hProcess, ~0u);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ goto Cleanup;
+ }
+
+ ResumeThread(pi.hThread);
+
+ 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=%d\n", dwResult));
+ goto Cleanup;
+ }
+ Result = TRUE;
+ }
+ else {
+ DETOUR_TRACE(("CreateProcess failed: %d\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/src/detours/detours.cpp b/src/detours/detours.cpp
new file mode 100644
index 0000000..fb18ef8
--- /dev/null
+++ b/src/detours/detours.cpp
@@ -0,0 +1,2489 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// Core Detours Functionality (detours.cpp of detours.lib)
+//
+// Microsoft Research Detours Package, Version 4.0.1
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+
+#pragma warning(disable:4068) // unknown pragma (suppress)
+
+#if _MSC_VER >= 1900
+#pragma warning(push)
+#pragma warning(disable:4091) // empty typedef
+#endif
+
+#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
+#include
+
+#if (_MSC_VER < 1299)
+#pragma warning(disable: 4710)
+#endif
+
+//#define DETOUR_DEBUG 1
+#define DETOURS_INTERNAL
+
+#include "detours.h"
+
+#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
+#error detours.h version mismatch
+#endif
+
+#if _MSC_VER >= 1900
+#pragma warning(pop)
+#endif
+
+#define NOTHROW
+
+//////////////////////////////////////////////////////////////////////////////
+//
+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", lo, pbCode, 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", lo, pbCode, 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", lo, pbCode, 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", lo, pbCode, 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", lo, pbCode, 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", lo, pbCode, 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 2 instructions plus a literal, so 16 bytes, 4 instructions.
+ //
+ // 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 = 16
+};
+
+inline ULONG fetch_opcode(PBYTE pbCode)
+{
+ return *(ULONG *)pbCode;
+}
+
+inline void write_opcode(PBYTE &pbCode, ULONG Opcode)
+{
+ *(ULONG *)pbCode = Opcode;
+ pbCode += 4;
+}
+
+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 ,