BOOL EnableSpeedControl(BOOL bEnable) { static BOOL bEnabled = FALSE; if ((bEnable && bEnabled) || (!bEnable && !bEnabled)) return FALSE; BOOL bSuccessed; if (bEnable) { InitializeCriticalSection(&g_csGetTickCount); InitializeCriticalSection(&g_csQueryPerformanceCounter); InitializeCriticalSection(&g_csTimeGetTime); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); *(PVOID *)&Real_GetTickCount = DetourCodeFromPointer((PVOID)GetTickCount, NULL); DetourAttach(&(PVOID&)Real_GetTickCount, Mine_GetTickCount); DetourAttach(&(PVOID&)Real_QueryPerformanceCounter, Mine_QueryPerformanceCounter); DetourAttach(&(PVOID&)Real_timeGetTime, Mine_timeGetTime); bSuccessed = DetourTransactionCommit() == 0; bEnabled = TRUE; } else { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)Real_GetTickCount, Mine_GetTickCount); DetourDetach(&(PVOID&)Real_QueryPerformanceCounter, Mine_QueryPerformanceCounter); DetourDetach(&(PVOID&)Real_timeGetTime, Mine_timeGetTime); bSuccessed = DetourTransactionCommit() == 0; DeleteCriticalSection(&g_csGetTickCount); DeleteCriticalSection(&g_csQueryPerformanceCounter); DeleteCriticalSection(&g_csTimeGetTime); bEnabled = FALSE; } return bSuccessed; }
LONG WINAPI DetourDetach(PVOID *ppPointer, PVOID pDetour) { LONG error = NO_ERROR; if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { return ERROR_INVALID_OPERATION; } // If any of the pending operations failed, then we don't need to do this. if (s_nPendingError != NO_ERROR) { return s_nPendingError; } if (ppPointer == NULL) { return ERROR_INVALID_HANDLE; } if (*ppPointer == NULL) { error = ERROR_INVALID_HANDLE; s_nPendingError = error; s_ppPendingError = ppPointer; DETOUR_BREAK(); return error; } DetourOperation *o = new DetourOperation; if (o == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; fail: s_nPendingError = error; DETOUR_BREAK(); stop: if (o != NULL) { delete o; o = NULL; } s_ppPendingError = ppPointer; return error; } PDETOUR_TRAMPOLINE pTrampoline = (PDETOUR_TRAMPOLINE)*ppPointer; pDetour = DetourCodeFromPointer(pDetour, NULL); ////////////////////////////////////// Verify that Trampoline is in place. // PBYTE pbTarget = pTrampoline->pbRemain - pTrampoline->cbTarget; LONG cbTarget = pTrampoline->cbTarget; if (cbTarget == 0 || cbTarget > sizeof(pTrampoline->rbCode)) { error = ERROR_INVALID_BLOCK; if (s_fIgnoreTooSmall) { goto stop; } else { DETOUR_BREAK(); goto fail; } } if (pTrampoline->pbDetour != pDetour) { error = ERROR_INVALID_BLOCK; if (s_fIgnoreTooSmall) { goto stop; } else { DETOUR_BREAK(); goto fail; } } DWORD dwOld = 0; if (!VirtualProtect(pbTarget, cbTarget, PAGE_EXECUTE_READWRITE, &dwOld)) { error = GetLastError(); DETOUR_BREAK(); goto fail; } o->fIsRemove = TRUE; o->ppbPointer = (PBYTE*)ppPointer; o->pTrampoline = pTrampoline; o->pbTarget = pbTarget; o->dwPerm = dwOld; o->pNext = s_pPendingOperations; s_pPendingOperations = o; return NO_ERROR; }
LONG WINAPI DetourAttachEx(PVOID *ppPointer, PVOID pDetour, PDETOUR_TRAMPOLINE *ppRealTrampoline, PVOID *ppRealTarget, PVOID *ppRealDetour) { LONG error = NO_ERROR; if (ppRealTrampoline != NULL) { *ppRealTrampoline = NULL; } if (ppRealTarget != NULL) { *ppRealTarget = NULL; } if (ppRealDetour != NULL) { *ppRealDetour = NULL; } if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { DETOUR_TRACE(("transaction conflict with thread id=%d\n", s_nPendingThreadId)); return ERROR_INVALID_OPERATION; } // If any of the pending operations failed, then we don't need to do this. if (s_nPendingError != NO_ERROR) { DETOUR_TRACE(("pending transaction error=%d\n", s_nPendingError)); return s_nPendingError; } if (ppPointer == NULL) { DETOUR_TRACE(("ppPointer is null\n")); return ERROR_INVALID_HANDLE; } if (*ppPointer == NULL) { error = ERROR_INVALID_HANDLE; s_nPendingError = error; s_ppPendingError = ppPointer; DETOUR_TRACE(("*ppPointer is null (ppPointer=%p)\n", ppPointer)); DETOUR_BREAK(); return error; } PBYTE pbTarget = (PBYTE)*ppPointer; PDETOUR_TRAMPOLINE pTrampoline = NULL; DetourOperation *o = NULL; #ifdef DETOURS_IA64 #error Feature not supported in this release. #else pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL); pDetour = DetourCodeFromPointer(pDetour, NULL); #endif // Don't follow a jump if its destination is the target function. // This happens when the detour does nothing other than call the target. if (pDetour == (PVOID)pbTarget) { if (s_fIgnoreTooSmall) { goto stop; } else { DETOUR_BREAK(); goto fail; } } if (ppRealTarget != NULL) { *ppRealTarget = pbTarget; } if (ppRealDetour != NULL) { *ppRealDetour = pDetour; } o = new DetourOperation; if (o == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; fail: s_nPendingError = error; DETOUR_BREAK(); stop: if (pTrampoline != NULL) { detour_free_trampoline(pTrampoline); pTrampoline = NULL; } if (o != NULL) { delete o; o = NULL; } s_ppPendingError = ppPointer; return error; } // Mark process as having detoured code. #ifdef DETOURS_INTERNAL_USAGE #error Feature not supported in this release. #else // Detoured(); #endif pTrampoline = detour_alloc_trampoline(pbTarget); if (pTrampoline == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; DETOUR_BREAK(); goto fail; } if (ppRealTrampoline != NULL) { *ppRealTrampoline = pTrampoline; } DETOUR_TRACE(("detours: pbTramp=%p, pDetour=%p\n", pTrampoline, pDetour)); // Determine the number of movable target instructions. PBYTE pbSrc = pbTarget; LONG cbTarget = 0; while (cbTarget < SIZE_OF_JMP) { PBYTE pbOp = pbSrc; LONG lExtra = 0; DETOUR_TRACE((" DetourCopyInstructionEx(%p,%p)\n", pTrampoline->rbCode + cbTarget, pbSrc)); pbSrc = (PBYTE)DetourCopyInstructionEx(pTrampoline->rbCode + cbTarget, pbSrc, NULL, &lExtra); DETOUR_TRACE((" DetourCopyInstructionEx() = %p (%d bytes)\n", pbSrc, (int)(pbSrc - pbOp))); if (lExtra != 0) { break; // Abort if offset doesn't fit. } cbTarget = (LONG)(pbSrc - pbTarget); if (detour_does_code_end_function(pbOp)) { break; } } if (cbTarget < SIZE_OF_JMP) { // Too few instructions. error = ERROR_INVALID_BLOCK; if (s_fIgnoreTooSmall) { goto stop; } else { DETOUR_BREAK(); goto fail; } } #if !defined(DETOURS_IA64) if (cbTarget > sizeof(pTrampoline->rbCode) - SIZE_OF_JMP) { // Too many instructions. error = ERROR_INVALID_HANDLE; DETOUR_BREAK(); goto fail; } #endif pTrampoline->pbRemain = pbTarget + cbTarget; pTrampoline->pbDetour = (PBYTE)pDetour; pTrampoline->cbTarget = (BYTE)cbTarget; #ifdef DETOURS_IA64 #error Feature not supported in this release. #endif // DETOURS_IA64 #ifdef DETOURS_X64 #error Feature not supported in this release. #endif // DETOURS_X64 #ifdef DETOURS_X86 pbSrc = detour_gen_jmp_immediate(pTrampoline->rbCode + cbTarget, pTrampoline->pbRemain); pbSrc = detour_gen_brk(pbSrc, pTrampoline->rbCode + sizeof(pTrampoline->rbCode)); #endif // DETOURS_X86 DWORD dwOld = 0; if (!VirtualProtect(pbTarget, cbTarget, PAGE_EXECUTE_READWRITE, &dwOld)) { error = GetLastError(); DETOUR_BREAK(); goto fail; } DETOUR_TRACE(("detours: pbTarget=%p: " "%02x %02x %02x %02x " "%02x %02x %02x %02x " "%02x %02x %02x %02x\n", pbTarget, pbTarget[0], pbTarget[1], pbTarget[2], pbTarget[3], pbTarget[4], pbTarget[5], pbTarget[6], pbTarget[7], pbTarget[8], pbTarget[9], pbTarget[10], pbTarget[11])); DETOUR_TRACE(("detours: pbTramp =%p: " "%02x %02x %02x %02x " "%02x %02x %02x %02x " "%02x %02x %02x %02x\n", pTrampoline, pTrampoline->rbCode[0], pTrampoline->rbCode[1], pTrampoline->rbCode[2], pTrampoline->rbCode[3], pTrampoline->rbCode[4], pTrampoline->rbCode[5], pTrampoline->rbCode[6], pTrampoline->rbCode[7], pTrampoline->rbCode[8], pTrampoline->rbCode[9], pTrampoline->rbCode[10], pTrampoline->rbCode[11])); /////////////////////////////////////////// Mark binary as being detoured. // PIMAGE_DOS_HEADER pDosHeader = detour_find_header(pbTarget); if (pDosHeader != NULL && pDosHeader->e_res[0] != 'eD') { DWORD dwDos = 0; if (!VirtualProtect(pDosHeader, sizeof(*pDosHeader), PAGE_EXECUTE_READWRITE, &dwDos)) { error = GetLastError(); DETOUR_BREAK(); goto fail; } pDosHeader->e_res[0] = 'eD'; pDosHeader->e_res[1] = 'ot'; pDosHeader->e_res[2] = 'ru'; pDosHeader->e_res[3] = '!s'; } o->fIsRemove = FALSE; o->ppbPointer = (PBYTE*)ppPointer; o->pTrampoline = pTrampoline; o->pbTarget = pbTarget; o->dwPerm = dwOld; o->pNext = s_pPendingOperations; s_pPendingOperations = o; return NO_ERROR; }
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR lpszCmdLine, int nCmdShow) { (void)hprev; (void)hinst; (void)lpszCmdLine; (void)nCmdShow; #ifdef DETOURS_IA64 #error Feature not supported in this release. #endif // DETOURS_IA64 #if defined(DETOURS_X64) || defined(DETOURS_X86) // First we check the pre-canned TestCodes from disasm.asm // PBYTE pbBegin = (PBYTE)DetourCodeFromPointer(TestCodes, NULL); printf("%p:\n", pbBegin); for (PBYTE pbTest = pbBegin;;) { if (pbTest[0] != 0xcc) { // int 3 printf("%08x ", pbTest - pbBegin); DumpMemoryFragment(pbTest, 8, 8); printf("\n"); printf("failed on last.\n"); return 1; } pbTest++; if (pbTest[0] == 0x70 || pbTest[0] == 0x71) { printf("[%p]:\n", pbTest); } BYTE rbDst[128]; PVOID pbDstPool = (PVOID)(rbDst + sizeof(rbDst)); LONG lExtra = 0; PVOID pbTarget = NULL; PBYTE pbNext = (PBYTE)DetourCopyInstruction(rbDst, &pbDstPool, pbTest, &pbTarget, &lExtra); LONG cbTest = (LONG)(pbNext - pbTest); printf("%08x ", pbTest - pbBegin); DumpMemoryFragment(pbTest, cbTest, 12); printf("[%16p] ", pbTarget); DumpMemoryFragment(rbDst, cbTest + lExtra, 11); printf("\n"); if (pbTest[cbTest] != 0xcc) { printf("failed!\n"); return 1; } pbTest += cbTest; if (pbTest[0] == 0xcc && pbTest[1] == 0xcc) { break; } } #if 0 // Then we check all of the code we can find in user32.dll // HINSTANCE hInst = LoadLibrary("user32.dll"); printf("Loaded: user32.dll: %p\n", hInst); s_pbBegin = (PBYTE)hInst; s_pbLimit = s_pbBegin + DetourGetModuleSize(hInst); PBYTE pbEntry = DetourGetEntryPoint(hInst); (VOID) new BasicBlockLink(pbEntry, "user32.dll"); DetourEnumerateExports(hInst, NULL, ExportCallback); ULONG nIns = 0; for (BasicBlockLink *pLink = BasicBlockLink::GetListHead(); pLink; pLink = pLink->Next()) { nIns += TestDetourCopyInstruction(pLink->m_pbEntry, pLink->m_pszName); if (nIns > 100000) { break; } } printf("Disassembled %d instructions.\n", nIns); #endif #endif // DETOURS_X86 || DETOURS_X64 #ifdef DETOURS_ARM #error Feature not supported in this release. #endif // DETOURS_ARM return 0; }
LONG WINAPI DetourAttachEx(PVOID *ppPointer, PVOID pDetour, PDETOUR_TRAMPOLINE *ppRealTrampoline, PVOID *ppRealTarget, PVOID *ppRealDetour) { LONG error = NO_ERROR; if (ppRealTrampoline != NULL) { *ppRealTrampoline = NULL; } if (ppRealTarget != NULL) { *ppRealTarget = NULL; } if (ppRealDetour != NULL) { *ppRealDetour = NULL; } if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { DETOUR_TRACE(("transaction conflict with thread id=%d\n", s_nPendingThreadId)); return ERROR_INVALID_OPERATION; } // If any of the pending operations failed, then we don't need to do this. if (s_nPendingError != NO_ERROR) { DETOUR_TRACE(("pending transaction error=%d\n", s_nPendingError)); return s_nPendingError; } if (ppPointer == NULL) { DETOUR_TRACE(("ppPointer is null\n")); return ERROR_INVALID_HANDLE; } if (*ppPointer == NULL) { error = ERROR_INVALID_HANDLE; s_nPendingError = error; s_ppPendingError = ppPointer; DETOUR_TRACE(("*ppPointer is null (ppPointer=%p)\n", ppPointer)); DETOUR_BREAK(); return error; } PBYTE pbTarget = (PBYTE)*ppPointer; PDETOUR_TRAMPOLINE pTrampoline = NULL; DetourOperation *o = NULL; #ifdef DETOURS_IA64 #error Feature not supported in this release. #else // 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 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; PBYTE pbPool = pbTrampoline + sizeof(pTrampoline->rbCode); ULONG cbTarget = 0; ULONG cbJump = SIZE_OF_JMP; ULONG nAlign = 0; #ifdef DETOURS_ARM #error Feature not supported in this release. #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; 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(); } #if 0 // [GalenH] if (cbTarget < pbTrampoline - pTrampoline->rbCode) { __debugbreak(); } #endif 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 #error Feature not supported in this release. #endif // DETOURS_IA64 pbTrampoline = pTrampoline->rbCode + pTrampoline->cbCode; #ifdef DETOURS_X64 #error Feature not supported in this release. #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 #error Feature not supported in this release. #endif // DETOURS_ARM 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; }