| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410 |
- #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"
- #define DETOURS_X86
- #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
- }
|