///////////////////////////////////////////////////////////////////////////// // // Module: disas.cpp (disas.exe - Detours Test Program) // // Microsoft Research Detours Package // // Copyright (c) Microsoft Corporation. All rights reserved. // #define DETOURS_INTERNAL #include #include #include ///////////////////////////////////////////////////////////////////////// ARM. // #ifdef DETOURS_ARM extern "C" BYTE TestCodes[]; void DumpMemoryFragment(PBYTE pbData, ULONG cbData, ULONG cbSpace) { ULONG n = 0; if (cbData >= 4) { printf("%04x%04x ", ((PUSHORT)pbData)[0], ((PUSHORT)pbData)[1]); n += 4; } else if (cbData >= 2) { printf("%04x ", *((PUSHORT)pbData)); n += 2; } for (; n < cbSpace; n++) { if (n < cbData) { printf("%02x", pbData[n]); } else { printf(" "); } } if (n < cbData) { printf("."); } else { printf(" "); } } inline ULONG fetch_thumb_opcode(PBYTE pbCode) { ULONG Opcode = *(UINT16 *)&pbCode[0]; if (Opcode >= 0xe800) { Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2]; } return Opcode; } BOOL IsTerminate(PBYTE pbSrc) { ULONG opcode = fetch_thumb_opcode(pbSrc); if ((opcode & 0xff87) == 0x4700) { // bx r return TRUE; } #if 0 if ((opcode & 0xfbf08f00) == 0xf2400c00) { // movw r12,#xxxx return TRUE; } if ((opcode == 0xf8dcf000) { // ldr pc,[r12] ULONG Immediate = ((opcode2 << 12) & 0xf7000000) | ((opcode2 << 1) & 0x08000000) | ((opcode2 << 16) & 0x00ff0000) | ((opcode >> 4) & 0x0000f700) | ((opcode >> 15) & 0x00000800) | ((opcode >> 0) & 0x000000ff); PBYTE pbTarget = *(PBYTE *)Immediate; if (detour_is_imported(pbCode, pbTarget)) { PBYTE pbNew = *(PBYTE *)pbTarget; DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); return pbNew; } } } } #endif return FALSE; } #endif // DETOURS_ARM ///////////////////////////////////////////////////////////////// X86 and X64. // #if defined(DETOURS_X86) || defined(DETOURS_X64) extern "C" BYTE TestCodes[]; void DumpMemoryFragment(PBYTE pbData, ULONG cbData, ULONG cbSpace) { ULONG n = 0; for (; n < cbSpace; n++) { if (n < cbData) { printf("%02x", pbData[n]); } else { printf(" "); } } if (n < cbData) { printf("."); } else { printf(" "); } } BOOL IsTerminate(PBYTE pbSrc) { if ((0xC3 == pbSrc[0] && 0x00 == pbSrc[1]) || // bx lr 0xCB == pbSrc[0] || // RETF 0xC2 == pbSrc[0] || // RET dw 0xCA == pbSrc[0] || // RETF dw 0xEB == pbSrc[0] || // JMP ob 0xE9 == pbSrc[0] || // JMP ol 0xEA == pbSrc[0]) { // JMP ol return TRUE; } if (0xff == pbSrc[0] && 0x25 == pbSrc[1]) // JMP [addr] return TRUE; return FALSE; } #endif // DETOURS_X86 || DETOURS_X64 /////////////////////////////////////////////////////////// X86, X64, and ARM. // #if defined(DETOURS_X86) || defined(DETOURS_X64) || defined(DETOURS_ARM) struct BasicBlockLink { public: BasicBlockLink * m_pNext; PBYTE m_pbEntry; PCHAR m_pszName; public: BasicBlockLink(PBYTE pbEntry, PCHAR pszName = NULL) { m_pNext = NULL; m_pbEntry = pbEntry; m_pszName = pszName; *s_ppTail = this; s_ppTail = &m_pNext; } BasicBlockLink * Next() { return m_pNext; } static BasicBlockLink * GetListHead() { return s_pHead; } protected: static BasicBlockLink * s_pHead; static BasicBlockLink ** s_ppTail; }; BasicBlockLink * BasicBlockLink::s_pHead = NULL; BasicBlockLink ** BasicBlockLink::s_ppTail = &BasicBlockLink::s_pHead; static PBYTE s_pbBegin = NULL; static PBYTE s_pbLimit = NULL; int TestDetourCopyInstruction(PBYTE pbSrcInstruction, PCHAR pszFunction) { PBYTE pbSrc = pbSrcInstruction; ULONG nIns = 0; if (pszFunction) { printf("%s:\n", pszFunction); } for (; nIns < 4096; nIns++) { BYTE rbDst[128]; PVOID pbDstPool = (PVOID)(rbDst + sizeof(rbDst)); LONG lExtra = 0; PVOID pbTarget = NULL; ULONG cbStep = (ULONG)((PBYTE)DetourCopyInstruction(rbDst, &pbDstPool, pbSrc, &pbTarget, &lExtra) - pbSrc); printf(" %p:", pbSrc); DumpMemoryFragment(rbDst, cbStep, 10); printf(" "); DumpMemoryFragment(rbDst, cbStep, 10); if (pbTarget) { if (pbTarget == DETOUR_INSTRUCTION_TARGET_DYNAMIC) { printf(" Dynamic\n"); } else { printf(" %p%c\n", pbTarget, (pbTarget >= s_pbBegin && pbTarget < s_pbLimit) ? ' ' : '!'); } } else { printf("\n"); } if (pbTarget && pbTarget != DETOUR_INSTRUCTION_TARGET_DYNAMIC) { if (pbTarget > pbSrc && pbTarget >= s_pbBegin && pbTarget < s_pbLimit ) { (void) new BasicBlockLink((PBYTE)pbTarget, NULL); } } if (IsTerminate(pbSrc)) { break; } pbSrc += cbStep; } return nIns; } BOOL CALLBACK ExportCallback(_In_opt_ PVOID pContext, _In_ ULONG nOrdinal, _In_opt_ LPCSTR pszName, _In_opt_ PVOID pCode) { (void)pContext; (void)nOrdinal; (void)pCode; (VOID) new BasicBlockLink((PBYTE)pCode, pszName ? pszName : "[NO NAME]"); return TRUE; } #endif // DETOURS_X86 || DETOURS_X64 //////////////////////////////////////////////////////////////////////// IA64. // #ifdef DETOURS_IA64 #pragma warning(disable: 4201) // ignore warning about unnamed sturcture in union. void DumpHi(PBYTE pbData, ULONG cbData, ULONG cbSpace) { ULONG n = 0; for (; n < cbSpace; n++) { if (n < cbData) { printf("%02x", pbData[(cbData - 1) - n]); } else { printf(" "); } } printf("\n"); } struct DETOUR_IA64_BUNDLE_DISASSEMBLE : public DETOUR_IA64_BUNDLE { public: void SetBrx(UINT64 raw) { SetBrl(); SetBrlImm(raw); } void Dis() { const char szUnitNames[17] = "?aimbflx?AIMBFLX"; printf("%p: ", data); BYTE nTemplate = GetTemplate(); BYTE nInst0 = GetInst0(); BYTE nInst1 = GetInst1(); BYTE nInst2 = GetInst2(); BYTE nUnit0 = GetUnit0(); BYTE nUnit1 = GetUnit1(); BYTE nUnit2 = GetUnit2(); if (nUnit1 == L_UNIT) { // MLX instruction UINT64 d2 = ( // 0x0000000000fffff0 ((wide[1] & 0x00fffff000000000) >> 32) | // 0x000000ffff000000 ((wide[0] & 0xffff000000000000) >> 24) | // 0x7fffff0000000000 ((wide[1] & 0x00000000007fffff) << 40) | // 0x8000000000000000 ((wide[1] & 0x0800000000000000) << 4) ); printf("%02x %c%01x %010I64lx %c%01x %016I64lx", nTemplate, szUnitNames[nUnit0], nInst0, GetData0(), szUnitNames[nUnit2], nInst2, d2); } else { printf("%02x %c%01x %010I64lx %c%01x %010I64lx %c%01x %010I64lx", nTemplate, szUnitNames[nUnit0], nInst0, GetData0(), szUnitNames[nUnit1], nInst1, GetData1(), szUnitNames[nUnit2], nInst2, GetData2()); } if (IsBrl()) { printf(" brl %p", GetBrlTarget()); } else if (IsMovlGp()) { printf(" movl gp=%p", GetMovlGp()); } if ((wide[0] & 0xfffffc000603ffff) == 0x002024000200100b && wide[1] == 0x0004000000203008) { ULONG64 offset = ((wide[0] & 0x0000000001fc0000) >> 18) | // imm7b ((wide[0] & 0x000001ff00000000) >> 25) | // imm9d ((wide[0] & 0x00000000f8000000) >> 11); // imm5c if (wide[0] & 0x0000020000000000) { offset |= 0xffffffffffe00000; } printf(" imm=%016I64lx", offset); } printf("\n"); } }; ////////////////////////////////////////////////////////////////////////////// // BOOL CALLBACK ExportCallbackIA64(_In_opt_ PVOID pContext, _In_ ULONG nOrdinal, _In_opt_ LPCSTR pszName, _In_opt_ PVOID pCode) { (void)pContext; (void)nOrdinal; DETOUR_IA64_BUNDLE_DISASSEMBLE *pb = *(DETOUR_IA64_BUNDLE_DISASSEMBLE **)pCode; DETOUR_IA64_BUNDLE temp; if (!pb[0].Copy(&temp)) { printf("%s:\n ", pszName ? pszName : "[NO NAME]"); pb[0].Dis(); } return TRUE; } #if 0 void TestBoth() { LPVOID pvBase = VirtualAlloc((PBYTE)0x800000000, 0x10000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); DETOUR_IA64_BUNDLE *pbBase = (DETOUR_IA64_BUNDLE *)pvBase; DETOUR_IA64_BUNDLE *pb = pbBase; printf("TestBoth:\n"); for (UINT64 i = 0x10; i < 0x8000000000000000; i <<= 1) { pb->SetMovlGp(i); if (pb->GetMovlGp() != i) { printf("Error in MovlGp!\n"); return; } pb++; pb->SetBrl(i); if (pb->GetBrlEip() != i) { printf("Error in Brl!\n"); return; } pb++; } for (UINT64 i = (UINT64)(INT64)-0x10; i > 0; i <<= 1) { pb->SetMovlGp(i); if (pb->GetMovlGp() != i) { printf("Error in MovlGp!\n"); return; } pb++; pb->SetBrl(i); if (pb->GetBrlEip() != i) { printf("Error in Brl!\n"); return; } pb++; } printf("u %p %p\n", pbBase, pb); } #endif #endif // DETOURS_IA64 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR lpszCmdLine, int nCmdShow) { (void)hprev; (void)hinst; (void)lpszCmdLine; (void)nCmdShow; // Bug report, but it works here. // 07ff8`4b783054 49ba 70b3d93a d40fb998 mov r10,98B90FD43AD9B370h // { static const UCHAR mov_r10_imm64[] = {0x49, 0xba, 1, 2, 3, 4, 5, 6, 7, 8 }; PVOID const after = DetourCopyInstructionX64(0, 0, const_cast(mov_r10_imm64), 0, 0); if (after != &mov_r10_imm64 + 1) { printf("mov_r10_imm64 failed, expected:%p vs. got:%p\n", &mov_r10_imm64 + 1, after); if (IsDebuggerPresent()) { __debugbreak(); DetourCopyInstructionX64(0, 0, const_cast(mov_r10_imm64), 0, 0); } return 1; } } #ifdef DETOURS_IA64 // First we check the pre-canned TestCodes from disasm.asm // PBYTE pbTest = *(PBYTE*)WinMain; for (;; pbTest += 16) { DETOUR_IA64_BUNDLE_DISASSEMBLE *pb = (DETOUR_IA64_BUNDLE_DISASSEMBLE *)pbTest; pb->Dis(); if (pbTest[0] == 0xff) { break; } DumpHi(pbTest, 16, 16); } #if 0 printf("\n\n"); DETOUR_IA64_BUNDLE_DISASSEMBLE *pb = (DETOUR_IA64_BUNDLE_DISASSEMBLE *)pbTest; DETOUR_IA64_BUNDLE_DISASSEMBLE *pbBeg = pb; DWORD dwOld; VirtualProtect(pb, 0x2000, PAGE_EXECUTE_READWRITE, &dwOld); printf("%p: (%d)\n", pb, sizeof(pb)); pb++; printf("%p: (%d)\n", pb, sizeof(pb)); pb++; pb->SetBrx(0); pb++; pb->SetBrx(0); pb++; pb->SetBrx(0); pb++; pb->SetBrx(0xffffffffffffffff); pb++; pb->SetBrx(0x0fffffffffffffff); pb++; pb->SetBrx(0x00ffffffffffffff); pb++; pb->SetBrx(0x000fffffffffffff); pb++; pb->SetBrx(0x0000ffffffffffff); pb++; pb->SetBrx(0x00000fffffffffff); pb++; pb->SetBrx(0x000000ffffffffff); pb++; pb->SetBrx(0x0000000fffffffff); pb++; pb->SetBrx(0x00000000ffffffff); pb++; pb->SetBrx(0x000000000fffffff); pb++; pb->SetBrx(0x0000000000ffffff); pb++; pb->SetBrx(0x00000000000fffff); pb++; pb->SetBrx(0x000000000000ffff); pb++; pb->SetBrx(0x0000000000000fff); pb++; pb->SetBrx(0x00000000000000ff); pb++; pb->SetBrx(0x000000000000000f); pb++; pb->SetBrx(0x0000000000000000); pb++; pb->SetBrx(0xffffffffffffffff); pb++; pb->SetBrx(0xffffffffffffffff); pb->SetInst0(0xff); pb->SetData0(0xffffffffffffffff); printf("%p:\n", pb); DETOUR_IA64_BUNDLE_DISASSEMBLE *pbEnd = pb; for (pb = pbBeg; pb < pbEnd; pb++) { printf(" %p: ", pb); DumpHi((BYTE*)pb, 16, 16); } #endif #if 1 { // Then we check all of the code we can find in user32.dll // printf("\n"); HINSTANCE hInst = LoadLibraryA("user32.dll"); printf("Loaded: user32.dll: %p\n", hInst); PBYTE pbEntry = (PBYTE)DetourGetEntryPoint(hInst); printf("Entry: %p\n", pbEntry); ExportCallbackIA64(NULL, 0, "[Entry]", pbEntry); DetourEnumerateExports(hInst, NULL, ExportCallbackIA64); } { // Then we check all of the code we can find in opengl32.dll // printf("\n"); HINSTANCE hInst = LoadLibraryA("opengl32.dll"); printf("Loaded: opengl32.dll: %p\n", hInst); PBYTE pbEntry = (PBYTE)DetourGetEntryPoint(hInst); printf("Entry: %p\n", pbEntry); ExportCallbackIA64(NULL, 0, "[Entry]", pbEntry); DetourEnumerateExports(hInst, NULL, ExportCallbackIA64); } printf("\n"); for (HINSTANCE hInst = NULL; (hInst = DetourEnumerateModules(hInst)) != NULL;) { CHAR szModuleName[512]; GetModuleFileNameA(hInst, szModuleName, sizeof(szModuleName)/sizeof(szModuleName[0])); printf("%p : %s\n", hInst, szModuleName); DetourEnumerateExports(hInst, NULL, ExportCallbackIA64); } printf("\n"); #endif #if 0 TestBoth(); #endif #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("%08lx ", (ULONG)(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("%08lx ", (ULONG)(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 = LoadLibraryA("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 // Create an output buffer and fill it with debugbreaks. // PBYTE pbBuffer = (PBYTE)VirtualAlloc(NULL, 0x400, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); for (PBYTE pbOut = pbBuffer; pbOut < pbBuffer + 0x400;) { *pbOut++ = 0xfe; *pbOut++ = 0xde; } PBYTE pbDst = pbBuffer; PVOID pvDstPool = (PVOID)(pbBuffer + 0x400); // First we check the pre-canned TestCodes from disasm.asm // PBYTE pbBegin = (PBYTE)DetourCodeFromPointer(TestCodes, NULL); printf("%p: (TestCodes %p) => %p\n", pbBegin, TestCodes, pbBuffer); for (PBYTE pbSrc = pbBegin;;) { if (pbSrc[0] != 0xfe && pbSrc[1] != 0xde) { // BREAK printf("%08x ", pbSrc - pbBegin); DumpMemoryFragment(pbSrc, 8, 8); printf("\n"); printf("failed on last.\n"); return 1; } pbSrc += 2; *pbDst++ = 0xfe; *pbDst++ = 0xde; if ((pbSrc[0] == 0x00 && pbSrc[1] == 0xbf) && // NOP (pbSrc[2] != 0xfe && pbSrc[3] != 0xde)) { // BREAK // Skip over a single NOP so we can test alignment. pbSrc += 2; } if ((pbSrc[0] == 0x00 && pbSrc[1] == 0xbf) && // NOP (pbSrc[2] != 0xfe && pbSrc[3] != 0xde)) { // BREAK // If there is a second NOP, then we insert alignment. pbSrc += 2; *pbDst++ = 0x00; *pbDst++ = 0xbf; } LONG lExtra = 0; PVOID pbTarget = NULL; PBYTE pbNext = (PBYTE)DetourCopyInstruction(pbDst, &pvDstPool, pbSrc, &pbTarget, &lExtra); LONG cbTest = (LONG)(pbNext - pbSrc); printf("%08x ", pbSrc - pbBegin); DumpMemoryFragment(pbSrc, cbTest, 4); printf("[%8p] ", pbTarget); DumpMemoryFragment(pbDst, cbTest + lExtra, 16); printf("\n"); if (pbSrc[cbTest] != 0xfe || pbSrc[cbTest+1] != 0xde) { printf("%p: failed! (pbSrc[n]=%02x, pbSrc[n+1]=%02x\n", pbSrc, pbSrc[cbTest], pbSrc[cbTest+1]); __debugbreak(); pbNext = (PBYTE)DetourCopyInstruction(pbDst, &pvDstPool, pbSrc, &pbTarget, &lExtra); cbTest = (LONG)(pbNext - pbSrc); return 1; } pbDst += cbTest + lExtra; pbSrc += cbTest; if (pbSrc[0] == 0xfe && pbSrc[1] == 0xde && pbSrc[2] == 0xfe && pbSrc[3] == 0xde) { break; } } #if 0 // Then we check all of the code we can find in user32.dll // HINSTANCE hInst = LoadLibraryA("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_ARM return 0; } // ///////////////////////////////////////////////////////////////// End of File.