diff options
author | Andrzej Janik <[email protected]> | 2021-01-03 17:52:14 +0100 |
---|---|---|
committer | Andrzej Janik <[email protected]> | 2021-01-03 17:52:14 +0100 |
commit | 77523940b39d522adc26b18f00c3373407523c55 (patch) | |
tree | 666a5541f635b56d1f56305767a09cab2ae48d81 /ext/detours/src | |
parent | ae950163cd05f7a2205740af0acc6e427f0ffd92 (diff) | |
parent | dabc40cb19bf4e297c32284d26c74adbd6775e49 (diff) | |
download | ZLUDA-77523940b39d522adc26b18f00c3373407523c55.tar.gz ZLUDA-77523940b39d522adc26b18f00c3373407523c55.zip |
Merge commit 'dabc40cb19bf4e297c32284d26c74adbd6775e49' as 'ext/detours'
Diffstat (limited to 'ext/detours/src')
-rw-r--r-- | ext/detours/src/Makefile | 117 | ||||
-rw-r--r-- | ext/detours/src/creatwth.cpp | 1614 | ||||
-rw-r--r-- | ext/detours/src/detours.cpp | 2554 | ||||
-rw-r--r-- | ext/detours/src/detours.h | 1132 | ||||
-rw-r--r-- | ext/detours/src/detver.h | 27 | ||||
-rw-r--r-- | ext/detours/src/disasm.cpp | 4301 | ||||
-rw-r--r-- | ext/detours/src/disolarm.cpp | 2 | ||||
-rw-r--r-- | ext/detours/src/disolarm64.cpp | 2 | ||||
-rw-r--r-- | ext/detours/src/disolia64.cpp | 2 | ||||
-rw-r--r-- | ext/detours/src/disolx64.cpp | 2 | ||||
-rw-r--r-- | ext/detours/src/disolx86.cpp | 2 | ||||
-rw-r--r-- | ext/detours/src/image.cpp | 2226 | ||||
-rw-r--r-- | ext/detours/src/modules.cpp | 904 | ||||
-rw-r--r-- | ext/detours/src/uimports.cpp | 280 |
14 files changed, 13165 insertions, 0 deletions
diff --git a/ext/detours/src/Makefile b/ext/detours/src/Makefile new file mode 100644 index 0000000..4d8fe74 --- /dev/null +++ b/ext/detours/src/Makefile @@ -0,0 +1,117 @@ +############################################################################## +## +## Makefile for Detours. +## +## Microsoft Research Detours Package, Version 4.0.1 +## +## Copyright (c) Microsoft Corporation. All rights reserved. +## + +ROOT = .. +!include "$(ROOT)\system.mak" + +!IF "$(DETOURS_SOURCE_BROWSING)" == "" +DETOURS_SOURCE_BROWSING = 0 +!ENDIF + +#######################/####################################################### +## +CFLAGS=/nologo /W4 /WX /we4777 /we4800 /Zi /MT /Gy /Gm- /Zl /Od + +!IF $(DETOURS_SOURCE_BROWSING)==1 +CFLAGS=$(CFLAGS) /FR +!ELSE +CFLAGS=$(CFLAGS) /DWIN32_LEAN_AND_MEAN /D_WIN32_WINNT=0x501 +!ENDIF + +!IF "$(DETOURS_TARGET_PROCESSOR)" == "IA64" +CFLAGS=$(CFLAGS) /wd4163 # intrinsic rdtebex not available; using newer Windows headers with older compiler +!ENDIF + +!if defined(DETOURS_WIN_7) && defined(DETOURS_CL_17_OR_NEWER) +CFLAGS=$(CFLAGS) /D_USING_V110_SDK71_ +!elseif defined(DETOURS_ANALYZE) +CFLAGS=$(CFLAGS) /analyze +!endif + +OBJS = \ + $(OBJD)\detours.obj \ + $(OBJD)\modules.obj \ + $(OBJD)\disasm.obj \ + $(OBJD)\image.obj \ + $(OBJD)\creatwth.obj \ + $(OBJD)\disolx86.obj \ + $(OBJD)\disolx64.obj \ + $(OBJD)\disolia64.obj \ + $(OBJD)\disolarm.obj \ + $(OBJD)\disolarm64.obj \ + +############################################################################## +## +.SUFFIXES: .cpp .h .obj + +!ifdef DETOURS_ANALYZE +.cpp{$(OBJD)}.obj: + $(CC) $(CFLAGS) /Fd$(LIBD)\detours.pdb /Fo$(OBJD)\ /c $< +!else +.cpp{$(OBJD)}.obj:: + $(CC) $(CFLAGS) /Fd$(LIBD)\detours.pdb /Fo$(OBJD)\ /c $< +!endif + +############################################################################## + +all: dirs \ + $(LIBD)\detours.lib \ + $(INCD)\detours.h \ + $(INCD)\detver.h \ +!IF $(DETOURS_SOURCE_BROWSING)==1 + $(OBJD)\detours.bsc \ +!endif + +############################################################################## + +clean: + -del *~ 2>nul + -del $(LIBD)\detours.pdb $(LIBD)\detours.lib 2>nul + -rmdir /q /s $(OBJD) 2>nul + +realclean: clean + -rmdir /q /s $(OBJDS) 2>nul + +############################################################################## + +dirs: + @if not exist "$(INCD)" mkdir "$(INCD)" && echo. Created $(INCD) + @if not exist "$(LIBD)" mkdir "$(LIBD)" && echo. Created $(LIBD) + @if not exist "$(BIND)" mkdir "$(BIND)" && echo. Created $(BIND) + @if not exist "$(OBJD)" mkdir "$(OBJD)" && echo. Created $(OBJD) + +$(OBJD)\detours.bsc : $(OBJS) + bscmake /v /n /o $@ $(OBJS:.obj=.sbr) + +$(LIBD)\detours.lib : $(OBJS) + link /lib /out:$@ /nologo $(OBJS) + +$(INCD)\detours.h : detours.h + copy detours.h $@ + +$(INCD)\detver.h : detver.h + copy detver.h $@ + +$(OBJD)\detours.obj : detours.cpp detours.h +$(OBJD)\modules.obj : modules.cpp detours.h +$(OBJD)\disasm.obj : disasm.cpp detours.h +$(OBJD)\image.obj : image.cpp detours.h +$(OBJD)\creatwth.obj : creatwth.cpp uimports.cpp detours.h +$(OBJD)\disolx86.obj: disasm.cpp detours.h +$(OBJD)\disolx64.obj: disasm.cpp detours.h +$(OBJD)\disolia64.obj: disasm.cpp detours.h +$(OBJD)\disolarm.obj: disasm.cpp detours.h +$(OBJD)\disolarm64.obj: disasm.cpp detours.h + +test: all + cd $(MAKEDIR)\..\samples\slept + nmake /nologo test + cd $(MAKEDIR) + +################################################################# End of File. diff --git a/ext/detours/src/creatwth.cpp b/ext/detours/src/creatwth.cpp new file mode 100644 index 0000000..f7b51f4 --- /dev/null +++ b/ext/detours/src/creatwth.cpp @@ -0,0 +1,1614 @@ +////////////////////////////////////////////////////////////////////////////// +// +// 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 <stddef.h> + +#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 }}; + +////////////////////////////////////////////////////////////////////////////// +// +// 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 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: %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 & 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: %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 + +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: %d\n", GetLastError())); + return FALSE; + } + + LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( + hKernel32, "IsWow64Process"); + + if (pfnIsWow64Process == NULL) { + DETOUR_TRACE(("GetProcAddress failed: %d\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=%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; + 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((" 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 & 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: %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 & 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", 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; + } + + //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: %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; + WCHAR szDllName[MAX_PATH]; + int cchWrittenWideChar; + 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; + } + + //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: %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 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/ext/detours/src/detours.cpp b/ext/detours/src/detours.cpp new file mode 100644 index 0000000..34f2458 --- /dev/null +++ b/ext/detours/src/detours.cpp @@ -0,0 +1,2554 @@ +////////////////////////////////////////////////////////////////////////////// +// +// 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 + +////////////////////////////////////////////////////////////////////////////// +// +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; + // <relocated target bundle> + // 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 <reg> + (Opcode & 0xf800d000) == 0xf0009000) { // b <imm20> + 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 <Xd>, <label> + imm = SignExtend(immhi:immlo:Zeros(12), 64); + + 31 30 29 28 27 26 25 24 23 5 4 0 + 1 immlo 1 0 0 0 0 immhi Rd + 9 0 + +Rd is hardcoded as 0x10 above. +Immediate is 21 signed bits split into 2 bits and 19 bits, and is scaled by 4K. +*/ + UINT64 const pageLow2 = (Opcode >> 29) & 3; + UINT64 const pageHigh19 = (Opcode >> 5) & ~(~0ui64 << 19); + INT64 const page = detour_sign_extend((pageHigh19 << 2) | pageLow2, 21) << 12; + +/* https://static.docs.arm.com/ddi0487/bb/DDI0487B_b_armv8_arm.pdf + + C6.2.101 LDR (immediate) + Load Register (immediate) loads a word or doubleword from memory and writes it to a register. The address that is + used for the load is calculated from a base register and an immediate offset. + The Unsigned offset variant scales the immediate offset value by the size of the value accessed before adding it + to the base register value. + +Unsigned offset +64-bit variant Applies when size == 11. + 31 30 29 28 27 26 25 24 23 22 21 10 9 5 4 0 + 1 x 1 1 1 0 0 1 0 1 imm12 Rn Rt + F 9 4 200 10 + +That is, two low 5 bit fields are registers, hardcoded as 0x10 and 0x10 << 5 above, +then unsigned size-unscaled (8) 12-bit offset, then opcode bits 0xF94. +*/ + UINT64 const offset = ((Opcode2 >> 10) & ~(~0ui64 << 12)) << 3; + + PBYTE const pbTarget = (PBYTE)((ULONG64)pbCode & 0xfffffffffffff000ULL) + page + offset; + + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(PBYTE *)pbTarget; + 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) +{ + // The encoding used by detour_gen_jmp_indirect actually enables a + // displacement of +/- 4GiB. In the future, this could be changed to + // reflect that. For now, just reuse the x86 logic which is plenty. + + 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_opcode(pbCode); + if ((Opcode & 0xfffffc1f) == 0xd65f0000 || // br <reg> + (Opcode & 0xfc000000) == 0x14000000) { // b <imm26> + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + if (*(ULONG *)pbCode == 0xd503201f) { // nop. + return 4; + } + if (*(ULONG *)pbCode == 0x00000000) { // zero-filled padding. + return 4; + } + return 0; +} + +#endif // DETOURS_ARM64 + +//////////////////////////////////////////////// Trampoline Memory Management. +// +struct DETOUR_REGION +{ + ULONG dwSignature; + DETOUR_REGION * pNext; // Next region in list of regions. + DETOUR_TRAMPOLINE * pFree; // List of free trampolines in this region. +}; +typedef DETOUR_REGION * PDETOUR_REGION; + +const ULONG DETOUR_REGION_SIGNATURE = 'Rrtd'; +const ULONG DETOUR_REGION_SIZE = 0x10000; +const ULONG DETOUR_TRAMPOLINES_PER_REGION = (DETOUR_REGION_SIZE + / sizeof(DETOUR_TRAMPOLINE)) - 1; +static PDETOUR_REGION s_pRegions = NULL; // List of all regions. +static PDETOUR_REGION s_pRegion = NULL; // Default region. + +static DWORD detour_writable_trampoline_regions() +{ + // Mark all of the regions as writable. + for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) { + DWORD dwOld; + if (!VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READWRITE, &dwOld)) { + return GetLastError(); + } + } + return NO_ERROR; +} + +static void detour_runnable_trampoline_regions() +{ + HANDLE hProcess = GetCurrentProcess(); + + // Mark all of the regions as executable. + for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) { + DWORD dwOld; + VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READ, &dwOld); + FlushInstructionCache(hProcess, pRegion, DETOUR_REGION_SIZE); + } +} + +static PBYTE detour_alloc_round_down_to_region(PBYTE pbTry) +{ + // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications. + ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1); + if (extra != 0) { + pbTry -= extra; + } + return pbTry; +} + +static PBYTE detour_alloc_round_up_to_region(PBYTE pbTry) +{ + // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications. + ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1); + if (extra != 0) { + ULONG_PTR adjust = DETOUR_REGION_SIZE - extra; + pbTry += adjust; + } + return pbTry; +} + +// Starting at pbLo, try to allocate a memory region, continue until pbHi. + +static PVOID detour_alloc_region_from_lo(PBYTE pbLo, PBYTE pbHi) +{ + PBYTE pbTry = detour_alloc_round_up_to_region(pbLo); + + DETOUR_TRACE((" Looking for free region in %p..%p from %p:\n", pbLo, pbHi, pbTry)); + + for (; pbTry < pbHi;) { + MEMORY_BASIC_INFORMATION mbi; + + if (pbTry >= s_pSystemRegionLowerBound && pbTry <= s_pSystemRegionUpperBound) { + // Skip region reserved for system DLLs, but preserve address space entropy. + pbTry += 0x08000000; + continue; + } + + ZeroMemory(&mbi, sizeof(mbi)); + if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) { + break; + } + + DETOUR_TRACE((" Try %p => %p..%p %6x\n", + pbTry, + mbi.BaseAddress, + (PBYTE)mbi.BaseAddress + mbi.RegionSize - 1, + mbi.State)); + + if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) { + + PVOID pv = VirtualAlloc(pbTry, + DETOUR_REGION_SIZE, + MEM_COMMIT|MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + if (pv != NULL) { + return pv; + } + else if (GetLastError() == ERROR_DYNAMIC_CODE_BLOCKED) { + return NULL; + } + pbTry += DETOUR_REGION_SIZE; + } + else { + pbTry = detour_alloc_round_up_to_region((PBYTE)mbi.BaseAddress + mbi.RegionSize); + } + } + return NULL; +} + +// Starting at pbHi, try to allocate a memory region, continue until pbLo. + +static PVOID detour_alloc_region_from_hi(PBYTE pbLo, PBYTE pbHi) +{ + PBYTE pbTry = detour_alloc_round_down_to_region(pbHi - DETOUR_REGION_SIZE); + + DETOUR_TRACE((" Looking for free region in %p..%p from %p:\n", pbLo, pbHi, pbTry)); + + for (; pbTry > pbLo;) { + MEMORY_BASIC_INFORMATION mbi; + + DETOUR_TRACE((" Try %p\n", pbTry)); + if (pbTry >= s_pSystemRegionLowerBound && pbTry <= s_pSystemRegionUpperBound) { + // Skip region reserved for system DLLs, but preserve address space entropy. + pbTry -= 0x08000000; + continue; + } + + ZeroMemory(&mbi, sizeof(mbi)); + if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) { + break; + } + + DETOUR_TRACE((" Try %p => %p..%p %6x\n", + pbTry, + mbi.BaseAddress, + (PBYTE)mbi.BaseAddress + mbi.RegionSize - 1, + mbi.State)); + + if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) { + + PVOID pv = VirtualAlloc(pbTry, + DETOUR_REGION_SIZE, + MEM_COMMIT|MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + if (pv != NULL) { + return pv; + } + else if (GetLastError() == ERROR_DYNAMIC_CODE_BLOCKED) { + return NULL; + } + pbTry -= DETOUR_REGION_SIZE; + } + else { + pbTry = detour_alloc_round_down_to_region((PBYTE)mbi.AllocationBase + - DETOUR_REGION_SIZE); + } + } + return NULL; +} + +static PVOID detour_alloc_trampoline_allocate_new(PBYTE pbTarget, + PDETOUR_TRAMPOLINE pLo, + PDETOUR_TRAMPOLINE pHi) +{ + PVOID pbTry = NULL; + + // NB: We must always also start the search at an offset from pbTarget + // in order to maintain ASLR entropy. + +#if defined(DETOURS_64BIT) + // Try looking 1GB below or lower. + if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) { + pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget - 0x40000000); + } + // Try looking 1GB above or higher. + if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) { + pbTry = detour_alloc_region_from_lo(pbTarget + 0x40000000, (PBYTE)pHi); + } + // Try looking 1GB below or higher. + if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) { + pbTry = detour_alloc_region_from_lo(pbTarget - 0x40000000, pbTarget); + } + // Try looking 1GB above or lower. + if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) { + pbTry = detour_alloc_region_from_hi(pbTarget, pbTarget + 0x40000000); + } +#endif + + // Try anything below. + if (pbTry == NULL) { + pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget); + } + // try anything above. + if (pbTry == NULL) { + pbTry = detour_alloc_region_from_lo(pbTarget, (PBYTE)pHi); + } + + return pbTry; +} + +PVOID WINAPI DetourAllocateRegionWithinJumpBounds(_In_ LPCVOID pbTarget, + _Out_ PDWORD pcbAllocatedSize) +{ + PDETOUR_TRAMPOLINE pLo; + PDETOUR_TRAMPOLINE pHi; + detour_find_jmp_bounds((PBYTE)pbTarget, &pLo, &pHi); + + PVOID pbNewlyAllocated = + detour_alloc_trampoline_allocate_new((PBYTE)pbTarget, pLo, pHi); + if (pbNewlyAllocated == NULL) { + DETOUR_TRACE(("Couldn't find available memory region!\n")); + *pcbAllocatedSize = 0; + return NULL; + } + + *pcbAllocatedSize = DETOUR_REGION_SIZE; + return pbNewlyAllocated; +} + +static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget) +{ + // We have to place trampolines within +/- 2GB of target. + + PDETOUR_TRAMPOLINE pLo; + PDETOUR_TRAMPOLINE pHi; + + detour_find_jmp_bounds(pbTarget, &pLo, &pHi); + + PDETOUR_TRAMPOLINE pTrampoline = NULL; + + // Insure that there is a default region. + if (s_pRegion == NULL && s_pRegions != NULL) { + s_pRegion = s_pRegions; + } + + // First check the default region for an valid free block. + if (s_pRegion != NULL && s_pRegion->pFree != NULL && + s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) { + + found_region: + pTrampoline = s_pRegion->pFree; + // do a last sanity check on region. + if (pTrampoline < pLo || pTrampoline > pHi) { + return NULL; + } + s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pTrampoline->pbRemain; + memset(pTrampoline, 0xcc, sizeof(*pTrampoline)); + return pTrampoline; + } + + // Then check the existing regions for a valid free block. + for (s_pRegion = s_pRegions; s_pRegion != NULL; s_pRegion = s_pRegion->pNext) { + if (s_pRegion != NULL && s_pRegion->pFree != NULL && + s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) { + goto found_region; + } + } + + // We need to allocate a new region. + + // Round pbTarget down to 64KB block. + pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff); + + PVOID pbNewlyAllocated = + detour_alloc_trampoline_allocate_new(pbTarget, pLo, pHi); + if (pbNewlyAllocated != NULL) { + s_pRegion = (DETOUR_REGION*)pbNewlyAllocated; + s_pRegion->dwSignature = DETOUR_REGION_SIGNATURE; + s_pRegion->pFree = NULL; + s_pRegion->pNext = s_pRegions; + s_pRegions = s_pRegion; + DETOUR_TRACE((" Allocated region %p..%p\n\n", + s_pRegion, ((PBYTE)s_pRegion) + DETOUR_REGION_SIZE - 1)); + + // Put everything but the first trampoline on the free list. + PBYTE pFree = NULL; + pTrampoline = ((PDETOUR_TRAMPOLINE)s_pRegion) + 1; + for (int i = DETOUR_TRAMPOLINES_PER_REGION - 1; i > 1; i--) { + pTrampoline[i].pbRemain = pFree; + pFree = (PBYTE)&pTrampoline[i]; + } + s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pFree; + goto found_region; + } + + DETOUR_TRACE(("Couldn't find available memory region!\n")); + return NULL; +} + +static void detour_free_trampoline(PDETOUR_TRAMPOLINE pTrampoline) +{ + PDETOUR_REGION pRegion = (PDETOUR_REGION) + ((ULONG_PTR)pTrampoline & ~(ULONG_PTR)0xffff); + + memset(pTrampoline, 0, sizeof(*pTrampoline)); + pTrampoline->pbRemain = (PBYTE)pRegion->pFree; + pRegion->pFree = pTrampoline; +} + +static BOOL detour_is_region_empty(PDETOUR_REGION pRegion) +{ + // Stop if the region isn't a region (this would be bad). + if (pRegion->dwSignature != DETOUR_REGION_SIGNATURE) { + return FALSE; + } + + PBYTE pbRegionBeg = (PBYTE)pRegion; + PBYTE pbRegionLim = pbRegionBeg + DETOUR_REGION_SIZE; + + // Stop if any of the trampolines aren't free. + PDETOUR_TRAMPOLINE pTrampoline = ((PDETOUR_TRAMPOLINE)pRegion) + 1; + for (int i = 0; i < DETOUR_TRAMPOLINES_PER_REGION; i++) { + if (pTrampoline[i].pbRemain != NULL && + (pTrampoline[i].pbRemain < pbRegionBeg || + pTrampoline[i].pbRemain >= pbRegionLim)) { + return FALSE; + } + } + + // OK, the region is empty. + return TRUE; +} + +static void detour_free_unused_trampoline_regions() +{ + PDETOUR_REGION *ppRegionBase = &s_pRegions; + PDETOUR_REGION pRegion = s_pRegions; + + while (pRegion != NULL) { + if (detour_is_region_empty(pRegion)) { + *ppRegionBase = pRegion->pNext; + + VirtualFree(pRegion, 0, MEM_RELEASE); + s_pRegion = NULL; + } + else { + ppRegionBase = &pRegion->pNext; + } + pRegion = *ppRegionBase; + } +} + +///////////////////////////////////////////////////////// Transaction Structs. +// +struct DetourThread +{ + DetourThread * pNext; + HANDLE hThread; +}; + +struct DetourOperation +{ + DetourOperation * pNext; + BOOL fIsRemove; + PBYTE * ppbPointer; + PBYTE pbTarget; + PDETOUR_TRAMPOLINE pTrampoline; + ULONG dwPerm; +}; + +static BOOL s_fIgnoreTooSmall = FALSE; +static BOOL s_fRetainRegions = FALSE; + +static LONG s_nPendingThreadId = 0; // Thread owning pending transaction. +static LONG s_nPendingError = NO_ERROR; +static PVOID * s_ppPendingError = NULL; +static DetourThread * s_pPendingThreads = NULL; +static DetourOperation * s_pPendingOperations = NULL; + +////////////////////////////////////////////////////////////////////////////// +// +PVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer, + _Out_opt_ PVOID *ppGlobals) +{ + return detour_skip_jmp((PBYTE)pPointer, ppGlobals); +} + +//////////////////////////////////////////////////////////// Transaction APIs. +// +BOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore) +{ + BOOL fPrevious = s_fIgnoreTooSmall; + s_fIgnoreTooSmall = fIgnore; + return fPrevious; +} + +BOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain) +{ + BOOL fPrevious = s_fRetainRegions; + s_fRetainRegions = fRetain; + return fPrevious; +} + +PVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound) +{ + PVOID pPrevious = s_pSystemRegionLowerBound; + s_pSystemRegionLowerBound = pSystemRegionLowerBound; + return pPrevious; +} + +PVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound) +{ + PVOID pPrevious = s_pSystemRegionUpperBound; + s_pSystemRegionUpperBound = pSystemRegionUpperBound; + return pPrevious; +} + +LONG WINAPI DetourTransactionBegin() +{ + // Only one transaction is allowed at a time. +_Benign_race_begin_ + if (s_nPendingThreadId != 0) { + return ERROR_INVALID_OPERATION; + } +_Benign_race_end_ + + // Make sure only one thread can start a transaction. + if (InterlockedCompareExchange(&s_nPendingThreadId, (LONG)GetCurrentThreadId(), 0) != 0) { + return ERROR_INVALID_OPERATION; + } + + s_pPendingOperations = NULL; + s_pPendingThreads = NULL; + s_ppPendingError = NULL; + + // Make sure the trampoline pages are writable. + s_nPendingError = detour_writable_trampoline_regions(); + + return s_nPendingError; +} + +LONG WINAPI DetourTransactionAbort() +{ + if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { + return ERROR_INVALID_OPERATION; + } + + // Restore all of the page permissions. + for (DetourOperation *o = s_pPendingOperations; o != NULL;) { + // We don't care if this fails, because the code is still accessible. + DWORD dwOld; + VirtualProtect(o->pbTarget, o->pTrampoline->cbRestore, + o->dwPerm, &dwOld); + + if (!o->fIsRemove) { + if (o->pTrampoline) { + detour_free_trampoline(o->pTrampoline); + o->pTrampoline = NULL; + } + } + + DetourOperation *n = o->pNext; + delete o; + o = n; + } + s_pPendingOperations = NULL; + + // Make sure the trampoline pages are no longer writable. + detour_runnable_trampoline_regions(); + + // Resume any suspended threads. + for (DetourThread *t = s_pPendingThreads; t != NULL;) { + // There is nothing we can do if this fails. + ResumeThread(t->hThread); + + DetourThread *n = t->pNext; + delete t; + t = n; + } + s_pPendingThreads = NULL; + s_nPendingThreadId = 0; + + return NO_ERROR; +} + +LONG WINAPI DetourTransactionCommit() +{ + return DetourTransactionCommitEx(NULL); +} + +static BYTE detour_align_from_trampoline(PDETOUR_TRAMPOLINE pTrampoline, BYTE obTrampoline) +{ + for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) { + if (pTrampoline->rAlign[n].obTrampoline == obTrampoline) { + return pTrampoline->rAlign[n].obTarget; + } + } + return 0; +} + +static LONG detour_align_from_target(PDETOUR_TRAMPOLINE pTrampoline, LONG obTarget) +{ + for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) { + if (pTrampoline->rAlign[n].obTarget == obTarget) { + return pTrampoline->rAlign[n].obTrampoline; + } + } + return 0; +} + +LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer) +{ + if (pppFailedPointer != NULL) { + // Used to get the last error. + *pppFailedPointer = s_ppPendingError; + } + if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { + return ERROR_INVALID_OPERATION; + } + + // If any of the pending operations failed, then we abort the whole transaction. + if (s_nPendingError != NO_ERROR) { + DETOUR_BREAK(); + DetourTransactionAbort(); + return s_nPendingError; + } + + // Common variables. + DetourOperation *o; + DetourThread *t; + BOOL freed = FALSE; + + // Insert or remove each of the detours. + for (o = s_pPendingOperations; o != NULL; o = o->pNext) { + if (o->fIsRemove) { + CopyMemory(o->pbTarget, + o->pTrampoline->rbRestore, + o->pTrampoline->cbRestore); +#ifdef DETOURS_IA64 + *o->ppbPointer = (PBYTE)o->pTrampoline->ppldTarget; +#endif // DETOURS_IA64 + +#ifdef DETOURS_X86 + *o->ppbPointer = o->pbTarget; +#endif // DETOURS_X86 + +#ifdef DETOURS_X64 + *o->ppbPointer = o->pbTarget; +#endif // DETOURS_X64 + +#ifdef DETOURS_ARM + *o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pbTarget); +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + *o->ppbPointer = o->pbTarget; +#endif // DETOURS_ARM + } + else { + DETOUR_TRACE(("detours: pbTramp =%p, pbRemain=%p, pbDetour=%p, cbRestore=%d\n", + o->pTrampoline, + o->pTrampoline->pbRemain, + o->pTrampoline->pbDetour, + o->pTrampoline->cbRestore)); + + DETOUR_TRACE(("detours: pbTarget=%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x [before]\n", + o->pbTarget, + o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3], + o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7], + o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11])); + +#ifdef DETOURS_IA64 + ((DETOUR_IA64_BUNDLE*)o->pbTarget) + ->SetBrl((UINT64)&o->pTrampoline->bAllocFrame); + *o->ppbPointer = (PBYTE)&o->pTrampoline->pldTrampoline; +#endif // DETOURS_IA64 + +#ifdef DETOURS_X64 + detour_gen_jmp_indirect(o->pTrampoline->rbCodeIn, &o->pTrampoline->pbDetour); + PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->rbCodeIn); + pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain); + *o->ppbPointer = o->pTrampoline->rbCode; + UNREFERENCED_PARAMETER(pbCode); +#endif // DETOURS_X64 + +#ifdef DETOURS_X86 + PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->pbDetour); + pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain); + *o->ppbPointer = o->pTrampoline->rbCode; + UNREFERENCED_PARAMETER(pbCode); +#endif // DETOURS_X86 + +#ifdef DETOURS_ARM + PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour); + pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain); + *o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pTrampoline->rbCode); + UNREFERENCED_PARAMETER(pbCode); +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + PBYTE pbCode = detour_gen_jmp_indirect(o->pbTarget, (ULONG64*)&(o->pTrampoline->pbDetour)); + pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain); + *o->ppbPointer = o->pTrampoline->rbCode; + UNREFERENCED_PARAMETER(pbCode); +#endif // DETOURS_ARM64 + + DETOUR_TRACE(("detours: pbTarget=%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x [after]\n", + o->pbTarget, + o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3], + o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7], + o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11])); + + DETOUR_TRACE(("detours: pbTramp =%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + o->pTrampoline, + o->pTrampoline->rbCode[0], o->pTrampoline->rbCode[1], + o->pTrampoline->rbCode[2], o->pTrampoline->rbCode[3], + o->pTrampoline->rbCode[4], o->pTrampoline->rbCode[5], + o->pTrampoline->rbCode[6], o->pTrampoline->rbCode[7], + o->pTrampoline->rbCode[8], o->pTrampoline->rbCode[9], + o->pTrampoline->rbCode[10], o->pTrampoline->rbCode[11])); + +#ifdef DETOURS_IA64 + DETOUR_TRACE(("\n")); + DETOUR_TRACE(("detours: &pldTrampoline =%p\n", + &o->pTrampoline->pldTrampoline)); + DETOUR_TRACE(("detours: &bMovlTargetGp =%p [%p]\n", + &o->pTrampoline->bMovlTargetGp, + o->pTrampoline->bMovlTargetGp.GetMovlGp())); + DETOUR_TRACE(("detours: &rbCode =%p [%p]\n", + &o->pTrampoline->rbCode, + ((DETOUR_IA64_BUNDLE&)o->pTrampoline->rbCode).GetBrlTarget())); + DETOUR_TRACE(("detours: &bBrlRemainEip =%p [%p]\n", + &o->pTrampoline->bBrlRemainEip, + o->pTrampoline->bBrlRemainEip.GetBrlTarget())); + DETOUR_TRACE(("detours: &bMovlDetourGp =%p [%p]\n", + &o->pTrampoline->bMovlDetourGp, + o->pTrampoline->bMovlDetourGp.GetMovlGp())); + DETOUR_TRACE(("detours: &bBrlDetourEip =%p [%p]\n", + &o->pTrampoline->bCallDetour, + o->pTrampoline->bCallDetour.GetBrlTarget())); + DETOUR_TRACE(("detours: pldDetour =%p [%p]\n", + o->pTrampoline->ppldDetour->EntryPoint, + o->pTrampoline->ppldDetour->GlobalPointer)); + DETOUR_TRACE(("detours: pldTarget =%p [%p]\n", + o->pTrampoline->ppldTarget->EntryPoint, + o->pTrampoline->ppldTarget->GlobalPointer)); + DETOUR_TRACE(("detours: pbRemain =%p\n", + o->pTrampoline->pbRemain)); + DETOUR_TRACE(("detours: pbDetour =%p\n", + o->pTrampoline->pbDetour)); + DETOUR_TRACE(("\n")); +#endif // DETOURS_IA64 + } + } + + // Update any suspended threads. + for (t = s_pPendingThreads; t != NULL; t = t->pNext) { + CONTEXT cxt; + cxt.ContextFlags = CONTEXT_CONTROL; + +#undef DETOURS_EIP + +#ifdef DETOURS_X86 +#define DETOURS_EIP Eip +#endif // DETOURS_X86 + +#ifdef DETOURS_X64 +#define DETOURS_EIP Rip +#endif // DETOURS_X64 + +#ifdef DETOURS_IA64 +#define DETOURS_EIP StIIP +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM +#define DETOURS_EIP Pc +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 +#define DETOURS_EIP Pc +#endif // DETOURS_ARM64 + +typedef ULONG_PTR DETOURS_EIP_TYPE; + + if (GetThreadContext(t->hThread, &cxt)) { + for (o = s_pPendingOperations; o != NULL; o = o->pNext) { + if (o->fIsRemove) { + if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pTrampoline && + cxt.DETOURS_EIP < (DETOURS_EIP_TYPE)((ULONG_PTR)o->pTrampoline + + sizeof(o->pTrampoline)) + ) { + + cxt.DETOURS_EIP = (DETOURS_EIP_TYPE) + ((ULONG_PTR)o->pbTarget + + detour_align_from_trampoline(o->pTrampoline, + (BYTE)(cxt.DETOURS_EIP + - (DETOURS_EIP_TYPE)(ULONG_PTR) + o->pTrampoline))); + + SetThreadContext(t->hThread, &cxt); + } + } + else { + if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget && + cxt.DETOURS_EIP < (DETOURS_EIP_TYPE)((ULONG_PTR)o->pbTarget + + o->pTrampoline->cbRestore) + ) { + + cxt.DETOURS_EIP = (DETOURS_EIP_TYPE) + ((ULONG_PTR)o->pTrampoline + + detour_align_from_target(o->pTrampoline, + (BYTE)(cxt.DETOURS_EIP + - (DETOURS_EIP_TYPE)(ULONG_PTR) + o->pbTarget))); + + SetThreadContext(t->hThread, &cxt); + } + } + } + } +#undef DETOURS_EIP + } + + // Restore all of the page permissions and flush the icache. + HANDLE hProcess = GetCurrentProcess(); + for (o = s_pPendingOperations; o != NULL;) { + // We don't care if this fails, because the code is still accessible. + DWORD dwOld; + VirtualProtect(o->pbTarget, o->pTrampoline->cbRestore, o->dwPerm, &dwOld); + FlushInstructionCache(hProcess, o->pbTarget, o->pTrampoline->cbRestore); + + if (o->fIsRemove && o->pTrampoline) { + detour_free_trampoline(o->pTrampoline); + o->pTrampoline = NULL; + freed = true; + } + + DetourOperation *n = o->pNext; + delete o; + o = n; + } + s_pPendingOperations = NULL; + + // Free any trampoline regions that are now unused. + if (freed && !s_fRetainRegions) { + detour_free_unused_trampoline_regions(); + } + + // Make sure the trampoline pages are no longer writable. + detour_runnable_trampoline_regions(); + + // Resume any suspended threads. + for (t = s_pPendingThreads; t != NULL;) { + // There is nothing we can do if this fails. + ResumeThread(t->hThread); + + DetourThread *n = t->pNext; + delete t; + t = n; + } + s_pPendingThreads = NULL; + s_nPendingThreadId = 0; + + if (pppFailedPointer != NULL) { + *pppFailedPointer = s_ppPendingError; + } + + return s_nPendingError; +} + +LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread) +{ + LONG error; + + // If any of the pending operations failed, then we don't need to do this. + if (s_nPendingError != NO_ERROR) { + return s_nPendingError; + } + + // Silently (and safely) drop any attempt to suspend our own thread. + if (hThread == GetCurrentThread()) { + return NO_ERROR; + } + + DetourThread *t = new NOTHROW DetourThread; + if (t == NULL) { + error = ERROR_NOT_ENOUGH_MEMORY; + fail: + if (t != NULL) { + delete t; + t = NULL; + } + s_nPendingError = error; + s_ppPendingError = NULL; + DETOUR_BREAK(); + return error; + } + + if (SuspendThread(hThread) == (DWORD)-1) { + error = GetLastError(); + DETOUR_BREAK(); + goto fail; + } + + t->hThread = hThread; + t->pNext = s_pPendingThreads; + s_pPendingThreads = t; + + return NO_ERROR; +} + +///////////////////////////////////////////////////////////// Transacted APIs. +// +LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour) +{ + return DetourAttachEx(ppPointer, pDetour, NULL, NULL, NULL); +} + +LONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour, + _Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline, + _Out_opt_ PVOID *ppRealTarget, + _Out_opt_ PVOID *ppRealDetour) +{ + LONG error = NO_ERROR; + + if (ppRealTrampoline != NULL) { + *ppRealTrampoline = NULL; + } + if (ppRealTarget != NULL) { + *ppRealTarget = NULL; + } + if (ppRealDetour != NULL) { + *ppRealDetour = NULL; + } + if (pDetour == NULL) { + DETOUR_TRACE(("empty detour\n")); + return ERROR_INVALID_PARAMETER; + } + + if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { + DETOUR_TRACE(("transaction conflict with thread id=%d\n", s_nPendingThreadId)); + return ERROR_INVALID_OPERATION; + } + + // If any of the pending operations failed, then we don't need to do this. + if (s_nPendingError != NO_ERROR) { + DETOUR_TRACE(("pending transaction error=%d\n", s_nPendingError)); + return s_nPendingError; + } + + if (ppPointer == NULL) { + DETOUR_TRACE(("ppPointer is null\n")); + return ERROR_INVALID_HANDLE; + } + if (*ppPointer == NULL) { + error = ERROR_INVALID_HANDLE; + s_nPendingError = error; + s_ppPendingError = ppPointer; + DETOUR_TRACE(("*ppPointer is null (ppPointer=%p)\n", ppPointer)); + DETOUR_BREAK(); + return error; + } + + PBYTE pbTarget = (PBYTE)*ppPointer; + PDETOUR_TRAMPOLINE pTrampoline = NULL; + DetourOperation *o = NULL; + +#ifdef DETOURS_IA64 + PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour; + PPLABEL_DESCRIPTOR ppldTarget = (PPLABEL_DESCRIPTOR)pbTarget; + PVOID pDetourGlobals = NULL; + PVOID pTargetGlobals = NULL; + + pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals); + pbTarget = (PBYTE)DetourCodeFromPointer(ppldTarget, &pTargetGlobals); + DETOUR_TRACE((" ppldDetour=%p, code=%p [gp=%p]\n", + ppldDetour, pDetour, pDetourGlobals)); + DETOUR_TRACE((" ppldTarget=%p, code=%p [gp=%p]\n", + ppldTarget, pbTarget, pTargetGlobals)); +#else // DETOURS_IA64 + pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL); + pDetour = DetourCodeFromPointer(pDetour, NULL); +#endif // !DETOURS_IA64 + + // Don't follow a jump if its destination is the target function. + // This happens when the detour does nothing other than call the target. + if (pDetour == (PVOID)pbTarget) { + if (s_fIgnoreTooSmall) { + goto stop; + } + else { + DETOUR_BREAK(); + goto fail; + } + } + + if (ppRealTarget != NULL) { + *ppRealTarget = pbTarget; + } + if (ppRealDetour != NULL) { + *ppRealDetour = pDetour; + } + + o = new NOTHROW DetourOperation; + if (o == NULL) { + error = ERROR_NOT_ENOUGH_MEMORY; + fail: + s_nPendingError = error; + DETOUR_BREAK(); + stop: + if (pTrampoline != NULL) { + detour_free_trampoline(pTrampoline); + pTrampoline = NULL; + if (ppRealTrampoline != NULL) { + *ppRealTrampoline = NULL; + } + } + if (o != NULL) { + delete o; + o = NULL; + } + s_ppPendingError = ppPointer; + return error; + } + + pTrampoline = detour_alloc_trampoline(pbTarget); + if (pTrampoline == NULL) { + error = ERROR_NOT_ENOUGH_MEMORY; + DETOUR_BREAK(); + goto fail; + } + + if (ppRealTrampoline != NULL) { + *ppRealTrampoline = pTrampoline; + } + + DETOUR_TRACE(("detours: pbTramp=%p, pDetour=%p\n", pTrampoline, pDetour)); + + memset(pTrampoline->rAlign, 0, sizeof(pTrampoline->rAlign)); + + // Determine the number of movable target instructions. + PBYTE pbSrc = pbTarget; + PBYTE pbTrampoline = pTrampoline->rbCode; +#ifdef DETOURS_IA64 + PBYTE pbPool = (PBYTE)(&pTrampoline->bBranchIslands + 1); +#else + PBYTE pbPool = pbTrampoline + sizeof(pTrampoline->rbCode); +#endif + ULONG cbTarget = 0; + ULONG cbJump = SIZE_OF_JMP; + ULONG nAlign = 0; + +#ifdef DETOURS_ARM + // On ARM, we need an extra instruction when the function isn't 32-bit aligned. + // Check if the existing code is another detour (or at least a similar + // "ldr pc, [PC+0]" jump. + if ((ULONG)pbTarget & 2) { + cbJump += 2; + + ULONG op = fetch_thumb_opcode(pbSrc); + if (op == 0xbf00) { + op = fetch_thumb_opcode(pbSrc + 2); + if (op == 0xf8dff000) { // LDR PC,[PC] + *((PUSHORT&)pbTrampoline)++ = *((PUSHORT&)pbSrc)++; + *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++; + *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++; + cbTarget = (LONG)(pbSrc - pbTarget); + // We will fall through the "while" because cbTarget is now >= cbJump. + } + } + } + else { + ULONG op = fetch_thumb_opcode(pbSrc); + if (op == 0xf8dff000) { // LDR PC,[PC] + *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++; + *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++; + cbTarget = (LONG)(pbSrc - pbTarget); + // We will fall through the "while" because cbTarget is now >= cbJump. + } + } +#endif + + while (cbTarget < cbJump) { + PBYTE pbOp = pbSrc; + LONG lExtra = 0; + + DETOUR_TRACE((" DetourCopyInstruction(%p,%p)\n", + pbTrampoline, pbSrc)); + pbSrc = (PBYTE) + DetourCopyInstruction(pbTrampoline, (PVOID*)&pbPool, pbSrc, NULL, &lExtra); + DETOUR_TRACE((" DetourCopyInstruction() = %p (%d bytes)\n", + pbSrc, (int)(pbSrc - pbOp))); + pbTrampoline += (pbSrc - pbOp) + lExtra; + cbTarget = (LONG)(pbSrc - pbTarget); + pTrampoline->rAlign[nAlign].obTarget = cbTarget; + pTrampoline->rAlign[nAlign].obTrampoline = pbTrampoline - pTrampoline->rbCode; + nAlign++; + + if (nAlign >= ARRAYSIZE(pTrampoline->rAlign)) { + break; + } + + if (detour_does_code_end_function(pbOp)) { + break; + } + } + + // Consume, but don't duplicate padding if it is needed and available. + while (cbTarget < cbJump) { + LONG cFiller = detour_is_code_filler(pbSrc); + if (cFiller == 0) { + break; + } + + pbSrc += cFiller; + cbTarget = (LONG)(pbSrc - pbTarget); + } + +#if DETOUR_DEBUG + { + DETOUR_TRACE((" detours: rAlign [")); + LONG n = 0; + for (n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) { + if (pTrampoline->rAlign[n].obTarget == 0 && + pTrampoline->rAlign[n].obTrampoline == 0) { + break; + } + DETOUR_TRACE((" %d/%d", + pTrampoline->rAlign[n].obTarget, + pTrampoline->rAlign[n].obTrampoline + )); + + } + DETOUR_TRACE((" ]\n")); + } +#endif + + if (cbTarget < cbJump || nAlign > ARRAYSIZE(pTrampoline->rAlign)) { + // Too few instructions. + + error = ERROR_INVALID_BLOCK; + if (s_fIgnoreTooSmall) { + goto stop; + } + else { + DETOUR_BREAK(); + goto fail; + } + } + + if (pbTrampoline > pbPool) { + __debugbreak(); + } + + pTrampoline->cbCode = (BYTE)(pbTrampoline - pTrampoline->rbCode); + pTrampoline->cbRestore = (BYTE)cbTarget; + CopyMemory(pTrampoline->rbRestore, pbTarget, cbTarget); + +#if !defined(DETOURS_IA64) + if (cbTarget > sizeof(pTrampoline->rbCode) - cbJump) { + // Too many instructions. + error = ERROR_INVALID_HANDLE; + DETOUR_BREAK(); + goto fail; + } +#endif // !DETOURS_IA64 + + pTrampoline->pbRemain = pbTarget + cbTarget; + pTrampoline->pbDetour = (PBYTE)pDetour; + +#ifdef DETOURS_IA64 + pTrampoline->ppldDetour = ppldDetour; + pTrampoline->ppldTarget = ppldTarget; + pTrampoline->pldTrampoline.EntryPoint = (UINT64)&pTrampoline->bMovlTargetGp; + pTrampoline->pldTrampoline.GlobalPointer = (UINT64)pDetourGlobals; + + ((DETOUR_IA64_BUNDLE *)pTrampoline->rbCode)->SetStop(); + + pTrampoline->bMovlTargetGp.SetMovlGp((UINT64)pTargetGlobals); + pTrampoline->bBrlRemainEip.SetBrl((UINT64)pTrampoline->pbRemain); + + // Alloc frame: alloc r41=ar.pfs,11,0,8,0; mov r40=rp + pTrampoline->bAllocFrame.wide[0] = 0x00000580164d480c; + pTrampoline->bAllocFrame.wide[1] = 0x00c4000500000200; + // save r36, r37, r38. + pTrampoline->bSave37to39.wide[0] = 0x031021004e019001; + pTrampoline->bSave37to39.wide[1] = 0x8401280600420098; + // save r34,r35,r36: adds r47=0,r36; adds r46=0,r35; adds r45=0,r34 + pTrampoline->bSave34to36.wide[0] = 0x02e0210048017800; + pTrampoline->bSave34to36.wide[1] = 0x84011005a042008c; + // save gp,r32,r33" adds r44=0,r33; adds r43=0,r32; adds r42=0,gp ;; + pTrampoline->bSaveGPto33.wide[0] = 0x02b0210042016001; + pTrampoline->bSaveGPto33.wide[1] = 0x8400080540420080; + // set detour GP. + pTrampoline->bMovlDetourGp.SetMovlGp((UINT64)pDetourGlobals); + // call detour: brl.call.sptk.few rp=detour ;; + pTrampoline->bCallDetour.wide[0] = 0x0000000100000005; + pTrampoline->bCallDetour.wide[1] = 0xd000001000000000; + pTrampoline->bCallDetour.SetBrlTarget((UINT64)pDetour); + // pop frame & gp: adds gp=0,r42; mov rp=r40,+0;; mov.i ar.pfs=r41 + pTrampoline->bPopFrameGp.wide[0] = 0x4000210054000802; + pTrampoline->bPopFrameGp.wide[1] = 0x00aa029000038005; + // return to caller: br.ret.sptk.many rp ;; + pTrampoline->bReturn.wide[0] = 0x0000000100000019; + pTrampoline->bReturn.wide[1] = 0x0084000880000200; + + DETOUR_TRACE(("detours: &bMovlTargetGp=%p\n", &pTrampoline->bMovlTargetGp)); + DETOUR_TRACE(("detours: &bMovlDetourGp=%p\n", &pTrampoline->bMovlDetourGp)); +#endif // DETOURS_IA64 + + pbTrampoline = pTrampoline->rbCode + pTrampoline->cbCode; +#ifdef DETOURS_X64 + pbTrampoline = detour_gen_jmp_indirect(pbTrampoline, &pTrampoline->pbRemain); + pbTrampoline = detour_gen_brk(pbTrampoline, pbPool); +#endif // DETOURS_X64 + +#ifdef DETOURS_X86 + pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, pTrampoline->pbRemain); + pbTrampoline = detour_gen_brk(pbTrampoline, pbPool); +#endif // DETOURS_X86 + +#ifdef DETOURS_ARM + pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain); + pbTrampoline = detour_gen_brk(pbTrampoline, pbPool); +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain); + pbTrampoline = detour_gen_brk(pbTrampoline, pbPool); +#endif // DETOURS_ARM64 + + (void)pbTrampoline; + + DWORD dwOld = 0; + if (!VirtualProtect(pbTarget, cbTarget, PAGE_EXECUTE_READWRITE, &dwOld)) { + error = GetLastError(); + DETOUR_BREAK(); + goto fail; + } + + DETOUR_TRACE(("detours: pbTarget=%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + pbTarget, + pbTarget[0], pbTarget[1], pbTarget[2], pbTarget[3], + pbTarget[4], pbTarget[5], pbTarget[6], pbTarget[7], + pbTarget[8], pbTarget[9], pbTarget[10], pbTarget[11])); + DETOUR_TRACE(("detours: pbTramp =%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + pTrampoline, + pTrampoline->rbCode[0], pTrampoline->rbCode[1], + pTrampoline->rbCode[2], pTrampoline->rbCode[3], + pTrampoline->rbCode[4], pTrampoline->rbCode[5], + pTrampoline->rbCode[6], pTrampoline->rbCode[7], + pTrampoline->rbCode[8], pTrampoline->rbCode[9], + pTrampoline->rbCode[10], pTrampoline->rbCode[11])); + + o->fIsRemove = FALSE; + o->ppbPointer = (PBYTE*)ppPointer; + o->pTrampoline = pTrampoline; + o->pbTarget = pbTarget; + o->dwPerm = dwOld; + o->pNext = s_pPendingOperations; + s_pPendingOperations = o; + + return NO_ERROR; +} + +LONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour) +{ + LONG error = NO_ERROR; + + if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { + return ERROR_INVALID_OPERATION; + } + + // If any of the pending operations failed, then we don't need to do this. + if (s_nPendingError != NO_ERROR) { + return s_nPendingError; + } + + if (pDetour == NULL) { + return ERROR_INVALID_PARAMETER; + } + if (ppPointer == NULL) { + return ERROR_INVALID_HANDLE; + } + if (*ppPointer == NULL) { + error = ERROR_INVALID_HANDLE; + s_nPendingError = error; + s_ppPendingError = ppPointer; + DETOUR_BREAK(); + return error; + } + + DetourOperation *o = new NOTHROW DetourOperation; + if (o == NULL) { + error = ERROR_NOT_ENOUGH_MEMORY; + fail: + s_nPendingError = error; + DETOUR_BREAK(); + stop: + if (o != NULL) { + delete o; + o = NULL; + } + s_ppPendingError = ppPointer; + return error; + } + + +#ifdef DETOURS_IA64 + PPLABEL_DESCRIPTOR ppldTrampo = (PPLABEL_DESCRIPTOR)*ppPointer; + PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour; + PVOID pDetourGlobals = NULL; + PVOID pTrampoGlobals = NULL; + + pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals); + PDETOUR_TRAMPOLINE pTrampoline = (PDETOUR_TRAMPOLINE) + DetourCodeFromPointer(ppldTrampo, &pTrampoGlobals); + DETOUR_TRACE((" ppldDetour=%p, code=%p [gp=%p]\n", + ppldDetour, pDetour, pDetourGlobals)); + DETOUR_TRACE((" ppldTrampo=%p, code=%p [gp=%p]\n", + ppldTrampo, pTrampoline, pTrampoGlobals)); + + + DETOUR_TRACE(("\n")); + DETOUR_TRACE(("detours: &pldTrampoline =%p\n", + &pTrampoline->pldTrampoline)); + DETOUR_TRACE(("detours: &bMovlTargetGp =%p [%p]\n", + &pTrampoline->bMovlTargetGp, + pTrampoline->bMovlTargetGp.GetMovlGp())); + DETOUR_TRACE(("detours: &rbCode =%p [%p]\n", + &pTrampoline->rbCode, + ((DETOUR_IA64_BUNDLE&)pTrampoline->rbCode).GetBrlTarget())); + DETOUR_TRACE(("detours: &bBrlRemainEip =%p [%p]\n", + &pTrampoline->bBrlRemainEip, + pTrampoline->bBrlRemainEip.GetBrlTarget())); + DETOUR_TRACE(("detours: &bMovlDetourGp =%p [%p]\n", + &pTrampoline->bMovlDetourGp, + pTrampoline->bMovlDetourGp.GetMovlGp())); + DETOUR_TRACE(("detours: &bBrlDetourEip =%p [%p]\n", + &pTrampoline->bCallDetour, + pTrampoline->bCallDetour.GetBrlTarget())); + DETOUR_TRACE(("detours: pldDetour =%p [%p]\n", + pTrampoline->ppldDetour->EntryPoint, + pTrampoline->ppldDetour->GlobalPointer)); + DETOUR_TRACE(("detours: pldTarget =%p [%p]\n", + pTrampoline->ppldTarget->EntryPoint, + pTrampoline->ppldTarget->GlobalPointer)); + DETOUR_TRACE(("detours: pbRemain =%p\n", + pTrampoline->pbRemain)); + DETOUR_TRACE(("detours: pbDetour =%p\n", + pTrampoline->pbDetour)); + DETOUR_TRACE(("\n")); +#else // !DETOURS_IA64 + PDETOUR_TRAMPOLINE pTrampoline = + (PDETOUR_TRAMPOLINE)DetourCodeFromPointer(*ppPointer, NULL); + pDetour = DetourCodeFromPointer(pDetour, NULL); +#endif // !DETOURS_IA64 + + ////////////////////////////////////// Verify that Trampoline is in place. + // + LONG cbTarget = pTrampoline->cbRestore; + PBYTE pbTarget = pTrampoline->pbRemain - cbTarget; + if (cbTarget == 0 || cbTarget > sizeof(pTrampoline->rbCode)) { + error = ERROR_INVALID_BLOCK; + if (s_fIgnoreTooSmall) { + goto stop; + } + else { + DETOUR_BREAK(); + goto fail; + } + } + + if (pTrampoline->pbDetour != pDetour) { + error = ERROR_INVALID_BLOCK; + if (s_fIgnoreTooSmall) { + goto stop; + } + else { + DETOUR_BREAK(); + goto fail; + } + } + + DWORD dwOld = 0; + if (!VirtualProtect(pbTarget, cbTarget, + PAGE_EXECUTE_READWRITE, &dwOld)) { + error = GetLastError(); + DETOUR_BREAK(); + goto fail; + } + + o->fIsRemove = TRUE; + o->ppbPointer = (PBYTE*)ppPointer; + o->pTrampoline = pTrampoline; + o->pbTarget = pbTarget; + o->dwPerm = dwOld; + o->pNext = s_pPendingOperations; + s_pPendingOperations = o; + + return NO_ERROR; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Helpers for manipulating page protection. +// + +// For reference: +// PAGE_NOACCESS 0x01 +// PAGE_READONLY 0x02 +// PAGE_READWRITE 0x04 +// PAGE_WRITECOPY 0x08 +// PAGE_EXECUTE 0x10 +// PAGE_EXECUTE_READ 0x20 +// PAGE_EXECUTE_READWRITE 0x40 +// PAGE_EXECUTE_WRITECOPY 0x80 +// PAGE_GUARD ... +// PAGE_NOCACHE ... +// PAGE_WRITECOMBINE ... + +#define DETOUR_PAGE_EXECUTE_ALL (PAGE_EXECUTE | \ + PAGE_EXECUTE_READ | \ + PAGE_EXECUTE_READWRITE | \ + PAGE_EXECUTE_WRITECOPY) + +#define DETOUR_PAGE_NO_EXECUTE_ALL (PAGE_NOACCESS | \ + PAGE_READONLY | \ + PAGE_READWRITE | \ + PAGE_WRITECOPY) + +#define DETOUR_PAGE_ATTRIBUTES (~(DETOUR_PAGE_EXECUTE_ALL | DETOUR_PAGE_NO_EXECUTE_ALL)) + +C_ASSERT((DETOUR_PAGE_NO_EXECUTE_ALL << 4) == DETOUR_PAGE_EXECUTE_ALL); + +static DWORD DetourPageProtectAdjustExecute(_In_ DWORD dwOldProtect, + _In_ DWORD dwNewProtect) +// Copy EXECUTE from dwOldProtect to dwNewProtect. +{ + bool const fOldExecute = ((dwOldProtect & DETOUR_PAGE_EXECUTE_ALL) != 0); + bool const fNewExecute = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) != 0); + + if (fOldExecute && !fNewExecute) { + dwNewProtect = ((dwNewProtect & DETOUR_PAGE_NO_EXECUTE_ALL) << 4) + | (dwNewProtect & DETOUR_PAGE_ATTRIBUTES); + } + else if (!fOldExecute && fNewExecute) { + dwNewProtect = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) >> 4) + | (dwNewProtect & DETOUR_PAGE_ATTRIBUTES); + } + return dwNewProtect; +} + +_Success_(return != FALSE) +BOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_ HANDLE hProcess, + _In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect) +// Some systems do not allow executability of a page to change. This function applies +// dwNewProtect to [pAddress, nSize), but preserving the previous executability. +// This function is meant to be a drop-in replacement for some uses of VirtualProtectEx. +// When "restoring" page protection, there is no need to use this function. +{ + MEMORY_BASIC_INFORMATION mbi; + + // Query to get existing execute access. + + ZeroMemory(&mbi, sizeof(mbi)); + + if (VirtualQueryEx(hProcess, pAddress, &mbi, sizeof(mbi)) == 0) { + return FALSE; + } + return VirtualProtectEx(hProcess, pAddress, nSize, + DetourPageProtectAdjustExecute(mbi.Protect, dwNewProtect), + pdwOldProtect); +} + +_Success_(return != FALSE) +BOOL WINAPI DetourVirtualProtectSameExecute(_In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect) +{ + return DetourVirtualProtectSameExecuteEx(GetCurrentProcess(), + pAddress, nSize, dwNewProtect, pdwOldProtect); +} + +// End of File diff --git a/ext/detours/src/detours.h b/ext/detours/src/detours.h new file mode 100644 index 0000000..c08b6f1 --- /dev/null +++ b/ext/detours/src/detours.h @@ -0,0 +1,1132 @@ +///////////////////////////////////////////////////////////////////////////// +// +// 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 + +////////////////////////////////////////////////////////////////////////////// +// + +#ifdef DETOURS_INTERNAL + +#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 +#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1 + +#pragma warning(disable:4068) // unknown pragma (suppress) + +#if _MSC_VER >= 1900 +#pragma warning(push) +#pragma warning(disable:4091) // empty typedef +#endif + +// Suppress declspec(dllimport) for the sake of Detours +// users that provide kernel32 functionality themselves. +// This is ok in the mainstream case, it will just cost +// an extra instruction calling some functions, which +// LTCG optimizes away. +// +#define _KERNEL32_ 1 +#define _USER32_ 1 + +#include <windows.h> +#if (_MSC_VER < 1310) +#else +#pragma warning(push) +#if _MSC_VER > 1400 +#pragma warning(disable:6102 6103) // /analyze warnings +#endif +#include <strsafe.h> +#pragma warning(pop) +#endif + +// From winerror.h, as this error isn't found in some SDKs: +// +// MessageId: ERROR_DYNAMIC_CODE_BLOCKED +// +// MessageText: +// +// The operation was blocked as the process prohibits dynamic code generation. +// +#define ERROR_DYNAMIC_CODE_BLOCKED 1655L + +#endif // DETOURS_INTERNAL + +////////////////////////////////////////////////////////////////////////////// +// + +#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 + +/////////////////////////////////////////////////////////////// Helper Macros. +// +#define DETOURS_STRINGIFY_(x) #x +#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) + +#define VER_DETOURS_BITS DETOURS_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_ecount +#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_ecount +#define __in_ecount(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; // all environments have this +#ifdef IMAGE_NT_OPTIONAL_HDR32_MAGIC // some environments do not have this + IMAGE_NT_HEADERS32 inh32; +#endif +#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC // some environments do not have this + IMAGE_NT_HEADERS64 inh64; +#endif +#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC // some environments do not have this + BYTE raw[sizeof(IMAGE_NT_HEADERS64) + + sizeof(IMAGE_SECTION_HEADER) * 32]; +#else + BYTE raw[0x108 + sizeof(IMAGE_SECTION_HEADER) * 32]; +#endif + }; + DETOUR_CLR_HEADER clr; + +} DETOUR_EXE_RESTORE, *PDETOUR_EXE_RESTORE; + +#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC +C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == 0x108); +#endif + +// The size can change, but assert for clarity due to the muddying #ifdefs. +#ifdef _WIN64 +C_ASSERT(sizeof(DETOUR_EXE_RESTORE) == 0x688); +#else +C_ASSERT(sizeof(DETOUR_EXE_RESTORE) == 0x678); +#endif + +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,\ +} + +///////////////////////////////////////////////////////////// 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); +PVOID WINAPI DetourAllocateRegionWithinJumpBounds(_In_ LPCVOID pbTarget, + _Out_ PDWORD pcbAllocatedSize); + +///////////////////////////////////////////////////// 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 <imagehlp.h> +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 <dbghelp.h> +#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 <stdio.h> +#include <limits.h> +#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/ext/detours/src/detver.h b/ext/detours/src/detver.h new file mode 100644 index 0000000..3d4f544 --- /dev/null +++ b/ext/detours/src/detver.h @@ -0,0 +1,27 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Common version parameters. +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#define _USING_V110_SDK71_ 1 +#include "winver.h" +#if 0 +#include <windows.h> +#include <detours.h> +#else +#ifndef DETOURS_STRINGIFY +#define DETOURS_STRINGIFY_(x) #x +#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) +#endif + +#define VER_FILEFLAGSMASK 0x3fL +#define VER_FILEFLAGS 0x0L +#define VER_FILEOS 0x00040004L +#define VER_FILETYPE 0x00000002L +#define VER_FILESUBTYPE 0x00000000L +#endif +#define VER_DETOURS_BITS DETOURS_STRINGIFY(DETOURS_BITS) diff --git a/ext/detours/src/disasm.cpp b/ext/detours/src/disasm.cpp new file mode 100644 index 0000000..c12c1c1 --- /dev/null +++ b/ext/detours/src/disasm.cpp @@ -0,0 +1,4301 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Detours Disassembler (disasm.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 <limits.h> + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +#undef ASSERT +#define ASSERT(x) + +////////////////////////////////////////////////////////////////////////////// +// +// Special macros to handle the case when we are building disassembler for +// offline processing. +// + + +#if defined(DETOURS_X86_OFFLINE_LIBRARY) \ + || defined(DETOURS_X64_OFFLINE_LIBRARY) \ + || defined(DETOURS_ARM_OFFLINE_LIBRARY) \ + || defined(DETOURS_ARM64_OFFLINE_LIBRARY) \ + || defined(DETOURS_IA64_OFFLINE_LIBRARY) + +#undef DETOURS_X64 +#undef DETOURS_X86 +#undef DETOURS_IA64 +#undef DETOURS_ARM +#undef DETOURS_ARM64 + +#if defined(DETOURS_X86_OFFLINE_LIBRARY) + +#define DetourCopyInstruction DetourCopyInstructionX86 +#define DetourSetCodeModule DetourSetCodeModuleX86 +#define CDetourDis CDetourDisX86 +#define DETOURS_X86 + +#elif defined(DETOURS_X64_OFFLINE_LIBRARY) + +#if !defined(DETOURS_64BIT) +// Fix this as/if bugs are discovered. +//#error X64 disassembler can only build for 64-bit. +#endif + +#define DetourCopyInstruction DetourCopyInstructionX64 +#define DetourSetCodeModule DetourSetCodeModuleX64 +#define CDetourDis CDetourDisX64 +#define DETOURS_X64 + +#elif defined(DETOURS_ARM_OFFLINE_LIBRARY) + +#define DetourCopyInstruction DetourCopyInstructionARM +#define DetourSetCodeModule DetourSetCodeModuleARM +#define CDetourDis CDetourDisARM +#define DETOURS_ARM + +#elif defined(DETOURS_ARM64_OFFLINE_LIBRARY) + +#define DetourCopyInstruction DetourCopyInstructionARM64 +#define DetourSetCodeModule DetourSetCodeModuleARM64 +#define CDetourDis CDetourDisARM64 +#define DETOURS_ARM64 + +#elif defined(DETOURS_IA64_OFFLINE_LIBRARY) + +#define DetourCopyInstruction DetourCopyInstructionIA64 +#define DetourSetCodeModule DetourSetCodeModuleIA64 +#define DETOURS_IA64 + +#else + +#error + +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Function: +// DetourCopyInstruction(PVOID pDst, +// PVOID *ppDstPool +// PVOID pSrc, +// PVOID *ppTarget, +// LONG *plExtra) +// Purpose: +// Copy a single instruction from pSrc to pDst. +// +// Arguments: +// pDst: +// Destination address for the instruction. May be NULL in which +// case DetourCopyInstruction is used to measure an instruction. +// If not NULL then the source instruction is copied to the +// destination instruction and any relative arguments are adjusted. +// ppDstPool: +// Destination address for the end of the constant pool. The +// constant pool works backwards toward pDst. All memory between +// pDst and *ppDstPool must be available for use by this function. +// ppDstPool may be NULL if pDst is NULL. +// pSrc: +// Source address of the instruction. +// ppTarget: +// Out parameter for any target instruction address pointed to by +// the instruction. For example, a branch or a jump insruction has +// a target, but a load or store instruction doesn't. A target is +// another instruction that may be executed as a result of this +// instruction. ppTarget may be NULL. +// plExtra: +// Out parameter for the number of extra bytes needed by the +// instruction to reach the target. For example, lExtra = 3 if the +// instruction had an 8-bit relative offset, but needs a 32-bit +// relative offset. +// +// Returns: +// Returns the address of the next instruction (following in the source) +// instruction. By subtracting pSrc from the return value, the caller +// can determinte the size of the instruction copied. +// +// Comments: +// By following the pTarget, the caller can follow alternate +// instruction streams. However, it is not always possible to determine +// the target based on static analysis. For example, the destination of +// a jump relative to a register cannot be determined from just the +// instruction stream. The output value, pTarget, can have any of the +// following outputs: +// DETOUR_INSTRUCTION_TARGET_NONE: +// The instruction has no targets. +// DETOUR_INSTRUCTION_TARGET_DYNAMIC: +// The instruction has a non-deterministic (dynamic) target. +// (i.e. the jump is to an address held in a register.) +// Address: The instruction has the specified target. +// +// When copying instructions, DetourCopyInstruction insures that any +// targets remain constant. It does so by adjusting any IP relative +// offsets. +// + +#pragma data_seg(".detourd") +#pragma const_seg(".detourc") + +//////////////////////////////////////////////////// X86 and X64 Disassembler. +// +// Includes full support for all x86 chips prior to the Pentium III, and some newer stuff. +// +#if defined(DETOURS_X64) || defined(DETOURS_X86) + +class CDetourDis +{ + public: + CDetourDis(_Out_opt_ PBYTE *ppbTarget, + _Out_opt_ LONG *plExtra); + + PBYTE CopyInstruction(PBYTE pbDst, PBYTE pbSrc); + static BOOL SanityCheckSystem(); + static BOOL SetCodeModule(PBYTE pbBeg, PBYTE pbEnd, BOOL fLimitReferencesToModule); + + public: + struct COPYENTRY; + typedef const COPYENTRY * REFCOPYENTRY; + + typedef PBYTE (CDetourDis::* COPYFUNC)(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + + // nFlagBits flags. + enum { + DYNAMIC = 0x1u, + ADDRESS = 0x2u, + NOENLARGE = 0x4u, + RAX = 0x8u, + }; + + // ModR/M Flags + enum { + SIB = 0x10u, + RIP = 0x20u, + NOTSIB = 0x0fu, + }; + + struct COPYENTRY + { + // Many of these fields are often ignored. See ENTRY_DataIgnored. + ULONG nFixedSize : 4; // Fixed size of opcode + ULONG nFixedSize16 : 4; // Fixed size when 16 bit operand + ULONG nModOffset : 4; // Offset to mod/rm byte (0=none) + ULONG nRelOffset : 4; // Offset to relative target. + ULONG nFlagBits : 4; // Flags for DYNAMIC, etc. + COPYFUNC pfCopy; // Function pointer. + }; + + protected: +// These macros define common uses of nFixedSize, nFixedSize16, nModOffset, nRelOffset, nFlagBits, pfCopy. +#define ENTRY_DataIgnored 0, 0, 0, 0, 0, +#define ENTRY_CopyBytes1 { 1, 1, 0, 0, 0, &CDetourDis::CopyBytes } +#ifdef DETOURS_X64 +#define ENTRY_CopyBytes1Address { 9, 5, 0, 0, ADDRESS, &CDetourDis::CopyBytes } +#else +#define ENTRY_CopyBytes1Address { 5, 3, 0, 0, ADDRESS, &CDetourDis::CopyBytes } +#endif +#define ENTRY_CopyBytes1Dynamic { 1, 1, 0, 0, DYNAMIC, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes2 { 2, 2, 0, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes2Jump { ENTRY_DataIgnored &CDetourDis::CopyBytesJump } +#define ENTRY_CopyBytes2CantJump { 2, 2, 0, 1, NOENLARGE, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes2Dynamic { 2, 2, 0, 0, DYNAMIC, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes3 { 3, 3, 0, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes3Dynamic { 3, 3, 0, 0, DYNAMIC, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes3Or5 { 5, 3, 0, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes3Or5Dynamic { 5, 3, 0, 0, DYNAMIC, &CDetourDis::CopyBytes }// x86 only +#ifdef DETOURS_X64 +#define ENTRY_CopyBytes3Or5Rax { 5, 3, 0, 0, RAX, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes3Or5Target { 5, 5, 0, 1, 0, &CDetourDis::CopyBytes } +#else +#define ENTRY_CopyBytes3Or5Rax { 5, 3, 0, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes3Or5Target { 5, 3, 0, 1, 0, &CDetourDis::CopyBytes } +#endif +#define ENTRY_CopyBytes4 { 4, 4, 0, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes5 { 5, 5, 0, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes5Or7Dynamic { 7, 5, 0, 0, DYNAMIC, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes7 { 7, 7, 0, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes2Mod { 2, 2, 1, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes2ModDynamic { 2, 2, 1, 0, DYNAMIC, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes2Mod1 { 3, 3, 1, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes2ModOperand { 6, 4, 1, 0, 0, &CDetourDis::CopyBytes } +#define ENTRY_CopyBytes3Mod { 3, 3, 2, 0, 0, &CDetourDis::CopyBytes } // SSE3 0F 38 opcode modrm +#define ENTRY_CopyBytes3Mod1 { 4, 4, 2, 0, 0, &CDetourDis::CopyBytes } // SSE3 0F 3A opcode modrm .. imm8 +#define ENTRY_CopyBytesPrefix { ENTRY_DataIgnored &CDetourDis::CopyBytesPrefix } +#define ENTRY_CopyBytesSegment { ENTRY_DataIgnored &CDetourDis::CopyBytesSegment } +#define ENTRY_CopyBytesRax { ENTRY_DataIgnored &CDetourDis::CopyBytesRax } +#define ENTRY_CopyF2 { ENTRY_DataIgnored &CDetourDis::CopyF2 } +#define ENTRY_CopyF3 { ENTRY_DataIgnored &CDetourDis::CopyF3 } // 32bit x86 only +#define ENTRY_Copy0F { ENTRY_DataIgnored &CDetourDis::Copy0F } +#define ENTRY_Copy0F78 { ENTRY_DataIgnored &CDetourDis::Copy0F78 } +#define ENTRY_Copy0F00 { ENTRY_DataIgnored &CDetourDis::Copy0F00 } // 32bit x86 only +#define ENTRY_Copy0FB8 { ENTRY_DataIgnored &CDetourDis::Copy0FB8 } // 32bit x86 only +#define ENTRY_Copy66 { ENTRY_DataIgnored &CDetourDis::Copy66 } +#define ENTRY_Copy67 { ENTRY_DataIgnored &CDetourDis::Copy67 } +#define ENTRY_CopyF6 { ENTRY_DataIgnored &CDetourDis::CopyF6 } +#define ENTRY_CopyF7 { ENTRY_DataIgnored &CDetourDis::CopyF7 } +#define ENTRY_CopyFF { ENTRY_DataIgnored &CDetourDis::CopyFF } +#define ENTRY_CopyVex2 { ENTRY_DataIgnored &CDetourDis::CopyVex2 } +#define ENTRY_CopyVex3 { ENTRY_DataIgnored &CDetourDis::CopyVex3 } +#define ENTRY_CopyEvex { ENTRY_DataIgnored &CDetourDis::CopyEvex } // 62, 3 byte payload, then normal with implied prefixes like vex +#define ENTRY_CopyXop { ENTRY_DataIgnored &CDetourDis::CopyXop } // 0x8F ... POP /0 or AMD XOP +#define ENTRY_CopyBytesXop { 5, 5, 4, 0, 0, &CDetourDis::CopyBytes } // 0x8F xop1 xop2 opcode modrm +#define ENTRY_CopyBytesXop1 { 6, 6, 4, 0, 0, &CDetourDis::CopyBytes } // 0x8F xop1 xop2 opcode modrm ... imm8 +#define ENTRY_CopyBytesXop4 { 9, 9, 4, 0, 0, &CDetourDis::CopyBytes } // 0x8F xop1 xop2 opcode modrm ... imm32 +#define ENTRY_Invalid { ENTRY_DataIgnored &CDetourDis::Invalid } + + PBYTE CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyBytesPrefix(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyBytesSegment(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyBytesRax(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyBytesJump(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + + PBYTE Invalid(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + + PBYTE AdjustTarget(PBYTE pbDst, PBYTE pbSrc, UINT cbOp, + UINT cbTargetOffset, UINT cbTargetSize); + + protected: + PBYTE Copy0F(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE Copy0F00(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // x86 only sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7 + PBYTE Copy0F78(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // vmread, 66/extrq/ib/ib, F2/insertq/ib/ib + PBYTE Copy0FB8(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // jmpe or F3/popcnt + PBYTE Copy66(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE Copy67(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyF2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyF3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // x86 only + PBYTE CopyF6(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyF7(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyVex2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyVex3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyVexEvexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc, BYTE p); + PBYTE CopyEvex(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyXop(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + + protected: + static const COPYENTRY s_rceCopyTable[]; + static const COPYENTRY s_rceCopyTable0F[]; + static const BYTE s_rbModRm[256]; + static PBYTE s_pbModuleBeg; + static PBYTE s_pbModuleEnd; + static BOOL s_fLimitReferencesToModule; + + protected: + BOOL m_bOperandOverride; + BOOL m_bAddressOverride; + BOOL m_bRaxOverride; // AMD64 only + BOOL m_bVex; + BOOL m_bEvex; + BOOL m_bF2; + BOOL m_bF3; // x86 only + BYTE m_nSegmentOverride; + + PBYTE * m_ppbTarget; + LONG * m_plExtra; + + LONG m_lScratchExtra; + PBYTE m_pbScratchTarget; + BYTE m_rbScratchDst[64]; // matches or exceeds rbCode +}; + +PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, + _Inout_opt_ PVOID *ppDstPool, + _In_ PVOID pSrc, + _Out_opt_ PVOID *ppTarget, + _Out_opt_ LONG *plExtra) +{ + UNREFERENCED_PARAMETER(ppDstPool); // x86 & x64 don't use a constant pool. + + CDetourDis oDetourDisasm((PBYTE*)ppTarget, plExtra); + return oDetourDisasm.CopyInstruction((PBYTE)pDst, (PBYTE)pSrc); +} + +/////////////////////////////////////////////////////////// Disassembler Code. +// +CDetourDis::CDetourDis(_Out_opt_ PBYTE *ppbTarget, _Out_opt_ LONG *plExtra) : + m_bOperandOverride(FALSE), + m_bAddressOverride(FALSE), + m_bRaxOverride(FALSE), + m_bF2(FALSE), + m_bF3(FALSE), + m_bVex(FALSE), + m_bEvex(FALSE) +{ + m_ppbTarget = ppbTarget ? ppbTarget : &m_pbScratchTarget; + m_plExtra = plExtra ? plExtra : &m_lScratchExtra; + + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_NONE; + *m_plExtra = 0; +} + +PBYTE CDetourDis::CopyInstruction(PBYTE pbDst, PBYTE pbSrc) +{ + // Configure scratch areas if real areas are not available. + if (NULL == pbDst) { + pbDst = m_rbScratchDst; + } + if (NULL == pbSrc) { + // We can't copy a non-existent instruction. + SetLastError(ERROR_INVALID_DATA); + return NULL; + } + + // Figure out how big the instruction is, do the appropriate copy, + // and figure out what the target of the instruction is if any. + // + REFCOPYENTRY pEntry = &s_rceCopyTable[pbSrc[0]]; + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + UINT nBytesFixed; + + if (m_bVex || m_bEvex) + { + ASSERT(pEntry->nFlagBits == 0); + ASSERT(pEntry->nFixedSize == pEntry->nFixedSize16); + } + + UINT const nModOffset = pEntry->nModOffset; + UINT const nFlagBits = pEntry->nFlagBits; + UINT const nFixedSize = pEntry->nFixedSize; + UINT const nFixedSize16 = pEntry->nFixedSize16; + + if (nFlagBits & ADDRESS) { + nBytesFixed = m_bAddressOverride ? nFixedSize16 : nFixedSize; + } +#ifdef DETOURS_X64 + // REX.W trumps 66 + else if (m_bRaxOverride) { + nBytesFixed = nFixedSize + ((nFlagBits & RAX) ? 4 : 0); + } +#endif + else { + nBytesFixed = m_bOperandOverride ? nFixedSize16 : nFixedSize; + } + + UINT nBytes = nBytesFixed; + UINT nRelOffset = pEntry->nRelOffset; + UINT cbTarget = nBytes - nRelOffset; + if (nModOffset > 0) { + ASSERT(nRelOffset == 0); + BYTE const bModRm = pbSrc[nModOffset]; + BYTE const bFlags = s_rbModRm[bModRm]; + + nBytes += bFlags & NOTSIB; + + if (bFlags & SIB) { + BYTE const bSib = pbSrc[nModOffset + 1]; + + if ((bSib & 0x07) == 0x05) { + if ((bModRm & 0xc0) == 0x00) { + nBytes += 4; + } + else if ((bModRm & 0xc0) == 0x40) { + nBytes += 1; + } + else if ((bModRm & 0xc0) == 0x80) { + nBytes += 4; + } + } + cbTarget = nBytes - nRelOffset; + } +#ifdef DETOURS_X64 + else if (bFlags & RIP) { + nRelOffset = nModOffset + 1; + cbTarget = 4; + } +#endif + } + CopyMemory(pbDst, pbSrc, nBytes); + + if (nRelOffset) { + *m_ppbTarget = AdjustTarget(pbDst, pbSrc, nBytes, nRelOffset, cbTarget); +#ifdef DETOURS_X64 + if (pEntry->nRelOffset == 0) { + // This is a data target, not a code target, so we shouldn't return it. + *m_ppbTarget = NULL; + } +#endif + } + if (nFlagBits & NOENLARGE) { + *m_plExtra = -*m_plExtra; + } + if (nFlagBits & DYNAMIC) { + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + return pbSrc + nBytes; +} + +PBYTE CDetourDis::CopyBytesPrefix(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + pbDst[0] = pbSrc[0]; + pEntry = &s_rceCopyTable[pbSrc[1]]; + return (this->*pEntry->pfCopy)(pEntry, pbDst + 1, pbSrc + 1); +} + +PBYTE CDetourDis::CopyBytesSegment(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ + m_nSegmentOverride = pbSrc[0]; + return CopyBytesPrefix(0, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyBytesRax(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ // AMD64 only + if (pbSrc[0] & 0x8) { + m_bRaxOverride = TRUE; + } + return CopyBytesPrefix(0, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyBytesJump(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + (void)pEntry; + + PVOID pvSrcAddr = &pbSrc[1]; + PVOID pvDstAddr = NULL; + LONG_PTR nOldOffset = (LONG_PTR)*(signed char*&)pvSrcAddr; + LONG_PTR nNewOffset = 0; + + *m_ppbTarget = pbSrc + 2 + nOldOffset; + + if (pbSrc[0] == 0xeb) { + pbDst[0] = 0xe9; + pvDstAddr = &pbDst[1]; + nNewOffset = nOldOffset - ((pbDst - pbSrc) + 3); + *(UNALIGNED LONG*&)pvDstAddr = (LONG)nNewOffset; + + *m_plExtra = 3; + return pbSrc + 2; + } + + ASSERT(pbSrc[0] >= 0x70 && pbSrc[0] <= 0x7f); + + pbDst[0] = 0x0f; + pbDst[1] = 0x80 | (pbSrc[0] & 0xf); + pvDstAddr = &pbDst[2]; + nNewOffset = nOldOffset - ((pbDst - pbSrc) + 4); + *(UNALIGNED LONG*&)pvDstAddr = (LONG)nNewOffset; + + *m_plExtra = 4; + return pbSrc + 2; +} + +PBYTE CDetourDis::AdjustTarget(PBYTE pbDst, PBYTE pbSrc, UINT cbOp, + UINT cbTargetOffset, UINT cbTargetSize) +{ + PBYTE pbTarget = NULL; +#if 1 // fault injection to test test code +#if defined(DETOURS_X64) + typedef LONGLONG T; +#else + typedef LONG T; +#endif + T nOldOffset; + T nNewOffset; + PVOID pvTargetAddr = &pbDst[cbTargetOffset]; + + switch (cbTargetSize) { + case 1: + nOldOffset = *(signed char*&)pvTargetAddr; + break; + case 2: + nOldOffset = *(UNALIGNED SHORT*&)pvTargetAddr; + break; + case 4: + nOldOffset = *(UNALIGNED LONG*&)pvTargetAddr; + break; +#if defined(DETOURS_X64) + case 8: + nOldOffset = *(UNALIGNED LONGLONG*&)pvTargetAddr; + break; +#endif + default: + ASSERT(!"cbTargetSize is invalid."); + nOldOffset = 0; + break; + } + + pbTarget = pbSrc + cbOp + nOldOffset; + nNewOffset = nOldOffset - (T)(pbDst - pbSrc); + + switch (cbTargetSize) { + case 1: + *(CHAR*&)pvTargetAddr = (CHAR)nNewOffset; + if (nNewOffset < SCHAR_MIN || nNewOffset > SCHAR_MAX) { + *m_plExtra = sizeof(ULONG) - 1; + } + break; + case 2: + *(UNALIGNED SHORT*&)pvTargetAddr = (SHORT)nNewOffset; + if (nNewOffset < SHRT_MIN || nNewOffset > SHRT_MAX) { + *m_plExtra = sizeof(ULONG) - 2; + } + break; + case 4: + *(UNALIGNED LONG*&)pvTargetAddr = (LONG)nNewOffset; + if (nNewOffset < LONG_MIN || nNewOffset > LONG_MAX) { + *m_plExtra = sizeof(ULONG) - 4; + } + break; +#if defined(DETOURS_X64) + case 8: + *(UNALIGNED LONGLONG*&)pvTargetAddr = nNewOffset; + break; +#endif + } +#ifdef DETOURS_X64 + // When we are only computing size, source and dest can be + // far apart, distance not encodable in 32bits. Ok. + // At least still check the lower 32bits. + + if (pbDst >= m_rbScratchDst && pbDst < (sizeof(m_rbScratchDst) + m_rbScratchDst)) { + ASSERT((((size_t)pbDst + cbOp + nNewOffset) & 0xFFFFFFFF) == (((size_t)pbTarget) & 0xFFFFFFFF)); + } + else +#endif + { + ASSERT(pbDst + cbOp + nNewOffset == pbTarget); + } +#endif + return pbTarget; +} + +PBYTE CDetourDis::Invalid(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + (void)pbDst; + (void)pEntry; + ASSERT(!"Invalid Instruction"); + return pbSrc + 1; +} + +////////////////////////////////////////////////////// Individual Bytes Codes. +// +PBYTE CDetourDis::Copy0F(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + pbDst[0] = pbSrc[0]; + pEntry = &s_rceCopyTable0F[pbSrc[1]]; + return (this->*pEntry->pfCopy)(pEntry, pbDst + 1, pbSrc + 1); +} + +PBYTE CDetourDis::Copy0F78(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ + // vmread, 66/extrq, F2/insertq + + static const COPYENTRY vmread = /* 78 */ ENTRY_CopyBytes2Mod; + static const COPYENTRY extrq_insertq = /* 78 */ ENTRY_CopyBytes4; + + ASSERT(!(m_bF2 && m_bOperandOverride)); + + // For insertq and presumably despite documentation extrq, mode must be 11, not checked. + // insertq/extrq/78 are followed by two immediate bytes, and given mode == 11, mod/rm byte is always one byte, + // and the 0x78 makes 4 bytes (not counting the 66/F2/F which are accounted for elsewhere) + + REFCOPYENTRY const pEntry = ((m_bF2 || m_bOperandOverride) ? &extrq_insertq : &vmread); + + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::Copy0F00(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ + // jmpe is 32bit x86 only + // Notice that the sizes are the same either way, but jmpe is marked as "dynamic". + + static const COPYENTRY other = /* B8 */ ENTRY_CopyBytes2Mod; // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6 invalid/7 + static const COPYENTRY jmpe = /* B8 */ ENTRY_CopyBytes2ModDynamic; // jmpe/6 x86-on-IA64 syscalls + + REFCOPYENTRY const pEntry = (((6 << 3) == ((7 << 3) & pbSrc[1])) ? &jmpe : &other); + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::Copy0FB8(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ + // jmpe is 32bit x86 only + + static const COPYENTRY popcnt = /* B8 */ ENTRY_CopyBytes2Mod; + static const COPYENTRY jmpe = /* B8 */ ENTRY_CopyBytes3Or5Dynamic; // jmpe x86-on-IA64 syscalls + REFCOPYENTRY const pEntry = m_bF3 ? &popcnt : &jmpe; + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::Copy66(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ // Operand-size override prefix + m_bOperandOverride = TRUE; + return CopyBytesPrefix(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::Copy67(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ // Address size override prefix + m_bAddressOverride = TRUE; + return CopyBytesPrefix(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyF2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + m_bF2 = TRUE; + return CopyBytesPrefix(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyF3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ // x86 only + m_bF3 = TRUE; + return CopyBytesPrefix(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyF6(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + (void)pEntry; + + // TEST BYTE /0 + if (0x00 == (0x38 & pbSrc[1])) { // reg(bits 543) of ModR/M == 0 + static const COPYENTRY ce = /* f6 */ ENTRY_CopyBytes2Mod1; + return (this->*ce.pfCopy)(&ce, pbDst, pbSrc); + } + // DIV /6 + // IDIV /7 + // IMUL /5 + // MUL /4 + // NEG /3 + // NOT /2 + + static const COPYENTRY ce = /* f6 */ ENTRY_CopyBytes2Mod; + return (this->*ce.pfCopy)(&ce, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyF7(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + (void)pEntry; + + // TEST WORD /0 + if (0x00 == (0x38 & pbSrc[1])) { // reg(bits 543) of ModR/M == 0 + static const COPYENTRY ce = /* f7 */ ENTRY_CopyBytes2ModOperand; + return (this->*ce.pfCopy)(&ce, pbDst, pbSrc); + } + + // DIV /6 + // IDIV /7 + // IMUL /5 + // MUL /4 + // NEG /3 + // NOT /2 + static const COPYENTRY ce = /* f7 */ ENTRY_CopyBytes2Mod; + return (this->*ce.pfCopy)(&ce, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ // INC /0 + // DEC /1 + // CALL /2 + // CALL /3 + // JMP /4 + // JMP /5 + // PUSH /6 + // invalid/7 + (void)pEntry; + + static const COPYENTRY ce = /* ff */ ENTRY_CopyBytes2Mod; + PBYTE pbOut = (this->*ce.pfCopy)(&ce, pbDst, pbSrc); + + BYTE const b1 = pbSrc[1]; + + if (0x15 == b1 || 0x25 == b1) { // CALL [], JMP [] +#ifdef DETOURS_X64 + // All segments but FS and GS are equivalent. + if (m_nSegmentOverride != 0x64 && m_nSegmentOverride != 0x65) +#else + if (m_nSegmentOverride == 0 || m_nSegmentOverride == 0x2E) +#endif + { +#ifdef DETOURS_X64 + INT32 offset = *(UNALIGNED INT32*)&pbSrc[2]; + PBYTE *ppbTarget = (PBYTE *)(pbSrc + 6 + offset); +#else + PBYTE *ppbTarget = (PBYTE *)(SIZE_T)*(UNALIGNED ULONG*)&pbSrc[2]; +#endif + if (s_fLimitReferencesToModule && + (ppbTarget < (PVOID)s_pbModuleBeg || ppbTarget >= (PVOID)s_pbModuleEnd)) { + + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + else { + // This can access violate on random bytes. Use DetourSetCodeModule. + *m_ppbTarget = *ppbTarget; + } + } + else { + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + } + else if (0x10 == (0x30 & b1) || // CALL /2 or /3 --> reg(bits 543) of ModR/M == 010 or 011 + 0x20 == (0x30 & b1)) { // JMP /4 or /5 --> reg(bits 543) of ModR/M == 100 or 101 + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + return pbOut; +} + +PBYTE CDetourDis::CopyVexEvexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc, BYTE p) +// m is first instead of last in the hopes of pbDst/pbSrc being +// passed along efficiently in the registers they were already in. +{ + static const COPYENTRY ceF38 = /* 38 */ ENTRY_CopyBytes2Mod; + static const COPYENTRY ceF3A = /* 3A */ ENTRY_CopyBytes2Mod1; + static const COPYENTRY ceInvalid = /* C4 */ ENTRY_Invalid; + + switch (p & 3) { + case 0: break; + case 1: m_bOperandOverride = TRUE; break; + case 2: m_bF3 = TRUE; break; + case 3: m_bF2 = TRUE; break; + } + + REFCOPYENTRY pEntry; + + switch (m) { + default: return Invalid(&ceInvalid, pbDst, pbSrc); + case 1: pEntry = &s_rceCopyTable0F[pbSrc[0]]; + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); + case 2: return CopyBytes(&ceF38, pbDst, pbSrc); + case 3: return CopyBytes(&ceF3A, pbDst, pbSrc); + } +} + +PBYTE CDetourDis::CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc) +// m is first instead of last in the hopes of pbDst/pbSrc being +// passed along efficiently in the registers they were already in. +{ + m_bVex = TRUE; + BYTE const p = (BYTE)(pbSrc[-1] & 3); // p in last byte + return CopyVexEvexCommon(m, pbDst, pbSrc, p); +} + + +PBYTE CDetourDis::CopyVex3(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +// 3 byte VEX prefix 0xC4 +{ +#ifdef DETOURS_X86 + const static COPYENTRY ceLES = /* C4 */ ENTRY_CopyBytes2Mod; + if ((pbSrc[1] & 0xC0) != 0xC0) { + REFCOPYENTRY pEntry = &ceLES; + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); + } +#endif + pbDst[0] = pbSrc[0]; + pbDst[1] = pbSrc[1]; + pbDst[2] = pbSrc[2]; +#ifdef DETOURS_X64 + m_bRaxOverride |= !!(pbSrc[2] & 0x80); // w in last byte, see CopyBytesRax +#else + // + // TODO + // + // Usually the VEX.W bit changes the size of a general purpose register and is ignored for 32bit. + // Sometimes it is an opcode extension. + // Look in the Intel manual, in the instruction-by-instruction reference, for ".W1", + // without nearby wording saying it is ignored for 32bit. + // For example: "VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values". + // + // Then, go through each such case and determine if W0 vs. W1 affect the size of the instruction. Probably not. + // Look for the same encoding but with "W1" changed to "W0". + // Here is one such pairing: + // VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values + // + // VEX.DDS.128.66.0F38.W1 98 /r A V/V FMA Multiply packed double-precision floating-point values + // from xmm0 and xmm2/mem, add to xmm1 and + // put result in xmm0. + // VFMADD132PD xmm0, xmm1, xmm2/m128 + // + // VFMADD132PS/VFMADD213PS/VFMADD231PS Fused Multiply-Add of Packed Single-Precision Floating-Point Values + // VEX.DDS.128.66.0F38.W0 98 /r A V/V FMA Multiply packed single-precision floating-point values + // from xmm0 and xmm2/mem, add to xmm1 and put + // result in xmm0. + // VFMADD132PS xmm0, xmm1, xmm2/m128 + // +#endif + return CopyVexCommon(pbSrc[1] & 0x1F, pbDst + 3, pbSrc + 3); +} + +PBYTE CDetourDis::CopyVex2(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +// 2 byte VEX prefix 0xC5 +{ +#ifdef DETOURS_X86 + const static COPYENTRY ceLDS = /* C5 */ ENTRY_CopyBytes2Mod; + if ((pbSrc[1] & 0xC0) != 0xC0) { + REFCOPYENTRY pEntry = &ceLDS; + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); + } +#endif + pbDst[0] = pbSrc[0]; + pbDst[1] = pbSrc[1]; + return CopyVexCommon(1, pbDst + 2, pbSrc + 2); +} + +PBYTE CDetourDis::CopyEvex(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +// 62, 3 byte payload, x86 with implied prefixes like Vex +// for 32bit, mode 0xC0 else fallback to bound /r +{ + // NOTE: Intel and Wikipedia number these differently. + // Intel says 0-2, Wikipedia says 1-3. + + BYTE const p0 = pbSrc[1]; + +#ifdef DETOURS_X86 + const static COPYENTRY ceBound = /* 62 */ ENTRY_CopyBytes2Mod; + if ((p0 & 0xC0) != 0xC0) { + return CopyBytes(&ceBound, pbDst, pbSrc); + } +#endif + + static const COPYENTRY ceInvalid = /* 62 */ ENTRY_Invalid; + + if ((p0 & 0x0C) != 0) + return Invalid(&ceInvalid, pbDst, pbSrc); + + BYTE const p1 = pbSrc[2]; + + if ((p1 & 0x04) != 0x04) + return Invalid(&ceInvalid, pbDst, pbSrc); + + // Copy 4 byte prefix. + *(UNALIGNED ULONG *)pbDst = *(UNALIGNED ULONG*)pbSrc; + + m_bEvex = TRUE; + +#ifdef DETOURS_X64 + m_bRaxOverride |= !!(p1 & 0x80); // w +#endif + + return CopyVexEvexCommon(p0 & 3u, pbDst + 4, pbSrc + 4, p1 & 3u); +} + +PBYTE CDetourDis::CopyXop(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +/* 3 byte AMD XOP prefix 0x8F +byte0: 0x8F +byte1: RXBmmmmm +byte2: WvvvvLpp +byte3: opcode +mmmmm >= 8, else pop +mmmmm only otherwise defined for 8, 9, A. +pp is like VEX but only instructions with 0 are defined +*/ +{ + const static COPYENTRY cePop = /* 8F */ ENTRY_CopyBytes2Mod; + const static COPYENTRY ceXop = /* 8F */ ENTRY_CopyBytesXop; + const static COPYENTRY ceXop1 = /* 8F */ ENTRY_CopyBytesXop1; + const static COPYENTRY ceXop4 = /* 8F */ ENTRY_CopyBytesXop4; + + BYTE const m = (BYTE)(pbSrc[1] & 0x1F); + ASSERT(m <= 10); + switch (m) + { + default: + return CopyBytes(&cePop, pbDst, pbSrc); + + case 8: // modrm with 8bit immediate + return CopyBytes(&ceXop1, pbDst, pbSrc); + + case 9: // modrm with no immediate + return CopyBytes(&ceXop, pbDst, pbSrc); + + case 10: // modrm with 32bit immediate + return CopyBytes(&ceXop4, pbDst, pbSrc); + } +} + +////////////////////////////////////////////////////////////////////////////// +// +PBYTE CDetourDis::s_pbModuleBeg = NULL; +PBYTE CDetourDis::s_pbModuleEnd = (PBYTE)~(ULONG_PTR)0; +BOOL CDetourDis::s_fLimitReferencesToModule = FALSE; + +BOOL CDetourDis::SetCodeModule(PBYTE pbBeg, PBYTE pbEnd, BOOL fLimitReferencesToModule) +{ + if (pbEnd < pbBeg) { + return FALSE; + } + + s_pbModuleBeg = pbBeg; + s_pbModuleEnd = pbEnd; + s_fLimitReferencesToModule = fLimitReferencesToModule; + + return TRUE; +} + +///////////////////////////////////////////////////////// Disassembler Tables. +// +const BYTE CDetourDis::s_rbModRm[256] = { + 0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 0x + 0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 1x + 0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 2x + 0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 3x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 4x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 5x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 6x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 7x + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // 8x + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // 9x + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // Ax + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // Bx + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Cx + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Dx + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Ex + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 // Fx +}; + +const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[] = +{ + /* 00 */ ENTRY_CopyBytes2Mod, // ADD /r + /* 01 */ ENTRY_CopyBytes2Mod, // ADD /r + /* 02 */ ENTRY_CopyBytes2Mod, // ADD /r + /* 03 */ ENTRY_CopyBytes2Mod, // ADD /r + /* 04 */ ENTRY_CopyBytes2, // ADD ib + /* 05 */ ENTRY_CopyBytes3Or5, // ADD iw +#ifdef DETOURS_X64 + /* 06 */ ENTRY_Invalid, // Invalid + /* 07 */ ENTRY_Invalid, // Invalid +#else + /* 06 */ ENTRY_CopyBytes1, // PUSH + /* 07 */ ENTRY_CopyBytes1, // POP +#endif + /* 08 */ ENTRY_CopyBytes2Mod, // OR /r + /* 09 */ ENTRY_CopyBytes2Mod, // OR /r + /* 0A */ ENTRY_CopyBytes2Mod, // OR /r + /* 0B */ ENTRY_CopyBytes2Mod, // OR /r + /* 0C */ ENTRY_CopyBytes2, // OR ib + /* 0D */ ENTRY_CopyBytes3Or5, // OR iw +#ifdef DETOURS_X64 + /* 0E */ ENTRY_Invalid, // Invalid +#else + /* 0E */ ENTRY_CopyBytes1, // PUSH +#endif + /* 0F */ ENTRY_Copy0F, // Extension Ops + /* 10 */ ENTRY_CopyBytes2Mod, // ADC /r + /* 11 */ ENTRY_CopyBytes2Mod, // ADC /r + /* 12 */ ENTRY_CopyBytes2Mod, // ADC /r + /* 13 */ ENTRY_CopyBytes2Mod, // ADC /r + /* 14 */ ENTRY_CopyBytes2, // ADC ib + /* 15 */ ENTRY_CopyBytes3Or5, // ADC id +#ifdef DETOURS_X64 + /* 16 */ ENTRY_Invalid, // Invalid + /* 17 */ ENTRY_Invalid, // Invalid +#else + /* 16 */ ENTRY_CopyBytes1, // PUSH + /* 17 */ ENTRY_CopyBytes1, // POP +#endif + /* 18 */ ENTRY_CopyBytes2Mod, // SBB /r + /* 19 */ ENTRY_CopyBytes2Mod, // SBB /r + /* 1A */ ENTRY_CopyBytes2Mod, // SBB /r + /* 1B */ ENTRY_CopyBytes2Mod, // SBB /r + /* 1C */ ENTRY_CopyBytes2, // SBB ib + /* 1D */ ENTRY_CopyBytes3Or5, // SBB id +#ifdef DETOURS_X64 + /* 1E */ ENTRY_Invalid, // Invalid + /* 1F */ ENTRY_Invalid, // Invalid +#else + /* 1E */ ENTRY_CopyBytes1, // PUSH + /* 1F */ ENTRY_CopyBytes1, // POP +#endif + /* 20 */ ENTRY_CopyBytes2Mod, // AND /r + /* 21 */ ENTRY_CopyBytes2Mod, // AND /r + /* 22 */ ENTRY_CopyBytes2Mod, // AND /r + /* 23 */ ENTRY_CopyBytes2Mod, // AND /r + /* 24 */ ENTRY_CopyBytes2, // AND ib + /* 25 */ ENTRY_CopyBytes3Or5, // AND id + /* 26 */ ENTRY_CopyBytesSegment, // ES prefix +#ifdef DETOURS_X64 + /* 27 */ ENTRY_Invalid, // Invalid +#else + /* 27 */ ENTRY_CopyBytes1, // DAA +#endif + /* 28 */ ENTRY_CopyBytes2Mod, // SUB /r + /* 29 */ ENTRY_CopyBytes2Mod, // SUB /r + /* 2A */ ENTRY_CopyBytes2Mod, // SUB /r + /* 2B */ ENTRY_CopyBytes2Mod, // SUB /r + /* 2C */ ENTRY_CopyBytes2, // SUB ib + /* 2D */ ENTRY_CopyBytes3Or5, // SUB id + /* 2E */ ENTRY_CopyBytesSegment, // CS prefix +#ifdef DETOURS_X64 + /* 2F */ ENTRY_Invalid, // Invalid +#else + /* 2F */ ENTRY_CopyBytes1, // DAS +#endif + /* 30 */ ENTRY_CopyBytes2Mod, // XOR /r + /* 31 */ ENTRY_CopyBytes2Mod, // XOR /r + /* 32 */ ENTRY_CopyBytes2Mod, // XOR /r + /* 33 */ ENTRY_CopyBytes2Mod, // XOR /r + /* 34 */ ENTRY_CopyBytes2, // XOR ib + /* 35 */ ENTRY_CopyBytes3Or5, // XOR id + /* 36 */ ENTRY_CopyBytesSegment, // SS prefix +#ifdef DETOURS_X64 + /* 37 */ ENTRY_Invalid, // Invalid +#else + /* 37 */ ENTRY_CopyBytes1, // AAA +#endif + /* 38 */ ENTRY_CopyBytes2Mod, // CMP /r + /* 39 */ ENTRY_CopyBytes2Mod, // CMP /r + /* 3A */ ENTRY_CopyBytes2Mod, // CMP /r + /* 3B */ ENTRY_CopyBytes2Mod, // CMP /r + /* 3C */ ENTRY_CopyBytes2, // CMP ib + /* 3D */ ENTRY_CopyBytes3Or5, // CMP id + /* 3E */ ENTRY_CopyBytesSegment, // DS prefix +#ifdef DETOURS_X64 + /* 3F */ ENTRY_Invalid, // Invalid +#else + /* 3F */ ENTRY_CopyBytes1, // AAS +#endif +#ifdef DETOURS_X64 // For Rax Prefix + /* 40 */ ENTRY_CopyBytesRax, // Rax + /* 41 */ ENTRY_CopyBytesRax, // Rax + /* 42 */ ENTRY_CopyBytesRax, // Rax + /* 43 */ ENTRY_CopyBytesRax, // Rax + /* 44 */ ENTRY_CopyBytesRax, // Rax + /* 45 */ ENTRY_CopyBytesRax, // Rax + /* 46 */ ENTRY_CopyBytesRax, // Rax + /* 47 */ ENTRY_CopyBytesRax, // Rax + /* 48 */ ENTRY_CopyBytesRax, // Rax + /* 49 */ ENTRY_CopyBytesRax, // Rax + /* 4A */ ENTRY_CopyBytesRax, // Rax + /* 4B */ ENTRY_CopyBytesRax, // Rax + /* 4C */ ENTRY_CopyBytesRax, // Rax + /* 4D */ ENTRY_CopyBytesRax, // Rax + /* 4E */ ENTRY_CopyBytesRax, // Rax + /* 4F */ ENTRY_CopyBytesRax, // Rax +#else + /* 40 */ ENTRY_CopyBytes1, // INC + /* 41 */ ENTRY_CopyBytes1, // INC + /* 42 */ ENTRY_CopyBytes1, // INC + /* 43 */ ENTRY_CopyBytes1, // INC + /* 44 */ ENTRY_CopyBytes1, // INC + /* 45 */ ENTRY_CopyBytes1, // INC + /* 46 */ ENTRY_CopyBytes1, // INC + /* 47 */ ENTRY_CopyBytes1, // INC + /* 48 */ ENTRY_CopyBytes1, // DEC + /* 49 */ ENTRY_CopyBytes1, // DEC + /* 4A */ ENTRY_CopyBytes1, // DEC + /* 4B */ ENTRY_CopyBytes1, // DEC + /* 4C */ ENTRY_CopyBytes1, // DEC + /* 4D */ ENTRY_CopyBytes1, // DEC + /* 4E */ ENTRY_CopyBytes1, // DEC + /* 4F */ ENTRY_CopyBytes1, // DEC +#endif + /* 50 */ ENTRY_CopyBytes1, // PUSH + /* 51 */ ENTRY_CopyBytes1, // PUSH + /* 52 */ ENTRY_CopyBytes1, // PUSH + /* 53 */ ENTRY_CopyBytes1, // PUSH + /* 54 */ ENTRY_CopyBytes1, // PUSH + /* 55 */ ENTRY_CopyBytes1, // PUSH + /* 56 */ ENTRY_CopyBytes1, // PUSH + /* 57 */ ENTRY_CopyBytes1, // PUSH + /* 58 */ ENTRY_CopyBytes1, // POP + /* 59 */ ENTRY_CopyBytes1, // POP + /* 5A */ ENTRY_CopyBytes1, // POP + /* 5B */ ENTRY_CopyBytes1, // POP + /* 5C */ ENTRY_CopyBytes1, // POP + /* 5D */ ENTRY_CopyBytes1, // POP + /* 5E */ ENTRY_CopyBytes1, // POP + /* 5F */ ENTRY_CopyBytes1, // POP +#ifdef DETOURS_X64 + /* 60 */ ENTRY_Invalid, // Invalid + /* 61 */ ENTRY_Invalid, // Invalid + /* 62 */ ENTRY_CopyEvex, // EVEX / AVX512 +#else + /* 60 */ ENTRY_CopyBytes1, // PUSHAD + /* 61 */ ENTRY_CopyBytes1, // POPAD + /* 62 */ ENTRY_CopyEvex, // BOUND /r and EVEX / AVX512 +#endif + /* 63 */ ENTRY_CopyBytes2Mod, // 32bit ARPL /r, 64bit MOVSXD + /* 64 */ ENTRY_CopyBytesSegment, // FS prefix + /* 65 */ ENTRY_CopyBytesSegment, // GS prefix + /* 66 */ ENTRY_Copy66, // Operand Prefix + /* 67 */ ENTRY_Copy67, // Address Prefix + /* 68 */ ENTRY_CopyBytes3Or5, // PUSH + /* 69 */ ENTRY_CopyBytes2ModOperand, // IMUL /r iz + /* 6A */ ENTRY_CopyBytes2, // PUSH + /* 6B */ ENTRY_CopyBytes2Mod1, // IMUL /r ib + /* 6C */ ENTRY_CopyBytes1, // INS + /* 6D */ ENTRY_CopyBytes1, // INS + /* 6E */ ENTRY_CopyBytes1, // OUTS/OUTSB + /* 6F */ ENTRY_CopyBytes1, // OUTS/OUTSW + /* 70 */ ENTRY_CopyBytes2Jump, // JO // 0f80 + /* 71 */ ENTRY_CopyBytes2Jump, // JNO // 0f81 + /* 72 */ ENTRY_CopyBytes2Jump, // JB/JC/JNAE // 0f82 + /* 73 */ ENTRY_CopyBytes2Jump, // JAE/JNB/JNC // 0f83 + /* 74 */ ENTRY_CopyBytes2Jump, // JE/JZ // 0f84 + /* 75 */ ENTRY_CopyBytes2Jump, // JNE/JNZ // 0f85 + /* 76 */ ENTRY_CopyBytes2Jump, // JBE/JNA // 0f86 + /* 77 */ ENTRY_CopyBytes2Jump, // JA/JNBE // 0f87 + /* 78 */ ENTRY_CopyBytes2Jump, // JS // 0f88 + /* 79 */ ENTRY_CopyBytes2Jump, // JNS // 0f89 + /* 7A */ ENTRY_CopyBytes2Jump, // JP/JPE // 0f8a + /* 7B */ ENTRY_CopyBytes2Jump, // JNP/JPO // 0f8b + /* 7C */ ENTRY_CopyBytes2Jump, // JL/JNGE // 0f8c + /* 7D */ ENTRY_CopyBytes2Jump, // JGE/JNL // 0f8d + /* 7E */ ENTRY_CopyBytes2Jump, // JLE/JNG // 0f8e + /* 7F */ ENTRY_CopyBytes2Jump, // JG/JNLE // 0f8f + /* 80 */ ENTRY_CopyBytes2Mod1, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 byte reg, immediate byte + /* 81 */ ENTRY_CopyBytes2ModOperand, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 byte reg, immediate word or dword +#ifdef DETOURS_X64 + /* 82 */ ENTRY_Invalid, // Invalid +#else + /* 82 */ ENTRY_CopyBytes2Mod1, // MOV al,x +#endif + /* 83 */ ENTRY_CopyBytes2Mod1, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 reg, immediate byte + /* 84 */ ENTRY_CopyBytes2Mod, // TEST /r + /* 85 */ ENTRY_CopyBytes2Mod, // TEST /r + /* 86 */ ENTRY_CopyBytes2Mod, // XCHG /r @todo + /* 87 */ ENTRY_CopyBytes2Mod, // XCHG /r @todo + /* 88 */ ENTRY_CopyBytes2Mod, // MOV /r + /* 89 */ ENTRY_CopyBytes2Mod, // MOV /r + /* 8A */ ENTRY_CopyBytes2Mod, // MOV /r + /* 8B */ ENTRY_CopyBytes2Mod, // MOV /r + /* 8C */ ENTRY_CopyBytes2Mod, // MOV /r + /* 8D */ ENTRY_CopyBytes2Mod, // LEA /r + /* 8E */ ENTRY_CopyBytes2Mod, // MOV /r + /* 8F */ ENTRY_CopyXop, // POP /0 or AMD XOP + /* 90 */ ENTRY_CopyBytes1, // NOP + /* 91 */ ENTRY_CopyBytes1, // XCHG + /* 92 */ ENTRY_CopyBytes1, // XCHG + /* 93 */ ENTRY_CopyBytes1, // XCHG + /* 94 */ ENTRY_CopyBytes1, // XCHG + /* 95 */ ENTRY_CopyBytes1, // XCHG + /* 96 */ ENTRY_CopyBytes1, // XCHG + /* 97 */ ENTRY_CopyBytes1, // XCHG + /* 98 */ ENTRY_CopyBytes1, // CWDE + /* 99 */ ENTRY_CopyBytes1, // CDQ +#ifdef DETOURS_X64 + /* 9A */ ENTRY_Invalid, // Invalid +#else + /* 9A */ ENTRY_CopyBytes5Or7Dynamic, // CALL cp +#endif + /* 9B */ ENTRY_CopyBytes1, // WAIT/FWAIT + /* 9C */ ENTRY_CopyBytes1, // PUSHFD + /* 9D */ ENTRY_CopyBytes1, // POPFD + /* 9E */ ENTRY_CopyBytes1, // SAHF + /* 9F */ ENTRY_CopyBytes1, // LAHF + /* A0 */ ENTRY_CopyBytes1Address, // MOV + /* A1 */ ENTRY_CopyBytes1Address, // MOV + /* A2 */ ENTRY_CopyBytes1Address, // MOV + /* A3 */ ENTRY_CopyBytes1Address, // MOV + /* A4 */ ENTRY_CopyBytes1, // MOVS + /* A5 */ ENTRY_CopyBytes1, // MOVS/MOVSD + /* A6 */ ENTRY_CopyBytes1, // CMPS/CMPSB + /* A7 */ ENTRY_CopyBytes1, // CMPS/CMPSW + /* A8 */ ENTRY_CopyBytes2, // TEST + /* A9 */ ENTRY_CopyBytes3Or5, // TEST + /* AA */ ENTRY_CopyBytes1, // STOS/STOSB + /* AB */ ENTRY_CopyBytes1, // STOS/STOSW + /* AC */ ENTRY_CopyBytes1, // LODS/LODSB + /* AD */ ENTRY_CopyBytes1, // LODS/LODSW + /* AE */ ENTRY_CopyBytes1, // SCAS/SCASB + /* AF */ ENTRY_CopyBytes1, // SCAS/SCASD + /* B0 */ ENTRY_CopyBytes2, // MOV B0+rb + /* B1 */ ENTRY_CopyBytes2, // MOV B0+rb + /* B2 */ ENTRY_CopyBytes2, // MOV B0+rb + /* B3 */ ENTRY_CopyBytes2, // MOV B0+rb + /* B4 */ ENTRY_CopyBytes2, // MOV B0+rb + /* B5 */ ENTRY_CopyBytes2, // MOV B0+rb + /* B6 */ ENTRY_CopyBytes2, // MOV B0+rb + /* B7 */ ENTRY_CopyBytes2, // MOV B0+rb + /* B8 */ ENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* B9 */ ENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BA */ ENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BB */ ENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BC */ ENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BD */ ENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BE */ ENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BF */ ENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* C0 */ ENTRY_CopyBytes2Mod1, // RCL/2 ib, etc. + /* C1 */ ENTRY_CopyBytes2Mod1, // RCL/2 ib, etc. + /* C2 */ ENTRY_CopyBytes3, // RET + /* C3 */ ENTRY_CopyBytes1, // RET + /* C4 */ ENTRY_CopyVex3, // LES, VEX 3-byte opcodes. + /* C5 */ ENTRY_CopyVex2, // LDS, VEX 2-byte opcodes. + /* C6 */ ENTRY_CopyBytes2Mod1, // MOV + /* C7 */ ENTRY_CopyBytes2ModOperand, // MOV/0 XBEGIN/7 + /* C8 */ ENTRY_CopyBytes4, // ENTER + /* C9 */ ENTRY_CopyBytes1, // LEAVE + /* CA */ ENTRY_CopyBytes3Dynamic, // RET + /* CB */ ENTRY_CopyBytes1Dynamic, // RET + /* CC */ ENTRY_CopyBytes1Dynamic, // INT 3 + /* CD */ ENTRY_CopyBytes2Dynamic, // INT ib +#ifdef DETOURS_X64 + /* CE */ ENTRY_Invalid, // Invalid +#else + /* CE */ ENTRY_CopyBytes1Dynamic, // INTO +#endif + /* CF */ ENTRY_CopyBytes1Dynamic, // IRET + /* D0 */ ENTRY_CopyBytes2Mod, // RCL/2, etc. + /* D1 */ ENTRY_CopyBytes2Mod, // RCL/2, etc. + /* D2 */ ENTRY_CopyBytes2Mod, // RCL/2, etc. + /* D3 */ ENTRY_CopyBytes2Mod, // RCL/2, etc. +#ifdef DETOURS_X64 + /* D4 */ ENTRY_Invalid, // Invalid + /* D5 */ ENTRY_Invalid, // Invalid +#else + /* D4 */ ENTRY_CopyBytes2, // AAM + /* D5 */ ENTRY_CopyBytes2, // AAD +#endif + /* D6 */ ENTRY_Invalid, // Invalid + /* D7 */ ENTRY_CopyBytes1, // XLAT/XLATB + /* D8 */ ENTRY_CopyBytes2Mod, // FADD, etc. + /* D9 */ ENTRY_CopyBytes2Mod, // F2XM1, etc. + /* DA */ ENTRY_CopyBytes2Mod, // FLADD, etc. + /* DB */ ENTRY_CopyBytes2Mod, // FCLEX, etc. + /* DC */ ENTRY_CopyBytes2Mod, // FADD/0, etc. + /* DD */ ENTRY_CopyBytes2Mod, // FFREE, etc. + /* DE */ ENTRY_CopyBytes2Mod, // FADDP, etc. + /* DF */ ENTRY_CopyBytes2Mod, // FBLD/4, etc. + /* E0 */ ENTRY_CopyBytes2CantJump, // LOOPNE cb + /* E1 */ ENTRY_CopyBytes2CantJump, // LOOPE cb + /* E2 */ ENTRY_CopyBytes2CantJump, // LOOP cb + /* E3 */ ENTRY_CopyBytes2CantJump, // JCXZ/JECXZ + /* E4 */ ENTRY_CopyBytes2, // IN ib + /* E5 */ ENTRY_CopyBytes2, // IN id + /* E6 */ ENTRY_CopyBytes2, // OUT ib + /* E7 */ ENTRY_CopyBytes2, // OUT ib + /* E8 */ ENTRY_CopyBytes3Or5Target, // CALL cd + /* E9 */ ENTRY_CopyBytes3Or5Target, // JMP cd +#ifdef DETOURS_X64 + /* EA */ ENTRY_Invalid, // Invalid +#else + /* EA */ ENTRY_CopyBytes5Or7Dynamic, // JMP cp +#endif + /* EB */ ENTRY_CopyBytes2Jump, // JMP cb + /* EC */ ENTRY_CopyBytes1, // IN ib + /* ED */ ENTRY_CopyBytes1, // IN id + /* EE */ ENTRY_CopyBytes1, // OUT + /* EF */ ENTRY_CopyBytes1, // OUT + /* F0 */ ENTRY_CopyBytesPrefix, // LOCK prefix + /* F1 */ ENTRY_CopyBytes1Dynamic, // INT1 / ICEBP somewhat documented by AMD, not by Intel + /* F2 */ ENTRY_CopyF2, // REPNE prefix +//#ifdef DETOURS_X86 + /* F3 */ ENTRY_CopyF3, // REPE prefix +//#else +// This does presently suffice for AMD64 but it requires tracing +// through a bunch of code to verify and seems not worth maintaining. +// /* F3 */ ENTRY_CopyBytesPrefix, // REPE prefix +//#endif + /* F4 */ ENTRY_CopyBytes1, // HLT + /* F5 */ ENTRY_CopyBytes1, // CMC + /* F6 */ ENTRY_CopyF6, // TEST/0, DIV/6 + /* F7 */ ENTRY_CopyF7, // TEST/0, DIV/6 + /* F8 */ ENTRY_CopyBytes1, // CLC + /* F9 */ ENTRY_CopyBytes1, // STC + /* FA */ ENTRY_CopyBytes1, // CLI + /* FB */ ENTRY_CopyBytes1, // STI + /* FC */ ENTRY_CopyBytes1, // CLD + /* FD */ ENTRY_CopyBytes1, // STD + /* FE */ ENTRY_CopyBytes2Mod, // DEC/1,INC/0 + /* FF */ ENTRY_CopyFF, // CALL/2 +}; + +const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable0F[] = +{ +#ifdef DETOURS_X86 + /* 00 */ ENTRY_Copy0F00, // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7 +#else + /* 00 */ ENTRY_CopyBytes2Mod, // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7 +#endif + /* 01 */ ENTRY_CopyBytes2Mod, // INVLPG/7, etc. + /* 02 */ ENTRY_CopyBytes2Mod, // LAR/r + /* 03 */ ENTRY_CopyBytes2Mod, // LSL/r + /* 04 */ ENTRY_Invalid, // _04 + /* 05 */ ENTRY_CopyBytes1, // SYSCALL + /* 06 */ ENTRY_CopyBytes1, // CLTS + /* 07 */ ENTRY_CopyBytes1, // SYSRET + /* 08 */ ENTRY_CopyBytes1, // INVD + /* 09 */ ENTRY_CopyBytes1, // WBINVD + /* 0A */ ENTRY_Invalid, // _0A + /* 0B */ ENTRY_CopyBytes1, // UD2 + /* 0C */ ENTRY_Invalid, // _0C + /* 0D */ ENTRY_CopyBytes2Mod, // PREFETCH + /* 0E */ ENTRY_CopyBytes1, // FEMMS (3DNow -- not in Intel documentation) + /* 0F */ ENTRY_CopyBytes2Mod1, // 3DNow Opcodes + /* 10 */ ENTRY_CopyBytes2Mod, // MOVSS MOVUPD MOVSD + /* 11 */ ENTRY_CopyBytes2Mod, // MOVSS MOVUPD MOVSD + /* 12 */ ENTRY_CopyBytes2Mod, // MOVLPD + /* 13 */ ENTRY_CopyBytes2Mod, // MOVLPD + /* 14 */ ENTRY_CopyBytes2Mod, // UNPCKLPD + /* 15 */ ENTRY_CopyBytes2Mod, // UNPCKHPD + /* 16 */ ENTRY_CopyBytes2Mod, // MOVHPD + /* 17 */ ENTRY_CopyBytes2Mod, // MOVHPD + /* 18 */ ENTRY_CopyBytes2Mod, // PREFETCHINTA... + /* 19 */ ENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1A */ ENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1B */ ENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1C */ ENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1D */ ENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1E */ ENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1F */ ENTRY_CopyBytes2Mod, // NOP/r multi byte nop + /* 20 */ ENTRY_CopyBytes2Mod, // MOV/r + /* 21 */ ENTRY_CopyBytes2Mod, // MOV/r + /* 22 */ ENTRY_CopyBytes2Mod, // MOV/r + /* 23 */ ENTRY_CopyBytes2Mod, // MOV/r +#ifdef DETOURS_X64 + /* 24 */ ENTRY_Invalid, // _24 +#else + /* 24 */ ENTRY_CopyBytes2Mod, // MOV/r,TR TR is test register on 80386 and 80486, removed in Pentium +#endif + /* 25 */ ENTRY_Invalid, // _25 +#ifdef DETOURS_X64 + /* 26 */ ENTRY_Invalid, // _26 +#else + /* 26 */ ENTRY_CopyBytes2Mod, // MOV TR/r TR is test register on 80386 and 80486, removed in Pentium +#endif + /* 27 */ ENTRY_Invalid, // _27 + /* 28 */ ENTRY_CopyBytes2Mod, // MOVAPS MOVAPD + /* 29 */ ENTRY_CopyBytes2Mod, // MOVAPS MOVAPD + /* 2A */ ENTRY_CopyBytes2Mod, // CVPI2PS & + /* 2B */ ENTRY_CopyBytes2Mod, // MOVNTPS MOVNTPD + /* 2C */ ENTRY_CopyBytes2Mod, // CVTTPS2PI & + /* 2D */ ENTRY_CopyBytes2Mod, // CVTPS2PI & + /* 2E */ ENTRY_CopyBytes2Mod, // UCOMISS UCOMISD + /* 2F */ ENTRY_CopyBytes2Mod, // COMISS COMISD + /* 30 */ ENTRY_CopyBytes1, // WRMSR + /* 31 */ ENTRY_CopyBytes1, // RDTSC + /* 32 */ ENTRY_CopyBytes1, // RDMSR + /* 33 */ ENTRY_CopyBytes1, // RDPMC + /* 34 */ ENTRY_CopyBytes1, // SYSENTER + /* 35 */ ENTRY_CopyBytes1, // SYSEXIT + /* 36 */ ENTRY_Invalid, // _36 + /* 37 */ ENTRY_CopyBytes1, // GETSEC + /* 38 */ ENTRY_CopyBytes3Mod, // SSE3 Opcodes + /* 39 */ ENTRY_Invalid, // _39 + /* 3A */ ENTRY_CopyBytes3Mod1, // SSE3 Opcodes + /* 3B */ ENTRY_Invalid, // _3B + /* 3C */ ENTRY_Invalid, // _3C + /* 3D */ ENTRY_Invalid, // _3D + /* 3E */ ENTRY_Invalid, // _3E + /* 3F */ ENTRY_Invalid, // _3F + /* 40 */ ENTRY_CopyBytes2Mod, // CMOVO (0F 40) + /* 41 */ ENTRY_CopyBytes2Mod, // CMOVNO (0F 41) + /* 42 */ ENTRY_CopyBytes2Mod, // CMOVB & CMOVNE (0F 42) + /* 43 */ ENTRY_CopyBytes2Mod, // CMOVAE & CMOVNB (0F 43) + /* 44 */ ENTRY_CopyBytes2Mod, // CMOVE & CMOVZ (0F 44) + /* 45 */ ENTRY_CopyBytes2Mod, // CMOVNE & CMOVNZ (0F 45) + /* 46 */ ENTRY_CopyBytes2Mod, // CMOVBE & CMOVNA (0F 46) + /* 47 */ ENTRY_CopyBytes2Mod, // CMOVA & CMOVNBE (0F 47) + /* 48 */ ENTRY_CopyBytes2Mod, // CMOVS (0F 48) + /* 49 */ ENTRY_CopyBytes2Mod, // CMOVNS (0F 49) + /* 4A */ ENTRY_CopyBytes2Mod, // CMOVP & CMOVPE (0F 4A) + /* 4B */ ENTRY_CopyBytes2Mod, // CMOVNP & CMOVPO (0F 4B) + /* 4C */ ENTRY_CopyBytes2Mod, // CMOVL & CMOVNGE (0F 4C) + /* 4D */ ENTRY_CopyBytes2Mod, // CMOVGE & CMOVNL (0F 4D) + /* 4E */ ENTRY_CopyBytes2Mod, // CMOVLE & CMOVNG (0F 4E) + /* 4F */ ENTRY_CopyBytes2Mod, // CMOVG & CMOVNLE (0F 4F) + /* 50 */ ENTRY_CopyBytes2Mod, // MOVMSKPD MOVMSKPD + /* 51 */ ENTRY_CopyBytes2Mod, // SQRTPS & + /* 52 */ ENTRY_CopyBytes2Mod, // RSQRTTS RSQRTPS + /* 53 */ ENTRY_CopyBytes2Mod, // RCPPS RCPSS + /* 54 */ ENTRY_CopyBytes2Mod, // ANDPS ANDPD + /* 55 */ ENTRY_CopyBytes2Mod, // ANDNPS ANDNPD + /* 56 */ ENTRY_CopyBytes2Mod, // ORPS ORPD + /* 57 */ ENTRY_CopyBytes2Mod, // XORPS XORPD + /* 58 */ ENTRY_CopyBytes2Mod, // ADDPS & + /* 59 */ ENTRY_CopyBytes2Mod, // MULPS & + /* 5A */ ENTRY_CopyBytes2Mod, // CVTPS2PD & + /* 5B */ ENTRY_CopyBytes2Mod, // CVTDQ2PS & + /* 5C */ ENTRY_CopyBytes2Mod, // SUBPS & + /* 5D */ ENTRY_CopyBytes2Mod, // MINPS & + /* 5E */ ENTRY_CopyBytes2Mod, // DIVPS & + /* 5F */ ENTRY_CopyBytes2Mod, // MASPS & + /* 60 */ ENTRY_CopyBytes2Mod, // PUNPCKLBW/r + /* 61 */ ENTRY_CopyBytes2Mod, // PUNPCKLWD/r + /* 62 */ ENTRY_CopyBytes2Mod, // PUNPCKLWD/r + /* 63 */ ENTRY_CopyBytes2Mod, // PACKSSWB/r + /* 64 */ ENTRY_CopyBytes2Mod, // PCMPGTB/r + /* 65 */ ENTRY_CopyBytes2Mod, // PCMPGTW/r + /* 66 */ ENTRY_CopyBytes2Mod, // PCMPGTD/r + /* 67 */ ENTRY_CopyBytes2Mod, // PACKUSWB/r + /* 68 */ ENTRY_CopyBytes2Mod, // PUNPCKHBW/r + /* 69 */ ENTRY_CopyBytes2Mod, // PUNPCKHWD/r + /* 6A */ ENTRY_CopyBytes2Mod, // PUNPCKHDQ/r + /* 6B */ ENTRY_CopyBytes2Mod, // PACKSSDW/r + /* 6C */ ENTRY_CopyBytes2Mod, // PUNPCKLQDQ + /* 6D */ ENTRY_CopyBytes2Mod, // PUNPCKHQDQ + /* 6E */ ENTRY_CopyBytes2Mod, // MOVD/r + /* 6F */ ENTRY_CopyBytes2Mod, // MOV/r + /* 70 */ ENTRY_CopyBytes2Mod1, // PSHUFW/r ib + /* 71 */ ENTRY_CopyBytes2Mod1, // PSLLW/6 ib,PSRAW/4 ib,PSRLW/2 ib + /* 72 */ ENTRY_CopyBytes2Mod1, // PSLLD/6 ib,PSRAD/4 ib,PSRLD/2 ib + /* 73 */ ENTRY_CopyBytes2Mod1, // PSLLQ/6 ib,PSRLQ/2 ib + /* 74 */ ENTRY_CopyBytes2Mod, // PCMPEQB/r + /* 75 */ ENTRY_CopyBytes2Mod, // PCMPEQW/r + /* 76 */ ENTRY_CopyBytes2Mod, // PCMPEQD/r + /* 77 */ ENTRY_CopyBytes1, // EMMS + // extrq/insertq require mode=3 and are followed by two immediate bytes + /* 78 */ ENTRY_Copy0F78, // VMREAD/r, 66/EXTRQ/r/ib/ib, F2/INSERTQ/r/ib/ib + // extrq/insertq require mod=3, therefore ENTRY_CopyBytes2, but it ends up the same + /* 79 */ ENTRY_CopyBytes2Mod, // VMWRITE/r, 66/EXTRQ/r, F2/INSERTQ/r + /* 7A */ ENTRY_Invalid, // _7A + /* 7B */ ENTRY_Invalid, // _7B + /* 7C */ ENTRY_CopyBytes2Mod, // HADDPS + /* 7D */ ENTRY_CopyBytes2Mod, // HSUBPS + /* 7E */ ENTRY_CopyBytes2Mod, // MOVD/r + /* 7F */ ENTRY_CopyBytes2Mod, // MOV/r + /* 80 */ ENTRY_CopyBytes3Or5Target, // JO + /* 81 */ ENTRY_CopyBytes3Or5Target, // JNO + /* 82 */ ENTRY_CopyBytes3Or5Target, // JB,JC,JNAE + /* 83 */ ENTRY_CopyBytes3Or5Target, // JAE,JNB,JNC + /* 84 */ ENTRY_CopyBytes3Or5Target, // JE,JZ,JZ + /* 85 */ ENTRY_CopyBytes3Or5Target, // JNE,JNZ + /* 86 */ ENTRY_CopyBytes3Or5Target, // JBE,JNA + /* 87 */ ENTRY_CopyBytes3Or5Target, // JA,JNBE + /* 88 */ ENTRY_CopyBytes3Or5Target, // JS + /* 89 */ ENTRY_CopyBytes3Or5Target, // JNS + /* 8A */ ENTRY_CopyBytes3Or5Target, // JP,JPE + /* 8B */ ENTRY_CopyBytes3Or5Target, // JNP,JPO + /* 8C */ ENTRY_CopyBytes3Or5Target, // JL,NGE + /* 8D */ ENTRY_CopyBytes3Or5Target, // JGE,JNL + /* 8E */ ENTRY_CopyBytes3Or5Target, // JLE,JNG + /* 8F */ ENTRY_CopyBytes3Or5Target, // JG,JNLE + /* 90 */ ENTRY_CopyBytes2Mod, // CMOVO (0F 40) + /* 91 */ ENTRY_CopyBytes2Mod, // CMOVNO (0F 41) + /* 92 */ ENTRY_CopyBytes2Mod, // CMOVB & CMOVC & CMOVNAE (0F 42) + /* 93 */ ENTRY_CopyBytes2Mod, // CMOVAE & CMOVNB & CMOVNC (0F 43) + /* 94 */ ENTRY_CopyBytes2Mod, // CMOVE & CMOVZ (0F 44) + /* 95 */ ENTRY_CopyBytes2Mod, // CMOVNE & CMOVNZ (0F 45) + /* 96 */ ENTRY_CopyBytes2Mod, // CMOVBE & CMOVNA (0F 46) + /* 97 */ ENTRY_CopyBytes2Mod, // CMOVA & CMOVNBE (0F 47) + /* 98 */ ENTRY_CopyBytes2Mod, // CMOVS (0F 48) + /* 99 */ ENTRY_CopyBytes2Mod, // CMOVNS (0F 49) + /* 9A */ ENTRY_CopyBytes2Mod, // CMOVP & CMOVPE (0F 4A) + /* 9B */ ENTRY_CopyBytes2Mod, // CMOVNP & CMOVPO (0F 4B) + /* 9C */ ENTRY_CopyBytes2Mod, // CMOVL & CMOVNGE (0F 4C) + /* 9D */ ENTRY_CopyBytes2Mod, // CMOVGE & CMOVNL (0F 4D) + /* 9E */ ENTRY_CopyBytes2Mod, // CMOVLE & CMOVNG (0F 4E) + /* 9F */ ENTRY_CopyBytes2Mod, // CMOVG & CMOVNLE (0F 4F) + /* A0 */ ENTRY_CopyBytes1, // PUSH + /* A1 */ ENTRY_CopyBytes1, // POP + /* A2 */ ENTRY_CopyBytes1, // CPUID + /* A3 */ ENTRY_CopyBytes2Mod, // BT (0F A3) + /* A4 */ ENTRY_CopyBytes2Mod1, // SHLD + /* A5 */ ENTRY_CopyBytes2Mod, // SHLD + /* A6 */ ENTRY_CopyBytes2Mod, // XBTS + /* A7 */ ENTRY_CopyBytes2Mod, // IBTS + /* A8 */ ENTRY_CopyBytes1, // PUSH + /* A9 */ ENTRY_CopyBytes1, // POP + /* AA */ ENTRY_CopyBytes1, // RSM + /* AB */ ENTRY_CopyBytes2Mod, // BTS (0F AB) + /* AC */ ENTRY_CopyBytes2Mod1, // SHRD + /* AD */ ENTRY_CopyBytes2Mod, // SHRD + + // 0F AE mod76=mem mod543=0 fxsave + // 0F AE mod76=mem mod543=1 fxrstor + // 0F AE mod76=mem mod543=2 ldmxcsr + // 0F AE mod76=mem mod543=3 stmxcsr + // 0F AE mod76=mem mod543=4 xsave + // 0F AE mod76=mem mod543=5 xrstor + // 0F AE mod76=mem mod543=6 saveopt + // 0F AE mod76=mem mod543=7 clflush + // 0F AE mod76=11b mod543=5 lfence + // 0F AE mod76=11b mod543=6 mfence + // 0F AE mod76=11b mod543=7 sfence + // F3 0F AE mod76=11b mod543=0 rdfsbase + // F3 0F AE mod76=11b mod543=1 rdgsbase + // F3 0F AE mod76=11b mod543=2 wrfsbase + // F3 0F AE mod76=11b mod543=3 wrgsbase + /* AE */ ENTRY_CopyBytes2Mod, // fxsave fxrstor ldmxcsr stmxcsr xsave xrstor saveopt clflush lfence mfence sfence rdfsbase rdgsbase wrfsbase wrgsbase + /* AF */ ENTRY_CopyBytes2Mod, // IMUL (0F AF) + /* B0 */ ENTRY_CopyBytes2Mod, // CMPXCHG (0F B0) + /* B1 */ ENTRY_CopyBytes2Mod, // CMPXCHG (0F B1) + /* B2 */ ENTRY_CopyBytes2Mod, // LSS/r + /* B3 */ ENTRY_CopyBytes2Mod, // BTR (0F B3) + /* B4 */ ENTRY_CopyBytes2Mod, // LFS/r + /* B5 */ ENTRY_CopyBytes2Mod, // LGS/r + /* B6 */ ENTRY_CopyBytes2Mod, // MOVZX/r + /* B7 */ ENTRY_CopyBytes2Mod, // MOVZX/r +#ifdef DETOURS_X86 + /* B8 */ ENTRY_Copy0FB8, // jmpe f3/popcnt +#else + /* B8 */ ENTRY_CopyBytes2Mod, // f3/popcnt +#endif + /* B9 */ ENTRY_Invalid, // _B9 + /* BA */ ENTRY_CopyBytes2Mod1, // BT & BTC & BTR & BTS (0F BA) + /* BB */ ENTRY_CopyBytes2Mod, // BTC (0F BB) + /* BC */ ENTRY_CopyBytes2Mod, // BSF (0F BC) + /* BD */ ENTRY_CopyBytes2Mod, // BSR (0F BD) + /* BE */ ENTRY_CopyBytes2Mod, // MOVSX/r + /* BF */ ENTRY_CopyBytes2Mod, // MOVSX/r + /* C0 */ ENTRY_CopyBytes2Mod, // XADD/r + /* C1 */ ENTRY_CopyBytes2Mod, // XADD/r + /* C2 */ ENTRY_CopyBytes2Mod1, // CMPPS & + /* C3 */ ENTRY_CopyBytes2Mod, // MOVNTI + /* C4 */ ENTRY_CopyBytes2Mod1, // PINSRW /r ib + /* C5 */ ENTRY_CopyBytes2Mod1, // PEXTRW /r ib + /* C6 */ ENTRY_CopyBytes2Mod1, // SHUFPS & SHUFPD + /* C7 */ ENTRY_CopyBytes2Mod, // CMPXCHG8B (0F C7) + /* C8 */ ENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* C9 */ ENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CA */ ENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CB */ ENTRY_CopyBytes1, // CVTPD2PI BSWAP 0F C8 + rd + /* CC */ ENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CD */ ENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CE */ ENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CF */ ENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* D0 */ ENTRY_CopyBytes2Mod, // ADDSUBPS (untestd) + /* D1 */ ENTRY_CopyBytes2Mod, // PSRLW/r + /* D2 */ ENTRY_CopyBytes2Mod, // PSRLD/r + /* D3 */ ENTRY_CopyBytes2Mod, // PSRLQ/r + /* D4 */ ENTRY_CopyBytes2Mod, // PADDQ + /* D5 */ ENTRY_CopyBytes2Mod, // PMULLW/r + /* D6 */ ENTRY_CopyBytes2Mod, // MOVDQ2Q / MOVQ2DQ + /* D7 */ ENTRY_CopyBytes2Mod, // PMOVMSKB/r + /* D8 */ ENTRY_CopyBytes2Mod, // PSUBUSB/r + /* D9 */ ENTRY_CopyBytes2Mod, // PSUBUSW/r + /* DA */ ENTRY_CopyBytes2Mod, // PMINUB/r + /* DB */ ENTRY_CopyBytes2Mod, // PAND/r + /* DC */ ENTRY_CopyBytes2Mod, // PADDUSB/r + /* DD */ ENTRY_CopyBytes2Mod, // PADDUSW/r + /* DE */ ENTRY_CopyBytes2Mod, // PMAXUB/r + /* DF */ ENTRY_CopyBytes2Mod, // PANDN/r + /* E0 */ ENTRY_CopyBytes2Mod , // PAVGB + /* E1 */ ENTRY_CopyBytes2Mod, // PSRAW/r + /* E2 */ ENTRY_CopyBytes2Mod, // PSRAD/r + /* E3 */ ENTRY_CopyBytes2Mod, // PAVGW + /* E4 */ ENTRY_CopyBytes2Mod, // PMULHUW/r + /* E5 */ ENTRY_CopyBytes2Mod, // PMULHW/r + /* E6 */ ENTRY_CopyBytes2Mod, // CTDQ2PD & + /* E7 */ ENTRY_CopyBytes2Mod, // MOVNTQ + /* E8 */ ENTRY_CopyBytes2Mod, // PSUBB/r + /* E9 */ ENTRY_CopyBytes2Mod, // PSUBW/r + /* EA */ ENTRY_CopyBytes2Mod, // PMINSW/r + /* EB */ ENTRY_CopyBytes2Mod, // POR/r + /* EC */ ENTRY_CopyBytes2Mod, // PADDSB/r + /* ED */ ENTRY_CopyBytes2Mod, // PADDSW/r + /* EE */ ENTRY_CopyBytes2Mod, // PMAXSW /r + /* EF */ ENTRY_CopyBytes2Mod, // PXOR/r + /* F0 */ ENTRY_CopyBytes2Mod, // LDDQU + /* F1 */ ENTRY_CopyBytes2Mod, // PSLLW/r + /* F2 */ ENTRY_CopyBytes2Mod, // PSLLD/r + /* F3 */ ENTRY_CopyBytes2Mod, // PSLLQ/r + /* F4 */ ENTRY_CopyBytes2Mod, // PMULUDQ/r + /* F5 */ ENTRY_CopyBytes2Mod, // PMADDWD/r + /* F6 */ ENTRY_CopyBytes2Mod, // PSADBW/r + /* F7 */ ENTRY_CopyBytes2Mod, // MASKMOVQ + /* F8 */ ENTRY_CopyBytes2Mod, // PSUBB/r + /* F9 */ ENTRY_CopyBytes2Mod, // PSUBW/r + /* FA */ ENTRY_CopyBytes2Mod, // PSUBD/r + /* FB */ ENTRY_CopyBytes2Mod, // FSUBQ/r + /* FC */ ENTRY_CopyBytes2Mod, // PADDB/r + /* FD */ ENTRY_CopyBytes2Mod, // PADDW/r + /* FE */ ENTRY_CopyBytes2Mod, // PADDD/r + /* FF */ ENTRY_Invalid, // _FF +}; + +BOOL CDetourDis::SanityCheckSystem() +{
+ C_ASSERT(ARRAYSIZE(CDetourDis::s_rceCopyTable) == 256);
+ C_ASSERT(ARRAYSIZE(CDetourDis::s_rceCopyTable0F) == 256); + return TRUE; +} +#endif // defined(DETOURS_X64) || defined(DETOURS_X86) + +/////////////////////////////////////////////////////////// IA64 Disassembler. +// +#ifdef DETOURS_IA64 + +#if defined(_IA64_) != defined(DETOURS_IA64_OFFLINE_LIBRARY) +// Compile DETOUR_IA64_BUNDLE for native IA64 or cross, but not both -- we get duplicates otherwise. +const DETOUR_IA64_BUNDLE::DETOUR_IA64_METADATA DETOUR_IA64_BUNDLE::s_rceCopyTable[33] = +{ + { 0x00, M_UNIT, I_UNIT, I_UNIT, }, + { 0x01, M_UNIT, I_UNIT, I_UNIT, }, + { 0x02, M_UNIT, I_UNIT, I_UNIT, }, + { 0x03, M_UNIT, I_UNIT, I_UNIT, }, + { 0x04, M_UNIT, L_UNIT, X_UNIT, }, + { 0x05, M_UNIT, L_UNIT, X_UNIT, }, + { 0x06, 0, 0, 0, }, + { 0x07, 0, 0, 0, }, + { 0x08, M_UNIT, M_UNIT, I_UNIT, }, + { 0x09, M_UNIT, M_UNIT, I_UNIT, }, + { 0x0a, M_UNIT, M_UNIT, I_UNIT, }, + { 0x0b, M_UNIT, M_UNIT, I_UNIT, }, + { 0x0c, M_UNIT, F_UNIT, I_UNIT, }, + { 0x0d, M_UNIT, F_UNIT, I_UNIT, }, + { 0x0e, M_UNIT, M_UNIT, F_UNIT, }, + { 0x0f, M_UNIT, M_UNIT, F_UNIT, }, + { 0x10, M_UNIT, I_UNIT, B_UNIT, }, + { 0x11, M_UNIT, I_UNIT, B_UNIT, }, + { 0x12, M_UNIT, B_UNIT, B_UNIT, }, + { 0x13, M_UNIT, B_UNIT, B_UNIT, }, + { 0x14, 0, 0, 0, }, + { 0x15, 0, 0, 0, }, + { 0x16, B_UNIT, B_UNIT, B_UNIT, }, + { 0x17, B_UNIT, B_UNIT, B_UNIT, }, + { 0x18, M_UNIT, M_UNIT, B_UNIT, }, + { 0x19, M_UNIT, M_UNIT, B_UNIT, }, + { 0x1a, 0, 0, 0, }, + { 0x1b, 0, 0, 0, }, + { 0x1c, M_UNIT, F_UNIT, B_UNIT, }, + { 0x1d, M_UNIT, F_UNIT, B_UNIT, }, + { 0x1e, 0, 0, 0, }, + { 0x1f, 0, 0, 0, }, + { 0x00, 0, 0, 0, }, +}; + +// 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 DETOUR_IA64_BUNDLE::GetTemplate() const +{ + return (data[0] & 0x1f); +} + +BYTE DETOUR_IA64_BUNDLE::GetInst0() const +{ + return ((data[5] & 0x3c) >> 2); +} + +BYTE DETOUR_IA64_BUNDLE::GetInst1() const +{ + return ((data[10] & 0x78) >> 3); +} + +BYTE DETOUR_IA64_BUNDLE::GetInst2() const +{ + return ((data[15] & 0xf0) >> 4); +} + +BYTE DETOUR_IA64_BUNDLE::GetUnit(BYTE slot) const +{ + switch (slot) { + case 0: return GetUnit0(); + case 1: return GetUnit1(); + case 2: return GetUnit2(); + } + __debugbreak(); + return 0; +} + +BYTE DETOUR_IA64_BUNDLE::GetUnit0() const +{ + return s_rceCopyTable[data[0] & 0x1f].nUnit0; +} + +BYTE DETOUR_IA64_BUNDLE::GetUnit1() const +{ + return s_rceCopyTable[data[0] & 0x1f].nUnit1; +} + +BYTE DETOUR_IA64_BUNDLE::GetUnit2() const +{ + return s_rceCopyTable[data[0] & 0x1f].nUnit2; +} + +UINT64 DETOUR_IA64_BUNDLE::GetData0() const +{ + return (((wide[0] & 0x000003ffffffffe0) >> 5)); +} + +UINT64 DETOUR_IA64_BUNDLE::GetData1() const +{ + return (((wide[0] & 0xffffc00000000000) >> 46) | + ((wide[1] & 0x000000000007ffff) << 18)); +} + +UINT64 DETOUR_IA64_BUNDLE::GetData2() const +{ + return (((wide[1] & 0x0fffffffff800000) >> 23)); +} + +VOID DETOUR_IA64_BUNDLE::SetInst(BYTE slot, BYTE nInst) +{ + switch (slot) + { + case 0: SetInst0(nInst); return; + case 1: SetInst1(nInst); return; + case 2: SetInst2(nInst); return; + } + __debugbreak(); +} + +VOID DETOUR_IA64_BUNDLE::SetInst0(BYTE nInst) +{ + data[5] = (data[5] & ~0x3c) | ((nInst << 2) & 0x3c); +} + +VOID DETOUR_IA64_BUNDLE::SetInst1(BYTE nInst) +{ + data[10] = (data[10] & ~0x78) | ((nInst << 3) & 0x78); +} + +VOID DETOUR_IA64_BUNDLE::SetInst2(BYTE nInst) +{ + data[15] = (data[15] & ~0xf0) | ((nInst << 4) & 0xf0); +} + +VOID DETOUR_IA64_BUNDLE::SetData(BYTE slot, UINT64 nData) +{ + switch (slot) + { + case 0: SetData0(nData); return; + case 1: SetData1(nData); return; + case 2: SetData2(nData); return; + } + __debugbreak(); +} + +VOID DETOUR_IA64_BUNDLE::SetData0(UINT64 nData) +{ + wide[0] = (wide[0] & ~0x000003ffffffffe0) | (( nData << 5) & 0x000003ffffffffe0); +} + +VOID DETOUR_IA64_BUNDLE::SetData1(UINT64 nData) +{ + wide[0] = (wide[0] & ~0xffffc00000000000) | ((nData << 46) & 0xffffc00000000000); + wide[1] = (wide[1] & ~0x000000000007ffff) | ((nData >> 18) & 0x000000000007ffff); +} + +VOID DETOUR_IA64_BUNDLE::SetData2(UINT64 nData) +{ + wide[1] = (wide[1] & ~0x0fffffffff800000) | ((nData << 23) & 0x0fffffffff800000); +} + +UINT64 DETOUR_IA64_BUNDLE::GetInstruction(BYTE slot) const +{ + switch (slot) { + case 0: return GetInstruction0(); + case 1: return GetInstruction1(); + case 2: return GetInstruction2(); + } + __debugbreak(); + return 0; +} + +UINT64 DETOUR_IA64_BUNDLE::GetInstruction0() const +{ + // 41 bits from wide[0], skipping the 5 bit template. + return GetBits(wide[0], DETOUR_IA64_INSTRUCTION0_OFFSET, DETOUR_IA64_INSTRUCTION_SIZE); +} + +UINT64 DETOUR_IA64_BUNDLE::GetInstruction1() const +{ + // 64-46 bits from wide[0] and the rest from wide[1]. + const UINT count0 = 64 - DETOUR_IA64_INSTRUCTION1_OFFSET; + const UINT count1 = DETOUR_IA64_INSTRUCTION_SIZE - count0; + return GetBits(wide[0], DETOUR_IA64_INSTRUCTION1_OFFSET, count0) | (GetBits(wide[1], 0, count1) << count0); +} + +UINT64 DETOUR_IA64_BUNDLE::GetInstruction2() const +{ + // Upper 41 bits of wide[1]. + return wide[1] >> (64 - DETOUR_IA64_INSTRUCTION_SIZE); +} + +void DETOUR_IA64_BUNDLE::SetInstruction(BYTE slot, UINT64 instruction) +{ + switch (slot) { + case 0: SetInstruction0(instruction); return; + case 1: SetInstruction1(instruction); return; + case 2: SetInstruction2(instruction); return; + } + __debugbreak(); +} + +void DETOUR_IA64_BUNDLE::SetInstruction0(UINT64 instruction) +{ + wide[0] = SetBits(wide[0], DETOUR_IA64_INSTRUCTION0_OFFSET, DETOUR_IA64_INSTRUCTION_SIZE, instruction); +} + +void DETOUR_IA64_BUNDLE::SetInstruction1(UINT64 instruction) +{ + UINT const count0 = 64 - DETOUR_IA64_INSTRUCTION1_OFFSET; + UINT const count1 = DETOUR_IA64_INSTRUCTION_SIZE - count0; + UINT64 const wide0 = SetBits(wide[0], DETOUR_IA64_INSTRUCTION1_OFFSET, count0, instruction); + UINT64 const wide1 = SetBits(wide[1], 0, count1, instruction >> count0); + wide[0] = wide0; + wide[1] = wide1; +} + +void DETOUR_IA64_BUNDLE::SetInstruction2(UINT64 instruction) +{ + // Set upper 41 bits of wide[1]. + wide[1] = SetBits(wide[1], 64 - DETOUR_IA64_INSTRUCTION_SIZE, DETOUR_IA64_INSTRUCTION_SIZE, instruction); +} + +UINT64 DETOUR_IA64_BUNDLE::SignExtend(UINT64 Value, UINT64 Offset) +// This definition is from the IA64 manual. +{ + if ((Value & (((UINT64)1) << (Offset - 1))) == 0) + return Value; + UINT64 const new_value = Value | ((~(UINT64)0) << Offset); + return new_value; +} + +UINT64 DETOUR_IA64_BUNDLE::GetBits(UINT64 Value, UINT64 Offset, UINT64 Count) +{ + UINT64 const new_value = (Value >> Offset) & ~(~((UINT64)0) << Count); + return new_value; +} + +UINT64 DETOUR_IA64_BUNDLE::SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field) +{ + UINT64 const mask = (~((~(UINT64)0) << Count)) << Offset; + UINT64 const new_value = (Value & ~mask) | ((Field << Offset) & mask); + return new_value; +} + +UINT64 DETOUR_IA64_BUNDLE::GetOpcode(UINT64 instruction) +// Get 4bit primary opcode. +{ + UINT64 const opcode = GetBits(instruction, DETOUR_IA64_INSTRUCTION_SIZE - 4, 4); + return opcode; +} + +UINT64 DETOUR_IA64_BUNDLE::GetX(UINT64 instruction) +// Get 1bit opcode extension. +{ + UINT64 const x = GetBits(instruction, 33, 1); + return x; +} + +UINT64 DETOUR_IA64_BUNDLE::GetX3(UINT64 instruction) +// Get 3bit opcode extension. +{ + UINT64 const x3 = GetBits(instruction, 33, 3); + return x3; +} + +UINT64 DETOUR_IA64_BUNDLE::GetX6(UINT64 instruction) +// Get 6bit opcode extension. +{ + UINT64 const x6 = GetBits(instruction, 27, 6); + return x6; +} + +UINT64 DETOUR_IA64_BUNDLE::GetImm7a(UINT64 instruction) +{ + UINT64 const imm7a = GetBits(instruction, 6, 7); + return imm7a; +} + +UINT64 DETOUR_IA64_BUNDLE::SetImm7a(UINT64 instruction, UINT64 imm7a) +{ + UINT64 const new_instruction = SetBits(instruction, 6, 7, imm7a); + return new_instruction; +} + +UINT64 DETOUR_IA64_BUNDLE::GetImm13c(UINT64 instruction) +{ + UINT64 const imm13c = GetBits(instruction, 20, 13); + return imm13c; +} + +UINT64 DETOUR_IA64_BUNDLE::SetImm13c(UINT64 instruction, UINT64 imm13c) +{ + UINT64 const new_instruction = SetBits(instruction, 20, 13, imm13c); + return new_instruction; +} + +UINT64 DETOUR_IA64_BUNDLE::GetSignBit(UINT64 instruction) +{ + UINT64 const signBit = GetBits(instruction, 36, 1); + return signBit; +} + +UINT64 DETOUR_IA64_BUNDLE::SetSignBit(UINT64 instruction, UINT64 signBit) +{ + UINT64 const new_instruction = SetBits(instruction, 36, 1, signBit); + return new_instruction; +} + +UINT64 DETOUR_IA64_BUNDLE::GetImm20a(UINT64 instruction) +{ + UINT64 const imm20a = GetBits(instruction, 6, 20); + return imm20a; +} + +UINT64 DETOUR_IA64_BUNDLE::SetImm20a(UINT64 instruction, UINT64 imm20a) +{ + UINT64 const new_instruction = SetBits(instruction, 6, 20, imm20a); + return new_instruction; +} + +UINT64 DETOUR_IA64_BUNDLE::GetImm20b(UINT64 instruction) +{ + UINT64 const imm20b = GetBits(instruction, 13, 20); + return imm20b; +} + +UINT64 DETOUR_IA64_BUNDLE::SetImm20b(UINT64 instruction, UINT64 imm20b) +{ + UINT64 const new_instruction = SetBits(instruction, 13, 20, imm20b); + return new_instruction; +} + +bool DETOUR_IA64_BUNDLE::RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst, + _In_ BYTE slot, + _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const +/* + If pBundleExtra is provided and instruction is IP-relative, + this function relocates instruction to target pBundleExtra, + pBundleExtra is set to brl the original target, and return true. + + [Not used] If pBundleExtra is not provided and instruction is IP-relative, return true. + + Else return false. + + The following IP-relative forms are recognized: + br and br.call + chk.s.m integer and float + chk.a.nc integer and float + chk.a.clr integer and float + chk.s.i + fchkf + + Brl is handled elsewhere, because the code was previously written. + + Branch prediction hints are not relocated. +*/ +{ + UINT64 const instruction = GetInstruction(slot); + UINT64 const opcode = GetOpcode(instruction); + size_t const dest = (size_t)pDst; + size_t const extra = (size_t)pBundleExtra; + + switch (GetUnit(slot)) { + case F_UNIT: + // F14 fchkf + if (opcode == 0 && GetX(instruction) == 0 && GetX6(instruction) == 8) { + goto imm20a; + } + return false; + + case M_UNIT: + // M20 x3 == 1 integer chk.s.m + // M21 x3 == 3 floating point chk.s + if (opcode == 1) { + UINT64 const x3 = GetX3(instruction); + if (x3 == 1 || x3 == 3) { + goto imm13_7; + } + } + + // M22 x3 == 4 integer chk.a.nc + // M22 x3 == 5 integer chk.a.clr + // M23 x3 == 6 floating point chk.a.nc + // M23 x3 == 7 floating point chk.a.clr + if (opcode == 0) { + UINT64 const x3 = GetX3(instruction); + if (x3 == 4 || x3 == 5 || x3 == 6 || x3 == 7) { + goto imm20b; + } + } + return false; + case I_UNIT: + // I20 + if (opcode == 0 && GetX3(instruction) == 1) { // chk.s.i + goto imm13_7; + } + return false; + case B_UNIT: + // B1 B2 B3 + // 4 br + // 5 br.call + if (opcode == 4 || opcode == 5) { + goto imm20b; + } + return false; + } + return false; + + UINT64 imm; + UINT64 new_instruction; + +imm13_7: + imm = SignExtend((GetSignBit(instruction) << 20) | (GetImm13c(instruction) << 7) | GetImm7a(instruction), 21) << 4; + new_instruction = SetSignBit(SetImm13c(SetImm7a(instruction, (extra - dest) >> 4), (extra - dest) >> 11), extra < dest); + goto set_brl; + +imm20a: + imm = SignExtend((GetSignBit(instruction) << 20) | GetImm20a(instruction), 21) << 4; + new_instruction = SetSignBit(SetImm20a(instruction, (extra - dest) >> 4), extra < dest); + goto set_brl; + +imm20b: + imm = SignExtend((GetSignBit(instruction) << 20) | GetImm20b(instruction), 21) << 4; + new_instruction = SetSignBit(SetImm20b(instruction, (extra - dest) >> 4), extra < dest); + goto set_brl; + +set_brl: + if (pBundleExtra != NULL) { + pDst->SetInstruction(slot, new_instruction); + pBundleExtra->SetBrl((size_t)this + imm); + } + return true; +} + +UINT DETOUR_IA64_BUNDLE::RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst, + _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const +/* + Having already copied the bundle unchanged, then relocate its instructions one at a time. + Return how many extra bytes are required to relocate the bundle. +*/ +{ + UINT nExtraBytes = 0; + for (BYTE slot = 0; slot < DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE; ++slot) { + if (!RelocateInstruction(pDst, slot, pBundleExtra)) { + continue; + } + pBundleExtra -= !!pBundleExtra; + nExtraBytes += sizeof(DETOUR_IA64_BUNDLE); + } + return nExtraBytes; +} + +BOOL DETOUR_IA64_BUNDLE::IsBrl() const +{ + // f.e. d.c. b.a. 9.8. 7.6. 5. 4. 3. 2. 1. 0. + // c000 0070 0000 0000 0000 00 01 00 00 00 05 : brl.sptk.few + // c8ff fff0 007f fff0 ffff 00 01 00 00 00 05 : brl.sptk.few + // c000 0048 0000 0000 0001 00 00 00 00 00 05 : brl.sptk.many + return ((wide[0] & 0x000000000000001e) == 0x0000000000000004 && // 4 or 5. + (wide[1] & 0xe000000000000000) == 0xc000000000000000); // c or d. +} + +VOID DETOUR_IA64_BUNDLE::SetBrl() +{ + wide[0] = 0x0000000100000005; // few + //wide[0] = 0x0000000180000005; // many + wide[1] = 0xc000000800000000; +} + +UINT64 DETOUR_IA64_BUNDLE::GetBrlImm() const +{ + return ( + // 0x0000000000fffff0 + ((wide[1] & 0x00fffff000000000) >> 32) | // all 20 bits of imm20b. + // 0x000000ffff000000 + ((wide[0] & 0xffff000000000000) >> 24) | // bottom 16 bits of imm39. + // 0x7fffff0000000000 + ((wide[1] & 0x00000000007fffff) << 40) | // top 23 bits of imm39. + // 0x8000000000000000 + ((wide[1] & 0x0800000000000000) << 4) // single bit of i. + ); +} + +VOID DETOUR_IA64_BUNDLE::SetBrlImm(UINT64 imm) +{ + wide[0] = ((wide[0] & ~0xffff000000000000) | + // 0xffff000000000000 + ((imm & 0x000000ffff000000) << 24) // bottom 16 bits of imm39. + ); + wide[1] = ((wide[1] & ~0x08fffff0007fffff) | + // 0x00fffff000000000 + ((imm & 0x0000000000fffff0) << 32) | // all 20 bits of imm20b. + // 0x00000000007fffff + ((imm & 0x7fffff0000000000) >> 40) | // top 23 bits of imm39. + // 0x0800000000000000 + ((imm & 0x8000000000000000) >> 4) // single bit of i. + ); +} + +UINT64 DETOUR_IA64_BUNDLE::GetBrlTarget() const +{ + return (UINT64)this + GetBrlImm(); +} + +VOID DETOUR_IA64_BUNDLE::SetBrl(UINT64 target) +{ + UINT64 imm = target - (UINT64)this; + SetBrl(); + SetBrlImm(imm); +} + +VOID DETOUR_IA64_BUNDLE::SetBrlTarget(UINT64 target) +{ + UINT64 imm = target - (UINT64)this; + SetBrlImm(imm); +} + +BOOL DETOUR_IA64_BUNDLE::IsMovlGp() const +{ + // f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0. + // 6fff f7f0 207f ffff ffff c001 0000 0004 + // 6000 0000 2000 0000 0000 0001 0000 0004 + return ((wide[0] & 0x00003ffffffffffe) == 0x0000000100000004 && + (wide[1] & 0xf000080fff800000) == 0x6000000020000000); +} + +UINT64 DETOUR_IA64_BUNDLE::GetMovlGp() const +{ + UINT64 raw = ( + // 0x0000000000000070 + ((wide[1] & 0x000007f000000000) >> 36) | + // 0x000000000000ff80 + ((wide[1] & 0x07fc000000000000) >> 43) | + // 0x00000000001f0000 + ((wide[1] & 0x0003e00000000000) >> 29) | + // 0x0000000000200000 + ((wide[1] & 0x0000100000000000) >> 23) | + // 0x000000ffffc00000 + ((wide[0] & 0xffffc00000000000) >> 24) | + // 0x7fffff0000000000 + ((wide[1] & 0x00000000007fffff) << 40) | + // 0x8000000000000000 + ((wide[1] & 0x0800000000000000) << 4) + ); + + return (INT64)raw; +} + +VOID DETOUR_IA64_BUNDLE::SetMovlGp(UINT64 gp) +{ + UINT64 raw = (UINT64)gp; + + wide[0] = (0x0000000100000005 | + // 0xffffc00000000000 + ((raw & 0x000000ffffc00000) << 24) + ); + wide[1] = ( + 0x6000000020000000 | + // 0x0000070000000000 + ((raw & 0x0000000000000070) << 36) | + // 0x07fc000000000000 + ((raw & 0x000000000000ff80) << 43) | + // 0x0003e00000000000 + ((raw & 0x00000000001f0000) << 29) | + // 0x0000100000000000 + ((raw & 0x0000000000200000) << 23) | + // 0x00000000007fffff + ((raw & 0x7fffff0000000000) >> 40) | + // 0x0800000000000000 + ((raw & 0x8000000000000000) >> 4) + ); +} + +UINT DETOUR_IA64_BUNDLE::Copy(_Out_ DETOUR_IA64_BUNDLE *pDst, + _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const +{ + // Copy the bytes unchanged. + +#pragma warning(suppress:6001) // using uninitialized *pDst + pDst->wide[0] = wide[0]; + pDst->wide[1] = wide[1]; + + // Relocate if necessary. + + UINT nExtraBytes = RelocateBundle(pDst, pBundleExtra); + + if (GetUnit1() == L_UNIT && IsBrl()) { + pDst->SetBrlTarget(GetBrlTarget()); + } + + return nExtraBytes; +} + +BOOL DETOUR_IA64_BUNDLE::SetNop(BYTE slot) +{ + switch (GetUnit(slot)) { + case I_UNIT: + case M_UNIT: + case F_UNIT: + SetInst(slot, 0); + SetData(slot, 0x8000000); + return true; + case B_UNIT: + SetInst(slot, 2); + SetData(slot, 0); + return true; + } + DebugBreak(); + return false; +} + +BOOL DETOUR_IA64_BUNDLE::SetNop0() +{ + return SetNop(0); +} + +BOOL DETOUR_IA64_BUNDLE::SetNop1() +{ + return SetNop(1); +} + +BOOL DETOUR_IA64_BUNDLE::SetNop2() +{ + return SetNop(2); +} + +VOID DETOUR_IA64_BUNDLE::SetStop() +{ + data[0] |= 0x01; +} + +#endif // DETOURS_IA64 + +PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, + _Inout_opt_ PVOID *ppDstPool, + _In_ PVOID pSrc, + _Out_opt_ PVOID *ppTarget, + _Out_opt_ LONG *plExtra) +{ + LONG nExtra; + DETOUR_IA64_BUNDLE bExtra; + DETOUR_IA64_BUNDLE *pbSrc = (DETOUR_IA64_BUNDLE *)pSrc; + DETOUR_IA64_BUNDLE *pbDst = pDst ? (DETOUR_IA64_BUNDLE *)pDst : &bExtra; + + plExtra = plExtra ? plExtra : &nExtra; + *plExtra = 0; + + if (ppTarget != NULL) { + if (pbSrc->IsBrl()) { + *ppTarget = (PVOID)pbSrc->GetBrlTarget(); + } + else { + *ppTarget = DETOUR_INSTRUCTION_TARGET_NONE; + } + } + *plExtra = (LONG)pbSrc->Copy(pbDst, ppDstPool ? ((DETOUR_IA64_BUNDLE*)*ppDstPool) - 1 : (DETOUR_IA64_BUNDLE*)NULL); + return pbSrc + 1; +} + +#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)) + +#define c_PCAdjust 4 // The PC value of an instruction is the PC address plus 4. +#define c_PC 15 // The register number for the Program Counter +#define c_LR 14 // The register number for the Link Register +#define c_SP 13 // The register number for the Stack Pointer +#define c_NOP 0xbf00 // A nop instruction +#define c_BREAK 0xdefe // A nop instruction + +class CDetourDis +{ + public: + CDetourDis(); + + PBYTE CopyInstruction(PBYTE pDst, + PBYTE *ppDstPool, + PBYTE pSrc, + PBYTE *ppTarget, + LONG *plExtra); + + public: + typedef BYTE (CDetourDis::* COPYFUNC)(PBYTE pbDst, PBYTE pbSrc); + + struct COPYENTRY { + USHORT nOpcode; + COPYFUNC pfCopy; + }; + + typedef const COPYENTRY * REFCOPYENTRY; + + struct Branch5 + { + DWORD Register : 3; + DWORD Imm5 : 5; + DWORD Padding : 1; + DWORD I : 1; + DWORD OpCode : 6; + }; + + struct Branch5Target + { + DWORD Padding : 1; + DWORD Imm5 : 5; + DWORD I : 1; + DWORD Padding2 : 25; + }; + + struct Branch8 + { + DWORD Imm8 : 8; + DWORD Condition : 4; + DWORD OpCode : 4; + }; + + struct Branch8Target + { + DWORD Padding : 1; + DWORD Imm8 : 8; + DWORD Padding2 : 23; + }; + + struct Branch11 + { + DWORD Imm11 : 11; + DWORD OpCode : 5; + }; + + struct Branch11Target + { + DWORD Padding : 1; + DWORD Imm11 : 11; + DWORD Padding2 : 20; + }; + + struct Branch20 + { + DWORD Imm11 : 11; + DWORD J2 : 1; + DWORD IT : 1; + DWORD J1 : 1; + DWORD Other : 2; + DWORD Imm6 : 6; + DWORD Condition : 4; + DWORD Sign : 1; + DWORD OpCode : 5; + }; + + struct Branch20Target + { + DWORD Padding : 1; + DWORD Imm11 : 11; + DWORD Imm6 : 6; + DWORD J1 : 1; + DWORD J2 : 1; + DWORD Sign : 1; + INT32 Padding2 : 11; + }; + + struct Branch24 + { + DWORD Imm11 : 11; + DWORD J2 : 1; + DWORD InstructionSet : 1; + DWORD J1 : 1; + DWORD Link : 1; + DWORD Branch : 1; + DWORD Imm10 : 10; + DWORD Sign : 1; + DWORD OpCode : 5; + }; + + struct Branch24Target + { + DWORD Padding : 1; + DWORD Imm11 : 11; + DWORD Imm10 : 10; + DWORD I2 : 1; + DWORD I1 : 1; + DWORD Sign : 1; + INT32 Padding2 : 7; + }; + + struct LiteralLoad8 + { + DWORD Imm8 : 8; + DWORD Register : 3; + DWORD OpCode : 5; + }; + + struct LiteralLoad8Target + { + DWORD Padding : 2; + DWORD Imm8 : 8; + DWORD Padding2 : 22; + }; + + struct LiteralLoad12 + { + DWORD Imm12 : 12; + DWORD Register : 4; + DWORD OpCodeSuffix : 7; + DWORD Add : 1; + DWORD OpCodePrefix : 8; + }; + + struct LiteralLoad12Target + { + DWORD Imm12 : 12; + DWORD Padding : 20; + }; + + struct ImmediateRegisterLoad32 + { + DWORD Imm12 : 12; + DWORD DestinationRegister : 4; + DWORD SourceRegister: 4; + DWORD OpCode : 12; + }; + + struct ImmediateRegisterLoad16 + { + DWORD DestinationRegister : 3; + DWORD SourceRegister: 3; + DWORD OpCode : 10; + }; + + struct TableBranch + { + DWORD IndexRegister : 4; + DWORD HalfWord : 1; + DWORD OpCodeSuffix : 11; + DWORD BaseRegister : 4; + DWORD OpCodePrefix : 12; + }; + + struct Shift + { + DWORD Imm2 : 2; + DWORD Imm3 : 3; + }; + + struct Add32 + { + DWORD SecondOperandRegister : 4; + DWORD Type : 2; + DWORD Imm2 : 2; + DWORD DestinationRegister : 4; + DWORD Imm3 : 3; + DWORD Padding : 1; + DWORD FirstOperandRegister : 4; + DWORD SetFlags : 1; + DWORD OpCode : 11; + }; + + struct LogicalShiftLeft32 + { + DWORD SourceRegister : 4; + DWORD Padding : 2; + DWORD Imm2 : 2; + DWORD DestinationRegister : 4; + DWORD Imm3 : 3; + DWORD Padding2 : 5; + DWORD SetFlags : 1; + DWORD OpCode : 11; + }; + + struct StoreImmediate12 + { + DWORD Imm12 : 12; + DWORD SourceRegister : 4; + DWORD BaseRegister : 4; + DWORD OpCode : 12; + }; + + protected: + BYTE PureCopy16(BYTE* pSource, BYTE* pDest); + BYTE PureCopy32(BYTE* pSource, BYTE* pDest); + BYTE CopyMiscellaneous16(BYTE* pSource, BYTE* pDest); + BYTE CopyConditionalBranchOrOther16(BYTE* pSource, BYTE* pDest); + BYTE CopyUnConditionalBranch16(BYTE* pSource, BYTE* pDest); + BYTE CopyLiteralLoad16(BYTE* pSource, BYTE* pDest); + BYTE CopyBranchExchangeOrDataProcessing16(BYTE* pSource, BYTE* pDest); + BYTE CopyBranch24(BYTE* pSource, BYTE* pDest); + BYTE CopyBranchOrMiscellaneous32(BYTE* pSource, BYTE* pDest); + BYTE CopyLiteralLoad32(BYTE* pSource, BYTE* pDest); + BYTE CopyLoadAndStoreSingle(BYTE* pSource, BYTE* pDest); + BYTE CopyLoadAndStoreMultipleAndSRS(BYTE* pSource, BYTE* pDest); + BYTE CopyTableBranch(BYTE* pSource, BYTE* pDest); + BYTE BeginCopy32(BYTE* pSource, BYTE* pDest); + + LONG DecodeBranch5(ULONG opcode); + USHORT EncodeBranch5(ULONG originalOpCode, LONG delta); + LONG DecodeBranch8(ULONG opcode); + USHORT EncodeBranch8(ULONG originalOpCode, LONG delta); + LONG DecodeBranch11(ULONG opcode); + USHORT EncodeBranch11(ULONG originalOpCode, LONG delta); + BYTE EmitBranch11(PUSHORT& pDest, LONG relativeAddress); + LONG DecodeBranch20(ULONG opcode); + ULONG EncodeBranch20(ULONG originalOpCode, LONG delta); + LONG DecodeBranch24(ULONG opcode, BOOL& fLink); + ULONG EncodeBranch24(ULONG originalOpCode, LONG delta, BOOL fLink); + LONG DecodeLiteralLoad8(ULONG instruction); + LONG DecodeLiteralLoad12(ULONG instruction); + BYTE EmitLiteralLoad8(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral); + BYTE EmitLiteralLoad12(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral); + BYTE EmitImmediateRegisterLoad32(PUSHORT& pDest, BYTE reg); + BYTE EmitImmediateRegisterLoad16(PUSHORT& pDest, BYTE reg); + BYTE EmitLongLiteralLoad(PUSHORT& pDest, BYTE reg, PVOID pTarget); + BYTE EmitLongBranch(PUSHORT& pDest, PVOID pTarget); + USHORT CalculateExtra(BYTE sourceLength, BYTE* pDestStart, BYTE* pDestEnd); + + protected: + ULONG GetLongInstruction(BYTE* pSource) + { + return (((PUSHORT)pSource)[0] << 16) | (((PUSHORT)pSource)[1]); + } + + BYTE EmitLongInstruction(PUSHORT& pDstInst, ULONG instruction) + { + *pDstInst++ = (USHORT)(instruction >> 16); + *pDstInst++ = (USHORT)instruction; + return sizeof(ULONG); + } + + BYTE EmitShortInstruction(PUSHORT& pDstInst, USHORT instruction) + { + *pDstInst++ = instruction; + return sizeof(USHORT); + } + + PBYTE Align4(PBYTE pValue) + { + return (PBYTE)(((size_t)pValue) & ~(ULONG)3u); + } + + PBYTE CalculateTarget(PBYTE pSource, LONG delta) + { + return (pSource + delta + c_PCAdjust); + } + + LONG CalculateNewDelta(PBYTE pTarget, BYTE* pDest) + { + return (LONG)(pTarget - (pDest + c_PCAdjust)); + } + + BYTE EmitAdd32(PUSHORT& pDstInst, BYTE op1Reg, BYTE op2Reg, BYTE dstReg, BYTE shiftAmount) + { + Shift& shift = (Shift&)(shiftAmount); + const BYTE shiftType = 0x00; // LSL + Add32 add = { op2Reg, shiftType, shift.Imm2, dstReg, shift.Imm3, + 0x0, op1Reg, 0x0, 0x758 }; + return EmitLongInstruction(pDstInst, (ULONG&)add); + } + + BYTE EmitLogicalShiftLeft32(PUSHORT& pDstInst, BYTE srcReg, BYTE dstReg, BYTE shiftAmount) + { + Shift& shift = (Shift&)(shiftAmount); + LogicalShiftLeft32 shiftLeft = { srcReg, 0x00, shift.Imm2, dstReg, shift.Imm3, 0x1E, + 0x00, 0x752 }; + return EmitLongInstruction(pDstInst, (ULONG&)shiftLeft); + } + + BYTE EmitStoreImmediate12(PUSHORT& pDstInst, BYTE srcReg, BYTE baseReg, USHORT offset) + { + StoreImmediate12 store = { offset, srcReg, baseReg, 0xF8C }; + return EmitLongInstruction(pDstInst, (ULONG&)store); + } + + protected: + PBYTE m_pbTarget; + PBYTE m_pbPool; + LONG m_lExtra; + + BYTE m_rbScratchDst[64]; // matches or exceeds rbCode + + static const COPYENTRY s_rceCopyTable[33]; +}; + +LONG CDetourDis::DecodeBranch5(ULONG opcode) +{ + Branch5& branch = (Branch5&)(opcode); + + Branch5Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm5 = branch.Imm5; + target.I = branch.I; + + // Return zero-extended value + return (LONG&)target; +} + +USHORT CDetourDis::EncodeBranch5(ULONG originalOpCode, LONG delta) +{ + // Too large for a 5 bit branch (5 bit branches can be up to 7 bits due to I and the trailing 0) + if (delta < 0 || delta > 0x7F) { + return 0; + } + + Branch5& branch = (Branch5&)(originalOpCode); + Branch5Target& target = (Branch5Target&)(delta); + + branch.Imm5 = target.Imm5; + branch.I = target.I; + + return (USHORT&)branch; +} + +LONG CDetourDis::DecodeBranch8(ULONG opcode) +{ + Branch8& branch = (Branch8&)(opcode); + + Branch8Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm8 = branch.Imm8; + + // Return sign extended value + return (((LONG&)target) << 23) >> 23; +} + +USHORT CDetourDis::EncodeBranch8(ULONG originalOpCode, LONG delta) +{ + // Too large for 8 bit branch (8 bit branches can be up to 9 bits due to the trailing 0) + if (delta < (-(int)0x100) || delta > 0xFF) { + return 0; + } + + Branch8& branch = (Branch8&)(originalOpCode); + Branch8Target& target = (Branch8Target&)(delta); + + branch.Imm8 = target.Imm8; + + return (USHORT&)branch; +} + +LONG CDetourDis::DecodeBranch11(ULONG opcode) +{ + Branch11& branch = (Branch11&)(opcode); + + Branch11Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm11 = branch.Imm11; + + // Return sign extended value + return (((LONG&)target) << 20) >> 20; +} + +USHORT CDetourDis::EncodeBranch11(ULONG originalOpCode, LONG delta) +{ + // Too large for an 11 bit branch (11 bit branches can be up to 12 bits due to the trailing 0) + if (delta < (-(int)0x800) || delta > 0x7FF) { + return 0; + } + + Branch11& branch = (Branch11&)(originalOpCode); + Branch11Target& target = (Branch11Target&)(delta); + + branch.Imm11 = target.Imm11; + + return (USHORT&)branch; +} + +BYTE CDetourDis::EmitBranch11(PUSHORT& pDest, LONG relativeAddress) +{ + Branch11Target& target = (Branch11Target&)(relativeAddress); + Branch11 branch11 = { target.Imm11, 0x1C }; + + *pDest++ = (USHORT&)branch11; + return sizeof(USHORT); +} + +LONG CDetourDis::DecodeBranch20(ULONG opcode) +{ + Branch20& branch = (Branch20&)(opcode); + + Branch20Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm11 = branch.Imm11; + target.Imm6 = branch.Imm6; + target.Sign = branch.Sign; + target.J1 = branch.J1; + target.J2 = branch.J2; + + // Sign extend + if (target.Sign) { + target.Padding2 = -1; + } + + return (LONG&)target; +} + +ULONG CDetourDis::EncodeBranch20(ULONG originalOpCode, LONG delta) +{ + // Too large for 20 bit branch (20 bit branches can be up to 21 bits due to the trailing 0) + if (delta < (-(int)0x100000) || delta > 0xFFFFF) { + return 0; + } + + Branch20& branch = (Branch20&)(originalOpCode); + Branch20Target& target = (Branch20Target&)(delta); + + branch.Imm11 = target.Imm11; + branch.Imm6 = target.Imm6; + branch.Sign = target.Sign; + branch.J1 = target.J1; + branch.J2 = target.J2; + + return (ULONG&)branch; +} + +LONG CDetourDis::DecodeBranch24(ULONG opcode, BOOL& fLink) +{ + Branch24& branch = (Branch24&)(opcode); + + Branch24Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm11 = branch.Imm11; + target.Imm10 = branch.Imm10; + target.Sign = branch.Sign; + target.I1 = ~(branch.J1 ^ target.Sign); + target.I2 = ~(branch.J2 ^ target.Sign); + fLink = branch.Link; + + // Sign extend + if (target.Sign) { + target.Padding2 = -1; + } + + return (LONG&)target; +} + +ULONG CDetourDis::EncodeBranch24(ULONG originalOpCode, LONG delta, BOOL fLink) +{ + // Too large for 24 bit branch (24 bit branches can be up to 25 bits due to the trailing 0) + if (delta < static_cast<int>(0xFF000000) || delta > static_cast<int>(0xFFFFFF)) { + return 0; + } + + Branch24& branch = (Branch24&)(originalOpCode); + Branch24Target& target = (Branch24Target&)(delta); + + branch.Imm11 = target.Imm11; + branch.Imm10 = target.Imm10; + branch.Link = fLink; + branch.Sign = target.Sign; + branch.J1 = ~(target.I1 ^ branch.Sign); + branch.J2 = ~(target.I2 ^ branch.Sign); + + return (ULONG&)branch; +} + +LONG CDetourDis::DecodeLiteralLoad8(ULONG instruction) +{ + LiteralLoad8& load = (LiteralLoad8&)(instruction); + + LiteralLoad8Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm8 = load.Imm8; + + return (LONG&)target; +} + +BYTE CDetourDis::EmitLiteralLoad8(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral) +{ + // Note: We add 2 (which gets rounded down) because literals must be 32-bit + // aligned, but the ldr can be 16-bit aligned. + LONG newDelta = CalculateNewDelta((PBYTE)pLiteral + 2, (PBYTE)pDest); + LONG relative = ((newDelta > 0 ? newDelta : -newDelta) & 0x3FF); + + LiteralLoad8Target& target = (LiteralLoad8Target&)(relative); + LiteralLoad8 load = { target.Imm8, targetRegister, 0x9 }; + + return EmitShortInstruction(pDest, (USHORT&)load); +} + +LONG CDetourDis::DecodeLiteralLoad12(ULONG instruction) +{ + LiteralLoad12& load = (LiteralLoad12&)(instruction); + + LiteralLoad12Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm12 = load.Imm12; + + return (LONG&)target; +} + +BYTE CDetourDis::EmitLiteralLoad12(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral) +{ + // Note: We add 2 (which gets rounded down) because literals must be 32-bit + // aligned, but the ldr can be 16-bit aligned. + LONG newDelta = CalculateNewDelta((PBYTE)pLiteral + 2, (PBYTE)pDest); + LONG relative = ((newDelta > 0 ? newDelta : -newDelta) & 0xFFF); + + LiteralLoad12Target& target = (LiteralLoad12Target&)(relative); + target.Imm12 -= target.Imm12 & 3; + LiteralLoad12 load = { target.Imm12, targetRegister, 0x5F, (DWORD)(newDelta > 0), 0xF8 }; + + return EmitLongInstruction(pDest, (ULONG&)load); +} + +BYTE CDetourDis::EmitImmediateRegisterLoad32(PUSHORT& pDest, BYTE reg) +{ + ImmediateRegisterLoad32 load = { 0, reg, reg, 0xF8D }; + return EmitLongInstruction(pDest, (ULONG&)load); +} + +BYTE CDetourDis::EmitImmediateRegisterLoad16(PUSHORT& pDest, BYTE reg) +{ + ImmediateRegisterLoad16 load = { reg, reg, 0x680 >> 2 }; + return EmitShortInstruction(pDest, (USHORT&)load); +} + +BYTE CDetourDis::EmitLongLiteralLoad(PUSHORT& pDest, BYTE targetRegister, PVOID pTarget) +{ + *--((PULONG&)m_pbPool) = (ULONG)(size_t)pTarget; + + // ldr rn, target. + BYTE size = EmitLiteralLoad12(pDest, targetRegister, m_pbPool); + + // This only makes sense if targetRegister != PC; + // otherwise, we would have branched with the previous instruction anyway + if (targetRegister != c_PC) { + // ldr rn, [rn] + if (targetRegister <= 7) { + size = (BYTE)(size + EmitImmediateRegisterLoad16(pDest, targetRegister)); + } + else { + size = (BYTE)(size + EmitImmediateRegisterLoad32(pDest, targetRegister)); + } + } + + return size; +} + +BYTE CDetourDis::EmitLongBranch(PUSHORT& pDest, PVOID pTarget) +{ + // Emit a long literal load into PC + BYTE size = EmitLongLiteralLoad(pDest, c_PC, DETOURS_PBYTE_TO_PFUNC(pTarget)); + return size; +} + +BYTE CDetourDis::PureCopy16(BYTE* pSource, BYTE* pDest) +{ + *(USHORT *)pDest = *(USHORT *)pSource; + return sizeof(USHORT); +} + +BYTE CDetourDis::PureCopy32(BYTE* pSource, BYTE* pDest) +{ + *(UNALIGNED ULONG *)pDest = *(UNALIGNED ULONG*)pSource; + return sizeof(DWORD); +} + +USHORT CDetourDis::CalculateExtra(BYTE sourceLength, BYTE* pDestStart, BYTE* pDestEnd) +{ + ULONG destinationLength = (ULONG)(pDestEnd - pDestStart); + return static_cast<USHORT>((destinationLength > sourceLength) ? (destinationLength - sourceLength) : 0); +} + +BYTE CDetourDis::CopyMiscellaneous16(BYTE* pSource, BYTE* pDest) +{ + USHORT instruction = *(PUSHORT)(pSource); + + // Compare and branch imm5 (CBZ, CBNZ) + if ((instruction & 0x100) && !(instruction & 0x400)) { // (1011x0x1xxxxxxxx) + LONG oldDelta = DecodeBranch5(instruction); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + LONG newDelta = CalculateNewDelta(pTarget, pDest); + instruction = EncodeBranch5(instruction, newDelta); + + if (instruction) { + // Copy the 16 bit instruction over + *(PUSHORT)(pDest) = instruction; + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If that fails, re-encode with 'conditional branch' logic, without using the condition flags + // For example, cbz r2,+0x56 (0x90432) becomes: + // + // 001df73a b92a cbnz r2,001df748 + // 001df73c e002 b 001df744 + // 001df73e bf00 nop + // 001df740 0432 dc.h 0432 + // 001df742 0009 dc.h 0009 + // 001df744 f85ff008 ldr pc,=0x90432 + // + + // Store where we will be writing our conditional branch, and move past it so we can emit a long branch + PUSHORT pDstInst = (PUSHORT)(pDest); + PUSHORT pConditionalBranchInstruction = pDstInst++; + + // Emit the long branch instruction + BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget); + + // Invert the CBZ/CBNZ instruction to move past our 'long branch' if the inverse comparison succeeds + // Write the CBZ/CBNZ instruction *before* the long branch we emitted above + // This had to be done out of order, since the size of a long branch can vary due to alignment restrictions + instruction = EncodeBranch5(*(PUSHORT)(pSource), longBranchSize - c_PCAdjust + sizeof(USHORT)); + Branch5& branch = (Branch5&)(instruction); + branch.OpCode = (branch.OpCode & 0x02) ? 0x2C : 0x2E; // Invert the CBZ/CBNZ comparison + *pConditionalBranchInstruction = instruction; + + // Compute the extra space needed for the branch sequence + m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst)); + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If-Then Instruction (IT) + if ((instruction >> 8 == 0xBF) && (instruction & 0xF)) { //(10111111xxxx(mask != 0b0000)) + // ToDo: Implement IT handler + ASSERT(false); + return sizeof(USHORT); + } + + // ADD/SUB, SXTH, SXTB, UXTH, UXTB, CBZ, CBNZ, PUSH, POP, REV, REV15, REVSH, NOP, YIELD, WFE, WFI, SEV, etc. + return PureCopy16(pSource, pDest); +} + +BYTE CDetourDis::CopyConditionalBranchOrOther16(BYTE* pSource, BYTE* pDest) +{ + USHORT instruction = *(PUSHORT)(pSource); + + // Could be a conditional branch, an Undefined instruction or a Service System Call + // Only the former needs special logic + if ((instruction & 0xE00) != 0xE00) { // 1101(!=111x)xxxxxxxx + LONG oldDelta = DecodeBranch8(instruction); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + LONG newDelta = CalculateNewDelta(pTarget, pDest); + instruction = EncodeBranch8(instruction, newDelta); + if (instruction) { + // Copy the 16 bit instruction over + *(PUSHORT)(pDest) = instruction; + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If that fails, re-encode as a sequence of branches + // For example, bne +0x6E (0x90452) becomes: + // + // 001df758 d100 bne 001df75c + // 001df75a e005 b 001df768 + // 001df75c e002 b 001df764 + // 001df75e bf00 nop + // 001df760 0452 dc.h 0452 + // 001df762 0009 dc.h 0009 + // 001df764 f85ff008 ldr pc,=0x90452 + // + + // First, reuse the existing conditional branch to, if successful, branch down to a 'long branch' that we will emit below + USHORT newInstruction = EncodeBranch8(*(PUSHORT)(pSource), 0); // Due to the size of c_PCAdjust a zero-length branch moves 4 bytes forward, past the following unconditional branch + ASSERT(newInstruction); + PUSHORT pDstInst = (PUSHORT)(pDest); + *pDstInst++ = newInstruction; + + // Next, prepare to insert an unconditional branch that will be hit if the condition above is not met. This branch will branch over the following 'long branch' + // We can't actually encode this branch yet though, because 'long branches' can vary in size + PUSHORT pUnconditionalBranchInstruction = pDstInst++; + + // Then, emit a 'long branch' that will be hit if the original condition is met + BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget); + + // Finally, encode and emit the unconditional branch that will be used to branch past the 'long branch' if the initial condition was not met + Branch11 branch11 = { 0x00, 0x1C }; + newInstruction = EncodeBranch11(*(DWORD*)(&branch11), longBranchSize - c_PCAdjust + sizeof(USHORT)); + ASSERT(newInstruction); + *pUnconditionalBranchInstruction = newInstruction; + + // Compute the extra space needed for the branch sequence + m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst)); + return sizeof(USHORT); // The source instruction was 16 bits + } + + return PureCopy16(pSource, pDest); +} + +BYTE CDetourDis::CopyUnConditionalBranch16(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = *(PUSHORT)(pSource); + + LONG oldDelta = DecodeBranch11(instruction); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + LONG newDelta = CalculateNewDelta(pTarget, pDest); + instruction = EncodeBranch11(instruction, newDelta); + if (instruction) { + // Copy the 16 bit instruction over + *(PUSHORT)(pDest) = (USHORT)instruction; + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If that fails, re-encode as 32-bit + PUSHORT pDstInst = (PUSHORT)(pDest); + instruction = EncodeBranch24(0xf0009000, newDelta, FALSE); + if (instruction) { + // Copy both bytes of the instruction + EmitLongInstruction(pDstInst, instruction); + + m_lExtra = sizeof(DWORD) - sizeof(USHORT); // The destination instruction was 32 bits + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If that fails, emit as a 'long branch' + if (!instruction) { + // For example, b +0x7FE (00090be6) becomes: + // 003f6d02 e001 b 003f6d08 + // 003f6d04 0be6 dc.h 0be6 + // 003f6d06 0009 dc.h 0009 + // 003f6d08 f85ff008 ldr pc,=0x90BE6 + EmitLongBranch(pDstInst, pTarget); + + // Compute the extra space needed for the branch sequence + m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst)); + return sizeof(USHORT); // The source instruction was 16 bits + } + + return sizeof(USHORT); // The source instruction was 16 bits +} + +BYTE CDetourDis::CopyLiteralLoad16(BYTE* pSource, BYTE* pDest) +{ + PBYTE pStart = pDest; + USHORT instruction = *(PUSHORT)(pSource); + + LONG oldDelta = DecodeLiteralLoad8(instruction); + PBYTE pTarget = CalculateTarget(Align4(pSource), oldDelta); + + // Re-encode as a 'long literal load' + // For example, ldr r0, [PC + 1E0] (0x905B4) becomes: + // + // 001df72c f85f0008 ldr r0,=0x905B4 + // 001df730 f8d00000 ldr.w r0,[r0] + LiteralLoad8& load8 = (LiteralLoad8&)(instruction); + EmitLongLiteralLoad((PUSHORT&)pDest, load8.Register, pTarget); + + m_lExtra = (LONG)(pDest - pStart - sizeof(USHORT)); + return sizeof(USHORT); // The source instruction was 16 bits +} + +BYTE CDetourDis::CopyBranchExchangeOrDataProcessing16(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = *(PUSHORT)(pSource); + + // BX + if ((instruction & 0xff80) == 0x4700) { + // The target is stored in a register + m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + + // AND, LSR, TST, ADD, CMP, MOV + return PureCopy16(pSource, pDest); +} + +const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[33] = +{ + // Shift by immediate, move register + // ToDo: Not handling moves from PC + /* 0b00000 */ { 0x00, &CDetourDis::PureCopy16 }, + /* 0b00001 */ { 0x01, &CDetourDis::PureCopy16 }, + /* 0b00010 */ { 0x02, &CDetourDis::PureCopy16 }, + + // Add/subtract register + // Add/subtract immediate + /* 0b00011 */ { 0x03, &CDetourDis::PureCopy16}, + + // Add/subtract/compare/move immediate + /* 0b00100 */ { 0x04, &CDetourDis::PureCopy16 }, + /* 0b00101 */ { 0x05, &CDetourDis::PureCopy16 }, + /* 0b00110 */ { 0x06, &CDetourDis::PureCopy16 }, + /* 0b00111 */ { 0x07, &CDetourDis::PureCopy16 }, + + // Data-processing register + // Special data processing + // Branch/exchange instruction set + /* 0b01000 */ { 0x08, &CDetourDis::CopyBranchExchangeOrDataProcessing16 }, + + // Load from literal pool + /* 0b01001 */ { 0x09, &CDetourDis::CopyLiteralLoad16 }, + + // Load/store register offset + /* 0b01010 */ { 0x0a, &CDetourDis::PureCopy16 }, + /* 0b01011 */ { 0x0b, &CDetourDis::PureCopy16 }, + + // Load/store word/byte immediate offset. + /* 0b01100 */ { 0x0c, &CDetourDis::PureCopy16 }, + /* 0b01101 */ { 0x0d, &CDetourDis::PureCopy16 }, + /* 0b01110 */ { 0x0e, &CDetourDis::PureCopy16 }, + /* 0b01111 */ { 0x0f, &CDetourDis::PureCopy16 }, + + // Load/store halfword immediate offset. + /* 0b10000 */ { 0x10, &CDetourDis::PureCopy16 }, + /* 0b10001 */ { 0x11, &CDetourDis::PureCopy16 }, + + // Load from or store to stack + /* 0b10010 */ { 0x12, &CDetourDis::PureCopy16 }, + /* 0b10011 */ { 0x13, &CDetourDis::PureCopy16 }, + + // Add to SP or PC + /* 0b10100 */ { 0x14, &CDetourDis::PureCopy16 }, + // ToDo: Is ADR (T1) blitt-able? + // It adds a value to PC and stores the result in a register. + // Does this count as a 'target' for detours? + /* 0b10101 */ { 0x15, &CDetourDis::PureCopy16 }, + + // Miscellaneous + /* 0b10110 */ { 0x16, &CDetourDis::CopyMiscellaneous16 }, + /* 0b10111 */ { 0x17, &CDetourDis::CopyMiscellaneous16 }, + + // Load/store multiple + /* 0b11000 */ { 0x18, &CDetourDis::PureCopy16 }, + /* 0b11001 */ { 0x19, &CDetourDis::PureCopy16 }, + // ToDo: Are we sure these are all safe? + // LDMIA, for example, can include an 'embedded' branch. + // Does this count as a 'target' for detours? + + // Conditional branch + /* 0b11010 */ { 0x1a, &CDetourDis::CopyConditionalBranchOrOther16 }, + + // Conditional branch + // Undefined instruction + // Service (system) call + /* 0b11011 */ { 0x1b, &CDetourDis::CopyConditionalBranchOrOther16 }, + + // Unconditional branch + /* 0b11100 */ { 0x1c, &CDetourDis::CopyUnConditionalBranch16 }, + + // 32-bit instruction + /* 0b11101 */ { 0x1d, &CDetourDis::BeginCopy32 }, + /* 0b11110 */ { 0x1e, &CDetourDis::BeginCopy32 }, + /* 0b11111 */ { 0x1f, &CDetourDis::BeginCopy32 }, + { 0, NULL } +}; + +BYTE CDetourDis::CopyBranch24(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = GetLongInstruction(pSource); + BOOL fLink; + LONG oldDelta = DecodeBranch24(instruction, fLink); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + // Re-encode as 32-bit + PUSHORT pDstInst = (PUSHORT)(pDest); + LONG newDelta = CalculateNewDelta(pTarget, pDest); + instruction = EncodeBranch24(instruction, newDelta, fLink); + if (instruction) { + // Copy both bytes of the instruction + EmitLongInstruction(pDstInst, instruction); + return sizeof(DWORD); + } + + // If that fails, re-encode as a 'long branch' + EmitLongBranch(pDstInst, pTarget); + + // Compute the extra space needed for the instruction + m_lExtra = CalculateExtra(sizeof(DWORD), pDest, (BYTE*)(pDstInst)); + return sizeof(DWORD); // The source instruction was 32 bits +} + +BYTE CDetourDis::CopyBranchOrMiscellaneous32(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = GetLongInstruction(pSource); + if ((instruction & 0xf800d000) == 0xf0008000) { // B<c>.W <label> + LONG oldDelta = DecodeBranch20(instruction); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + // Re-encode as 32-bit + PUSHORT pDstInst = (PUSHORT)(pDest); + LONG newDelta = CalculateNewDelta(pTarget, pDest); + instruction = EncodeBranch20(instruction, newDelta); + if (instruction) { + // Copy both bytes of the instruction + EmitLongInstruction(pDstInst, instruction); + return sizeof(DWORD); + } + + // If that fails, re-encode as a sequence of branches + // For example, bls.w +0x86 (00090480)| becomes: + // + // 001df788 f2408001 bls.w 001df78e + // 001df78c e004 b 001df798 + // 001df78e e001 b 001df794 + // 001df790 0480 dc.h 0480 + // 001df792 0009 dc.h 0009 + // 001df794 f85ff008 ldr pc,=0x90480 + // + + // First, reuse the existing conditional branch to, if successful, + // branch down to a 'long branch' that we will emit below + instruction = EncodeBranch20(GetLongInstruction(pSource), 2); + // Due to the size of c_PCAdjust a two-length branch moves 6 bytes forward, + // past the following unconditional branch + ASSERT(instruction); + EmitLongInstruction(pDstInst, instruction); + + // Next, prepare to insert an unconditional branch that will be hit + // if the condition above is not met. This branch will branch over + // the following 'long branch' + // We can't actually encode this branch yet though, because + // 'long branches' can vary in size + PUSHORT pUnconditionalBranchInstruction = pDstInst++; + + // Then, emit a 'long branch' that will be hit if the original condition is met + BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget); + + // Finally, encode and emit the unconditional branch that will be used + // to branch past the 'long branch' if the initial condition was not met + Branch11 branch11 = { 0x00, 0x1C }; + instruction = EncodeBranch11(*(DWORD*)(&branch11), longBranchSize - c_PCAdjust + sizeof(USHORT)); + ASSERT(instruction); + *pUnconditionalBranchInstruction = static_cast<USHORT>(instruction); + + // Compute the extra space needed for the instruction + m_lExtra = CalculateExtra(sizeof(DWORD), pDest, (BYTE*)(pDstInst)); + return sizeof(DWORD); // The source instruction was 32 bits + } + + if ((instruction & 0xf800d000) == 0xf0009000) { // B.W <label> + // B <label> 11110xxxxxxxxxxx10xxxxxxxxxxxxxx + return CopyBranch24(pSource, pDest); + } + + if ((instruction & 0xf800d000) == 0xf000d000) { // BL.W <label> + // B <label> 11110xxxxxxxxxxx10xxxxxxxxxxxxxx + + PUSHORT pDstInst = (PUSHORT)(pDest); + BOOL fLink; + LONG oldDelta = DecodeBranch24(instruction, fLink); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + *--((PULONG&)m_pbPool) = (ULONG)(size_t)DETOURS_PBYTE_TO_PFUNC(pTarget); + + // ldr lr, target. + EmitLiteralLoad12(pDstInst, c_LR, m_pbPool); + // blx lr + EmitShortInstruction(pDstInst, 0x47f0); + + // Compute the extra space needed for the instruction + m_lExtra = CalculateExtra(sizeof(DWORD), pDest, (BYTE*)(pDstInst)); + return sizeof(DWORD); // The source instruction was 32 bits + } + + if ((instruction & 0xFFF0FFFF) == 0xF3C08F00) { + // BXJ 111100111100xxxx1000111100000000 + // BXJ switches to Jazelle mode, which is not supported + ASSERT(false); + } + + if ((instruction & 0xFFFFFF00) == 0xF3DE8F00) { + // SUBS PC, LR 111100111101111010001111xxxxxxxx + m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + + // Everything else should be blitt-able + return PureCopy32(pSource, pDest); +} + +BYTE CDetourDis::CopyLiteralLoad32(BYTE* pSource, BYTE* pDest) +{ + BYTE* pStart = pDest; + ULONG instruction = GetLongInstruction(pSource); + + LONG oldDelta = DecodeLiteralLoad12(instruction); + PBYTE pTarget = CalculateTarget(Align4(pSource), oldDelta); + + LiteralLoad12& load = (LiteralLoad12&)(instruction); + + EmitLongLiteralLoad((PUSHORT&)pDest, load.Register, pTarget); + + m_lExtra = (LONG)(pDest - pStart - sizeof(DWORD)); + + return sizeof(DWORD); // The source instruction was 32 bits +} + +BYTE CDetourDis::CopyLoadAndStoreSingle(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = GetLongInstruction(pSource); + + // Note: The following masks only look at the interesting bits + // (not the opCode prefix, since that check was performed in + // order to get to this function) + if (!(instruction & 0x100000)) { + // 1111 100x xxx0 xxxxxxxxxxxxxxxxxxxx : STR, STRB, STRH, etc. + return PureCopy32(pSource, pDest); + } + + if ((instruction & 0xF81F0000) == 0xF81F0000) { + // 1111100xxxx11111xxxxxxxxxxxxxxxx : PC +/- Imm12 + return CopyLiteralLoad32(pSource, pDest); + } + + if ((instruction & 0xFE70F000) == 0xF81FF000) { + // 1111100xx001xxxx1111xxxxxxxxxxxx : PLD, PLI + // Convert PC-Relative PLD/PLI instructions to noops (1111100Xx00111111111xxxxxxxxxxxx) + if ((instruction & 0xFE7FF000) == 0xF81FF000) { + PUSHORT pDstInst = (PUSHORT)(pDest); + *pDstInst++ = c_NOP; + *pDstInst++ = c_NOP; + return sizeof(DWORD); // The source instruction was 32 bits + } + + // All other PLD/PLI instructions are blitt-able + return PureCopy32(pSource, pDest); + } + + // If the load is writing to PC + if ((instruction & 0xF950F000) == 0xF850F000) { + m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + + // All other loads LDR (immediate), etc. + return PureCopy32(pSource, pDest); +} + +BYTE CDetourDis::CopyLoadAndStoreMultipleAndSRS(BYTE* pSource, BYTE* pDest) +{ + // Probably all blitt-able, although not positive since some of these can result in a branch (LDMIA, POP, etc.) + return PureCopy32(pSource, pDest); +} + +BYTE CDetourDis::CopyTableBranch(BYTE* pSource, BYTE* pDest) +{ + m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + ULONG instruction = GetLongInstruction(pSource); + TableBranch& tableBranch = (TableBranch&)(instruction); + + // If the base register is anything other than PC, we can simply copy the instruction + if (tableBranch.BaseRegister != c_PC) { + return PureCopy32(pSource, pDest); + } + + __debugbreak(); + + // If the base register is PC, we need to manually perform the table lookup + // For example, this: + // + // 7ef40000 e8dff002 tbb [pc,r2] + // + // becomes this: + // + // 7ef40404 b401 push {r0} ; pushed as a placeholder for the target address + // 7ef40406 e92d0005 push.w {r0,r2} ; scratch register and another register are pushed; there's a minimum of two registers in the list for push.w + // 7ef40410 4820 ldr r0,=0x7EF40004 ; load the table address from the literal pool + // 7ef40414 eb000042 add r0,r0,r2,lsl #1 ; add the index value to the address of the table to get the table entry; lsl only used if it's a TBH instruction + // 7ef40418 f8d00000 ldr.w r0,[r0] ; dereference the table entry to get the value of the target + // 7ef4041c ea4f0040 lsl r0,r0,#1 ; multiply the offset by 2 (per the spec) + // 7ef40420 eb00000f add.w r0,r0,pc ; Add the offset to pc to get the target address + // 7ef40424 f8cd000c str.w r0,[sp,#0xC] ; store the target address on the stack (into the first push) + // 7ef40428 e8bd0005 pop.w {r0,r2} ; scratch register and another register are popped; there's a minimum of two registers in the list for pop.w + // 7ef4042c bd00 pop {pc} ; pop the address into pc + // + + // Push r0 to make room for our jump address on the stack + PUSHORT pDstInst = (PUSHORT)(pDest); + *pDstInst++ = 0xb401; + + // Locate a scratch register + BYTE scrReg = 0; + while (scrReg == tableBranch.IndexRegister) { + ++scrReg; + } + + // Push scrReg and tableBranch.IndexRegister (push.w doesn't support pushing just 1 register) + DWORD pushInstruction = 0xe92d0000; + pushInstruction |= 1 << scrReg; + pushInstruction |= 1 << tableBranch.IndexRegister; + EmitLongInstruction(pDstInst, pushInstruction); + + // Write the target address out to the 'literal pool'; + // when the base register of a TBB/TBH is PC, + // the branch table immediately follows the instruction + BYTE* pTarget = CalculateTarget(pSource, 0); + *--((PUSHORT&)m_pbPool) = (USHORT)((size_t)pTarget & 0xffff); + *--((PUSHORT&)m_pbPool) = (USHORT)((size_t)pTarget >> 16); + + // Load the literal pool value into our scratch register (this contains the address of the branch table) + // ldr rn, target + EmitLiteralLoad8(pDstInst, scrReg, m_pbPool); + + // Add the index offset to the address of the branch table; the result will be the value within the table that contains the branch offset + // We need to multiply the index by two if we are using halfword indexing + // Will shift tableBranch.IndexRegister by 1 (multiply by 2) if using a TBH + EmitAdd32(pDstInst, scrReg, tableBranch.IndexRegister, scrReg, tableBranch.HalfWord); + + // Dereference rn into rn, to load the value within the table + // ldr rn, [rn] + if (scrReg < 0x7) { + EmitImmediateRegisterLoad16(pDstInst, scrReg); + } + else { + EmitImmediateRegisterLoad32(pDstInst, scrReg); + } + + // Multiply the offset by two to get the true offset value (as per the spec) + EmitLogicalShiftLeft32(pDstInst, scrReg, scrReg, 1); + + // Add the offset to PC to get the target + EmitAdd32(pDstInst, scrReg, c_PC, scrReg, 0); + + // Now write the contents of scrReg to the stack, so we can pop it into PC + // Write the address of the branch table entry to the stack, so we can pop it into PC + EmitStoreImmediate12(pDstInst, scrReg, c_SP, sizeof(DWORD) * 3); + + // Pop scrReg and tableBranch.IndexRegister (pop.w doesn't support popping just 1 register) + DWORD popInstruction = 0xe8bd0000; + popInstruction |= 1 << scrReg; + popInstruction |= 1 << tableBranch.IndexRegister; + EmitLongInstruction(pDstInst, popInstruction); + + // Pop PC + *pDstInst++ = 0xbd00; + + // Compute the extra space needed for the branch sequence + m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst)); + return sizeof(DWORD); +} + +BYTE CDetourDis::BeginCopy32(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = GetLongInstruction(pSource); + + // Immediate data processing instructions; ADD, SUB, MOV, MOVN, ADR, MOVT, BFC, SSAT16, etc. + if ((instruction & 0xF8008000) == 0xF0000000) { // 11110xxxxxxxxxxx0xxxxxxxxxxxxxxx + // Should all be blitt-able + // ToDo: What about ADR? Is it safe to do a straight-copy? + // ToDo: Not handling moves to or from PC + return PureCopy32(pSource, pDest); + } + + // Non-Immediate data processing instructions; ADD, EOR, TST, etc. + if ((instruction & 0xEE000000) == 0xEA000000) { // 111x101xxxxxxxxxxxxxxxxxxxxxxx + // Should all be blitt-able + return PureCopy32(pSource, pDest); + } + + // Load and store single data item, memory hints + if ((instruction & 0xFE000000) == 0xF8000000) { // 1111100xxxxxxxxxxxxxxxxxxxxxxxxx + return CopyLoadAndStoreSingle(pSource, pDest); + } + + // Load and store, double and exclusive, and table branch + if ((instruction & 0xFE400000) == 0xE8400000) { // 1110100xx1xxxxxxxxxxxxxxxxxxxxxx + // Load and store double + if (instruction & 0x1200000) { + // LDRD, STRD (immediate) : xxxxxxxPxxWxxxxxxxxxxxxxxxxxxxxx where PW != 0b00 + // The source register is PC + if ((instruction & 0xF0000) == 0xF0000) { + // ToDo: If the source register is PC, what should we do? + ASSERT(false); + } + + // If either target registers are PC + if (((instruction & 0xF000) == 0xF000) || + ((instruction & 0xF00) == 0xF00)) { + m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + + return PureCopy32(pSource, pDest); + } + + // Load and store exclusive + if (!(instruction & 0x800000)) { // LDREX, STREX : xxxxxxxx0xxxxxxxxxxxxxxxxxxxxxxx + if ((instruction & 0xF000) == 0xF000) { // xxxxxxxxxxxx1111xxxxxxxxxxxx + m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + return PureCopy32(pSource, pDest); + } + + // Table branch + if ((instruction & 0x1000F0) == 0x100000 || // TBB : xxxxxxxxxxx1xxxxxxxxxxxx0000xxxx + (instruction & 0x1000F0) == 0x100010) { // TBH : xxxxxxxxxxx1xxxxxxxxxxxx0001xxxx + return CopyTableBranch(pSource, pDest); + } + + // Load and store exclusive byte, halfword, doubleword (LDREXB, LDREXH, LDREXD, STREXB, STREXH, STREXD, etc.) + return PureCopy32(pSource, pDest); + } + + // Load and store multiple, RFE and SRS + if ((instruction & 0xFE400000) == 0xE8000000) { // 1110100xx0xxxxxxxxxxxxxxxxxxxxxx + // Return from exception (RFE) + if ((instruction & 0xE9900000) == 0xE9900000 || // 1110100110x1xxxxxxxxxxxxxxxxxxxx + (instruction & 0xE8100000) == 0xE8100000) { // 1110100000x1xxxxxxxxxxxxxxxxxxxx + return PureCopy32(pSource, pDest); + } + + return CopyLoadAndStoreMultipleAndSRS(pSource, pDest); + } + + // Branches, miscellaneous control + if ((instruction & 0xF8008000) == 0xF0008000) { // 11110xxxxxxxxxxx0xxxxxxxxxxxxxxx + // Branches, miscellaneous control + return CopyBranchOrMiscellaneous32(pSource, pDest); + } + + // Coprocessor instructions + if ((instruction & 0xEC000000) == 0xEC000000) { // 111x11xxxxxxxxxxxxxxxxxxxxxxxxxx + return PureCopy32(pSource, pDest); + } + + // Unhandled instruction; should never make it this far + ASSERT(false); + return PureCopy32(pSource, pDest); +} + +/////////////////////////////////////////////////////////// Disassembler Code. +// +CDetourDis::CDetourDis() : + m_pbTarget((PBYTE)DETOUR_INSTRUCTION_TARGET_NONE), + m_pbPool(NULL), + m_lExtra(0) +{ +} + +PBYTE CDetourDis::CopyInstruction(PBYTE pDst, + PBYTE *ppDstPool, + PBYTE pSrc, + PBYTE *ppTarget, + LONG *plExtra) +{ + if (pDst && ppDstPool && ppDstPool != NULL) { + m_pbPool = (PBYTE)*ppDstPool; + } + else { + pDst = m_rbScratchDst; + m_pbPool = m_rbScratchDst + sizeof(m_rbScratchDst); + } + // Make sure the constant pool is 32-bit aligned. + m_pbPool -= ((ULONG_PTR)m_pbPool) & 3; + + REFCOPYENTRY pEntry = &s_rceCopyTable[pSrc[1] >> 3]; + ULONG size = (this->*pEntry->pfCopy)(pSrc, pDst); + + pSrc += size; + + // If the target is needed, store our target + if (ppTarget) { + *ppTarget = m_pbTarget; + } + if (plExtra) { + *plExtra = m_lExtra; + } + if (ppDstPool) { + *ppDstPool = m_pbPool; + } + + return pSrc; +} + + +PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, + _Inout_opt_ PVOID *ppDstPool, + _In_ PVOID pSrc, + _Out_opt_ PVOID *ppTarget, + _Out_opt_ LONG *plExtra) +{ + CDetourDis state; + return (PVOID)state.CopyInstruction((PBYTE)pDst, + (PBYTE*)ppDstPool, + (PBYTE)pSrc, + (PBYTE*)ppTarget, + plExtra); +} + +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + +#define c_LR 30 // The register number for the Link Register +#define c_SP 31 // The register number for the Stack Pointer +#define c_NOP 0xd503201f // A nop instruction +#define c_BREAK (0xd4200000 | (0xf000 << 5)) // A break instruction + +// +// Problematic instructions: +// +// ADR 0ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x10000000 (l = low, h = high, d = Rd) +// ADRP 1ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x90000000 (l = low, h = high, d = Rd) +// +// B.cond 01010100 iiiiiiii iiiiiiii iii0cccc & 0xff000010 == 0x54000000 (i = delta = SignExtend(imm19:00, 64), c = cond) +// +// B 000101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x14000000 (i = delta = SignExtend(imm26:00, 64)) +// BL 100101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x94000000 (i = delta = SignExtend(imm26:00, 64)) +// +// CBNZ z0110101 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x35000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt) +// CBZ z0110100 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x34000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt) +// +// LDR Wt 00011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x18000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR Xt 01011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x58000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDRSW 10011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x98000000 (i = SignExtend(imm19:00, 64), t = Rt) +// PRFM 11011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xd8000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR St 00011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x1c000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR Dt 01011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x5c000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR Qt 10011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x9c000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR inv 11011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xdc000000 (i = SignExtend(imm19:00, 64), t = Rt) +// +// TBNZ z0110111 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x37000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt) +// TBZ z0110110 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x36000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt) +// + +class CDetourDis +{ + public: + CDetourDis(); + + PBYTE CopyInstruction(PBYTE pDst, + PBYTE pSrc, + PBYTE *ppTarget, + LONG *plExtra); + + public: + typedef BYTE (CDetourDis::* COPYFUNC)(PBYTE pbDst, PBYTE pbSrc); + + union AddImm12 + { + DWORD Assembled; + struct + { + DWORD Rd : 5; // Destination register + DWORD Rn : 5; // Source register + DWORD Imm12 : 12; // 12-bit immediate + DWORD Shift : 2; // shift (must be 0 or 1) + DWORD Opcode1 : 7; // Must be 0010001 == 0x11 + DWORD Size : 1; // 0 = 32-bit, 1 = 64-bit + } s; + static DWORD Assemble(DWORD size, DWORD rd, DWORD rn, ULONG imm, DWORD shift) + { + AddImm12 temp; + temp.s.Rd = rd; + temp.s.Rn = rn; + temp.s.Imm12 = imm & 0xfff; + temp.s.Shift = shift; + temp.s.Opcode1 = 0x11; + temp.s.Size = size; + return temp.Assembled; + } + static DWORD AssembleAdd32(DWORD rd, DWORD rn, ULONG imm, DWORD shift) { return Assemble(0, rd, rn, imm, shift); } + static DWORD AssembleAdd64(DWORD rd, DWORD rn, ULONG imm, DWORD shift) { return Assemble(1, rd, rn, imm, shift); } + }; + + union Adr19 + { + DWORD Assembled; + struct + { + DWORD Rd : 5; // Destination register + DWORD Imm19 : 19; // 19-bit upper immediate + DWORD Opcode1 : 5; // Must be 10000 == 0x10 + DWORD Imm2 : 2; // 2-bit lower immediate + DWORD Type : 1; // 0 = ADR, 1 = ADRP + } s; + inline LONG Imm() const { DWORD Imm = (s.Imm19 << 2) | s.Imm2; return (LONG)(Imm << 11) >> 11; } + static DWORD Assemble(DWORD type, DWORD rd, LONG delta) + { + Adr19 temp; + temp.s.Rd = rd; + temp.s.Imm19 = (delta >> 2) & 0x7ffff; + temp.s.Opcode1 = 0x10; + temp.s.Imm2 = delta & 3; + temp.s.Type = type; + return temp.Assembled; + } + static DWORD AssembleAdr(DWORD rd, LONG delta) { return Assemble(0, rd, delta); } + static DWORD AssembleAdrp(DWORD rd, LONG delta) { return Assemble(1, rd, delta); } + }; + + union Bcc19 + { + DWORD Assembled; + struct + { + DWORD Condition : 4; // Condition + DWORD Opcode1 : 1; // Must be 0 + DWORD Imm19 : 19; // 19-bit immediate + DWORD Opcode2 : 8; // Must be 01010100 == 0x54 + } s; + inline LONG Imm() const { return (LONG)(s.Imm19 << 13) >> 11; } + static DWORD AssembleBcc(DWORD condition, LONG delta) + { + Bcc19 temp; + temp.s.Condition = condition; + temp.s.Opcode1 = 0; + temp.s.Imm19 = delta >> 2; + temp.s.Opcode2 = 0x54; + return temp.Assembled; + } + }; + + union Branch26 + { + DWORD Assembled; + struct + { + DWORD Imm26 : 26; // 26-bit immediate + DWORD Opcode1 : 5; // Must be 00101 == 0x5 + DWORD Link : 1; // 0 = B, 1 = BL + } s; + inline LONG Imm() const { return (LONG)(s.Imm26 << 6) >> 4; } + static DWORD Assemble(DWORD link, LONG delta) + { + Branch26 temp; + temp.s.Imm26 = delta >> 2; + temp.s.Opcode1 = 0x5; + temp.s.Link = link; + return temp.Assembled; + } + static DWORD AssembleB(LONG delta) { return Assemble(0, delta); } + static DWORD AssembleBl(LONG delta) { return Assemble(1, delta); } + }; + + union Br + { + DWORD Assembled; + struct + { + DWORD Opcode1 : 5; // Must be 00000 == 0 + DWORD Rn : 5; // Register number + DWORD Opcode2 : 22; // Must be 1101011000011111000000 == 0x3587c0 for Br + // 0x358fc0 for Brl + } s; + static DWORD Assemble(DWORD rn, bool link) + { + Br temp; + temp.s.Opcode1 = 0; + temp.s.Rn = rn; + temp.s.Opcode2 = 0x3587c0; + if (link) + temp.Assembled |= 0x00200000; + return temp.Assembled; + } + static DWORD AssembleBr(DWORD rn) + { + return Assemble(rn, false); + } + static DWORD AssembleBrl(DWORD rn) + { + return Assemble(rn, true); + } + }; + + union Cbz19 + { + DWORD Assembled; + struct + { + DWORD Rt : 5; // Register to test + DWORD Imm19 : 19; // 19-bit immediate + DWORD Nz : 1; // 0 = CBZ, 1 = CBNZ + DWORD Opcode1 : 6; // Must be 011010 == 0x1a + DWORD Size : 1; // 0 = 32-bit, 1 = 64-bit + } s; + inline LONG Imm() const { return (LONG)(s.Imm19 << 13) >> 11; } + static DWORD Assemble(DWORD size, DWORD nz, DWORD rt, LONG delta) + { + Cbz19 temp; + temp.s.Rt = rt; + temp.s.Imm19 = delta >> 2; + temp.s.Nz = nz; + temp.s.Opcode1 = 0x1a; + temp.s.Size = size; + return temp.Assembled; + } + }; + + union LdrLit19 + { + DWORD Assembled; + struct + { + DWORD Rt : 5; // Destination register + DWORD Imm19 : 19; // 19-bit immediate + DWORD Opcode1 : 2; // Must be 0 + DWORD FpNeon : 1; // 0 = LDR Wt/LDR Xt/LDRSW/PRFM, 1 = LDR St/LDR Dt/LDR Qt + DWORD Opcode2 : 3; // Must be 011 = 3 + DWORD Size : 2; // 00 = LDR Wt/LDR St, 01 = LDR Xt/LDR Dt, 10 = LDRSW/LDR Qt, 11 = PRFM/invalid + } s; + inline LONG Imm() const { return (LONG)(s.Imm19 << 13) >> 11; } + static DWORD Assemble(DWORD size, DWORD fpneon, DWORD rt, LONG delta) + { + LdrLit19 temp; + temp.s.Rt = rt; + temp.s.Imm19 = delta >> 2; + temp.s.Opcode1 = 0; + temp.s.FpNeon = fpneon; + temp.s.Opcode2 = 3; + temp.s.Size = size; + return temp.Assembled; + } + }; + + union LdrFpNeonImm9 + { + DWORD Assembled; + struct + { + DWORD Rt : 5; // Destination register + DWORD Rn : 5; // Base register + DWORD Imm12 : 12; // 12-bit immediate + DWORD Opcode1 : 1; // Must be 1 == 1 + DWORD Opc : 1; // Part of size + DWORD Opcode2 : 6; // Must be 111101 == 0x3d + DWORD Size : 2; // Size (0=8-bit, 1=16-bit, 2=32-bit, 3=64-bit, 4=128-bit) + } s; + static DWORD Assemble(DWORD size, DWORD rt, DWORD rn, ULONG imm) + { + LdrFpNeonImm9 temp; + temp.s.Rt = rt; + temp.s.Rn = rn; + temp.s.Imm12 = imm; + temp.s.Opcode1 = 1; + temp.s.Opc = size >> 2; + temp.s.Opcode2 = 0x3d; + temp.s.Size = size & 3; + return temp.Assembled; + } + }; + + union Mov16 + { + DWORD Assembled; + struct + { + DWORD Rd : 5; // Destination register + DWORD Imm16 : 16; // Immediate + DWORD Shift : 2; // Shift amount (0=0, 1=16, 2=32, 3=48) + DWORD Opcode : 6; // Must be 100101 == 0x25 + DWORD Type : 2; // 0 = MOVN, 1 = reserved, 2 = MOVZ, 3 = MOVK + DWORD Size : 1; // 0 = 32-bit, 1 = 64-bit + } s; + static DWORD Assemble(DWORD size, DWORD type, DWORD rd, DWORD imm, DWORD shift) + { + Mov16 temp; + temp.s.Rd = rd; + temp.s.Imm16 = imm; + temp.s.Shift = shift; + temp.s.Opcode = 0x25; + temp.s.Type = type; + temp.s.Size = size; + return temp.Assembled; + } + static DWORD AssembleMovn32(DWORD rd, DWORD imm, DWORD shift) { return Assemble(0, 0, rd, imm, shift); } + static DWORD AssembleMovn64(DWORD rd, DWORD imm, DWORD shift) { return Assemble(1, 0, rd, imm, shift); } + static DWORD AssembleMovz32(DWORD rd, DWORD imm, DWORD shift) { return Assemble(0, 2, rd, imm, shift); } + static DWORD AssembleMovz64(DWORD rd, DWORD imm, DWORD shift) { return Assemble(1, 2, rd, imm, shift); } + static DWORD AssembleMovk32(DWORD rd, DWORD imm, DWORD shift) { return Assemble(0, 3, rd, imm, shift); } + static DWORD AssembleMovk64(DWORD rd, DWORD imm, DWORD shift) { return Assemble(1, 3, rd, imm, shift); } + }; + + union Tbz14 + { + DWORD Assembled; + struct + { + DWORD Rt : 5; // Register to test + DWORD Imm14 : 14; // 14-bit immediate + DWORD Bit : 5; // 5-bit index + DWORD Nz : 1; // 0 = TBZ, 1 = TBNZ + DWORD Opcode1 : 6; // Must be 011011 == 0x1b + DWORD Size : 1; // 0 = 32-bit, 1 = 64-bit + } s; + inline LONG Imm() const { return (LONG)(s.Imm14 << 18) >> 16; } + static DWORD Assemble(DWORD size, DWORD nz, DWORD rt, DWORD bit, LONG delta) + { + Tbz14 temp; + temp.s.Rt = rt; + temp.s.Imm14 = delta >> 2; + temp.s.Bit = bit; + temp.s.Nz = nz; + temp.s.Opcode1 = 0x1b; + temp.s.Size = size; + return temp.Assembled; + } + }; + + + protected: + BYTE PureCopy32(BYTE* pSource, BYTE* pDest); + BYTE EmitMovImmediate(PULONG& pDstInst, BYTE rd, UINT64 immediate); + BYTE CopyAdr(BYTE* pSource, BYTE* pDest, ULONG instruction); + BYTE CopyBcc(BYTE* pSource, BYTE* pDest, ULONG instruction); + BYTE CopyB(BYTE* pSource, BYTE* pDest, ULONG instruction); + BYTE CopyBl(BYTE* pSource, BYTE* pDest, ULONG instruction); + BYTE CopyB_or_Bl(BYTE* pSource, BYTE* pDest, ULONG instruction, bool link); + BYTE CopyCbz(BYTE* pSource, BYTE* pDest, ULONG instruction); + BYTE CopyTbz(BYTE* pSource, BYTE* pDest, ULONG instruction); + BYTE CopyLdrLiteral(BYTE* pSource, BYTE* pDest, ULONG instruction); + + protected: + ULONG GetInstruction(BYTE* pSource) + { + return ((PULONG)pSource)[0]; + } + + BYTE EmitInstruction(PULONG& pDstInst, ULONG instruction) + { + *pDstInst++ = instruction; + return sizeof(ULONG); + } + + protected: + PBYTE m_pbTarget; + BYTE m_rbScratchDst[128]; // matches or exceeds rbCode +}; + +BYTE CDetourDis::PureCopy32(BYTE* pSource, BYTE* pDest) +{ + *(ULONG *)pDest = *(ULONG*)pSource; + return sizeof(DWORD); +} + +/////////////////////////////////////////////////////////// Disassembler Code. +// +CDetourDis::CDetourDis() : + m_pbTarget((PBYTE)DETOUR_INSTRUCTION_TARGET_NONE) +{ +} + +PBYTE CDetourDis::CopyInstruction(PBYTE pDst, + PBYTE pSrc, + PBYTE *ppTarget, + LONG *plExtra) +{ + if (pDst == NULL) { + pDst = m_rbScratchDst; + } + + DWORD Instruction = GetInstruction(pSrc); + + ULONG CopiedSize; + if ((Instruction & 0x1f000000) == 0x10000000) { + CopiedSize = CopyAdr(pSrc, pDst, Instruction); + } else if ((Instruction & 0xff000010) == 0x54000000) { + CopiedSize = CopyBcc(pSrc, pDst, Instruction); + } else if ((Instruction & 0x7c000000) == 0x14000000) { + CopiedSize = CopyB_or_Bl(pSrc, pDst, Instruction, (Instruction & 0x80000000) != 0); + } else if ((Instruction & 0x7e000000) == 0x34000000) { + CopiedSize = CopyCbz(pSrc, pDst, Instruction); + } else if ((Instruction & 0x7e000000) == 0x36000000) { + CopiedSize = CopyTbz(pSrc, pDst, Instruction); + } else if ((Instruction & 0x3b000000) == 0x18000000) { + CopiedSize = CopyLdrLiteral(pSrc, pDst, Instruction); + } else { + CopiedSize = PureCopy32(pSrc, pDst); + } + + // If the target is needed, store our target + if (ppTarget) { + *ppTarget = m_pbTarget; + } + if (plExtra) { + *plExtra = CopiedSize - sizeof(DWORD); + } + + return pSrc + 4; +} + +BYTE CDetourDis::EmitMovImmediate(PULONG& pDstInst, BYTE rd, UINT64 immediate) +{ + DWORD piece[4]; + piece[3] = (DWORD)((immediate >> 48) & 0xffff); + piece[2] = (DWORD)((immediate >> 32) & 0xffff); + piece[1] = (DWORD)((immediate >> 16) & 0xffff); + piece[0] = (DWORD)((immediate >> 0) & 0xffff); + int count = 0; + + // special case: MOVN with 32-bit dest + if (piece[3] == 0 && piece[2] == 0 && piece[1] == 0xffff) + { + EmitInstruction(pDstInst, Mov16::AssembleMovn32(rd, piece[0] ^ 0xffff, 0)); + count++; + } + + // MOVN/MOVZ with 64-bit dest + else + { + int zero_pieces = (piece[3] == 0x0000) + (piece[2] == 0x0000) + (piece[1] == 0x0000) + (piece[0] == 0x0000); + int ffff_pieces = (piece[3] == 0xffff) + (piece[2] == 0xffff) + (piece[1] == 0xffff) + (piece[0] == 0xffff); + DWORD defaultPiece = (ffff_pieces > zero_pieces) ? 0xffff : 0x0000; + bool first = true; + for (int pieceNum = 3; pieceNum >= 0; pieceNum--) + { + DWORD curPiece = piece[pieceNum]; + if (curPiece != defaultPiece || (pieceNum == 0 && first)) + { + count++; + if (first) + { + if (defaultPiece == 0xffff) + { + EmitInstruction(pDstInst, Mov16::AssembleMovn64(rd, curPiece ^ 0xffff, pieceNum)); + } + else + { + EmitInstruction(pDstInst, Mov16::AssembleMovz64(rd, curPiece, pieceNum)); + } + first = false; + } + else + { + EmitInstruction(pDstInst, Mov16::AssembleMovk64(rd, curPiece, pieceNum)); + } + } + } + } + return (BYTE)(count * sizeof(DWORD)); +} + +BYTE CDetourDis::CopyAdr(BYTE* pSource, BYTE* pDest, ULONG instruction) +{ + Adr19& decoded = (Adr19&)(instruction); + PULONG pDstInst = (PULONG)(pDest); + + // ADR case + if (decoded.s.Type == 0) + { + BYTE* pTarget = pSource + decoded.Imm(); + LONG64 delta = pTarget - pDest; + LONG64 deltaPage = ((ULONG_PTR)pTarget >> 12) - ((ULONG_PTR)pDest >> 12); + + // output as ADR + if (delta >= -(1 << 20) && delta < (1 << 20)) + { + EmitInstruction(pDstInst, Adr19::AssembleAdr(decoded.s.Rd, (LONG)delta)); + } + + // output as ADRP; ADD + else if (deltaPage >= -(1 << 20) && (deltaPage < (1 << 20))) + { + EmitInstruction(pDstInst, Adr19::AssembleAdrp(decoded.s.Rd, (LONG)deltaPage)); + EmitInstruction(pDstInst, AddImm12::AssembleAdd32(decoded.s.Rd, decoded.s.Rd, ((ULONG)(ULONG_PTR)pTarget) & 0xfff, 0)); + } + + // output as immediate move + else + { + EmitMovImmediate(pDstInst, decoded.s.Rd, (ULONG_PTR)pTarget); + } + } + + // ADRP case + else + { + BYTE* pTarget = (BYTE*)((((ULONG_PTR)pSource >> 12) + decoded.Imm()) << 12); + LONG64 deltaPage = ((ULONG_PTR)pTarget >> 12) - ((ULONG_PTR)pDest >> 12); + + // output as ADRP + if (deltaPage >= -(1 << 20) && (deltaPage < (1 << 20))) + { + EmitInstruction(pDstInst, Adr19::AssembleAdrp(decoded.s.Rd, (LONG)deltaPage)); + } + + // output as immediate move + else + { + EmitMovImmediate(pDstInst, decoded.s.Rd, (ULONG_PTR)pTarget); + } + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +BYTE CDetourDis::CopyBcc(BYTE* pSource, BYTE* pDest, ULONG instruction) +{ + Bcc19& decoded = (Bcc19&)(instruction); + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + decoded.Imm(); + m_pbTarget = pTarget; + LONG64 delta = pTarget - pDest; + LONG64 delta4 = pTarget - (pDest + 4); + + // output as BCC + if (delta >= -(1 << 20) && delta < (1 << 20)) + { + EmitInstruction(pDstInst, Bcc19::AssembleBcc(decoded.s.Condition, (LONG)delta)); + } + + // output as BCC <skip>; B + else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) + { + EmitInstruction(pDstInst, Bcc19::AssembleBcc(decoded.s.Condition ^ 1, 8)); + EmitInstruction(pDstInst, Branch26::AssembleB((LONG)delta4)); + } + + // output as MOV x17, Target; BCC <skip>; BR x17 (BIG assumption that x17 isn't being used for anything!!) + else + { + EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + EmitInstruction(pDstInst, Bcc19::AssembleBcc(decoded.s.Condition ^ 1, 8)); + EmitInstruction(pDstInst, Br::AssembleBr(17)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +BYTE CDetourDis::CopyB_or_Bl(BYTE* pSource, BYTE* pDest, ULONG instruction, bool link) +{ + Branch26& decoded = (Branch26&)(instruction); + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + decoded.Imm(); + m_pbTarget = pTarget; + LONG64 delta = pTarget - pDest; + + // output as B or BRL + if (delta >= -(1 << 27) && (delta < (1 << 27))) + { + EmitInstruction(pDstInst, Branch26::Assemble(link, (LONG)delta)); + } + + // output as MOV x17, Target; BR or BRL x17 (BIG assumption that x17 isn't being used for anything!!) + else + { + EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + EmitInstruction(pDstInst, Br::Assemble(17, link)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +BYTE CDetourDis::CopyB(BYTE* pSource, BYTE* pDest, ULONG instruction) +{ + return CopyB_or_Bl(pSource, pDest, instruction, false); +} + +BYTE CDetourDis::CopyBl(BYTE* pSource, BYTE* pDest, ULONG instruction) +{ + return CopyB_or_Bl(pSource, pDest, instruction, true); +} + +BYTE CDetourDis::CopyCbz(BYTE* pSource, BYTE* pDest, ULONG instruction) +{ + Cbz19& decoded = (Cbz19&)(instruction); + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + decoded.Imm(); + m_pbTarget = pTarget; + LONG64 delta = pTarget - pDest; + LONG64 delta4 = pTarget - (pDest + 4); + + // output as CBZ/NZ + if (delta >= -(1 << 20) && delta < (1 << 20)) + { + EmitInstruction(pDstInst, Cbz19::Assemble(decoded.s.Size, decoded.s.Nz, decoded.s.Rt, (LONG)delta)); + } + + // output as CBNZ/Z <skip>; B + else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) + { + EmitInstruction(pDstInst, Cbz19::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, 8)); + EmitInstruction(pDstInst, Branch26::AssembleB((LONG)delta4)); + } + + // output as MOV x17, Target; CBNZ/Z <skip>; BR x17 (BIG assumption that x17 isn't being used for anything!!) + else + { + EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + EmitInstruction(pDstInst, Cbz19::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, 8)); + EmitInstruction(pDstInst, Br::AssembleBr(17)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +BYTE CDetourDis::CopyTbz(BYTE* pSource, BYTE* pDest, ULONG instruction) +{ + Tbz14& decoded = (Tbz14&)(instruction); + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + decoded.Imm(); + m_pbTarget = pTarget; + LONG64 delta = pTarget - pDest; + LONG64 delta4 = pTarget - (pDest + 4); + + // output as TBZ/NZ + if (delta >= -(1 << 13) && delta < (1 << 13)) + { + EmitInstruction(pDstInst, Tbz14::Assemble(decoded.s.Size, decoded.s.Nz, decoded.s.Rt, decoded.s.Bit, (LONG)delta)); + } + + // output as TBNZ/Z <skip>; B + else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) + { + EmitInstruction(pDstInst, Tbz14::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, decoded.s.Bit, 8)); + EmitInstruction(pDstInst, Branch26::AssembleB((LONG)delta4)); + } + + // output as MOV x17, Target; TBNZ/Z <skip>; BR x17 (BIG assumption that x17 isn't being used for anything!!) + else + { + EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + EmitInstruction(pDstInst, Tbz14::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, decoded.s.Bit, 8)); + EmitInstruction(pDstInst, Br::AssembleBr(17)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +BYTE CDetourDis::CopyLdrLiteral(BYTE* pSource, BYTE* pDest, ULONG instruction) +{ + LdrLit19& decoded = (LdrLit19&)(instruction); + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + decoded.Imm(); + LONG64 delta = pTarget - pDest; + + // output as LDR + if (delta >= -(1 << 21) && delta < (1 << 21)) + { + EmitInstruction(pDstInst, LdrLit19::Assemble(decoded.s.Size, decoded.s.FpNeon, decoded.s.Rt, (LONG)delta)); + } + + // output as move immediate + else if (decoded.s.FpNeon == 0) + { + UINT64 value = 0; + switch (decoded.s.Size) + { + case 0: value = *(ULONG*)pTarget; break; + case 1: value = *(UINT64*)pTarget; break; + case 2: value = *(LONG*)pTarget; break; + } + EmitMovImmediate(pDstInst, decoded.s.Rt, value); + } + + // FP/NEON register: compute address in x17 and load from there (BIG assumption that x17 isn't being used for anything!!) + else + { + EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + EmitInstruction(pDstInst, LdrFpNeonImm9::Assemble(2 + decoded.s.Size, decoded.s.Rt, 17, 0)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + + +PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, + _Inout_opt_ PVOID *ppDstPool, + _In_ PVOID pSrc, + _Out_opt_ PVOID *ppTarget, + _Out_opt_ LONG *plExtra) +{ + UNREFERENCED_PARAMETER(ppDstPool); + + CDetourDis state; + return (PVOID)state.CopyInstruction((PBYTE)pDst, + (PBYTE)pSrc, + (PBYTE*)ppTarget, + plExtra); +} + +#endif // DETOURS_ARM64 + +BOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule, + _In_ BOOL fLimitReferencesToModule) +{ +#if defined(DETOURS_X64) || defined(DETOURS_X86) + PBYTE pbBeg = NULL; + PBYTE pbEnd = (PBYTE)~(ULONG_PTR)0; + + if (hModule != NULL) { + ULONG cbModule = DetourGetModuleSize(hModule); + + pbBeg = (PBYTE)hModule; + pbEnd = (PBYTE)hModule + cbModule; + } + + return CDetourDis::SetCodeModule(pbBeg, pbEnd, fLimitReferencesToModule); +#elif defined(DETOURS_ARM) || defined(DETOURS_ARM64) || defined(DETOURS_IA64) + (void)hModule; + (void)fLimitReferencesToModule; + return TRUE; +#else +#error unknown architecture (x86, x64, arm, arm64, ia64) +#endif +} + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/ext/detours/src/disolarm.cpp b/ext/detours/src/disolarm.cpp new file mode 100644 index 0000000..57e3a2c --- /dev/null +++ b/ext/detours/src/disolarm.cpp @@ -0,0 +1,2 @@ +#define DETOURS_ARM_OFFLINE_LIBRARY +#include "disasm.cpp" diff --git a/ext/detours/src/disolarm64.cpp b/ext/detours/src/disolarm64.cpp new file mode 100644 index 0000000..f3a6aeb --- /dev/null +++ b/ext/detours/src/disolarm64.cpp @@ -0,0 +1,2 @@ +#define DETOURS_ARM64_OFFLINE_LIBRARY +#include "disasm.cpp" diff --git a/ext/detours/src/disolia64.cpp b/ext/detours/src/disolia64.cpp new file mode 100644 index 0000000..9dd2410 --- /dev/null +++ b/ext/detours/src/disolia64.cpp @@ -0,0 +1,2 @@ +#define DETOURS_IA64_OFFLINE_LIBRARY +#include "disasm.cpp" diff --git a/ext/detours/src/disolx64.cpp b/ext/detours/src/disolx64.cpp new file mode 100644 index 0000000..cd05a00 --- /dev/null +++ b/ext/detours/src/disolx64.cpp @@ -0,0 +1,2 @@ +#define DETOURS_X64_OFFLINE_LIBRARY +#include "disasm.cpp" diff --git a/ext/detours/src/disolx86.cpp b/ext/detours/src/disolx86.cpp new file mode 100644 index 0000000..91ff7d9 --- /dev/null +++ b/ext/detours/src/disolx86.cpp @@ -0,0 +1,2 @@ +#define DETOURS_X86_OFFLINE_LIBRARY +#include "disasm.cpp" diff --git a/ext/detours/src/image.cpp b/ext/detours/src/image.cpp new file mode 100644 index 0000000..96282d2 --- /dev/null +++ b/ext/detours/src/image.cpp @@ -0,0 +1,2226 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Image manipulation functions (image.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Used for for payloads, byways, and imports. +// + +#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 + +namespace Detour +{ +////////////////////////////////////////////////////////////////////////////// +// +#ifndef _STRSAFE_H_INCLUDED_ +_Must_inspect_result_ +static inline HRESULT StringCchLengthA( + _In_reads_or_z_(cchMax) LPCSTR psz, + _In_ + _In_range_(1, STRSAFE_MAX_CCH) size_t cchMax, + _Out_opt_ + _Deref_out_range_(<, cchMax) + _Deref_out_range_(<=, _String_length_(psz)) + _Out_ size_t* pcch) +{ + HRESULT hr = S_OK; + size_t cchMaxPrev = cchMax; + + if (cchMax > 2147483647) { + return ERROR_INVALID_PARAMETER; + } + + while (cchMax && (*psz != '\0')) { + psz++; + cchMax--; + } + + if (cchMax == 0) { + // the string is longer than cchMax + hr = ERROR_INVALID_PARAMETER; + } + + if (SUCCEEDED(hr) && pcch) { + *pcch = cchMaxPrev - cchMax; + } + + return hr; +} + +_Must_inspect_result_ +static inline HRESULT StringCchCopyA( + _Out_writes_(cchDest) _Always_(_Post_z_) LPSTR pszDest, + _In_ size_t cchDest, + _In_ LPCSTR pszSrc) +{ + HRESULT hr = S_OK; + + if (cchDest == 0) { + // can not null terminate a zero-byte dest buffer + hr = ERROR_INVALID_PARAMETER; + } + else { + while (cchDest && (*pszSrc != '\0')) { + *pszDest++ = *pszSrc++; + cchDest--; + } + + if (cchDest == 0) { + // we are going to truncate pszDest + pszDest--; + hr = ERROR_INVALID_PARAMETER; + } + + *pszDest= '\0'; + } + + return hr; +} + +_Must_inspect_result_ +static inline HRESULT StringCchCatA( + _Out_writes_(cchDest) _Always_(_Post_z_) LPSTR pszDest, + _In_ size_t cchDest, + _In_ LPCSTR pszSrc) +{ + HRESULT hr; + size_t cchDestCurrent; + + if (cchDest > 2147483647){ + return ERROR_INVALID_PARAMETER; + } + + hr = StringCchLengthA(pszDest, cchDest, &cchDestCurrent); + + if (SUCCEEDED(hr) && cchDestCurrent < cchDest) { + hr = StringCchCopyA(pszDest + cchDestCurrent, + cchDest - cchDestCurrent, + pszSrc); + } + + return hr; +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +class CImageData +{ + friend class CImage; + +public: + CImageData(PBYTE pbData, DWORD cbData); + ~CImageData(); + + PBYTE Enumerate(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator); + PBYTE Find(REFGUID rguid, DWORD *pcbData); + PBYTE Set(REFGUID rguid, PBYTE pbData, DWORD cbData); + + BOOL Delete(REFGUID rguid); + BOOL Purge(); + + BOOL IsEmpty() { return m_cbData == 0; } + BOOL IsValid(); + +protected: + BOOL SizeTo(DWORD cbData); + +protected: + _Field_size_(m_cbAlloc) + PBYTE m_pbData; + DWORD m_cbData; + DWORD m_cbAlloc; +}; + +class CImageImportFile +{ + friend class CImage; + friend class CImageImportName; + +public: + CImageImportFile(); + ~CImageImportFile(); + +public: + CImageImportFile * m_pNextFile; + BOOL m_fByway; + + _Field_size_(m_nImportNames) + CImageImportName * m_pImportNames; + DWORD m_nImportNames; + + DWORD m_rvaOriginalFirstThunk; + DWORD m_rvaFirstThunk; + + DWORD m_nForwarderChain; + LPCSTR m_pszOrig; + LPCSTR m_pszName; +}; + +class CImageImportName +{ + friend class CImage; + friend class CImageImportFile; + +public: + CImageImportName(); + ~CImageImportName(); + +public: + WORD m_nHint; + ULONG m_nOrig; + ULONG m_nOrdinal; + LPCSTR m_pszOrig; + LPCSTR m_pszName; +}; + +class CImage +{ + friend class CImageThunks; + friend class CImageChars; + friend class CImageImportFile; + friend class CImageImportName; + +public: + CImage(); + ~CImage(); + + static CImage * IsValid(PDETOUR_BINARY pBinary); + +public: // File Functions + BOOL Read(HANDLE hFile); + BOOL Write(HANDLE hFile); + BOOL Close(); + +public: // Manipulation Functions + PBYTE DataEnum(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator); + PBYTE DataFind(REFGUID rguid, DWORD *pcbData); + PBYTE DataSet(REFGUID rguid, PBYTE pbData, DWORD cbData); + BOOL DataDelete(REFGUID rguid); + BOOL DataPurge(); + + BOOL EditImports(PVOID pContext, + PF_DETOUR_BINARY_BYWAY_CALLBACK pfBywayCallback, + PF_DETOUR_BINARY_FILE_CALLBACK pfFileCallback, + PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbolCallback, + PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommitCallback); + +protected: + BOOL WriteFile(HANDLE hFile, + LPCVOID lpBuffer, + DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten); + BOOL CopyFileData(HANDLE hFile, DWORD nOldPos, DWORD cbData); + BOOL ZeroFileData(HANDLE hFile, DWORD cbData); + BOOL AlignFileData(HANDLE hFile); + + BOOL SizeOutputBuffer(DWORD cbData); + PBYTE AllocateOutput(DWORD cbData, DWORD *pnVirtAddr); + + PVOID RvaToVa(ULONG_PTR nRva); + DWORD RvaToFileOffset(DWORD nRva); + + DWORD FileAlign(DWORD nAddr); + DWORD SectionAlign(DWORD nAddr); + + BOOL CheckImportsNeeded(DWORD *pnTables, + DWORD *pnThunks, + DWORD *pnChars); + + CImageImportFile * NewByway(_In_ LPCSTR pszName); + +private: + DWORD m_dwValidSignature; + CImageData * m_pImageData; // Read & Write + + HANDLE m_hMap; // Read & Write + PBYTE m_pMap; // Read & Write + + DWORD m_nNextFileAddr; // Write + DWORD m_nNextVirtAddr; // Write + + IMAGE_DOS_HEADER m_DosHeader; // Read & Write + IMAGE_NT_HEADERS m_NtHeader; // Read & Write + IMAGE_SECTION_HEADER m_SectionHeaders[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + + DWORD m_nPrePE; + DWORD m_cbPrePE; + DWORD m_cbPostPE; + + DWORD m_nPeOffset; + DWORD m_nSectionsOffset; + DWORD m_nExtraOffset; + DWORD m_nFileSize; + + DWORD m_nOutputVirtAddr; + DWORD m_nOutputVirtSize; + DWORD m_nOutputFileAddr; + + _Field_size_(m_cbOutputBuffer) + PBYTE m_pbOutputBuffer; + DWORD m_cbOutputBuffer; + + CImageImportFile * m_pImportFiles; + DWORD m_nImportFiles; + + BOOL m_fHadDetourSection; + +private: + enum { + DETOUR_IMAGE_VALID_SIGNATURE = 0xfedcba01, // "Dtr\0" + }; +}; + +////////////////////////////////////////////////////////////////////////////// +// +static BYTE s_rbDosCode[0x10] = { + 0x0E,0x1F,0xBA,0x0E,0x00,0xB4,0x09,0xCD, + 0x21,0xB8,0x01,0x4C,0xCD,0x21,'*','*' +}; + +static inline DWORD Max(DWORD a, DWORD b) +{ + return a > b ? a : b; +} + +static inline DWORD Align(DWORD a, DWORD size) +{ + size--; + return (a + size) & ~size; +} + +static inline DWORD QuadAlign(DWORD a) +{ + return Align(a, 8); +} + +static LPCSTR DuplicateString(_In_ LPCSTR pszIn) +{ + if (pszIn == NULL) { + return NULL; + } + + size_t cch; + HRESULT hr = StringCchLengthA(pszIn, 8192, &cch); + if (FAILED(hr)) { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + PCHAR pszOut = new NOTHROW CHAR [cch + 1]; + if (pszOut == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + return NULL; + } + + hr = StringCchCopyA(pszOut, cch + 1, pszIn); + if (FAILED(hr)) { + delete[] pszOut; + return NULL; + } + + return pszOut; +} + +static VOID ReleaseString(_In_opt_ LPCSTR psz) +{ + if (psz != NULL) { + delete[] psz; + } +} + +////////////////////////////////////////////////////////////////////////////// +// +CImageImportFile::CImageImportFile() +{ + m_pNextFile = NULL; + m_fByway = FALSE; + + m_pImportNames = NULL; + m_nImportNames = 0; + + m_rvaOriginalFirstThunk = 0; + m_rvaFirstThunk = 0; + + m_nForwarderChain = (UINT)0; + m_pszName = NULL; + m_pszOrig = NULL; +} + +CImageImportFile::~CImageImportFile() +{ + if (m_pNextFile) { + delete m_pNextFile; + m_pNextFile = NULL; + } + if (m_pImportNames) { + delete[] m_pImportNames; + m_pImportNames = NULL; + m_nImportNames = 0; + } + if (m_pszName) { + delete[] m_pszName; + m_pszName = NULL; + } + if (m_pszOrig) { + delete[] m_pszOrig; + m_pszOrig = NULL; + } +} + +CImageImportName::CImageImportName() +{ + m_nOrig = 0; + m_nOrdinal = 0; + m_nHint = 0; + m_pszName = NULL; + m_pszOrig = NULL; +} + +CImageImportName::~CImageImportName() +{ + if (m_pszName) { + delete[] m_pszName; + m_pszName = NULL; + } + if (m_pszOrig) { + delete[] m_pszOrig; + m_pszOrig = NULL; + } +} + +////////////////////////////////////////////////////////////////////////////// +// +CImageData::CImageData(PBYTE pbData, DWORD cbData) +{ + m_pbData = pbData; + m_cbData = cbData; + m_cbAlloc = 0; +} + +CImageData::~CImageData() +{ + IsValid(); + + if (m_cbAlloc == 0) { + m_pbData = NULL; + } + if (m_pbData) { + delete[] m_pbData; + m_pbData = NULL; + } + m_cbData = 0; + m_cbAlloc = 0; +} + +BOOL CImageData::SizeTo(DWORD cbData) +{ + IsValid(); + + if (cbData <= m_cbAlloc) { + return TRUE; + } + + PBYTE pbNew = new NOTHROW BYTE [cbData]; + if (pbNew == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + if (m_pbData) { + CopyMemory(pbNew, m_pbData, m_cbData); + if (m_cbAlloc > 0) { + delete[] m_pbData; + } + m_pbData = NULL; + } + m_pbData = pbNew; + m_cbAlloc = cbData; + + IsValid(); + + return TRUE; +} + +BOOL CImageData::Purge() +{ + m_cbData = 0; + + IsValid(); + + return TRUE; +} + +BOOL CImageData::IsValid() +{ + if (m_pbData == NULL) { + return TRUE; + } + + PBYTE pbBeg = m_pbData; + PBYTE pbEnd = m_pbData + m_cbData; + + for (PBYTE pbIter = pbBeg; pbIter < pbEnd;) { + PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)pbIter; + + if (pRecord->cbBytes < sizeof(DETOUR_SECTION_RECORD)) { + return FALSE; + } + if (pRecord->nReserved != 0) { + return FALSE; + } + + pbIter += pRecord->cbBytes; + } + return TRUE; +} + +PBYTE CImageData::Enumerate(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator) +{ + IsValid(); + + if (pnIterator == NULL || + m_cbData < *pnIterator + sizeof(DETOUR_SECTION_RECORD)) { + + if (pcbData) { + *pcbData = 0; + } + if (pGuid) { + ZeroMemory(pGuid, sizeof(*pGuid)); + } + return NULL; + } + + PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)(m_pbData + *pnIterator); + + if (pGuid) { + *pGuid = pRecord->guid; + } + if (pcbData) { + *pcbData = pRecord->cbBytes - sizeof(DETOUR_SECTION_RECORD); + } + *pnIterator = (LONG)(((PBYTE)pRecord - m_pbData) + pRecord->cbBytes); + + return (PBYTE)(pRecord + 1); +} + +PBYTE CImageData::Find(REFGUID rguid, DWORD *pcbData) +{ + IsValid(); + + DWORD cbBytes = sizeof(DETOUR_SECTION_RECORD); + for (DWORD nOffset = 0; nOffset < m_cbData; nOffset += cbBytes) { + PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)(m_pbData + nOffset); + + cbBytes = pRecord->cbBytes; + if (cbBytes > m_cbData) { + break; + } + if (cbBytes < sizeof(DETOUR_SECTION_RECORD)) { + continue; + } + + if (pRecord->guid.Data1 == rguid.Data1 && + pRecord->guid.Data2 == rguid.Data2 && + pRecord->guid.Data3 == rguid.Data3 && + pRecord->guid.Data4[0] == rguid.Data4[0] && + pRecord->guid.Data4[1] == rguid.Data4[1] && + pRecord->guid.Data4[2] == rguid.Data4[2] && + pRecord->guid.Data4[3] == rguid.Data4[3] && + pRecord->guid.Data4[4] == rguid.Data4[4] && + pRecord->guid.Data4[5] == rguid.Data4[5] && + pRecord->guid.Data4[6] == rguid.Data4[6] && + pRecord->guid.Data4[7] == rguid.Data4[7]) { + + *pcbData = cbBytes - sizeof(DETOUR_SECTION_RECORD); + return (PBYTE)(pRecord + 1); + } + } + + if (pcbData) { + *pcbData = 0; + } + return NULL; +} + +BOOL CImageData::Delete(REFGUID rguid) +{ + IsValid(); + + PBYTE pbFound = NULL; + DWORD cbFound = 0; + + pbFound = Find(rguid, &cbFound); + if (pbFound == NULL) { + SetLastError(ERROR_MOD_NOT_FOUND); + return FALSE; + } + + pbFound -= sizeof(DETOUR_SECTION_RECORD); + cbFound += sizeof(DETOUR_SECTION_RECORD); + + PBYTE pbRestData = pbFound + cbFound; + DWORD cbRestData = m_cbData - (LONG)(pbRestData - m_pbData); + + if (cbRestData) { + MoveMemory(pbFound, pbRestData, cbRestData); + } + m_cbData -= cbFound; + + IsValid(); + return TRUE; +} + +PBYTE CImageData::Set(REFGUID rguid, PBYTE pbData, DWORD cbData) +{ + IsValid(); + Delete(rguid); + + DWORD cbAlloc = QuadAlign(cbData); + + if (!SizeTo(m_cbData + cbAlloc + sizeof(DETOUR_SECTION_RECORD))) { + return NULL; + } + + PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)(m_pbData + m_cbData); + pRecord->cbBytes = cbAlloc + sizeof(DETOUR_SECTION_RECORD); + pRecord->nReserved = 0; + pRecord->guid = rguid; + + PBYTE pbDest = (PBYTE)(pRecord + 1); + if (pbData) { + CopyMemory(pbDest, pbData, cbData); + if (cbData < cbAlloc) { + ZeroMemory(pbDest + cbData, cbAlloc - cbData); + } + } + else { + if (cbAlloc > 0) { + ZeroMemory(pbDest, cbAlloc); + } + } + + m_cbData += cbAlloc + sizeof(DETOUR_SECTION_RECORD); + + IsValid(); + return pbDest; +} + +////////////////////////////////////////////////////////////////////////////// +// +class CImageThunks +{ +private: + CImage * m_pImage; + PIMAGE_THUNK_DATA m_pThunks; + DWORD m_nThunks; + DWORD m_nThunksMax; + DWORD m_nThunkVirtAddr; + +public: + CImageThunks(CImage *pImage, DWORD nThunksMax, DWORD *pnAddr) + { + m_pImage = pImage; + m_nThunks = 0; + m_nThunksMax = nThunksMax; + m_pThunks = (PIMAGE_THUNK_DATA) + m_pImage->AllocateOutput(sizeof(IMAGE_THUNK_DATA) * nThunksMax, + &m_nThunkVirtAddr); + *pnAddr = m_nThunkVirtAddr; + } + + PIMAGE_THUNK_DATA Current(DWORD *pnVirtAddr) + { + if (m_nThunksMax > 1) { + *pnVirtAddr = m_nThunkVirtAddr; + return m_pThunks; + } + *pnVirtAddr = 0; + return NULL; + } + + PIMAGE_THUNK_DATA Allocate(ULONG_PTR nData, DWORD *pnVirtAddr) + { + if (m_nThunks < m_nThunksMax) { + *pnVirtAddr = m_nThunkVirtAddr; + + m_nThunks++; + m_nThunkVirtAddr += sizeof(IMAGE_THUNK_DATA); + m_pThunks->u1.Ordinal = nData; + return m_pThunks++; + } + *pnVirtAddr = 0; + return NULL; + } + + DWORD Size() + { + return m_nThunksMax * sizeof(IMAGE_THUNK_DATA); + } +}; + +////////////////////////////////////////////////////////////////////////////// +// +class CImageChars +{ +private: + CImage * m_pImage; + PCHAR m_pChars; + DWORD m_nChars; + DWORD m_nCharsMax; + DWORD m_nCharVirtAddr; + +public: + CImageChars(CImage *pImage, _In_ DWORD nCharsMax, _Out_ DWORD *pnAddr) + { + m_pImage = pImage; + m_nChars = 0; + m_nCharsMax = nCharsMax; + m_pChars = (PCHAR)m_pImage->AllocateOutput(nCharsMax, &m_nCharVirtAddr); + *pnAddr = m_nCharVirtAddr; + } + + LPCSTR Allocate(_In_ LPCSTR pszString, _Out_ DWORD *pnVirtAddr) + { + DWORD nLen = (DWORD)strlen(pszString) + 1; + nLen += (nLen & 1); + + if (m_nChars + nLen > m_nCharsMax) { + *pnVirtAddr = 0; + return NULL; + } + + *pnVirtAddr = m_nCharVirtAddr; + HRESULT hrRet = StringCchCopyA(m_pChars, m_nCharsMax, pszString); + + if (FAILED(hrRet)) { + return NULL; + } + + pszString = m_pChars; + + m_pChars += nLen; + m_nChars += nLen; + m_nCharVirtAddr += nLen; + + return pszString; + } + + LPCSTR Allocate(_In_ LPCSTR pszString, _In_ DWORD nHint, _Out_ DWORD *pnVirtAddr) + { + DWORD nLen = (DWORD)strlen(pszString) + 1 + sizeof(USHORT); + nLen += (nLen & 1); + + if (m_nChars + nLen > m_nCharsMax) { + *pnVirtAddr = 0; + return NULL; + } + + *pnVirtAddr = m_nCharVirtAddr; + *(USHORT *)m_pChars = (USHORT)nHint; + + HRESULT hrRet = StringCchCopyA(m_pChars + sizeof(USHORT), m_nCharsMax, pszString); + if (FAILED(hrRet)) { + return NULL; + } + + pszString = m_pChars + sizeof(USHORT); + + m_pChars += nLen; + m_nChars += nLen; + m_nCharVirtAddr += nLen; + + return pszString; + } + + DWORD Size() + { + return m_nChars; + } +}; + +////////////////////////////////////////////////////////////////////////////// +// +CImage * CImage::IsValid(PDETOUR_BINARY pBinary) +{ + if (pBinary) { + CImage *pImage = (CImage *)pBinary; + + if (pImage->m_dwValidSignature == DETOUR_IMAGE_VALID_SIGNATURE) { + return pImage; + } + } + SetLastError(ERROR_INVALID_HANDLE); + return NULL; +} + +CImage::CImage() +{ + m_dwValidSignature = (DWORD)DETOUR_IMAGE_VALID_SIGNATURE; + + m_hMap = NULL; + m_pMap = NULL; + + m_nPeOffset = 0; + m_nSectionsOffset = 0; + + m_pbOutputBuffer = NULL; + m_cbOutputBuffer = 0; + + m_pImageData = NULL; + + m_pImportFiles = NULL; + m_nImportFiles = 0; + + m_fHadDetourSection = FALSE; +} + +CImage::~CImage() +{ + Close(); + m_dwValidSignature = 0; +} + +BOOL CImage::Close() +{ + if (m_pImportFiles) { + delete m_pImportFiles; + m_pImportFiles = NULL; + m_nImportFiles = 0; + } + + if (m_pImageData) { + delete m_pImageData; + m_pImageData = NULL; + } + + if (m_pMap != NULL) { + UnmapViewOfFile(m_pMap); + m_pMap = NULL; + } + + if (m_hMap) { + CloseHandle(m_hMap); + m_hMap = NULL; + } + + if (m_pbOutputBuffer) { + delete[] m_pbOutputBuffer; + m_pbOutputBuffer = NULL; + m_cbOutputBuffer = 0; + } + return TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// +PBYTE CImage::DataEnum(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator) +{ + if (m_pImageData == NULL) { + return NULL; + } + return m_pImageData->Enumerate(pGuid, pcbData, pnIterator); +} + +PBYTE CImage::DataFind(REFGUID rguid, DWORD *pcbData) +{ + if (m_pImageData == NULL) { + return NULL; + } + return m_pImageData->Find(rguid, pcbData); +} + +PBYTE CImage::DataSet(REFGUID rguid, PBYTE pbData, DWORD cbData) +{ + if (m_pImageData == NULL) { + return NULL; + } + return m_pImageData->Set(rguid, pbData, cbData); +} + +BOOL CImage::DataDelete(REFGUID rguid) +{ + if (m_pImageData == NULL) { + return FALSE; + } + return m_pImageData->Delete(rguid); +} + +BOOL CImage::DataPurge() +{ + if (m_pImageData == NULL) { + return TRUE; + } + return m_pImageData->Purge(); +} + +////////////////////////////////////////////////////////////////////////////// +// +BOOL CImage::SizeOutputBuffer(DWORD cbData) +{ + if (m_cbOutputBuffer < cbData) { + if (cbData < 1024) {//65536 + cbData = 1024; + } + cbData = FileAlign(cbData); + + PBYTE pOutput = new NOTHROW BYTE [cbData]; + if (pOutput == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + if (m_pbOutputBuffer) { + CopyMemory(pOutput, m_pbOutputBuffer, m_cbOutputBuffer); + + delete[] m_pbOutputBuffer; + m_pbOutputBuffer = NULL; + } + + ZeroMemory(pOutput + m_cbOutputBuffer, cbData - m_cbOutputBuffer), + + m_pbOutputBuffer = pOutput; + m_cbOutputBuffer = cbData; + } + return TRUE; +} + +PBYTE CImage::AllocateOutput(DWORD cbData, DWORD *pnVirtAddr) +{ + cbData = QuadAlign(cbData); + + PBYTE pbData = m_pbOutputBuffer + m_nOutputVirtSize; + + *pnVirtAddr = m_nOutputVirtAddr + m_nOutputVirtSize; + m_nOutputVirtSize += cbData; + + if (m_nOutputVirtSize > m_cbOutputBuffer) { + SetLastError(ERROR_OUTOFMEMORY); + return NULL; + } + + ZeroMemory(pbData, cbData); + + return pbData; +} + +////////////////////////////////////////////////////////////////////////////// +// +DWORD CImage::FileAlign(DWORD nAddr) +{ + return Align(nAddr, m_NtHeader.OptionalHeader.FileAlignment); +} + +DWORD CImage::SectionAlign(DWORD nAddr) +{ + return Align(nAddr, m_NtHeader.OptionalHeader.SectionAlignment); +} + +////////////////////////////////////////////////////////////////////////////// +// +PVOID CImage::RvaToVa(ULONG_PTR nRva) +{ + if (nRva == 0) { + return NULL; + } + + for (DWORD n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) { + DWORD vaStart = m_SectionHeaders[n].VirtualAddress; + DWORD vaEnd = vaStart + m_SectionHeaders[n].SizeOfRawData; + + if (nRva >= vaStart && nRva < vaEnd) { + return (PBYTE)m_pMap + + m_SectionHeaders[n].PointerToRawData + + nRva - m_SectionHeaders[n].VirtualAddress; + } + } + return NULL; +} + +DWORD CImage::RvaToFileOffset(DWORD nRva) +{ + DWORD n; + for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) { + DWORD vaStart = m_SectionHeaders[n].VirtualAddress; + DWORD vaEnd = vaStart + m_SectionHeaders[n].SizeOfRawData; + + if (nRva >= vaStart && nRva < vaEnd) { + return m_SectionHeaders[n].PointerToRawData + + nRva - m_SectionHeaders[n].VirtualAddress; + } + } + return 0; +} + +////////////////////////////////////////////////////////////////////////////// +// +BOOL CImage::WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten) +{ + return ::WriteFile(hFile, + lpBuffer, + nNumberOfBytesToWrite, + lpNumberOfBytesWritten, + NULL); +} + + +BOOL CImage::CopyFileData(HANDLE hFile, DWORD nOldPos, DWORD cbData) +{ + DWORD cbDone = 0; + return WriteFile(hFile, m_pMap + nOldPos, cbData, &cbDone); +} + +BOOL CImage::ZeroFileData(HANDLE hFile, DWORD cbData) +{ + if (!SizeOutputBuffer(4096)) { + return FALSE; + } + + ZeroMemory(m_pbOutputBuffer, 4096); + + for (DWORD cbLeft = cbData; cbLeft > 0;) { + DWORD cbStep = cbLeft > sizeof(m_pbOutputBuffer) + ? sizeof(m_pbOutputBuffer) : cbLeft; + DWORD cbDone = 0; + + if (!WriteFile(hFile, m_pbOutputBuffer, cbStep, &cbDone)) { + return FALSE; + } + if (cbDone == 0) { + break; + } + + cbLeft -= cbDone; + } + return TRUE; +} + +BOOL CImage::AlignFileData(HANDLE hFile) +{ + DWORD nLastFileAddr = m_nNextFileAddr; + + m_nNextFileAddr = FileAlign(m_nNextFileAddr); + m_nNextVirtAddr = SectionAlign(m_nNextVirtAddr); + + if (hFile != INVALID_HANDLE_VALUE) { + if (m_nNextFileAddr > nLastFileAddr) { + if (SetFilePointer(hFile, nLastFileAddr, NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + return ZeroFileData(hFile, m_nNextFileAddr - nLastFileAddr); + } + } + return TRUE; +} + +BOOL CImage::Read(HANDLE hFile) +{ + DWORD n; + PBYTE pbData = NULL; + DWORD cbData = 0; + + if (hFile == INVALID_HANDLE_VALUE) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + ///////////////////////////////////////////////////////// Create mapping. + // + m_nFileSize = GetFileSize(hFile, NULL); + if (m_nFileSize == (DWORD)-1) { + return FALSE; + } + + m_hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (m_hMap == NULL) { + return FALSE; + } + + m_pMap = (PBYTE)MapViewOfFileEx(m_hMap, FILE_MAP_READ, 0, 0, 0, NULL); + if (m_pMap == NULL) { + return FALSE; + } + + ////////////////////////////////////////////////////// Process DOS Header. + // + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)m_pMap; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return FALSE; + } + m_nPeOffset = pDosHeader->e_lfanew; + m_nPrePE = 0; + m_cbPrePE = QuadAlign(pDosHeader->e_lfanew); + + if (m_nPeOffset > m_nFileSize || + m_nPeOffset + sizeof(m_NtHeader) > m_nFileSize) { + + SetLastError(ERROR_BAD_EXE_FORMAT); + return FALSE; + } + + CopyMemory(&m_DosHeader, m_pMap + m_nPrePE, sizeof(m_DosHeader)); + + /////////////////////////////////////////////////////// Process PE Header. + // + CopyMemory(&m_NtHeader, m_pMap + m_nPeOffset, sizeof(m_NtHeader)); + if (m_NtHeader.Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_INVALID_EXE_SIGNATURE); + return FALSE; + } + if (m_NtHeader.FileHeader.SizeOfOptionalHeader == 0) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } + m_nSectionsOffset = m_nPeOffset + + sizeof(m_NtHeader.Signature) + + sizeof(m_NtHeader.FileHeader) + + m_NtHeader.FileHeader.SizeOfOptionalHeader; + + ///////////////////////////////////////////////// Process Section Headers. + // + if (m_NtHeader.FileHeader.NumberOfSections > ARRAYSIZE(m_SectionHeaders)) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } + CopyMemory(&m_SectionHeaders, + m_pMap + m_nSectionsOffset, + sizeof(m_SectionHeaders[0]) * m_NtHeader.FileHeader.NumberOfSections); + + /////////////////////////////////////////////////// Parse .detour Section. + // + DWORD rvaOriginalImageDirectory = 0; + DWORD rvaDetourBeg = 0; + DWORD rvaDetourEnd = 0; + + _Analysis_assume_(m_NtHeader.FileHeader.NumberOfSections <= ARRAYSIZE(m_SectionHeaders)); + + for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) { + if (strcmp((PCHAR)m_SectionHeaders[n].Name, ".detour") == 0) { + DETOUR_SECTION_HEADER dh; + CopyMemory(&dh, + m_pMap + m_SectionHeaders[n].PointerToRawData, + sizeof(dh)); + + rvaOriginalImageDirectory = dh.nOriginalImportVirtualAddress; + if (dh.cbPrePE != 0) { + m_nPrePE = m_SectionHeaders[n].PointerToRawData + sizeof(dh); + m_cbPrePE = dh.cbPrePE; + } + rvaDetourBeg = m_SectionHeaders[n].VirtualAddress; + rvaDetourEnd = rvaDetourBeg + m_SectionHeaders[n].SizeOfRawData; + } + } + + //////////////////////////////////////////////////////// Get Import Table. + // + DWORD rvaImageDirectory = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + PIMAGE_IMPORT_DESCRIPTOR iidp + = (PIMAGE_IMPORT_DESCRIPTOR)RvaToVa(rvaImageDirectory); + PIMAGE_IMPORT_DESCRIPTOR oidp + = (PIMAGE_IMPORT_DESCRIPTOR)RvaToVa(rvaOriginalImageDirectory); + + if (oidp == NULL) { + oidp = iidp; + } + if (iidp == NULL || oidp == NULL) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } + + DWORD nFiles = 0; + for (; iidp[nFiles].OriginalFirstThunk != 0 || iidp[nFiles].FirstThunk != 0; nFiles++) { + } + + CImageImportFile **ppLastFile = &m_pImportFiles; + m_pImportFiles = NULL; + + for (n = 0; n < nFiles; n++, iidp++) { + ULONG_PTR rvaName = iidp->Name; + PCHAR pszName = (PCHAR)RvaToVa(rvaName); + if (pszName == NULL) { + SetLastError(ERROR_EXE_MARKED_INVALID); + goto fail; + } + + CImageImportFile *pImportFile = new NOTHROW CImageImportFile; + if (pImportFile == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + goto fail; + } + + *ppLastFile = pImportFile; + ppLastFile = &pImportFile->m_pNextFile; + m_nImportFiles++; + + pImportFile->m_pszName = DuplicateString(pszName); + if (pImportFile->m_pszName == NULL) { + goto fail; + } + + pImportFile->m_rvaOriginalFirstThunk = iidp->OriginalFirstThunk; + pImportFile->m_rvaFirstThunk = iidp->FirstThunk; + pImportFile->m_nForwarderChain = iidp->ForwarderChain; + pImportFile->m_pImportNames = NULL; + pImportFile->m_nImportNames = 0; + pImportFile->m_fByway = FALSE; + + if ((ULONG)iidp->FirstThunk >= rvaDetourBeg && + (ULONG)iidp->FirstThunk < rvaDetourEnd) { + + pImportFile->m_pszOrig = NULL; + pImportFile->m_fByway = TRUE; + continue; + } + + rvaName = oidp->Name; + pszName = (PCHAR)RvaToVa(rvaName); + if (pszName == NULL) { + SetLastError(ERROR_EXE_MARKED_INVALID); + goto fail; + } + pImportFile->m_pszOrig = DuplicateString(pszName); + if (pImportFile->m_pszOrig == NULL) { + goto fail; + } + + DWORD rvaThunk = iidp->OriginalFirstThunk; + if( !rvaThunk ) { + rvaThunk = iidp->FirstThunk; + } + PIMAGE_THUNK_DATA pAddrThunk = (PIMAGE_THUNK_DATA)RvaToVa(rvaThunk); + rvaThunk = oidp->OriginalFirstThunk; + if( !rvaThunk ) { + rvaThunk = oidp->FirstThunk; + } + PIMAGE_THUNK_DATA pLookThunk = (PIMAGE_THUNK_DATA)RvaToVa(rvaThunk); + + DWORD nNames = 0; + if (pAddrThunk) { + for (; pAddrThunk[nNames].u1.Ordinal; nNames++) { + } + } + + if (pAddrThunk && nNames) { + pImportFile->m_nImportNames = nNames; + pImportFile->m_pImportNames = new NOTHROW CImageImportName [nNames]; + if (pImportFile->m_pImportNames == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + goto fail; + } + + CImageImportName *pImportName = &pImportFile->m_pImportNames[0]; + + for (DWORD f = 0; f < nNames; f++, pImportName++) { + pImportName->m_nOrig = 0; + pImportName->m_nOrdinal = 0; + pImportName->m_nHint = 0; + pImportName->m_pszName = NULL; + pImportName->m_pszOrig = NULL; + + rvaName = pAddrThunk[f].u1.Ordinal; + if (rvaName & IMAGE_ORDINAL_FLAG) { + pImportName->m_nOrig = (ULONG)IMAGE_ORDINAL(rvaName); + pImportName->m_nOrdinal = pImportName->m_nOrig; + } + else { + PIMAGE_IMPORT_BY_NAME pName + = (PIMAGE_IMPORT_BY_NAME)RvaToVa(rvaName); + if (pName) { + pImportName->m_nHint = pName->Hint; + pImportName->m_pszName = DuplicateString((PCHAR)pName->Name); + if (pImportName->m_pszName == NULL) { + goto fail; + } + } + + rvaName = pLookThunk[f].u1.Ordinal; + if (rvaName & IMAGE_ORDINAL_FLAG) { + pImportName->m_nOrig = (ULONG)IMAGE_ORDINAL(rvaName); + pImportName->m_nOrdinal = (ULONG)IMAGE_ORDINAL(rvaName); + } + else { + pName = (PIMAGE_IMPORT_BY_NAME)RvaToVa(rvaName); + if (pName) { + pImportName->m_pszOrig + = DuplicateString((PCHAR)pName->Name); + if (pImportName->m_pszOrig == NULL) { + goto fail; + } + } + } + } + } + } + oidp++; + } + + ////////////////////////////////////////////////////////// Parse Sections. + // + m_nExtraOffset = 0; + for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) { + m_nExtraOffset = Max(m_SectionHeaders[n].PointerToRawData + + m_SectionHeaders[n].SizeOfRawData, + m_nExtraOffset); + + if (strcmp((PCHAR)m_SectionHeaders[n].Name, ".detour") == 0) { + DETOUR_SECTION_HEADER dh; + CopyMemory(&dh, + m_pMap + m_SectionHeaders[n].PointerToRawData, + sizeof(dh)); + + if (dh.nDataOffset == 0) { + dh.nDataOffset = dh.cbHeaderSize; + } + + cbData = dh.cbDataSize - dh.nDataOffset; + pbData = (m_pMap + + m_SectionHeaders[n].PointerToRawData + + dh.nDataOffset); + + m_nExtraOffset = Max(m_SectionHeaders[n].PointerToRawData + + m_SectionHeaders[n].SizeOfRawData, + m_nExtraOffset); + + m_NtHeader.FileHeader.NumberOfSections--; + + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + = dh.nOriginalImportVirtualAddress; + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size + = dh.nOriginalImportSize; + + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress + = dh.nOriginalBoundImportVirtualAddress; + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size + = dh.nOriginalBoundImportSize; + + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress + = dh.nOriginalIatVirtualAddress; + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size + = dh.nOriginalIatSize; + + m_NtHeader.OptionalHeader.CheckSum = 0; + m_NtHeader.OptionalHeader.SizeOfImage + = dh.nOriginalSizeOfImage; + + m_fHadDetourSection = TRUE; + } + } + + m_pImageData = new NOTHROW CImageData(pbData, cbData); + if (m_pImageData == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + } + return TRUE; + +fail: + return FALSE; +} + +static inline BOOL strneq(_In_ LPCSTR pszOne, _In_ LPCSTR pszTwo) +{ + if (pszOne == pszTwo) { + return FALSE; + } + if (!pszOne || !pszTwo) { + return TRUE; + } + return (strcmp(pszOne, pszTwo) != 0); +} + +BOOL CImage::CheckImportsNeeded(DWORD *pnTables, DWORD *pnThunks, DWORD *pnChars) +{ + DWORD nTables = 0; + DWORD nThunks = 0; + DWORD nChars = 0; + BOOL fNeedDetourSection = FALSE; + + for (CImageImportFile *pImportFile = m_pImportFiles; + pImportFile != NULL; pImportFile = pImportFile->m_pNextFile) { + + nChars += (int)strlen(pImportFile->m_pszName) + 1; + nChars += nChars & 1; + + if (pImportFile->m_fByway) { + fNeedDetourSection = TRUE; + nThunks++; + } + else { + if (!fNeedDetourSection && + strneq(pImportFile->m_pszName, pImportFile->m_pszOrig)) { + + fNeedDetourSection = TRUE; + } + for (DWORD n = 0; n < pImportFile->m_nImportNames; n++) { + CImageImportName *pImportName = &pImportFile->m_pImportNames[n]; + + if (!fNeedDetourSection && + strneq(pImportName->m_pszName, pImportName->m_pszOrig)) { + + fNeedDetourSection = TRUE; + } + + if (pImportName->m_pszName) { + nChars += sizeof(WORD); // Hint + nChars += (int)strlen(pImportName->m_pszName) + 1; + nChars += nChars & 1; + } + nThunks++; + } + } + nThunks++; + nTables++; + } + nTables++; + + *pnTables = nTables; + *pnThunks = nThunks; + *pnChars = nChars; + + return fNeedDetourSection; +} + +////////////////////////////////////////////////////////////////////////////// +// +CImageImportFile * CImage::NewByway(_In_ LPCSTR pszName) +{ + CImageImportFile *pImportFile = new NOTHROW CImageImportFile; + if (pImportFile == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + goto fail; + } + + pImportFile->m_pNextFile = NULL; + pImportFile->m_fByway = TRUE; + + pImportFile->m_pszName = DuplicateString(pszName); + if (pImportFile->m_pszName == NULL) { + goto fail; + } + + pImportFile->m_rvaOriginalFirstThunk = 0; + pImportFile->m_rvaFirstThunk = 0; + pImportFile->m_nForwarderChain = (UINT)0; + pImportFile->m_pImportNames = NULL; + pImportFile->m_nImportNames = 0; + + m_nImportFiles++; + return pImportFile; + +fail: + if (pImportFile) { + delete pImportFile; + pImportFile = NULL; + } + return NULL; +} + +BOOL CImage::EditImports(PVOID pContext, + PF_DETOUR_BINARY_BYWAY_CALLBACK pfBywayCallback, + PF_DETOUR_BINARY_FILE_CALLBACK pfFileCallback, + PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbolCallback, + PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommitCallback) +{ + CImageImportFile *pImportFile = NULL; + CImageImportFile **ppLastFile = &m_pImportFiles; + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + + while ((pImportFile = *ppLastFile) != NULL) { + + if (pfBywayCallback != NULL) { + LPCSTR pszFile = NULL; + if (!(*pfBywayCallback)(pContext, NULL, &pszFile)) { + goto fail; + } + + if (pszFile != NULL) { + // Insert a new Byway. + CImageImportFile *pByway = NewByway(pszFile); + if (pByway == NULL) { + return FALSE; + } + + pByway->m_pNextFile = pImportFile; + *ppLastFile = pByway; + ppLastFile = &pByway->m_pNextFile; + continue; // Retry after Byway. + } + } + + if (pImportFile->m_fByway) { + if (pfBywayCallback != NULL) { + LPCSTR pszFile = NULL; + + if (!(*pfBywayCallback)(pContext, pImportFile->m_pszName, &pszFile)) { + goto fail; + } + + if (pszFile != NULL) { + // Replace? Byway + if (pszFile != pImportFile->m_pszName) { + LPCSTR pszLast = pImportFile->m_pszName; + pImportFile->m_pszName = DuplicateString(pszFile); + ReleaseString(pszLast); + + if (pImportFile->m_pszName == NULL) { + goto fail; + } + } + } + else { // Delete Byway + *ppLastFile = pImportFile->m_pNextFile; + pImportFile->m_pNextFile = NULL; + delete pImportFile; + m_nImportFiles--; + continue; // Retry after delete. + } + } + } + else { + if (pfFileCallback != NULL) { + LPCSTR pszFile = NULL; + + if (!(*pfFileCallback)(pContext, + pImportFile->m_pszOrig, + pImportFile->m_pszName, + &pszFile)) { + goto fail; + } + + if (pszFile != NULL) { + if (pszFile != pImportFile->m_pszName) { + LPCSTR pszLast = pImportFile->m_pszName; + pImportFile->m_pszName = DuplicateString(pszFile); + ReleaseString(pszLast); + + if (pImportFile->m_pszName == NULL) { + goto fail; + } + } + } + } + + if (pfSymbolCallback != NULL) { + for (DWORD n = 0; n < pImportFile->m_nImportNames; n++) { + CImageImportName *pImportName = &pImportFile->m_pImportNames[n]; + + LPCSTR pszName = NULL; + ULONG nOrdinal = 0; + if (!(*pfSymbolCallback)(pContext, + pImportName->m_nOrig, + pImportName->m_nOrdinal, + &nOrdinal, + pImportName->m_pszOrig, + pImportName->m_pszName, + &pszName)) { + goto fail; + } + + if (pszName != NULL) { + if (pszName != pImportName->m_pszName) { + pImportName->m_nOrdinal = 0; + + LPCSTR pszLast = pImportName->m_pszName; + pImportName->m_pszName = DuplicateString(pszName); + ReleaseString(pszLast); + + if (pImportName->m_pszName == NULL) { + goto fail; + } + } + } + else if (nOrdinal != 0) { + pImportName->m_nOrdinal = nOrdinal; + + if (pImportName->m_pszName != NULL) { + delete[] pImportName->m_pszName; + pImportName->m_pszName = NULL; + } + } + } + } + } + + ppLastFile = &pImportFile->m_pNextFile; + pImportFile = pImportFile->m_pNextFile; + } + + for (;;) { + if (pfBywayCallback != NULL) { + LPCSTR pszFile = NULL; + if (!(*pfBywayCallback)(pContext, NULL, &pszFile)) { + goto fail; + } + if (pszFile != NULL) { + // Insert a new Byway. + CImageImportFile *pByway = NewByway(pszFile); + if (pByway == NULL) { + return FALSE; + } + + pByway->m_pNextFile = pImportFile; + *ppLastFile = pByway; + ppLastFile = &pByway->m_pNextFile; + continue; // Retry after Byway. + } + } + break; + } + + if (pfCommitCallback != NULL) { + if (!(*pfCommitCallback)(pContext)) { + goto fail; + } + } + + SetLastError(NO_ERROR); + return TRUE; + + fail: + return FALSE; +} + +BOOL CImage::Write(HANDLE hFile) +{ + DWORD cbDone; + + if (hFile == INVALID_HANDLE_VALUE) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + m_nNextFileAddr = 0; + m_nNextVirtAddr = 0; + + DWORD nTables = 0; + DWORD nThunks = 0; + DWORD nChars = 0; + BOOL fNeedDetourSection = CheckImportsNeeded(&nTables, &nThunks, &nChars); + + //////////////////////////////////////////////////////////// Copy Headers. + // + if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + if (!CopyFileData(hFile, 0, m_NtHeader.OptionalHeader.SizeOfHeaders)) { + return FALSE; + } + + if (fNeedDetourSection || !m_pImageData->IsEmpty()) { + // Replace the file's DOS header with our own. + m_nPeOffset = sizeof(m_DosHeader) + sizeof(s_rbDosCode); + m_nSectionsOffset = m_nPeOffset + + sizeof(m_NtHeader.Signature) + + sizeof(m_NtHeader.FileHeader) + + m_NtHeader.FileHeader.SizeOfOptionalHeader; + m_DosHeader.e_lfanew = m_nPeOffset; + + if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + if (!WriteFile(hFile, &m_DosHeader, sizeof(m_DosHeader), &cbDone)) { + return FALSE; + } + if (!WriteFile(hFile, &s_rbDosCode, sizeof(s_rbDosCode), &cbDone)) { + return FALSE; + } + } + else { + // Restore the file's original DOS header. + if (m_nPrePE != 0) { + m_nPeOffset = m_cbPrePE; + m_nSectionsOffset = m_nPeOffset + + sizeof(m_NtHeader.Signature) + + sizeof(m_NtHeader.FileHeader) + + m_NtHeader.FileHeader.SizeOfOptionalHeader; + m_DosHeader.e_lfanew = m_nPeOffset; + + + if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + if (!CopyFileData(hFile, m_nPrePE, m_cbPrePE)) { + return FALSE; + } + } + } + + m_nNextFileAddr = m_NtHeader.OptionalHeader.SizeOfHeaders; + m_nNextVirtAddr = 0; + if (!AlignFileData(hFile)) { + return FALSE; + } + + /////////////////////////////////////////////////////////// Copy Sections. + // + DWORD n = 0; + for (; n < m_NtHeader.FileHeader.NumberOfSections; n++) { + if (m_SectionHeaders[n].SizeOfRawData) { + if (SetFilePointer(hFile, + m_SectionHeaders[n].PointerToRawData, + NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + if (!CopyFileData(hFile, + m_SectionHeaders[n].PointerToRawData, + m_SectionHeaders[n].SizeOfRawData)) { + return FALSE; + } + } + m_nNextFileAddr = Max(m_SectionHeaders[n].PointerToRawData + + m_SectionHeaders[n].SizeOfRawData, + m_nNextFileAddr); + // Old images have VirtualSize == 0 as a matter of course, e.g. NT 3.1.
+ // In which case, use SizeOfRawData instead. + m_nNextVirtAddr = Max(m_SectionHeaders[n].VirtualAddress + + (m_SectionHeaders[n].Misc.VirtualSize + ? m_SectionHeaders[n].Misc.VirtualSize + : SectionAlign(m_SectionHeaders[n].SizeOfRawData)), + m_nNextVirtAddr); + + m_nExtraOffset = Max(m_nNextFileAddr, m_nExtraOffset); + + if (!AlignFileData(hFile)) { + return FALSE; + } + } + + if (fNeedDetourSection || !m_pImageData->IsEmpty()) { + + if (m_NtHeader.FileHeader.NumberOfSections >= ARRAYSIZE(m_SectionHeaders)) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } + + ////////////////////////////////////////////// Insert .detour Section. + // + DWORD nSection = m_NtHeader.FileHeader.NumberOfSections++; + DETOUR_SECTION_HEADER dh; + + ZeroMemory(&dh, sizeof(dh)); + ZeroMemory(&m_SectionHeaders[nSection], sizeof(m_SectionHeaders[nSection])); + + dh.cbHeaderSize = sizeof(DETOUR_SECTION_HEADER); + dh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE; + + dh.nOriginalImportVirtualAddress = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + dh.nOriginalImportSize = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size; + + dh.nOriginalBoundImportVirtualAddress + = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress; + dh.nOriginalBoundImportSize = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size; + + dh.nOriginalIatVirtualAddress = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress; + dh.nOriginalIatSize = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size; + + dh.nOriginalSizeOfImage = m_NtHeader.OptionalHeader.SizeOfImage; + + DWORD clrAddr = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress; + DWORD clrSize = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size; + if (clrAddr && clrSize) { + PDETOUR_CLR_HEADER pHdr = (PDETOUR_CLR_HEADER)RvaToVa(clrAddr); + if (pHdr != NULL) { + DETOUR_CLR_HEADER hdr; + hdr = *pHdr; + + dh.nOriginalClrFlags = hdr.Flags; + } + } + + HRESULT hrRet = StringCchCopyA((PCHAR)m_SectionHeaders[nSection].Name, IMAGE_SIZEOF_SHORT_NAME , ".detour"); + if (FAILED(hrRet)) + return FALSE; + + m_SectionHeaders[nSection].Characteristics + = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; + + m_nOutputVirtAddr = m_nNextVirtAddr; + m_nOutputVirtSize = 0; + m_nOutputFileAddr = m_nNextFileAddr; + + dh.nDataOffset = 0; // pbData + dh.cbDataSize = m_pImageData->m_cbData; + dh.cbPrePE = m_cbPrePE; + + ////////////////////////////////////////////////////////////////////////// + // + + DWORD rvaImportTable = 0; + DWORD rvaLookupTable = 0; + DWORD rvaBoundTable = 0; + DWORD rvaNameTable = 0; + DWORD nImportTableSize = nTables * sizeof(IMAGE_IMPORT_DESCRIPTOR); + + if (!SizeOutputBuffer(QuadAlign(sizeof(dh)) + + m_cbPrePE + + QuadAlign(m_pImageData->m_cbData) + + QuadAlign(sizeof(IMAGE_THUNK_DATA) * nThunks) + + QuadAlign(sizeof(IMAGE_THUNK_DATA) * nThunks) + + QuadAlign(nChars) + + QuadAlign(nImportTableSize))) { + return FALSE; + } + + DWORD vaHead = 0; + PBYTE pbHead = NULL; + DWORD vaPrePE = 0; + PBYTE pbPrePE = NULL; + DWORD vaData = 0; + PBYTE pbData = NULL; + + if ((pbHead = AllocateOutput(sizeof(dh), &vaHead)) == NULL) { + return FALSE; + } + + if ((pbPrePE = AllocateOutput(m_cbPrePE, &vaPrePE)) == NULL) { + return FALSE; + } + + CImageThunks lookupTable(this, nThunks, &rvaLookupTable); + CImageThunks boundTable(this, nThunks, &rvaBoundTable); + CImageChars nameTable(this, nChars, &rvaNameTable); + + if ((pbData = AllocateOutput(m_pImageData->m_cbData, &vaData)) == NULL) { + return FALSE; + } + + dh.nDataOffset = vaData - vaHead; + dh.cbDataSize = dh.nDataOffset + m_pImageData->m_cbData; + CopyMemory(pbHead, &dh, sizeof(dh)); + CopyMemory(pbPrePE, m_pMap + m_nPrePE, m_cbPrePE); + CopyMemory(pbData, m_pImageData->m_pbData, m_pImageData->m_cbData); + + PIMAGE_IMPORT_DESCRIPTOR piidDst = (PIMAGE_IMPORT_DESCRIPTOR) + AllocateOutput(nImportTableSize, &rvaImportTable); + if (piidDst == NULL) { + return FALSE; + } + + //////////////////////////////////////////////// Step Through Imports. + // + for (CImageImportFile *pImportFile = m_pImportFiles; + pImportFile != NULL; pImportFile = pImportFile->m_pNextFile) { + + ZeroMemory(piidDst, sizeof(*piidDst)); + nameTable.Allocate(pImportFile->m_pszName, (DWORD *)&piidDst->Name); + piidDst->TimeDateStamp = 0; + piidDst->ForwarderChain = pImportFile->m_nForwarderChain; + + if (pImportFile->m_fByway) { + ULONG rvaIgnored; + + lookupTable.Allocate(IMAGE_ORDINAL_FLAG+1, + (DWORD *)&piidDst->OriginalFirstThunk); + boundTable.Allocate(IMAGE_ORDINAL_FLAG+1, + (DWORD *)&piidDst->FirstThunk); + + lookupTable.Allocate(0, &rvaIgnored); + boundTable.Allocate(0, &rvaIgnored); + } + else { + ULONG rvaIgnored; + + piidDst->FirstThunk = (ULONG)pImportFile->m_rvaFirstThunk; + lookupTable.Current((DWORD *)&piidDst->OriginalFirstThunk); + + for (n = 0; n < pImportFile->m_nImportNames; n++) { + CImageImportName *pImportName = &pImportFile->m_pImportNames[n]; + + if (pImportName->m_pszName) { + ULONG nDstName = 0; + + nameTable.Allocate(pImportName->m_pszName, + pImportName->m_nHint, + &nDstName); + lookupTable.Allocate(nDstName, &rvaIgnored); + } + else { + lookupTable.Allocate(IMAGE_ORDINAL_FLAG + pImportName->m_nOrdinal, + &rvaIgnored); + } + } + lookupTable.Allocate(0, &rvaIgnored); + } + piidDst++; + } + ZeroMemory(piidDst, sizeof(*piidDst)); + + ////////////////////////////////////////////////////////////////////////// + // + m_nNextVirtAddr += m_nOutputVirtSize; + m_nNextFileAddr += FileAlign(m_nOutputVirtSize); + + if (!AlignFileData(hFile)) { + return FALSE; + } + + ////////////////////////////////////////////////////////////////////////// + // + m_SectionHeaders[nSection].VirtualAddress = m_nOutputVirtAddr; + m_SectionHeaders[nSection].Misc.VirtualSize = m_nOutputVirtSize; + m_SectionHeaders[nSection].PointerToRawData = m_nOutputFileAddr; + m_SectionHeaders[nSection].SizeOfRawData = FileAlign(m_nOutputVirtSize); + + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + = rvaImportTable; + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size + = nImportTableSize; + + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; + m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; + + ////////////////////////////////////////////////////////////////////////// + // + if (SetFilePointer(hFile, m_SectionHeaders[nSection].PointerToRawData, + NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + if (!WriteFile(hFile, m_pbOutputBuffer, m_SectionHeaders[nSection].SizeOfRawData, + &cbDone)) { + return FALSE; + } + } + + ///////////////////////////////////////////////////// Adjust Extra Data. + // + LONG nExtraAdjust = m_nNextFileAddr - m_nExtraOffset; + for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) { + if (m_SectionHeaders[n].PointerToRawData > m_nExtraOffset) { + m_SectionHeaders[n].PointerToRawData += nExtraAdjust; + } + if (m_SectionHeaders[n].PointerToRelocations > m_nExtraOffset) { + m_SectionHeaders[n].PointerToRelocations += nExtraAdjust; + } + if (m_SectionHeaders[n].PointerToLinenumbers > m_nExtraOffset) { + m_SectionHeaders[n].PointerToLinenumbers += nExtraAdjust; + } + } + if (m_NtHeader.FileHeader.PointerToSymbolTable > m_nExtraOffset) { + m_NtHeader.FileHeader.PointerToSymbolTable += nExtraAdjust; + } + + m_NtHeader.OptionalHeader.CheckSum = 0; + m_NtHeader.OptionalHeader.SizeOfImage = m_nNextVirtAddr; + + ////////////////////////////////////////////////// Adjust Debug Directory. + // + DWORD debugAddr = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; + DWORD debugSize = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; + if (debugAddr && debugSize) { + DWORD nFileOffset = RvaToFileOffset(debugAddr); + if (SetFilePointer(hFile, nFileOffset, NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + + PIMAGE_DEBUG_DIRECTORY pDir = (PIMAGE_DEBUG_DIRECTORY)RvaToVa(debugAddr); + if (pDir == NULL) { + return FALSE; + } + + DWORD nEntries = debugSize / sizeof(*pDir); + for (n = 0; n < nEntries; n++) { + IMAGE_DEBUG_DIRECTORY dir = pDir[n]; + + if (dir.PointerToRawData > m_nExtraOffset) { + dir.PointerToRawData += nExtraAdjust; + } + if (!WriteFile(hFile, &dir, sizeof(dir), &cbDone)) { + return FALSE; + } + } + } + + /////////////////////////////////////////////////////// Adjust CLR Header. + // + DWORD clrAddr = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress; + DWORD clrSize = m_NtHeader.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size; + if (clrAddr && clrSize && fNeedDetourSection) { + DWORD nFileOffset = RvaToFileOffset(clrAddr); + if (SetFilePointer(hFile, nFileOffset, NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + + PDETOUR_CLR_HEADER pHdr = (PDETOUR_CLR_HEADER)RvaToVa(clrAddr); + if (pHdr == NULL) { + return FALSE; + } + + DETOUR_CLR_HEADER hdr; + hdr = *pHdr; + hdr.Flags &= 0xfffffffe; // Clear the IL_ONLY flag. + + if (!WriteFile(hFile, &hdr, sizeof(hdr), &cbDone)) { + return FALSE; + } + } + + ///////////////////////////////////////////////// Copy Left-over Data. + // + if (m_nFileSize > m_nExtraOffset) { + if (SetFilePointer(hFile, m_nNextFileAddr, NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + if (!CopyFileData(hFile, m_nExtraOffset, m_nFileSize - m_nExtraOffset)) { + return FALSE; + } + } + + + //////////////////////////////////////////////////// Finalize Headers. + // + + if (SetFilePointer(hFile, m_nPeOffset, NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + if (!WriteFile(hFile, &m_NtHeader, sizeof(m_NtHeader), &cbDone)) { + return FALSE; + } + + if (SetFilePointer(hFile, m_nSectionsOffset, NULL, FILE_BEGIN) == ~0u) { + return FALSE; + } + if (!WriteFile(hFile, &m_SectionHeaders, + sizeof(m_SectionHeaders[0]) + * m_NtHeader.FileHeader.NumberOfSections, + &cbDone)) { + return FALSE; + } + + m_cbPostPE = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); + if (m_cbPostPE == ~0u) { + return FALSE; + } + m_cbPostPE = m_NtHeader.OptionalHeader.SizeOfHeaders - m_cbPostPE; + + return TRUE; +} + +}; // namespace Detour + +////////////////////////////////////////////////////////////////////////////// +// +PDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE hFile) +{ + Detour::CImage *pImage = new NOTHROW + Detour::CImage; + if (pImage == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + if (!pImage->Read(hFile)) { + delete pImage; + return FALSE; + } + + return (PDETOUR_BINARY)pImage; +} + +BOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pdi, + _In_ HANDLE hFile) +{ + Detour::CImage *pImage = Detour::CImage::IsValid(pdi); + if (pImage == NULL) { + return FALSE; + } + + return pImage->Write(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) +{ + Detour::CImage *pImage = Detour::CImage::IsValid(pBinary); + if (pImage == NULL) { + return FALSE; + } + + return pImage->DataEnum(pGuid, pcbData, pnIterator); +} + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary, + _In_ REFGUID rguid, + _Out_ DWORD *pcbData) +{ + Detour::CImage *pImage = Detour::CImage::IsValid(pBinary); + if (pImage == NULL) { + return FALSE; + } + + return pImage->DataFind(rguid, pcbData); +} + +PVOID WINAPI DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary, + _In_ REFGUID rguid, + _In_reads_opt_(cbData) PVOID pvData, + _In_ DWORD cbData) +{ + Detour::CImage *pImage = Detour::CImage::IsValid(pBinary); + if (pImage == NULL) { + return NULL; + } + + return pImage->DataSet(rguid, (PBYTE)pvData, cbData); +} + +BOOL WINAPI DetourBinaryDeletePayload(_In_ PDETOUR_BINARY pBinary, + _In_ REFGUID rguid) +{ + Detour::CImage *pImage = Detour::CImage::IsValid(pBinary); + if (pImage == NULL) { + return FALSE; + } + + return pImage->DataDelete(rguid); +} + +BOOL WINAPI DetourBinaryPurgePayloads(_In_ PDETOUR_BINARY pBinary) +{ + Detour::CImage *pImage = Detour::CImage::IsValid(pBinary); + if (pImage == NULL) { + return FALSE; + } + + return pImage->DataPurge(); +} + +////////////////////////////////////////////////////////////////////////////// +// +static BOOL CALLBACK ResetBywayCallback(_In_opt_ PVOID pContext, + _In_opt_ LPCSTR pszFile, + _Outptr_result_maybenull_ LPCSTR *ppszOutFile) +{ + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pszFile); + + *ppszOutFile = NULL; + return TRUE; +} + +static BOOL CALLBACK ResetFileCallback(_In_opt_ PVOID pContext, + _In_ LPCSTR pszOrigFile, + _In_ LPCSTR pszFile, + _Outptr_result_maybenull_ LPCSTR *ppszOutFile) +{ + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pszFile); + + *ppszOutFile = pszOrigFile; + return TRUE; +} + +static BOOL CALLBACK ResetSymbolCallback(_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) +{ + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(nOrdinal); + UNREFERENCED_PARAMETER(pszSymbol); + + *pnOutOrdinal = nOrigOrdinal; + *ppszOutSymbol = pszOrigSymbol; + return TRUE; +} + +BOOL WINAPI DetourBinaryResetImports(_In_ PDETOUR_BINARY pBinary) +{ + Detour::CImage *pImage = Detour::CImage::IsValid(pBinary); + if (pImage == NULL) { + return FALSE; + } + + return pImage->EditImports(NULL, + ResetBywayCallback, + ResetFileCallback, + ResetSymbolCallback, + NULL); +} + +////////////////////////////////////////////////////////////////////////////// +// +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) +{ + Detour::CImage *pImage = Detour::CImage::IsValid(pBinary); + if (pImage == NULL) { + return FALSE; + } + + return pImage->EditImports(pContext, + pfByway, + pfFile, + pfSymbol, + pfCommit); +} + +BOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary) +{ + Detour::CImage *pImage = Detour::CImage::IsValid(pBinary); + if (pImage == NULL) { + return FALSE; + } + + BOOL bSuccess = pImage->Close(); + delete pImage; + pImage = NULL; + + return bSuccess; +} + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/ext/detours/src/modules.cpp b/ext/detours/src/modules.cpp new file mode 100644 index 0000000..a797121 --- /dev/null +++ b/ext/detours/src/modules.cpp @@ -0,0 +1,904 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Module Enumeration Functions (modules.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Module enumeration functions. +// + +// #define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL +#include "detours.h" + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] +#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT] + +////////////////////////////////////////////////////////////////////////////// +// +const GUID DETOUR_EXE_RESTORE_GUID = { + 0x2ed7a3ff, 0x3339, 0x4a8d, + { 0x80, 0x5c, 0xd4, 0x98, 0x15, 0x3f, 0xc2, 0x8f }}; + +////////////////////////////////////////////////////////////////////////////// +// +PDETOUR_SYM_INFO DetourLoadImageHlp(VOID) +{ + static DETOUR_SYM_INFO symInfo; + static PDETOUR_SYM_INFO pSymInfo = NULL; + static BOOL failed = false; + + if (failed) { + return NULL; + } + if (pSymInfo != NULL) { + return pSymInfo; + } + + ZeroMemory(&symInfo, sizeof(symInfo)); + // Create a real handle to the process. +#if 0 + DuplicateHandle(GetCurrentProcess(), + GetCurrentProcess(), + GetCurrentProcess(), + &symInfo.hProcess, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); +#else + symInfo.hProcess = GetCurrentProcess(); +#endif + + symInfo.hDbgHelp = LoadLibraryExW(L"dbghelp.dll", NULL, 0); + if (symInfo.hDbgHelp == NULL) { + abort: + failed = true; + if (symInfo.hDbgHelp != NULL) { + FreeLibrary(symInfo.hDbgHelp); + } + symInfo.pfImagehlpApiVersionEx = NULL; + symInfo.pfSymInitialize = NULL; + symInfo.pfSymSetOptions = NULL; + symInfo.pfSymGetOptions = NULL; + symInfo.pfSymLoadModule64 = NULL; + symInfo.pfSymGetModuleInfo64 = NULL; + symInfo.pfSymFromName = NULL; + return NULL; + } + + symInfo.pfImagehlpApiVersionEx + = (PF_ImagehlpApiVersionEx)GetProcAddress(symInfo.hDbgHelp, + "ImagehlpApiVersionEx"); + symInfo.pfSymInitialize + = (PF_SymInitialize)GetProcAddress(symInfo.hDbgHelp, "SymInitialize"); + symInfo.pfSymSetOptions + = (PF_SymSetOptions)GetProcAddress(symInfo.hDbgHelp, "SymSetOptions"); + symInfo.pfSymGetOptions + = (PF_SymGetOptions)GetProcAddress(symInfo.hDbgHelp, "SymGetOptions"); + symInfo.pfSymLoadModule64 + = (PF_SymLoadModule64)GetProcAddress(symInfo.hDbgHelp, "SymLoadModule64"); + symInfo.pfSymGetModuleInfo64 + = (PF_SymGetModuleInfo64)GetProcAddress(symInfo.hDbgHelp, "SymGetModuleInfo64"); + symInfo.pfSymFromName + = (PF_SymFromName)GetProcAddress(symInfo.hDbgHelp, "SymFromName"); + + API_VERSION av; + ZeroMemory(&av, sizeof(av)); + av.MajorVersion = API_VERSION_NUMBER; + + if (symInfo.pfImagehlpApiVersionEx == NULL || + symInfo.pfSymInitialize == NULL || + symInfo.pfSymLoadModule64 == NULL || + symInfo.pfSymGetModuleInfo64 == NULL || + symInfo.pfSymFromName == NULL) { + goto abort; + } + + symInfo.pfImagehlpApiVersionEx(&av); + if (av.MajorVersion < API_VERSION_NUMBER) { + goto abort; + } + + if (!symInfo.pfSymInitialize(symInfo.hProcess, NULL, FALSE)) { + // We won't retry the initialize if it fails. + goto abort; + } + + if (symInfo.pfSymGetOptions != NULL && symInfo.pfSymSetOptions != NULL) { + DWORD dw = symInfo.pfSymGetOptions(); + + dw &= ~(SYMOPT_CASE_INSENSITIVE | + SYMOPT_UNDNAME | + SYMOPT_DEFERRED_LOADS | + 0); + dw |= ( +#if defined(SYMOPT_EXACT_SYMBOLS) + SYMOPT_EXACT_SYMBOLS | +#endif +#if defined(SYMOPT_NO_UNQUALIFIED_LOADS) + SYMOPT_NO_UNQUALIFIED_LOADS | +#endif + SYMOPT_DEFERRED_LOADS | +#if defined(SYMOPT_FAIL_CRITICAL_ERRORS) + SYMOPT_FAIL_CRITICAL_ERRORS | +#endif +#if defined(SYMOPT_INCLUDE_32BIT_MODULES) + SYMOPT_INCLUDE_32BIT_MODULES | +#endif + 0); + symInfo.pfSymSetOptions(dw); + } + + pSymInfo = &symInfo; + return pSymInfo; +} + +PVOID WINAPI DetourFindFunction(_In_ LPCSTR pszModule, + _In_ LPCSTR pszFunction) +{ + /////////////////////////////////////////////// First, try GetProcAddress. + // +#pragma prefast(suppress:28752, "We don't do the unicode conversion for LoadLibraryExA.") + HMODULE hModule = LoadLibraryExA(pszModule, NULL, 0); + if (hModule == NULL) { + return NULL; + } + + PBYTE pbCode = (PBYTE)GetProcAddress(hModule, pszFunction); + if (pbCode) { + return pbCode; + } + + ////////////////////////////////////////////////////// Then try ImageHelp. + // + DETOUR_TRACE(("DetourFindFunction(%hs, %hs)\n", pszModule, pszFunction)); + PDETOUR_SYM_INFO pSymInfo = DetourLoadImageHlp(); + if (pSymInfo == NULL) { + DETOUR_TRACE(("DetourLoadImageHlp failed: %d\n", + GetLastError())); + return NULL; + } + + if (pSymInfo->pfSymLoadModule64(pSymInfo->hProcess, NULL, + (PCHAR)pszModule, NULL, + (DWORD64)hModule, 0) == 0) { + if (ERROR_SUCCESS != GetLastError()) { + DETOUR_TRACE(("SymLoadModule64(%p) failed: %d\n", + pSymInfo->hProcess, GetLastError())); + return NULL; + } + } + + HRESULT hrRet; + CHAR szFullName[512]; + IMAGEHLP_MODULE64 modinfo; + ZeroMemory(&modinfo, sizeof(modinfo)); + modinfo.SizeOfStruct = sizeof(modinfo); + if (!pSymInfo->pfSymGetModuleInfo64(pSymInfo->hProcess, (DWORD64)hModule, &modinfo)) { + DETOUR_TRACE(("SymGetModuleInfo64(%p, %p) failed: %d\n", + pSymInfo->hProcess, hModule, GetLastError())); + return NULL; + } + + hrRet = StringCchCopyA(szFullName, sizeof(szFullName)/sizeof(CHAR), modinfo.ModuleName); + if (FAILED(hrRet)) { + DETOUR_TRACE(("StringCchCopyA failed: %08x\n", hrRet)); + return NULL; + } + hrRet = StringCchCatA(szFullName, sizeof(szFullName)/sizeof(CHAR), "!"); + if (FAILED(hrRet)) { + DETOUR_TRACE(("StringCchCatA failed: %08x\n", hrRet)); + return NULL; + } + hrRet = StringCchCatA(szFullName, sizeof(szFullName)/sizeof(CHAR), pszFunction); + if (FAILED(hrRet)) { + DETOUR_TRACE(("StringCchCatA failed: %08x\n", hrRet)); + return NULL; + } + + struct CFullSymbol : SYMBOL_INFO { + CHAR szRestOfName[512]; + } symbol; + ZeroMemory(&symbol, sizeof(symbol)); + //symbol.ModBase = (ULONG64)hModule; + symbol.SizeOfStruct = sizeof(SYMBOL_INFO); +#ifdef DBHLPAPI + symbol.MaxNameLen = sizeof(symbol.szRestOfName)/sizeof(symbol.szRestOfName[0]); +#else + symbol.MaxNameLength = sizeof(symbol.szRestOfName)/sizeof(symbol.szRestOfName[0]); +#endif + + if (!pSymInfo->pfSymFromName(pSymInfo->hProcess, szFullName, &symbol)) { + DETOUR_TRACE(("SymFromName(%hs) failed: %d\n", szFullName, GetLastError())); + return NULL; + } + +#if defined(DETOURS_IA64) + // On the IA64, we get a raw code pointer from the symbol engine + // and have to convert it to a wrapped [code pointer, global pointer]. + // + PPLABEL_DESCRIPTOR pldEntry = (PPLABEL_DESCRIPTOR)DetourGetEntryPoint(hModule); + PPLABEL_DESCRIPTOR pldSymbol = new PLABEL_DESCRIPTOR; + + pldSymbol->EntryPoint = symbol.Address; + pldSymbol->GlobalPointer = pldEntry->GlobalPointer; + return (PBYTE)pldSymbol; +#elif defined(DETOURS_ARM) + // On the ARM, we get a raw code pointer, which we must convert into a + // valied Thumb2 function pointer. + return DETOURS_PBYTE_TO_PFUNC(symbol.Address); +#else + return (PBYTE)symbol.Address; +#endif +} + +//////////////////////////////////////////////////// Module Image Functions. +// + +HMODULE WINAPI DetourEnumerateModules(_In_opt_ HMODULE hModuleLast) +{ + 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 (VirtualQuery(pbLast, &mbi, sizeof(mbi)) <= 0) { + break; + } + + // Skip uncommitted regions and guard pages. + // + if ((mbi.State != MEM_COMMIT) || + ((mbi.Protect & 0xff) == PAGE_NOACCESS) || + (mbi.Protect & PAGE_GUARD)) { + continue; + } + + __try { + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pbLast; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE || + (DWORD)pDosHeader->e_lfanew > mbi.RegionSize || + (DWORD)pDosHeader->e_lfanew < sizeof(*pDosHeader)) { + continue; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + continue; + } + + return (HMODULE)pDosHeader; + } +#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) { + continue; + } + } + return NULL; +} + +PVOID WINAPI DetourGetEntryPoint(_In_opt_ HMODULE hModule) +{ + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; + if (hModule == NULL) { + pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL); + } + + __try { +#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL. + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return NULL; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_INVALID_EXE_SIGNATURE); + return NULL; + } + if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } + + PDETOUR_CLR_HEADER pClrHeader = NULL; + if (pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (((PIMAGE_NT_HEADERS32)pNtHeader)->CLR_DIRECTORY.VirtualAddress != 0 && + ((PIMAGE_NT_HEADERS32)pNtHeader)->CLR_DIRECTORY.Size != 0) { + pClrHeader = (PDETOUR_CLR_HEADER) + (((PBYTE)pDosHeader) + + ((PIMAGE_NT_HEADERS32)pNtHeader)->CLR_DIRECTORY.VirtualAddress); + } + } + else if (pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + if (((PIMAGE_NT_HEADERS64)pNtHeader)->CLR_DIRECTORY.VirtualAddress != 0 && + ((PIMAGE_NT_HEADERS64)pNtHeader)->CLR_DIRECTORY.Size != 0) { + pClrHeader = (PDETOUR_CLR_HEADER) + (((PBYTE)pDosHeader) + + ((PIMAGE_NT_HEADERS64)pNtHeader)->CLR_DIRECTORY.VirtualAddress); + } + } + + if (pClrHeader != NULL) { + // For MSIL assemblies, we want to use the _Cor entry points. + + HMODULE hClr = GetModuleHandleW(L"MSCOREE.DLL"); + if (hClr == NULL) { + return NULL; + } + + SetLastError(NO_ERROR); + return GetProcAddress(hClr, "_CorExeMain"); + } + + SetLastError(NO_ERROR); + + // Pure resource DLLs have neither an entry point nor CLR information + // so handle them by returning NULL (LastError is NO_ERROR) + if (pNtHeader->OptionalHeader.AddressOfEntryPoint == 0) { + return NULL; + } + + return ((PBYTE)pDosHeader) + + pNtHeader->OptionalHeader.AddressOfEntryPoint; + } + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } +} + +ULONG WINAPI DetourGetModuleSize(_In_opt_ HMODULE hModule) +{ + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; + if (hModule == NULL) { + pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL); + } + + __try { +#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL. + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return NULL; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_INVALID_EXE_SIGNATURE); + return NULL; + } + if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } + SetLastError(NO_ERROR); + + return (pNtHeader->OptionalHeader.SizeOfImage); + } + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } +} + +HMODULE WINAPI DetourGetContainingModule(_In_ PVOID pvAddr) +{ + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + __try { + if (VirtualQuery(pvAddr, &mbi, sizeof(mbi)) <= 0) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return NULL; + } + + // Skip uncommitted regions and guard pages. + // + if ((mbi.State != MEM_COMMIT) || + ((mbi.Protect & 0xff) == PAGE_NOACCESS) || + (mbi.Protect & PAGE_GUARD)) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return NULL; + } + + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return NULL; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_INVALID_EXE_SIGNATURE); + return NULL; + } + if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } + SetLastError(NO_ERROR); + + return (HMODULE)pDosHeader; + } + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + SetLastError(ERROR_INVALID_EXE_SIGNATURE); + return NULL; + } +} + + +static inline PBYTE RvaAdjust(_Pre_notnull_ PIMAGE_DOS_HEADER pDosHeader, _In_ DWORD raddr) +{ + if (raddr != NULL) { + return ((PBYTE)pDosHeader) + raddr; + } + return NULL; +} + +BOOL WINAPI DetourEnumerateExports(_In_ HMODULE hModule, + _In_opt_ PVOID pContext, + _In_ PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport) +{ + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; + if (hModule == NULL) { + pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL); + } + + __try { +#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL. + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return NULL; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_INVALID_EXE_SIGNATURE); + return FALSE; + } + if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } + + PIMAGE_EXPORT_DIRECTORY pExportDir + = (PIMAGE_EXPORT_DIRECTORY) + RvaAdjust(pDosHeader, + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + + if (pExportDir == NULL) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } + + PBYTE pExportDirEnd = (PBYTE)pExportDir + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; + PDWORD pdwFunctions = (PDWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfFunctions); + PDWORD pdwNames = (PDWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfNames); + PWORD pwOrdinals = (PWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfNameOrdinals); + + for (DWORD nFunc = 0; nFunc < pExportDir->NumberOfFunctions; nFunc++) { + PBYTE pbCode = (pdwFunctions != NULL) + ? (PBYTE)RvaAdjust(pDosHeader, pdwFunctions[nFunc]) : NULL; + PCHAR pszName = NULL; + + // if the pointer is in the export region, then it is a forwarder. + if (pbCode > (PBYTE)pExportDir && pbCode < pExportDirEnd) { + pbCode = NULL; + } + + for (DWORD n = 0; n < pExportDir->NumberOfNames; n++) { + if (pwOrdinals[n] == nFunc) { + pszName = (pdwNames != NULL) + ? (PCHAR)RvaAdjust(pDosHeader, pdwNames[n]) : NULL; + break; + } + } + ULONG nOrdinal = pExportDir->Base + nFunc; + + if (!pfExport(pContext, nOrdinal, pszName, pbCode)) { + break; + } + } + SetLastError(NO_ERROR); + return TRUE; + } + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } +} + +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 pfImportFunc) +{ + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; + if (hModule == NULL) { + pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL); + } + + __try { +#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL. + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return FALSE; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_INVALID_EXE_SIGNATURE); + return FALSE; + } + if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } + + PIMAGE_IMPORT_DESCRIPTOR iidp + = (PIMAGE_IMPORT_DESCRIPTOR) + RvaAdjust(pDosHeader, + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + + if (iidp == NULL) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } + + for (; iidp->OriginalFirstThunk != 0; iidp++) { + + PCSTR pszName = (PCHAR)RvaAdjust(pDosHeader, iidp->Name); + if (pszName == NULL) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } + + PIMAGE_THUNK_DATA pThunks = (PIMAGE_THUNK_DATA) + RvaAdjust(pDosHeader, iidp->OriginalFirstThunk); + PVOID * pAddrs = (PVOID *) + RvaAdjust(pDosHeader, iidp->FirstThunk); + + HMODULE hFile = DetourGetContainingModule(pAddrs[0]); + + if (pfImportFile != NULL) { + if (!pfImportFile(pContext, hFile, pszName)) { + break; + } + } + + DWORD nNames = 0; + if (pThunks) { + for (; pThunks[nNames].u1.Ordinal; nNames++) { + DWORD nOrdinal = 0; + PCSTR pszFunc = NULL; + + if (IMAGE_SNAP_BY_ORDINAL(pThunks[nNames].u1.Ordinal)) { + nOrdinal = (DWORD)IMAGE_ORDINAL(pThunks[nNames].u1.Ordinal); + } + else { + pszFunc = (PCSTR)RvaAdjust(pDosHeader, + (DWORD)pThunks[nNames].u1.AddressOfData + 2); + } + + if (pfImportFunc != NULL) { + if (!pfImportFunc(pContext, + nOrdinal, + pszFunc, + &pAddrs[nNames])) { + break; + } + } + } + if (pfImportFunc != NULL) { + pfImportFunc(pContext, 0, NULL, NULL); + } + } + } + if (pfImportFile != NULL) { + pfImportFile(pContext, NULL, NULL); + } + SetLastError(NO_ERROR); + return TRUE; + } + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return FALSE; + } +} + +// Context for DetourEnumerateImportsThunk, which adapts "regular" callbacks for use with "Ex". +struct _DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT +{ + PVOID pContext; + PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc; +}; + +// Callback for DetourEnumerateImportsEx that adapts DetourEnumerateImportsEx +// for use with a DetourEnumerateImports callback -- derefence the IAT and pass the value on. + +static +BOOL +CALLBACK +DetourEnumerateImportsThunk(_In_ PVOID VoidContext, + _In_ DWORD nOrdinal, + _In_opt_ PCSTR pszFunc, + _In_opt_ PVOID* ppvFunc) +{ + _DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT const * const + pContext = (_DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT*)VoidContext; + return pContext->pfImportFunc(pContext->pContext, nOrdinal, pszFunc, ppvFunc ? *ppvFunc : NULL); +} + +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) +{ + _DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT const context = { pContext, pfImportFunc }; + + return DetourEnumerateImportsEx(hModule, + (PVOID)&context, + pfImportFile, + &DetourEnumerateImportsThunk); +} + +static PDETOUR_LOADED_BINARY WINAPI GetPayloadSectionFromModule(HMODULE hModule) +{ + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; + if (hModule == NULL) { + pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL); + } + + __try { +#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL. + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return NULL; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_INVALID_EXE_SIGNATURE); + return NULL; + } + if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } + + PIMAGE_SECTION_HEADER pSectionHeaders + = (PIMAGE_SECTION_HEADER)((PBYTE)pNtHeader + + sizeof(pNtHeader->Signature) + + sizeof(pNtHeader->FileHeader) + + pNtHeader->FileHeader.SizeOfOptionalHeader); + + for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; n++) { + if (strcmp((PCHAR)pSectionHeaders[n].Name, ".detour") == 0) { + if (pSectionHeaders[n].VirtualAddress == 0 || + pSectionHeaders[n].SizeOfRawData == 0) { + + break; + } + + PBYTE pbData = (PBYTE)pDosHeader + pSectionHeaders[n].VirtualAddress; + DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pbData; + if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) || + pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) { + + break; + } + + if (pHeader->nDataOffset == 0) { + pHeader->nDataOffset = pHeader->cbHeaderSize; + } + SetLastError(NO_ERROR); + return (PBYTE)pHeader; + } + } + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + SetLastError(ERROR_EXE_MARKED_INVALID); + return NULL; + } +} + +DWORD WINAPI DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule) +{ + PDETOUR_LOADED_BINARY pBinary = GetPayloadSectionFromModule(hModule); + if (pBinary == NULL) { + // Error set by GetPayloadSectionFromModule. + return 0; + } + + __try { + DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pBinary; + if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) || + pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) { + + SetLastError(ERROR_INVALID_HANDLE); + return 0; + } + SetLastError(NO_ERROR); + return pHeader->cbDataSize; + } + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + SetLastError(ERROR_INVALID_HANDLE); + return 0; + } +} + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourFindPayload(_In_opt_ HMODULE hModule, + _In_ REFGUID rguid, + _Out_ DWORD *pcbData) +{ + PBYTE pbData = NULL; + if (pcbData) { + *pcbData = 0; + } + + PDETOUR_LOADED_BINARY pBinary = GetPayloadSectionFromModule(hModule); + if (pBinary == NULL) { + // Error set by GetPayloadSectionFromModule. + return NULL; + } + + __try { + DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pBinary; + if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) || + pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) { + + SetLastError(ERROR_INVALID_EXE_SIGNATURE); + return NULL; + } + + PBYTE pbBeg = ((PBYTE)pHeader) + pHeader->nDataOffset; + PBYTE pbEnd = ((PBYTE)pHeader) + pHeader->cbDataSize; + + for (pbData = pbBeg; pbData < pbEnd;) { + DETOUR_SECTION_RECORD *pSection = (DETOUR_SECTION_RECORD *)pbData; + + if (pSection->guid.Data1 == rguid.Data1 && + pSection->guid.Data2 == rguid.Data2 && + pSection->guid.Data3 == rguid.Data3 && + pSection->guid.Data4[0] == rguid.Data4[0] && + pSection->guid.Data4[1] == rguid.Data4[1] && + pSection->guid.Data4[2] == rguid.Data4[2] && + pSection->guid.Data4[3] == rguid.Data4[3] && + pSection->guid.Data4[4] == rguid.Data4[4] && + pSection->guid.Data4[5] == rguid.Data4[5] && + pSection->guid.Data4[6] == rguid.Data4[6] && + pSection->guid.Data4[7] == rguid.Data4[7]) { + + if (pcbData) { + *pcbData = pSection->cbBytes - sizeof(*pSection); + } + SetLastError(NO_ERROR); + return (PBYTE)(pSection + 1); + } + + pbData = (PBYTE)pSection + pSection->cbBytes; + } + SetLastError(ERROR_INVALID_HANDLE); + return NULL; + } + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + SetLastError(ERROR_INVALID_HANDLE); + return NULL; + } +} + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourFindPayloadEx(_In_ REFGUID rguid, + _Out_ DWORD * pcbData) +{ + for (HMODULE hMod = NULL; (hMod = DetourEnumerateModules(hMod)) != NULL;) { + PVOID pvData; + + pvData = DetourFindPayload(hMod, rguid, pcbData); + if (pvData != NULL) { + return pvData; + } + } + SetLastError(ERROR_MOD_NOT_FOUND); + return NULL; +} + +BOOL WINAPI DetourRestoreAfterWithEx(_In_reads_bytes_(cbData) PVOID pvData, + _In_ DWORD cbData) +{ + PDETOUR_EXE_RESTORE pder = (PDETOUR_EXE_RESTORE)pvData; + + if (pder->cb != sizeof(*pder) || pder->cb > cbData) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return FALSE; + } + + DWORD dwPermIdh = ~0u; + DWORD dwPermInh = ~0u; + DWORD dwPermClr = ~0u; + DWORD dwIgnore; + BOOL fSucceeded = FALSE; + BOOL fUpdated32To64 = FALSE; + + if (pder->pclr != NULL && pder->clr.Flags != ((PDETOUR_CLR_HEADER)pder->pclr)->Flags) { + // If we had to promote the 32/64-bit agnostic IL to 64-bit, we can't restore + // that. + fUpdated32To64 = TRUE; + } + + if (DetourVirtualProtectSameExecute(pder->pidh, pder->cbidh, + PAGE_EXECUTE_READWRITE, &dwPermIdh)) { + if (DetourVirtualProtectSameExecute(pder->pinh, pder->cbinh, + PAGE_EXECUTE_READWRITE, &dwPermInh)) { + + CopyMemory(pder->pidh, &pder->idh, pder->cbidh); + CopyMemory(pder->pinh, &pder->inh, pder->cbinh); + + if (pder->pclr != NULL && !fUpdated32To64) { + if (DetourVirtualProtectSameExecute(pder->pclr, pder->cbclr, + PAGE_EXECUTE_READWRITE, &dwPermClr)) { + CopyMemory(pder->pclr, &pder->clr, pder->cbclr); + VirtualProtect(pder->pclr, pder->cbclr, dwPermClr, &dwIgnore); + fSucceeded = TRUE; + } + } + else { + fSucceeded = TRUE; + } + VirtualProtect(pder->pinh, pder->cbinh, dwPermInh, &dwIgnore); + } + VirtualProtect(pder->pidh, pder->cbidh, dwPermIdh, &dwIgnore); + } + return fSucceeded; +} + +BOOL WINAPI DetourRestoreAfterWith() +{ + PVOID pvData; + DWORD cbData; + + pvData = DetourFindPayloadEx(DETOUR_EXE_RESTORE_GUID, &cbData); + + if (pvData != NULL && cbData != 0) { + return DetourRestoreAfterWithEx(pvData, cbData); + } + SetLastError(ERROR_MOD_NOT_FOUND); + return FALSE; +} + +// End of File diff --git a/ext/detours/src/uimports.cpp b/ext/detours/src/uimports.cpp new file mode 100644 index 0000000..5f454dd --- /dev/null +++ b/ext/detours/src/uimports.cpp @@ -0,0 +1,280 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Add DLLs to a module import table (uimports.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note that this file is included into creatwth.cpp one or more times +// (once for each supported module format). +// + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +// UpdateImports32 aka UpdateImports64 +static BOOL UPDATE_IMPORTS_XX(HANDLE hProcess, + HMODULE hModule, + __in_ecount(nDlls) LPCSTR *plpDlls, + DWORD nDlls) +{ + BOOL fSucceeded = FALSE; + DWORD cbNew = 0; + + BYTE * pbNew = NULL; + DWORD i; + SIZE_T cbRead; + DWORD n; + + PBYTE pbModule = (PBYTE)hModule; + + IMAGE_DOS_HEADER idh; + ZeroMemory(&idh, sizeof(idh)); + if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), &cbRead) + || cbRead < sizeof(idh)) { + + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", + pbModule, pbModule + sizeof(idh), GetLastError())); + + finish: + if (pbNew != NULL) { + delete[] pbNew; + pbNew = NULL; + } + return fSucceeded; + } + + IMAGE_NT_HEADERS_XX inh; + ZeroMemory(&inh, sizeof(inh)); + + if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), &cbRead) + || cbRead < sizeof(inh)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", + pbModule + idh.e_lfanew, + pbModule + idh.e_lfanew + sizeof(inh), + GetLastError())); + goto finish; + } + + if (inh.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC_XX) { + DETOUR_TRACE(("Wrong size image (%04x != %04x).\n", + inh.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR_MAGIC_XX)); + SetLastError(ERROR_INVALID_BLOCK); + goto finish; + } + + // Zero out the bound table so loader doesn't use it instead of our new table. + inh.BOUND_DIRECTORY.VirtualAddress = 0; + inh.BOUND_DIRECTORY.Size = 0; + + // Find the size of the mapped file. + DWORD dwSec = idh.e_lfanew + + FIELD_OFFSET(IMAGE_NT_HEADERS_XX, OptionalHeader) + + inh.FileHeader.SizeOfOptionalHeader; + + for (i = 0; i < inh.FileHeader.NumberOfSections; i++) { + IMAGE_SECTION_HEADER ish; + ZeroMemory(&ish, sizeof(ish)); + + if (!ReadProcessMemory(hProcess, pbModule + dwSec + sizeof(ish) * i, &ish, + sizeof(ish), &cbRead) + || cbRead < sizeof(ish)) { + + DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %d\n", + pbModule + dwSec + sizeof(ish) * i, + pbModule + dwSec + sizeof(ish) * (i + 1), + GetLastError())); + goto finish; + } + + DETOUR_TRACE(("ish[%d] : va=%08x sr=%d\n", i, ish.VirtualAddress, ish.SizeOfRawData)); + + // If the linker didn't suggest an IAT in the data directories, the + // loader will look for the section of the import directory to be used + // for this instead. Since we put out new IMPORT_DIRECTORY outside any + // section boundary, the loader will not find it. So we provide one + // explicitly to avoid the search. + // + if (inh.IAT_DIRECTORY.VirtualAddress == 0 && + inh.IMPORT_DIRECTORY.VirtualAddress >= ish.VirtualAddress && + inh.IMPORT_DIRECTORY.VirtualAddress < ish.VirtualAddress + ish.SizeOfRawData) { + + inh.IAT_DIRECTORY.VirtualAddress = ish.VirtualAddress; + inh.IAT_DIRECTORY.Size = ish.SizeOfRawData; + } + } + + DETOUR_TRACE((" Imports: %p..%p\n", + (DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress, + (DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress + + inh.IMPORT_DIRECTORY.Size)); + + DWORD nOldDlls = inh.IMPORT_DIRECTORY.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR); + DWORD obRem = sizeof(IMAGE_IMPORT_DESCRIPTOR) * nDlls; + DWORD obOld = obRem + sizeof(IMAGE_IMPORT_DESCRIPTOR) * nOldDlls; + DWORD obTab = PadToDwordPtr(obOld); + DWORD obDll = obTab + sizeof(DWORD_XX) * 4 * nDlls; + DWORD obStr = obDll; + cbNew = obStr; + for (n = 0; n < nDlls; n++) { + cbNew += PadToDword((DWORD)strlen(plpDlls[n]) + 1); + } + + _Analysis_assume_(cbNew > + sizeof(IMAGE_IMPORT_DESCRIPTOR) * (nDlls + nOldDlls) + + sizeof(DWORD_XX) * 4 * nDlls); + pbNew = new BYTE [cbNew]; + if (pbNew == NULL) { + DETOUR_TRACE(("new BYTE [cbNew] failed.\n")); + goto finish; + } + ZeroMemory(pbNew, cbNew); + + PBYTE pbBase = pbModule; + PBYTE pbNext = pbBase + + inh.OptionalHeader.BaseOfCode + + inh.OptionalHeader.SizeOfCode + + inh.OptionalHeader.SizeOfInitializedData + + inh.OptionalHeader.SizeOfUninitializedData; + if (pbBase < pbNext) { + pbBase = pbNext; + } + DETOUR_TRACE(("pbBase = %p\n", pbBase)); + + PBYTE pbNewIid = FindAndAllocateNearBase(hProcess, pbModule, pbBase, cbNew); + if (pbNewIid == NULL) { + DETOUR_TRACE(("FindAndAllocateNearBase failed.\n")); + goto finish; + } + + PIMAGE_IMPORT_DESCRIPTOR piid = (PIMAGE_IMPORT_DESCRIPTOR)pbNew; + IMAGE_THUNK_DATAXX *pt = NULL; + + DWORD obBase = (DWORD)(pbNewIid - pbModule); + DWORD dwProtect = 0; + + if (inh.IMPORT_DIRECTORY.VirtualAddress != 0) { + // Read the old import directory if it exists. + DETOUR_TRACE(("IMPORT_DIRECTORY perms=%x\n", dwProtect)); + + if (!ReadProcessMemory(hProcess, + pbModule + inh.IMPORT_DIRECTORY.VirtualAddress, + &piid[nDlls], + nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR), &cbRead) + || cbRead < nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR)) { + + DETOUR_TRACE(("ReadProcessMemory(imports) failed: %d\n", GetLastError())); + goto finish; + } + } + + for (n = 0; n < nDlls; n++) { + HRESULT hrRet = StringCchCopyA((char*)pbNew + obStr, cbNew - obStr, plpDlls[n]); + if (FAILED(hrRet)) { + DETOUR_TRACE(("StringCchCopyA failed: %d\n", GetLastError())); + goto finish; + } + + // After copying the string, we patch up the size "??" bits if any. + hrRet = ReplaceOptionalSizeA((char*)pbNew + obStr, + cbNew - obStr, + DETOURS_STRINGIFY(DETOURS_BITS_XX)); + if (FAILED(hrRet)) { + DETOUR_TRACE(("ReplaceOptionalSizeA failed: %d\n", GetLastError())); + goto finish; + } + + DWORD nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * (4 * n)); + piid[n].OriginalFirstThunk = obBase + nOffset; + + // We need 2 thunks for the import table and 2 thunks for the IAT. + // One for an ordinal import and one to mark the end of the list. + pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset)); + pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1; + pt[1].u1.Ordinal = 0; + + nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * ((4 * n) + 2)); + piid[n].FirstThunk = obBase + nOffset; + pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset)); + pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1; + pt[1].u1.Ordinal = 0; + piid[n].TimeDateStamp = 0; + piid[n].ForwarderChain = 0; + piid[n].Name = obBase + obStr; + + obStr += PadToDword((DWORD)strlen(plpDlls[n]) + 1); + } + _Analysis_assume_(obStr <= cbNew); + +#if 0 + for (i = 0; i < nDlls + nOldDlls; i++) { + DETOUR_TRACE(("%8d. Look=%08x Time=%08x Fore=%08x Name=%08x Addr=%08x\n", + i, + piid[i].OriginalFirstThunk, + piid[i].TimeDateStamp, + piid[i].ForwarderChain, + piid[i].Name, + piid[i].FirstThunk)); + if (piid[i].OriginalFirstThunk == 0 && piid[i].FirstThunk == 0) { + break; + } + } +#endif + + if (!WriteProcessMemory(hProcess, pbNewIid, pbNew, obStr, NULL)) { + DETOUR_TRACE(("WriteProcessMemory(iid) failed: %d\n", GetLastError())); + goto finish; + } + + DETOUR_TRACE(("obBaseBef = %08x..%08x\n", + inh.IMPORT_DIRECTORY.VirtualAddress, + inh.IMPORT_DIRECTORY.VirtualAddress + inh.IMPORT_DIRECTORY.Size)); + DETOUR_TRACE(("obBaseAft = %08x..%08x\n", obBase, obBase + obStr)); + + // In this case the file didn't have an import directory in first place, + // so we couldn't fix the missing IAT above. We still need to explicitly + // provide an IAT to prevent to loader from looking for one. + // + if (inh.IAT_DIRECTORY.VirtualAddress == 0) { + inh.IAT_DIRECTORY.VirtualAddress = obBase; + inh.IAT_DIRECTORY.Size = cbNew; + } + + inh.IMPORT_DIRECTORY.VirtualAddress = obBase; + inh.IMPORT_DIRECTORY.Size = cbNew; + + /////////////////////// Update the NT header for the new import directory. + // + if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders, + PAGE_EXECUTE_READWRITE, &dwProtect)) { + DETOUR_TRACE(("VirtualProtectEx(inh) write failed: %d\n", GetLastError())); + goto finish; + } + + inh.OptionalHeader.CheckSum = 0; + + if (!WriteProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(idh) failed: %d\n", GetLastError())); + goto finish; + } + DETOUR_TRACE(("WriteProcessMemory(idh:%p..%p)\n", pbModule, pbModule + sizeof(idh))); + + if (!WriteProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(inh) failed: %d\n", GetLastError())); + goto finish; + } + DETOUR_TRACE(("WriteProcessMemory(inh:%p..%p)\n", + pbModule + idh.e_lfanew, + pbModule + idh.e_lfanew + sizeof(inh))); + + if (!VirtualProtectEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders, + dwProtect, &dwProtect)) { + DETOUR_TRACE(("VirtualProtectEx(idh) restore failed: %d\n", GetLastError())); + goto finish; + } + + fSucceeded = TRUE; + goto finish; +} |