in Detours/detours.cpp [1786:2138]
LONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour,
_Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline,
_Out_opt_ PVOID *ppRealTarget,
_Out_opt_ PVOID *ppRealDetour)
{
LONG error = NO_ERROR;
if (ppRealTrampoline != NULL) {
*ppRealTrampoline = NULL;
}
if (ppRealTarget != NULL) {
*ppRealTarget = NULL;
}
if (ppRealDetour != NULL) {
*ppRealDetour = NULL;
}
if (pDetour == NULL) {
DETOUR_TRACE(("empty detour\n"));
return ERROR_INVALID_PARAMETER;
}
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
DETOUR_TRACE(("transaction conflict with thread id=%d\n", s_nPendingThreadId));
return ERROR_INVALID_OPERATION;
}
// If any of the pending operations failed, then we don't need to do this.
if (s_nPendingError != NO_ERROR) {
DETOUR_TRACE(("pending transaction error=%d\n", s_nPendingError));
return s_nPendingError;
}
if (ppPointer == NULL) {
DETOUR_TRACE(("ppPointer is null\n"));
return ERROR_INVALID_HANDLE;
}
if (*ppPointer == NULL) {
error = ERROR_INVALID_HANDLE;
s_nPendingError = error;
s_ppPendingError = ppPointer;
DETOUR_TRACE(("*ppPointer is null (ppPointer=%p)\n", ppPointer));
DETOUR_BREAK();
return error;
}
PBYTE pbTarget = (PBYTE)*ppPointer;
PDETOUR_TRAMPOLINE pTrampoline = NULL;
DetourOperation *o = NULL;
#ifdef DETOURS_IA64
PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour;
PPLABEL_DESCRIPTOR ppldTarget = (PPLABEL_DESCRIPTOR)pbTarget;
PVOID pDetourGlobals = NULL;
PVOID pTargetGlobals = NULL;
pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals);
pbTarget = (PBYTE)DetourCodeFromPointer(ppldTarget, &pTargetGlobals);
DETOUR_TRACE((" ppldDetour=%p, code=%p [gp=%p]\n",
ppldDetour, pDetour, pDetourGlobals));
DETOUR_TRACE((" ppldTarget=%p, code=%p [gp=%p]\n",
ppldTarget, pbTarget, pTargetGlobals));
#else // DETOURS_IA64
pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL);
pDetour = DetourCodeFromPointer(pDetour, NULL);
#endif // !DETOURS_IA64
// Don't follow a jump if its destination is the target function.
// This happens when the detour does nothing other than call the target.
if (pDetour == (PVOID)pbTarget) {
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
if (ppRealTarget != NULL) {
*ppRealTarget = pbTarget;
}
if (ppRealDetour != NULL) {
*ppRealDetour = pDetour;
}
o = new NOTHROW DetourOperation;
if (o == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
fail:
s_nPendingError = error;
DETOUR_BREAK();
stop:
if (pTrampoline != NULL) {
detour_free_trampoline(pTrampoline);
pTrampoline = NULL;
if (ppRealTrampoline != NULL) {
*ppRealTrampoline = NULL;
}
}
if (o != NULL) {
delete o;
o = NULL;
}
s_ppPendingError = ppPointer;
return error;
}
pTrampoline = detour_alloc_trampoline(pbTarget);
if (pTrampoline == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
DETOUR_BREAK();
goto fail;
}
if (ppRealTrampoline != NULL) {
*ppRealTrampoline = pTrampoline;
}
DETOUR_TRACE(("detours: pbTramp=%p, pDetour=%p\n", pTrampoline, pDetour));
memset(pTrampoline->rAlign, 0, sizeof(pTrampoline->rAlign));
// Determine the number of movable target instructions.
PBYTE pbSrc = pbTarget;
PBYTE pbTrampoline = pTrampoline->rbCode;
#ifdef DETOURS_IA64
PBYTE pbPool = (PBYTE)(&pTrampoline->bBranchIslands + 1);
#else
PBYTE pbPool = pbTrampoline + sizeof(pTrampoline->rbCode);
#endif
ULONG cbTarget = 0;
ULONG cbJump = SIZE_OF_JMP;
ULONG nAlign = 0;
#ifdef DETOURS_ARM
// On ARM, we need an extra instruction when the function isn't 32-bit aligned.
// Check if the existing code is another detour (or at least a similar
// "ldr pc, [PC+0]" jump.
if ((ULONG)pbTarget & 2) {
cbJump += 2;
ULONG op = fetch_thumb_opcode(pbSrc);
if (op == 0xbf00) {
op = fetch_thumb_opcode(pbSrc + 2);
if (op == 0xf8dff000) { // LDR PC,[PC]
*((PUSHORT&)pbTrampoline)++ = *((PUSHORT&)pbSrc)++;
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
cbTarget = (LONG)(pbSrc - pbTarget);
// We will fall through the "while" because cbTarget is now >= cbJump.
}
}
}
else {
ULONG op = fetch_thumb_opcode(pbSrc);
if (op == 0xf8dff000) { // LDR PC,[PC]
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
cbTarget = (LONG)(pbSrc - pbTarget);
// We will fall through the "while" because cbTarget is now >= cbJump.
}
}
#endif
while (cbTarget < cbJump) {
PBYTE pbOp = pbSrc;
LONG lExtra = 0;
DETOUR_TRACE((" DetourCopyInstruction(%p,%p)\n",
pbTrampoline, pbSrc));
pbSrc = (PBYTE)
DetourCopyInstruction(pbTrampoline, (PVOID*)&pbPool, pbSrc, NULL, &lExtra);
DETOUR_TRACE((" DetourCopyInstruction() = %p (%d bytes)\n",
pbSrc, (int)(pbSrc - pbOp)));
pbTrampoline += (pbSrc - pbOp) + lExtra;
cbTarget = (LONG)(pbSrc - pbTarget);
pTrampoline->rAlign[nAlign].obTarget = cbTarget;
pTrampoline->rAlign[nAlign].obTrampoline = pbTrampoline - pTrampoline->rbCode;
nAlign++;
if (nAlign >= ARRAYSIZE(pTrampoline->rAlign)) {
break;
}
if (detour_does_code_end_function(pbOp)) {
break;
}
}
// Consume, but don't duplicate padding if it is needed and available.
while (cbTarget < cbJump) {
LONG cFiller = detour_is_code_filler(pbSrc);
if (cFiller == 0) {
break;
}
pbSrc += cFiller;
cbTarget = (LONG)(pbSrc - pbTarget);
}
#if DETOUR_DEBUG
{
DETOUR_TRACE((" detours: rAlign ["));
LONG n = 0;
for (n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {
if (pTrampoline->rAlign[n].obTarget == 0 &&
pTrampoline->rAlign[n].obTrampoline == 0) {
break;
}
DETOUR_TRACE((" %d/%d",
pTrampoline->rAlign[n].obTarget,
pTrampoline->rAlign[n].obTrampoline
));
}
DETOUR_TRACE((" ]\n"));
}
#endif
if (cbTarget < cbJump || nAlign > ARRAYSIZE(pTrampoline->rAlign)) {
// Too few instructions.
error = ERROR_INVALID_BLOCK;
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
if (pbTrampoline > pbPool) {
__debugbreak();
}
pTrampoline->cbCode = (BYTE)(pbTrampoline - pTrampoline->rbCode);
pTrampoline->cbRestore = (BYTE)cbTarget;
CopyMemory(pTrampoline->rbRestore, pbTarget, cbTarget);
#if !defined(DETOURS_IA64)
if (cbTarget > sizeof(pTrampoline->rbCode) - cbJump) {
// Too many instructions.
error = ERROR_INVALID_HANDLE;
DETOUR_BREAK();
goto fail;
}
#endif // !DETOURS_IA64
pTrampoline->pbRemain = pbTarget + cbTarget;
pTrampoline->pbDetour = (PBYTE)pDetour;
#ifdef DETOURS_IA64
pTrampoline->ppldDetour = ppldDetour;
pTrampoline->ppldTarget = ppldTarget;
pTrampoline->pldTrampoline.EntryPoint = (UINT64)&pTrampoline->bMovlTargetGp;
pTrampoline->pldTrampoline.GlobalPointer = (UINT64)pDetourGlobals;
((DETOUR_IA64_BUNDLE *)pTrampoline->rbCode)->SetStop();
pTrampoline->bMovlTargetGp.SetMovlGp((UINT64)pTargetGlobals);
pTrampoline->bBrlRemainEip.SetBrl((UINT64)pTrampoline->pbRemain);
// Alloc frame: alloc r41=ar.pfs,11,0,8,0; mov r40=rp
pTrampoline->bAllocFrame.wide[0] = 0x00000580164d480c;
pTrampoline->bAllocFrame.wide[1] = 0x00c4000500000200;
// save r36, r37, r38.
pTrampoline->bSave37to39.wide[0] = 0x031021004e019001;
pTrampoline->bSave37to39.wide[1] = 0x8401280600420098;
// save r34,r35,r36: adds r47=0,r36; adds r46=0,r35; adds r45=0,r34
pTrampoline->bSave34to36.wide[0] = 0x02e0210048017800;
pTrampoline->bSave34to36.wide[1] = 0x84011005a042008c;
// save gp,r32,r33" adds r44=0,r33; adds r43=0,r32; adds r42=0,gp ;;
pTrampoline->bSaveGPto33.wide[0] = 0x02b0210042016001;
pTrampoline->bSaveGPto33.wide[1] = 0x8400080540420080;
// set detour GP.
pTrampoline->bMovlDetourGp.SetMovlGp((UINT64)pDetourGlobals);
// call detour: brl.call.sptk.few rp=detour ;;
pTrampoline->bCallDetour.wide[0] = 0x0000000100000005;
pTrampoline->bCallDetour.wide[1] = 0xd000001000000000;
pTrampoline->bCallDetour.SetBrlTarget((UINT64)pDetour);
// pop frame & gp: adds gp=0,r42; mov rp=r40,+0;; mov.i ar.pfs=r41
pTrampoline->bPopFrameGp.wide[0] = 0x4000210054000802;
pTrampoline->bPopFrameGp.wide[1] = 0x00aa029000038005;
// return to caller: br.ret.sptk.many rp ;;
pTrampoline->bReturn.wide[0] = 0x0000000100000019;
pTrampoline->bReturn.wide[1] = 0x0084000880000200;
DETOUR_TRACE(("detours: &bMovlTargetGp=%p\n", &pTrampoline->bMovlTargetGp));
DETOUR_TRACE(("detours: &bMovlDetourGp=%p\n", &pTrampoline->bMovlDetourGp));
#endif // DETOURS_IA64
pbTrampoline = pTrampoline->rbCode + pTrampoline->cbCode;
#ifdef DETOURS_X64
pbTrampoline = detour_gen_jmp_indirect(pbTrampoline, &pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_X64
#ifdef DETOURS_X86
pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_X86
#ifdef DETOURS_ARM
pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_ARM64
(void)pbTrampoline;
DWORD dwOld = 0;
if (!VirtualProtect(pbTarget, cbTarget, PAGE_EXECUTE_READWRITE, &dwOld)) {
error = GetLastError();
DETOUR_BREAK();
goto fail;
}
DETOUR_TRACE(("detours: pbTarget=%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
pbTarget,
pbTarget[0], pbTarget[1], pbTarget[2], pbTarget[3],
pbTarget[4], pbTarget[5], pbTarget[6], pbTarget[7],
pbTarget[8], pbTarget[9], pbTarget[10], pbTarget[11]));
DETOUR_TRACE(("detours: pbTramp =%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
pTrampoline,
pTrampoline->rbCode[0], pTrampoline->rbCode[1],
pTrampoline->rbCode[2], pTrampoline->rbCode[3],
pTrampoline->rbCode[4], pTrampoline->rbCode[5],
pTrampoline->rbCode[6], pTrampoline->rbCode[7],
pTrampoline->rbCode[8], pTrampoline->rbCode[9],
pTrampoline->rbCode[10], pTrampoline->rbCode[11]));
o->fIsRemove = FALSE;
o->ppbPointer = (PBYTE*)ppPointer;
o->pTrampoline = pTrampoline;
o->pbTarget = pbTarget;
o->dwPerm = dwOld;
o->pNext = s_pPendingOperations;
s_pPendingOperations = o;
return NO_ERROR;
}