| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406 |
- //////////////////////////////////////////////////////////////////////////////
- //
- // Core Detours Functionality (detours.cpp of detours.lib)
- //
- // Microsoft Research Detours Package, Version 2.1.
- //
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //
- #include <windows.h>
- #if (_MSC_VER < 1299)
- #pragma warning(disable: 4710)
- #endif
- //#define DETOUR_DEBUG 1
- #define DETOURS_INTERNAL
- #include "detours.h"
- #include "detoured.h"
- #if !defined(DETOURS_X86) && !defined(DETOURS_X64) && !defined(DETOURS_IA64)
- #error Must define one of DETOURS_X86, DETOURS_X64, or DETOURS_IA64
- #endif
- //////////////////////////////////////////////////////////////////////////////
- //
- 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;
- }
- return false;
- }
- __except(EXCEPTION_EXECUTE_HANDLER) {
- return false;
- }
- }
- ///////////////////////////////////////////////////////////////////////// X86.
- //
- #ifdef DETOURS_X86
- struct _DETOUR_TRAMPOLINE
- {
- BYTE rbCode[23]; // target code + jmp to pbRemain
- BYTE cbTarget; // size of target code moved.
- PBYTE pbRemain; // first instruction after moved code. [free list]
- PBYTE pbDetour; // first instruction of detour function.
- };
- 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;
- }
- if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
- // Looks like an import alias jump, then get the code it points to.
- PBYTE pbTarget = *(PBYTE *)&pbCode[2];
- if (detour_is_imported(pbCode, pbTarget)) {
- PBYTE pbNew = *(PBYTE *)pbTarget;
- DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
- return pbNew;
- }
- }
- else if (pbCode[0] == 0xeb) { // jmp +imm8
- // These just started appearing with CL13.
- PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
- DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
- if (pbNew[0] == 0xe9) { // jmp +imm32
- pbCode = pbNew;
- pbNew = pbCode + *(INT32 *)&pbCode[1];
- DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
- }
- return pbNew;
- }
- return pbCode;
- }
- inline BOOL detour_does_code_end_function(PBYTE pbCode)
- {
- if (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] == 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] == 0xe3 || // jmp ds:
- pbCode[0] == 0x64 || // jmp fs:
- pbCode[0] == 0x65) && // jmp gs:
- pbCode[1] == 0xff && // jmp [+imm32]
- pbCode[2] == 0x25) {
- return TRUE;
- }
- return FALSE;
- }
- #endif // DETOURS_X86
- ///////////////////////////////////////////////////////////////////////// X64.
- //
- #ifdef DETOURS_X64
- #error Feature not supported in this release.
- #endif // DETOURS_X64
- //////////////////////////////////////////////////////////////////////// IA64.
- //
- #ifdef DETOURS_IA64
- #error Feature not supported in this release.
- #endif
- //////////////////////////////////////////////// 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 void detour_writable_trampoline_regions()
- {
- // Mark all of the regions as writable.
- for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {
- DWORD dwOld;
- VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READWRITE, &dwOld);
- }
- }
- static void detour_runnable_trampoline_regions()
- {
- // 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);
- }
- }
- static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)
- {
- // We have to place trampolines within +/- 2GB of target.
- // The allocation code assumes that
- PDETOUR_TRAMPOLINE pLo = (PDETOUR_TRAMPOLINE)
- ((pbTarget > (PBYTE)0x7ff80000)
- ? pbTarget - 0x7ff80000 : (PBYTE)(ULONG_PTR)DETOUR_REGION_SIZE);
- PDETOUR_TRAMPOLINE pHi = (PDETOUR_TRAMPOLINE)
- ((pbTarget < (PBYTE)0xffffffff80000000)
- ? pbTarget + 0x7ff80000 : (PBYTE)0xfffffffffff80000);
- DETOUR_TRACE(("[%p..%p..%p]\n", pLo, pbTarget, 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 64K block.
- pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff);
- // First we search down (within the valid region)
- DETOUR_TRACE((" Looking for free region below %p:\n", pbTarget));
- PBYTE pbTry;
- for (pbTry = pbTarget; pbTry > (PBYTE)pLo;) {
- MEMORY_BASIC_INFORMATION mbi;
- DETOUR_TRACE((" Try %p\n", pbTry));
- if (pbTry >= (PBYTE)(ULONG_PTR)0x70000000 &&
- pbTry <= (PBYTE)(ULONG_PTR)0x80000000) {
- // Skip region reserved for system DLLs.
- pbTry = (PBYTE)(ULONG_PTR)(0x70000000 - DETOUR_REGION_SIZE);
- }
- 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) {
- s_pRegion = (DETOUR_REGION*)VirtualAlloc(pbTry,
- DETOUR_REGION_SIZE,
- MEM_COMMIT|MEM_RESERVE,
- PAGE_EXECUTE_READWRITE);
- if (s_pRegion != NULL) {
- alloced_region:
- 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;
- }
- else {
- DETOUR_TRACE(("Error: %p %d\n", pbTry, GetLastError()));
- break;
- }
- }
- pbTry = (PBYTE)mbi.AllocationBase - DETOUR_REGION_SIZE;
- }
- DETOUR_TRACE((" Looking for free region above %p:\n", pbTarget));
- for (pbTry = pbTarget; pbTry < (PBYTE)pHi;) {
- MEMORY_BASIC_INFORMATION mbi;
- if (pbTry >= (PBYTE)(ULONG_PTR)0x70000000 &&
- pbTry <= (PBYTE)(ULONG_PTR)0x80000000) {
- // Skip region reserved for system DLLs.
- pbTry = (PBYTE)(ULONG_PTR)(0x80000000 + DETOUR_REGION_SIZE);
- }
- 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) {
- ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);
- if (extra != 0) {
- // WinXP64 returns free areas that aren't REGION aligned to
- // 32-bit applications.
- ULONG_PTR adjust = DETOUR_REGION_SIZE - extra;
- mbi.RegionSize -= adjust;
- ((PBYTE&)mbi.BaseAddress) += adjust;
- DETOUR_TRACE(("--Try %p => %p..%p %6x\n",
- pbTry,
- mbi.BaseAddress,
- (PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,
- mbi.State));
- pbTry = (PBYTE)mbi.BaseAddress;
- }
- s_pRegion = (DETOUR_REGION*)VirtualAlloc(pbTry,
- DETOUR_REGION_SIZE,
- MEM_COMMIT|MEM_RESERVE,
- PAGE_EXECUTE_READWRITE);
- if (s_pRegion != NULL) {
- goto alloced_region;
- }
- else {
- DETOUR_TRACE(("Error: %p %d\n", pbTry, GetLastError()));
- }
- }
- pbTry = (PBYTE)mbi.BaseAddress + mbi.RegionSize;
- }
- 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 PIMAGE_DOS_HEADER detour_find_header(PBYTE pbTarget)
- {
- MEMORY_BASIC_INFORMATION mbi;
- if (!VirtualQuery(pbTarget, &mbi, sizeof(mbi))) {
- return NULL;
- }
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
- __try {
- if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
- return NULL;
- }
- PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
- pDosHeader->e_lfanew);
- if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
- return NULL;
- }
- if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
- return NULL;
- }
- return pDosHeader;
- } __except(EXCEPTION_EXECUTE_HANDLER) {
- return NULL;
- }
- }
- ///////////////////////////////////////////////////////// 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 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(PVOID pPointer, PVOID *ppGlobals)
- {
- return detour_skip_jmp((PBYTE)pPointer, ppGlobals);
- }
- //////////////////////////////////////////////////////////// Transaction APIs.
- //
- VOID WINAPI DetourSetIgnoreTooSmall(BOOL fIgnore)
- {
- s_fIgnoreTooSmall = fIgnore;
- }
- LONG WINAPI DetourTransactionBegin()
- {
- // Only one transaction is allowed at a time.
- if (s_nPendingThreadId != 0) {
- return ERROR_INVALID_OPERATION;
- }
- // Make sure only one thread can start a transaction.
- if (InterlockedCompareExchange(&s_nPendingThreadId, (LONG)GetCurrentThreadId(), 0) != 0) {
- return ERROR_INVALID_OPERATION;
- }
- s_fIgnoreTooSmall = FALSE;
- s_pPendingOperations = NULL;
- s_pPendingThreads = NULL;
- s_nPendingError = NO_ERROR;
- s_ppPendingError = NULL;
- // Make sure the trampoline pages are writable.
- detour_writable_trampoline_regions();
- return NO_ERROR;
- }
- 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->cbTarget,
- 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);
- }
- LONG WINAPI DetourTransactionCommitEx(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;
- // Insert or remove each of the detours.
- for (o = s_pPendingOperations; o != NULL; o = o->pNext) {
- if (o->fIsRemove) {
- PBYTE pbSrc = o->pTrampoline->rbCode;
- LONG cbCopy = 0;
- for (; cbCopy < o->pTrampoline->cbTarget;) {
- LONG lExtra = 0;
- pbSrc = (PBYTE)DetourCopyInstructionEx(o->pbTarget + cbCopy,
- pbSrc, NULL, &lExtra);
- if (lExtra != 0) {
- break; // Abort if offset doesn't fit.
- }
- cbCopy = (LONG)(pbSrc - o->pTrampoline->rbCode);
- }
- if (cbCopy != o->pTrampoline->cbTarget) { // Count came out different!
- // This is a drastic error as the backward copy should never fail.
- s_nPendingError = ERROR_INVALID_DATA;
- s_ppPendingError = (PVOID*)o->ppbPointer;
- DETOUR_BREAK();
- }
- #ifdef DETOURS_IA64
- #error Feature not supported in this release.
- #else // DETOURS_IA64
- *o->ppbPointer = o->pbTarget;
- #endif
- }
- else {
- DETOUR_TRACE(("detours: pbTramp =%p, pbRemain=%p, pbDetour=%p, cbTarget=%d\n",
- o->pTrampoline,
- o->pTrampoline->pbRemain,
- o->pTrampoline->pbDetour,
- o->pTrampoline->cbTarget));
- 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
- #error Feature not supported in this release.
- #endif // DETOURS_IA64
- #ifdef DETOURS_X64
- #error Feature not supported in this release.
- #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;
- #endif // DETOURS_X86
- 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
- #error Feature not supported in this release.
- #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
- #undef DETOURS_EIP_TYPE
- #ifdef DETOURS_X86
- #define DETOURS_EIP Eip
- #define DETOURS_EIP_TYPE DWORD
- #endif // DETOURS_X86
- #ifdef DETOURS_X64
- #error Feature not supported in this release.
- #endif // DETOURS_X64
- #ifdef DETOURS_IA64
- #error Feature not supported in this release.
- #endif // DETOURS_IA64
- if (GetThreadContext(t->hThread, &cxt)) {
- for (DetourOperation *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->pTrampoline;
- cxt.DETOURS_EIP += (DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget;
- 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->cbTarget)) {
- cxt.DETOURS_EIP -= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget;
- cxt.DETOURS_EIP += (DETOURS_EIP_TYPE)(ULONG_PTR)o->pTrampoline;
- 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->cbTarget, o->dwPerm, &dwOld);
- FlushInstructionCache(hProcess, o->pbTarget, o->pTrampoline->cbTarget);
- if (o->fIsRemove && 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 (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(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 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(PVOID *ppPointer,
- PVOID pDetour)
- {
- return DetourAttachEx(ppPointer, pDetour, NULL, NULL, NULL);
- }
- LONG WINAPI DetourAttachEx(PVOID *ppPointer,
- PVOID pDetour,
- PDETOUR_TRAMPOLINE *ppRealTrampoline,
- PVOID *ppRealTarget,
- PVOID *ppRealDetour)
- {
- LONG error = NO_ERROR;
- if (ppRealTrampoline != NULL) {
- *ppRealTrampoline = NULL;
- }
- if (ppRealTarget != NULL) {
- *ppRealTarget = NULL;
- }
- if (ppRealDetour != NULL) {
- *ppRealDetour = NULL;
- }
- 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
- #error Feature not supported in this release.
- #else
- pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL);
- pDetour = DetourCodeFromPointer(pDetour, NULL);
- #endif
- // 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 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 (o != NULL) {
- delete o;
- o = NULL;
- }
- s_ppPendingError = ppPointer;
- return error;
- }
- // Mark process as having detoured code.
- #ifdef DETOURS_INTERNAL_USAGE
- #error Feature not supported in this release.
- #else
- Detoured();
- #endif
- 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));
- // Determine the number of movable target instructions.
- PBYTE pbSrc = pbTarget;
- LONG cbTarget = 0;
- while (cbTarget < SIZE_OF_JMP) {
- PBYTE pbOp = pbSrc;
- LONG lExtra = 0;
- DETOUR_TRACE((" DetourCopyInstructionEx(%p,%p)\n",
- pTrampoline->rbCode + cbTarget, pbSrc));
- pbSrc = (PBYTE)DetourCopyInstructionEx(pTrampoline->rbCode + cbTarget,
- pbSrc, NULL, &lExtra);
- DETOUR_TRACE((" DetourCopyInstructionEx() = %p (%d bytes)\n",
- pbSrc, (int)(pbSrc - pbOp)));
- if (lExtra != 0) {
- break; // Abort if offset doesn't fit.
- }
- cbTarget = (LONG)(pbSrc - pbTarget);
- if (detour_does_code_end_function(pbOp)) {
- break;
- }
- }
- if (cbTarget < SIZE_OF_JMP) {
- // Too few instructions.
- error = ERROR_INVALID_BLOCK;
- if (s_fIgnoreTooSmall) {
- goto stop;
- }
- else {
- DETOUR_BREAK();
- goto fail;
- }
- }
- #if !defined(DETOURS_IA64)
- if (cbTarget > sizeof(pTrampoline->rbCode) - SIZE_OF_JMP) {
- // Too many instructions.
- error = ERROR_INVALID_HANDLE;
- DETOUR_BREAK();
- goto fail;
- }
- #endif
- pTrampoline->pbRemain = pbTarget + cbTarget;
- pTrampoline->pbDetour = (PBYTE)pDetour;
- pTrampoline->cbTarget = (BYTE)cbTarget;
- #ifdef DETOURS_IA64
- #error Feature not supported in this release.
- #endif // DETOURS_IA64
- #ifdef DETOURS_X64
- #error Feature not supported in this release.
- #endif // DETOURS_X64
- #ifdef DETOURS_X86
- pbSrc = detour_gen_jmp_immediate(pTrampoline->rbCode + cbTarget, pTrampoline->pbRemain);
- pbSrc = detour_gen_brk(pbSrc,
- pTrampoline->rbCode + sizeof(pTrampoline->rbCode));
- #endif // DETOURS_X86
- 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]));
- /////////////////////////////////////////// Mark binary as being detoured.
- //
- PIMAGE_DOS_HEADER pDosHeader = detour_find_header(pbTarget);
- if (pDosHeader != NULL && pDosHeader->e_res[0] != 'eD') {
- DWORD dwDos = 0;
- if (!VirtualProtect(pDosHeader, sizeof(*pDosHeader), PAGE_EXECUTE_READWRITE, &dwDos)) {
- error = GetLastError();
- DETOUR_BREAK();
- goto fail;
- }
- pDosHeader->e_res[0] = 'eD';
- pDosHeader->e_res[1] = 'ot';
- pDosHeader->e_res[2] = 'ru';
- pDosHeader->e_res[3] = '!s';
- }
- 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(PVOID *ppPointer,
- 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 (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 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;
- }
- PDETOUR_TRAMPOLINE pTrampoline = (PDETOUR_TRAMPOLINE)*ppPointer;
- pDetour = DetourCodeFromPointer(pDetour, NULL);
- ////////////////////////////////////// Verify that Trampoline is in place.
- //
- PBYTE pbTarget = pTrampoline->pbRemain - pTrampoline->cbTarget;
- LONG cbTarget = pTrampoline->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;
- }
- HMODULE WINAPI DetourGetDetouredMarker()
- {
- #ifdef DETOURS_INTERNAL_USAGE
- #error Feature not supported in this release.
- #else
- return Detoured();
- #endif
- }
- // End of File
|