| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727 |
- //////////////////////////////////////////////////////////////////////////////
- //
- // 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 !defined(SUNRISE_DETOURS_ALLOCATOR_PROOF_LEGACY)
- #include "../../src/client/hooking/detour/transaction/detour_transaction_storage.h"
- #endif
- #if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
- #error detours.h version mismatch
- #endif
- #define NOTHROW
- //////////////////////////////////////////////////////////////////////////////
- //
- #ifdef _DEBUG
- extern "C" IMAGE_DOS_HEADER __ImageBase;
- int Detour_AssertExprWithFunctionName(int reportType, const char* filename, int linenumber, const char* FunctionName, const char* msg)
- {
- int nRet = 0;
- DWORD dwLastError = GetLastError();
- CHAR szModuleNameWithFunctionName[MAX_PATH * 2];
- szModuleNameWithFunctionName[0] = 0;
- GetModuleFileNameA((HMODULE)&__ImageBase, szModuleNameWithFunctionName, ARRAYSIZE(szModuleNameWithFunctionName));
- StringCchCatNA(szModuleNameWithFunctionName, ARRAYSIZE(szModuleNameWithFunctionName), ",", ARRAYSIZE(szModuleNameWithFunctionName) - strlen(szModuleNameWithFunctionName) - 1);
- StringCchCatNA(szModuleNameWithFunctionName, ARRAYSIZE(szModuleNameWithFunctionName), FunctionName, ARRAYSIZE(szModuleNameWithFunctionName) - strlen(szModuleNameWithFunctionName) - 1);
- SetLastError(dwLastError);
- nRet = _CrtDbgReport(reportType, filename, linenumber, szModuleNameWithFunctionName, msg);
- SetLastError(dwLastError);
- return nRet;
- }
- #endif// _DEBUG
- //////////////////////////////////////////////////////////////////////////////
- //
- struct _DETOUR_ALIGN
- {
- BYTE obTarget : 3;
- BYTE obTrampoline : 5;
- };
- C_ASSERT(sizeof(_DETOUR_ALIGN) == 1);
- //////////////////////////////////////////////////////////////////////////////
- //
- // Region reserved for system DLLs, which cannot be used for trampolines.
- //
- static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000;
- static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000;
- //////////////////////////////////////////////////////////////////////////////
- //
- static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
- {
- MEMORY_BASIC_INFORMATION mbi;
- VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
- __try {
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
- if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
- return false;
- }
- PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
- pDosHeader->e_lfanew);
- if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
- return false;
- }
- if (pbAddress >= ((PBYTE)pDosHeader +
- pNtHeader->OptionalHeader
- .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
- pbAddress < ((PBYTE)pDosHeader +
- pNtHeader->OptionalHeader
- .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
- pNtHeader->OptionalHeader
- .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
- return true;
- }
- }
- #pragma prefast(suppress:28940, "A bad pointer means this probably isn't a PE header.")
- __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
- EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
- return false;
- }
- return false;
- }
- inline ULONG_PTR detour_2gb_below(ULONG_PTR address)
- {
- return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000;
- }
- inline ULONG_PTR detour_2gb_above(ULONG_PTR address)
- {
- #if defined(DETOURS_64BIT)
- return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000;
- #else
- return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000;
- #endif
- }
- ///////////////////////////////////////////////////////////////////////// X86.
- //
- #ifdef DETOURS_X86
- struct _DETOUR_TRAMPOLINE
- {
- BYTE rbCode[30]; // target code + jmp to pbRemain
- BYTE cbCode; // size of moved target code.
- BYTE cbCodeBreak; // padding to make debugging easier.
- BYTE rbRestore[22]; // original target code.
- BYTE cbRestore; // size of original target code.
- BYTE cbRestoreBreak; // padding to make debugging easier.
- _DETOUR_ALIGN rAlign[8]; // instruction alignment array.
- PBYTE pbRemain; // first instruction after moved code. [free list]
- PBYTE pbDetour; // first instruction of detour function.
- };
- C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 72);
- enum {
- SIZE_OF_JMP = 5
- };
- inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
- {
- PBYTE pbJmpSrc = pbCode + 5;
- *pbCode++ = 0xE9; // jmp +imm32
- *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
- return pbCode;
- }
- inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
- {
- *pbCode++ = 0xff; // jmp [+imm32]
- *pbCode++ = 0x25;
- *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal);
- return pbCode;
- }
- inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
- {
- while (pbCode < pbLimit) {
- *pbCode++ = 0xcc; // brk;
- }
- return pbCode;
- }
- inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
- {
- PBYTE pbCodeOriginal;
- 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;
- pbCodeOriginal = pbCode;
- // 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;
- // Patches applied by the OS will jump through an HPAT page to get
- // the target function in the patch image. The jump is always performed
- // to the target function found at the current instruction pointer +
- // PAGE_SIZE - 6 (size of jump).
- // If this is an OS patch, we want to detour at the point of the target function
- // padding in the base image. Ideally, we would detour at the target function, but
- // since it's patched it begins with a short jump (to padding) which isn't long
- // enough to hold the detour code bytes.
- if (pbCode[0] == 0xff &&
- pbCode[1] == 0x25 &&
- *(UNALIGNED INT32 *)&pbCode[2] == (UNALIGNED INT32)(pbCode + 0x1000)) { // jmp [eip+PAGE_SIZE-6]
- DETOUR_TRACE(("%p->%p: OS patch encountered, reset back to long jump 5 bytes prior to target function.\n", pbCode, pbCodeOriginal));
- pbCode = pbCodeOriginal;
- }
- }
- }
- return pbCode;
- }
- inline void detour_find_jmp_bounds(PBYTE pbCode,
- PDETOUR_TRAMPOLINE *ppLower,
- PDETOUR_TRAMPOLINE *ppUpper)
- {
- // We have to place trampolines within +/- 2GB of code.
- ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
- ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
- DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi));
- // And, within +/- 2GB of relative jmp targets.
- if (pbCode[0] == 0xe9) { // jmp +imm32
- PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
- if (pbNew < pbCode) {
- hi = detour_2gb_above((ULONG_PTR)pbNew);
- }
- else {
- lo = detour_2gb_below((ULONG_PTR)pbNew);
- }
- DETOUR_TRACE(("[%p..%p..%p] +imm32\n", (PVOID)lo, pbCode, (PVOID)hi));
- }
- *ppLower = (PDETOUR_TRAMPOLINE)lo;
- *ppUpper = (PDETOUR_TRAMPOLINE)hi;
- }
- inline BOOL detour_does_code_end_function(PBYTE pbCode)
- {
- if (pbCode[0] == 0xeb || // jmp +imm8
- pbCode[0] == 0xe9 || // jmp +imm32
- pbCode[0] == 0xe0 || // jmp eax
- pbCode[0] == 0xc2 || // ret +imm8
- pbCode[0] == 0xc3 || // ret
- pbCode[0] == 0xcc) { // brk
- return TRUE;
- }
- else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret
- return TRUE;
- }
- else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
- return TRUE;
- }
- else if ((pbCode[0] == 0x26 || // jmp es:
- pbCode[0] == 0x2e || // jmp cs:
- pbCode[0] == 0x36 || // jmp ss:
- pbCode[0] == 0x3e || // jmp ds:
- pbCode[0] == 0x64 || // jmp fs:
- pbCode[0] == 0x65) && // jmp gs:
- pbCode[1] == 0xff && // jmp [+imm32]
- pbCode[2] == 0x25) {
- return TRUE;
- }
- return FALSE;
- }
- inline ULONG detour_is_code_filler(PBYTE pbCode)
- {
- // 1-byte through 11-byte NOPs.
- if (pbCode[0] == 0x90) {
- return 1;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {
- return 2;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {
- return 3;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&
- pbCode[3] == 0x00) {
- return 4;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&
- pbCode[3] == 0x00 && pbCode[4] == 0x00) {
- return 5;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
- pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {
- return 6;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&
- pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
- pbCode[6] == 0x00) {
- return 7;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&
- pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
- pbCode[6] == 0x00 && pbCode[7] == 0x00) {
- return 8;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
- pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
- pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {
- return 9;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&
- pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&
- pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
- pbCode[9] == 0x00) {
- return 10;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&
- pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&
- pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
- pbCode[9] == 0x00 && pbCode[10] == 0x00) {
- return 11;
- }
- // int 3.
- if (pbCode[0] == 0xcc) {
- return 1;
- }
- return 0;
- }
- #endif // DETOURS_X86
- ///////////////////////////////////////////////////////////////////////// X64.
- //
- #ifdef DETOURS_X64
- struct _DETOUR_TRAMPOLINE
- {
- // An X64 instuction can be 15 bytes long.
- // In practice 11 seems to be the limit.
- BYTE rbCode[30]; // target code + jmp to pbRemain.
- BYTE cbCode; // size of moved target code.
- BYTE cbCodeBreak; // padding to make debugging easier.
- BYTE rbRestore[30]; // original target code.
- BYTE cbRestore; // size of original target code.
- BYTE cbRestoreBreak; // padding to make debugging easier.
- _DETOUR_ALIGN rAlign[8]; // instruction alignment array.
- PBYTE pbRemain; // first instruction after moved code. [free list]
- PBYTE pbDetour; // first instruction of detour function.
- BYTE rbCodeIn[8]; // jmp [pbDetour]
- };
- C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 96);
- enum {
- SIZE_OF_JMP = 5
- };
- inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
- {
- PBYTE pbJmpSrc = pbCode + 5;
- *pbCode++ = 0xE9; // jmp +imm32
- *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
- return pbCode;
- }
- inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
- {
- PBYTE pbJmpSrc = pbCode + 6;
- *pbCode++ = 0xff; // jmp [+imm32]
- *pbCode++ = 0x25;
- *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);
- return pbCode;
- }
- inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
- {
- while (pbCode < pbLimit) {
- *pbCode++ = 0xcc; // brk;
- }
- return pbCode;
- }
- inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
- {
- PBYTE pbCodeOriginal;
- 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;
- pbCodeOriginal = pbCode;
- // 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;
- // Patches applied by the OS will jump through an HPAT page to get
- // the target function in the patch image. The jump is always performed
- // to the target function found at the current instruction pointer +
- // PAGE_SIZE - 6 (size of jump).
- // If this is an OS patch, we want to detour at the point of the target function
- // in the base image. Since we need 5 bytes to perform the jump, detour at the
- // point of the long jump instead of the short jump at the start of the target.
- if (pbCode[0] == 0xff &&
- pbCode[1] == 0x25 &&
- *(UNALIGNED INT32 *)&pbCode[2] == 0xFFA) { // jmp [rip+PAGE_SIZE-6]
- DETOUR_TRACE(("%p->%p: OS patch encountered, reset back to long jump 5 bytes prior to target function.\n", pbCode, pbCodeOriginal));
- pbCode = pbCodeOriginal;
- }
- }
- }
- return pbCode;
- }
- inline void detour_find_jmp_bounds(PBYTE pbCode,
- PDETOUR_TRAMPOLINE *ppLower,
- PDETOUR_TRAMPOLINE *ppUpper)
- {
- // We have to place trampolines within +/- 2GB of code.
- ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
- ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
- DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi));
- // And, within +/- 2GB of relative jmp vectors.
- if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
- PBYTE pbNew = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];
- if (pbNew < pbCode) {
- hi = detour_2gb_above((ULONG_PTR)pbNew);
- }
- else {
- lo = detour_2gb_below((ULONG_PTR)pbNew);
- }
- DETOUR_TRACE(("[%p..%p..%p] [+imm32]\n", (PVOID)lo, pbCode, (PVOID)hi));
- }
- // And, within +/- 2GB of relative jmp targets.
- else if (pbCode[0] == 0xe9) { // jmp +imm32
- PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
- if (pbNew < pbCode) {
- hi = detour_2gb_above((ULONG_PTR)pbNew);
- }
- else {
- lo = detour_2gb_below((ULONG_PTR)pbNew);
- }
- DETOUR_TRACE(("[%p..%p..%p] +imm32\n", (PVOID)lo, pbCode, (PVOID)hi));
- }
- *ppLower = (PDETOUR_TRAMPOLINE)lo;
- *ppUpper = (PDETOUR_TRAMPOLINE)hi;
- }
- inline BOOL detour_does_code_end_function(PBYTE pbCode)
- {
- if (pbCode[0] == 0xeb || // jmp +imm8
- pbCode[0] == 0xe9 || // jmp +imm32
- pbCode[0] == 0xe0 || // jmp eax
- pbCode[0] == 0xc2 || // ret +imm8
- pbCode[0] == 0xc3 || // ret
- pbCode[0] == 0xcc) { // brk
- return TRUE;
- }
- else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret
- return TRUE;
- }
- else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
- return TRUE;
- }
- else if ((pbCode[0] == 0x26 || // jmp es:
- pbCode[0] == 0x2e || // jmp cs:
- pbCode[0] == 0x36 || // jmp ss:
- pbCode[0] == 0x3e || // jmp ds:
- pbCode[0] == 0x64 || // jmp fs:
- pbCode[0] == 0x65) && // jmp gs:
- pbCode[1] == 0xff && // jmp [+imm32]
- pbCode[2] == 0x25) {
- return TRUE;
- }
- return FALSE;
- }
- inline ULONG detour_is_code_filler(PBYTE pbCode)
- {
- // 1-byte through 11-byte NOPs.
- if (pbCode[0] == 0x90) {
- return 1;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {
- return 2;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {
- return 3;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&
- pbCode[3] == 0x00) {
- return 4;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&
- pbCode[3] == 0x00 && pbCode[4] == 0x00) {
- return 5;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
- pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {
- return 6;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&
- pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
- pbCode[6] == 0x00) {
- return 7;
- }
- if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&
- pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
- pbCode[6] == 0x00 && pbCode[7] == 0x00) {
- return 8;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
- pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
- pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {
- return 9;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&
- pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&
- pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
- pbCode[9] == 0x00) {
- return 10;
- }
- if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&
- pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&
- pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
- pbCode[9] == 0x00 && pbCode[10] == 0x00) {
- return 11;
- }
- // int 3.
- if (pbCode[0] == 0xcc) {
- return 1;
- }
- return 0;
- }
- #endif // DETOURS_X64
- //////////////////////////////////////////////////////////////////////// IA64.
- //
- #ifdef DETOURS_IA64
- struct _DETOUR_TRAMPOLINE
- {
- // On the IA64, a trampoline is used for both incoming and outgoing calls.
- //
- // The trampoline contains the following bundles for the outgoing call:
- // movl gp=target_gp;
- // <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", (PVOID)lo, pbCode, (PVOID)hi));
- *ppLower = (PDETOUR_TRAMPOLINE)lo;
- *ppUpper = (PDETOUR_TRAMPOLINE)hi;
- }
- inline BOOL detour_does_code_end_function(PBYTE pbCode)
- {
- ULONG Opcode = fetch_thumb_opcode(pbCode);
- if ((Opcode & 0xffffff87) == 0x4700 || // bx <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", (PVOID)lo, pbCode, (PVOID)hi));
- *ppLower = (PDETOUR_TRAMPOLINE)lo;
- *ppUpper = (PDETOUR_TRAMPOLINE)hi;
- }
- inline BOOL detour_is_code_os_patched(PBYTE pbCode)
- {
- // Identify whether the provided code pointer is a OS patch jump.
- // We can do this by checking if a branch (b <imm26>) is present, and if so,
- // it must be jumping to an HPAT page containing ldr <reg> [PC+PAGE_SIZE-4], br <reg>.
- ULONG Opcode = fetch_opcode(pbCode);
- if ((Opcode & 0xfc000000) != 0x14000000) {
- return FALSE;
- }
- // The branch must be jumping forward if it's going into the HPAT.
- // Check that the sign bit is cleared.
- if ((Opcode & 0x2000000) != 0) {
- return FALSE;
- }
- ULONG Delta = (ULONG)((Opcode & 0x1FFFFFF) * 4);
- PBYTE BranchTarget = pbCode + Delta;
- // Now inspect the opcodes of the code we jumped to in order to determine if it's HPAT.
- ULONG HpatOpcode1 = fetch_opcode(BranchTarget);
- ULONG HpatOpcode2 = fetch_opcode(BranchTarget + 4);
- if (HpatOpcode1 != 0x58008010) { // ldr <reg> [PC+PAGE_SIZE]
- return FALSE;
- }
- if (HpatOpcode2 != 0xd61f0200) { // br <reg>
- return FALSE;
- }
- return TRUE;
- }
- inline BOOL detour_does_code_end_function(PBYTE pbCode)
- {
- ULONG Opcode = fetch_opcode(pbCode);
- // When the OS has patched a function entry point, it will incorrectly
- // appear as though the function is just a single branch instruction.
- if (detour_is_code_os_patched(pbCode)) {
- return FALSE;
- }
- if ((Opcode & 0xffbffc1f) == 0xd61f0000 || // ret/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 %6lx\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 %6lx\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;
- }
- BOOL WINAPI DetourIsFunctionImported(_In_ PBYTE pbCode,
- _In_ PBYTE pbAddress)
- {
- return detour_is_imported(pbCode, pbAddress);
- }
- 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.
- // /RTCc RuntimeChecks breaks PtrToUlong.
- pbTarget = pbTarget - (ULONG)((ULONG_PTR)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;
- #if !defined(SUNRISE_DETOURS_ALLOCATOR_PROOF_LEGACY)
- static void* operator new(size_t size) noexcept
- {
- return sunrise::client::hooking::detour::storage::allocate(
- sunrise::client::hooking::detour::storage::Kind::thread, size);
- }
- static void operator delete(void* pointer) noexcept
- {
- (void)sunrise::client::hooking::detour::storage::release(
- sunrise::client::hooking::detour::storage::Kind::thread, pointer);
- }
- #endif
- };
- struct DetourOperation
- {
- DetourOperation * pNext;
- BOOL fIsRemove;
- PBYTE * ppbPointer;
- PBYTE pbTarget;
- PDETOUR_TRAMPOLINE pTrampoline;
- ULONG dwPerm;
- #if !defined(SUNRISE_DETOURS_ALLOCATOR_PROOF_LEGACY)
- static void* operator new(size_t size) noexcept
- {
- return sunrise::client::hooking::detour::storage::allocate(
- sunrise::client::hooking::detour::storage::Kind::operation, size);
- }
- static void operator delete(void* pointer) noexcept
- {
- (void)sunrise::client::hooking::detour::storage::release(
- sunrise::client::hooking::detour::storage::Kind::operation, pointer);
- }
- #endif
- };
- #if !defined(SUNRISE_DETOURS_ALLOCATOR_PROOF_LEGACY)
- static_assert(sizeof(DetourThread) <= sunrise::client::hooking::detour::storage::kThreadBytes);
- static_assert(sizeof(DetourOperation) <= sunrise::client::hooking::detour::storage::kOperationBytes);
- static_assert(alignof(DetourThread) <= sunrise::client::hooking::detour::storage::kAlignment);
- static_assert(alignof(DetourOperation) <= sunrise::client::hooking::detour::storage::kAlignment);
- #endif
- 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=%u\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
- }
- }
- #undef DETOURS_EIP
- #undef DETOURS_CONTEXT_FLAGS
- #ifdef DETOURS_X86
- #define DETOURS_EIP Eip
- #define DETOURS_CONTEXT_FLAGS CONTEXT_CONTROL
- #endif // DETOURS_X86
- #ifdef DETOURS_X64
- #define DETOURS_EIP Rip
- #define DETOURS_CONTEXT_FLAGS (CONTEXT_CONTROL | CONTEXT_INTEGER)
- #endif // DETOURS_X64
- #ifdef DETOURS_IA64
- #define DETOURS_EIP StIIP
- #define DETOURS_CONTEXT_FLAGS CONTEXT_CONTROL
- #endif // DETOURS_IA64
- #ifdef DETOURS_ARM
- #define DETOURS_EIP Pc
- #define DETOURS_CONTEXT_FLAGS CONTEXT_CONTROL
- #endif // DETOURS_ARM
- #ifdef DETOURS_ARM64
- #define DETOURS_EIP Pc
- #define DETOURS_CONTEXT_FLAGS (CONTEXT_CONTROL | CONTEXT_INTEGER)
- #endif // DETOURS_ARM64
- typedef ULONG_PTR DETOURS_EIP_TYPE;
- // Update any suspended threads.
- for (t = s_pPendingThreads; t != NULL; t = t->pNext) {
- CONTEXT cxt;
- cxt.ContextFlags = DETOURS_CONTEXT_FLAGS;
- 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=%ld\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=%ld\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
- #if defined(_M_ARM64EC)
- if (RtlIsEcCode(reinterpret_cast<DWORD64>(*ppPointer))) {
- DETOUR_TRACE(("*ppPointer is an Arm64EC address (ppPointer=%p). "
- "An Arm64EC address cannot be legitimately detoured with an x64 jmp. "
- "Mark the target function with __declspec(hybrid_patchable) to make it detour-able. "
- "We still allow an Arm64EC function to be detoured with an x64 jmp to make it easy (crash) to debug.\n", ppPointer));
- DETOUR_BREAK();
- }
- #endif
- 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;
- }
- if (ppRealDetour != NULL) {
- *ppRealDetour = NULL;
- }
- if (ppRealTarget != NULL) {
- *ppRealTarget = 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((" %u/%u",
- 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);
- }
- BOOL WINAPI DetourAreSameGuid(_In_ REFGUID left, _In_ REFGUID right)
- {
- return
- left.Data1 == right.Data1 &&
- left.Data2 == right.Data2 &&
- left.Data3 == right.Data3 &&
- left.Data4[0] == right.Data4[0] &&
- left.Data4[1] == right.Data4[1] &&
- left.Data4[2] == right.Data4[2] &&
- left.Data4[3] == right.Data4[3] &&
- left.Data4[4] == right.Data4[4] &&
- left.Data4[5] == right.Data4[5] &&
- left.Data4[6] == right.Data4[6] &&
- left.Data4[7] == right.Data4[7];
- }
- // End of File
|