int main(int argc, char **argv) { printf("VBox Disassembler Test\n"); if (argc != 1) { //printf("DisasmBlock on printf:\n"); //DisasmBlock((uint8_t *)printf, 256); } else { RTUINTPTR pInstr = (uintptr_t)TestProc; for (int i=0;i<50;i++) { unsigned cb; DISCPUSTATE cpu; char szOutput[256]; memset(&cpu, 0, sizeof(cpu)); cpu.mode = CPUMODE_32BIT; if (RT_SUCCESS(DISInstr(&cpu, pInstr, 0, &cb, szOutput))) { printf("%s", szOutput); } else { printf("DISOne failed!\n"); return 1; } pInstr += cb; } #ifndef RT_OS_OS2 printf("\n64 bits disassembly\n"); pInstr = (uintptr_t)TestProc64; ////__debugbreak(); for (int i=0;i<50;i++) { unsigned cb; DISCPUSTATE cpu; char szOutput[256]; memset(&cpu, 0, sizeof(cpu)); cpu.mode = CPUMODE_64BIT; if (RT_SUCCESS(DISInstr(&cpu, pInstr, 0, &cb, szOutput))) printf("%s", szOutput); else { printf("DISOne failed!\n"); return 1; } pInstr += cb; } #endif } return 0; }
extern "C" DECLEXPORT(void *) SomeExportFunction4(void) { static unsigned cb; DISCPUSTATE Cpu; memset(&Cpu, 0, sizeof(Cpu)); DISInstr((void *)(uintptr_t)SomeExportFunction3, DISCPUMODE_32BIT, &Cpu, &cb); return (void *)(uintptr_t)&SomeExportFunction1; }
bool MyDisBlock(PDISCPUSTATE pCpu, RTHCUINTPTR pvCodeBlock, int32_t cbMax, RTUINTPTR off) { int32_t i = 0; while (i < cbMax) { char szOutput[256]; uint32_t cbInstr; if (RT_FAILURE(DISInstr(pCpu, pvCodeBlock + i, off, &cbInstr, szOutput))) return false; RTPrintf("%s", szOutput); /* next */ i += cbInstr; } return true; }
/** * Disassembles a code block. * * @returns VBox error code * @param pCpu Pointer to cpu structure which have DISCPUSTATE::mode * set correctly. * @param pvCodeBlock Pointer to the structure to disassemble. * @param cbMax Maximum number of bytes to disassemble. * @param pcbSize Where to store the size of the instruction. * NULL is allowed. * * * @todo Define output callback. * @todo Using signed integers as sizes is a bit odd. There are still * some GCC warnings about mixing signed and unsigned integers. * @todo Need to extend this interface to include a code address so we * can disassemble GC code. Perhaps a new function is better... * @remark cbMax isn't respected as a boundary. DISInstr() will read beyond cbMax. * This means *pcbSize >= cbMax sometimes. */ DISDECL(int) DISBlock(PDISCPUSTATE pCpu, RTUINTPTR pvCodeBlock, unsigned cbMax, unsigned *pSize) { unsigned i = 0; char szOutput[256]; while (i < cbMax) { unsigned cbInstr; int rc = DISInstr(pCpu, pvCodeBlock + i, 0, &cbInstr, szOutput); if (RT_FAILURE(rc)) return rc; i += cbInstr; } if (pSize) *pSize = i; return true; }
/** * Disassembles a block of memory. * * @returns VBox status code. * @param argv0 Program name (for errors and warnings). * @param enmCpuMode The cpu mode to disassemble in. * @param uAddress The address we're starting to disassemble at. * @param uHighlightAddr The address of the instruction that should be * highlighted. Pass UINT64_MAX to keep quiet. * @param pbFile Where to start disassemble. * @param cbFile How much to disassemble. * @param enmStyle The assembly output style. * @param fListing Whether to print in a listing like mode. * @param enmUndefOp How to deal with undefined opcodes. */ static int MyDisasmBlock(const char *argv0, DISCPUMODE enmCpuMode, uint64_t uAddress, uint64_t uHighlightAddr, uint8_t *pbFile, size_t cbFile, ASMSTYLE enmStyle, bool fListing, UNDEFOPHANDLING enmUndefOp) { /* * Initialize the CPU context. */ MYDISSTATE State; State.Cpu.mode = enmCpuMode; State.Cpu.pfnReadBytes = MyDisasInstrRead; State.uAddress = uAddress; State.pbInstr = pbFile; State.cbInstr = 0; State.enmUndefOp = enmUndefOp; State.rc = VINF_SUCCESS; State.cbLeft = cbFile; State.pbNext = pbFile; State.uNextAddr = uAddress; void (*pfnFormatter)(PMYDISSTATE pState); switch (enmStyle) { case kAsmStyle_Default: pfnFormatter = MyDisasDefaultFormatter; break; case kAsmStyle_yasm: RTPrintf(" BITS %d\n", enmCpuMode == CPUMODE_16BIT ? 16 : enmCpuMode == CPUMODE_32BIT ? 32 : 64); pfnFormatter = MyDisasYasmFormatter; break; case kAsmStyle_masm: pfnFormatter = MyDisasMasmFormatter; break; default: AssertFailedReturn(VERR_INTERNAL_ERROR); } /* * The loop. */ int rcRet = VINF_SUCCESS; while (State.cbLeft > 0) { /* * Disassemble it. */ State.cbInstr = 0; State.cbLeft += State.pbNext - State.pbInstr; State.uNextAddr = State.uAddress; State.pbNext = State.pbInstr; int rc = DISInstr(&State.Cpu, State.uAddress, 0, &State.cbInstr, State.szLine); if ( RT_SUCCESS(rc) || ( ( rc == VERR_DIS_INVALID_OPCODE || rc == VERR_DIS_GEN_FAILURE) && State.enmUndefOp == kUndefOp_DefineByte)) { State.fUndefOp = rc == VERR_DIS_INVALID_OPCODE || rc == VERR_DIS_GEN_FAILURE || State.Cpu.pCurInstr->opcode == OP_INVALID || State.Cpu.pCurInstr->opcode == OP_ILLUD2 || ( State.enmUndefOp == kUndefOp_DefineByte && !MyDisasIsValidInstruction(&State.Cpu)); if (State.fUndefOp && State.enmUndefOp == kUndefOp_DefineByte) { RTPrintf(" db"); if (!State.cbInstr) State.cbInstr = 1; for (unsigned off = 0; off < State.cbInstr; off++) { uint8_t b; State.Cpu.pfnReadBytes(State.uAddress + off, &b, 1, &State.Cpu); RTPrintf(off ? ", %03xh" : " %03xh", b); } RTPrintf(" ; %s\n", State.szLine); } else if (!State.fUndefOp && State.enmUndefOp == kUndefOp_All) { RTPrintf("%s: error at %#RX64: unexpected valid instruction (op=%d)\n", argv0, State.uAddress, State.Cpu.pCurInstr->opcode); pfnFormatter(&State); rcRet = VERR_GENERAL_FAILURE; } else if (State.fUndefOp && State.enmUndefOp == kUndefOp_Fail) { RTPrintf("%s: error at %#RX64: undefined opcode (op=%d)\n", argv0, State.uAddress, State.Cpu.pCurInstr->opcode); pfnFormatter(&State); rcRet = VERR_GENERAL_FAILURE; } else { /* Use db for odd encodings that we can't make the assembler use. */ if ( State.enmUndefOp == kUndefOp_DefineByte && MyDisasYasmFormatterIsOddEncoding(&State)) { RTPrintf(" db"); for (unsigned off = 0; off < State.cbInstr; off++) { uint8_t b; State.Cpu.pfnReadBytes(State.uAddress + off, &b, 1, &State.Cpu); RTPrintf(off ? ", %03xh" : " %03xh", b); } RTPrintf(" ; "); } pfnFormatter(&State); } } else { State.cbInstr = State.pbNext - State.pbInstr; if (!State.cbLeft) RTPrintf("%s: error at %#RX64: read beyond the end (%Rrc)\n", argv0, State.uAddress, rc); else if (State.cbInstr) RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d\n", argv0, State.uAddress, rc, State.cbInstr); else { RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d!\n", argv0, State.uAddress, rc, State.cbInstr); if (rcRet == VINF_SUCCESS) rcRet = rc; break; } } /* Highlight this instruction? */ if (uHighlightAddr - State.uAddress < State.cbInstr) RTPrintf("; ^^^^^^^^^^^^^^^^^^^^^\n"); /* next */ State.uAddress += State.cbInstr; State.pbInstr += State.cbInstr; } return rcRet; }
static void rtMemReplaceMallocAndFriends(void) { struct { const char *pszName; PFNRT pfnReplacement; PFNRT pfnOrg; PFNRT *ppfnJumpBack; } aApis[] = { { "free", (PFNRT)rtMemReplacementFree, (PFNRT)free, (PFNRT *)&g_pfnOrgFree }, { "realloc", (PFNRT)rtMemReplacementRealloc, (PFNRT)realloc, (PFNRT *)&g_pfnOrgRealloc }, { "calloc", (PFNRT)rtMemReplacementCalloc, (PFNRT)calloc, (PFNRT *)&g_pfnOrgCalloc }, { "malloc", (PFNRT)rtMemReplacementMalloc, (PFNRT)malloc, (PFNRT *)&g_pfnOrgMalloc }, #ifdef RT_OS_DARWIN { "malloc_size", (PFNRT)rtMemReplacementMallocSize, (PFNRT)malloc_size, (PFNRT *)&g_pfnOrgMallocSize }, #endif }; /* * Initialize the jump backs to avoid recursivly entering this function. */ for (unsigned i = 0; i < RT_ELEMENTS(aApis); i++) *aApis[i].ppfnJumpBack = aApis[i].pfnOrg; /* * Give the user an option to skip replacing malloc. */ if (getenv("IPRT_DONT_REPLACE_MALLOC")) return; /* * Allocate a page for jump back code (we leak it). */ uint8_t *pbExecPage = (uint8_t *)RTMemPageAlloc(PAGE_SIZE); AssertFatal(pbExecPage); int rc = RTMemProtect(pbExecPage, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC); AssertFatalRC(rc); /* * Do the ground work. */ uint8_t *pb = pbExecPage; for (unsigned i = 0; i < RT_ELEMENTS(aApis); i++) { /* Resolve it. */ PFNRT pfnOrg = (PFNRT)(uintptr_t)dlsym(RTLD_DEFAULT, aApis[i].pszName); if (pfnOrg) aApis[i].pfnOrg = pfnOrg; else pfnOrg = aApis[i].pfnOrg; /* Figure what we can replace and how much to duplicate in the jump back code. */ # ifdef RT_ARCH_AMD64 uint32_t cbNeeded = 12; DISCPUMODE const enmCpuMode = DISCPUMODE_64BIT; # elif defined(RT_ARCH_X86) uint32_t const cbNeeded = 5; DISCPUMODE const enmCpuMode = DISCPUMODE_32BIT; # else # error "Port me" # endif uint32_t offJmpBack = 0; uint32_t cbCopy = 0; while (offJmpBack < cbNeeded) { DISCPUSTATE Dis; uint32_t cbInstr = 1; rc = DISInstr((void *)((uintptr_t)pfnOrg + offJmpBack), enmCpuMode, &Dis, &cbInstr); AssertFatalRC(rc); AssertFatal(!(Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW))); # ifdef RT_ARCH_AMD64 # ifdef RT_OS_DARWIN /* Kludge for: cmp [malloc_def_zone_state], 1; jg 2; call _malloc_initialize; 2: */ DISQPVPARAMVAL Parm; if ( Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */ && (Dis.Param2.fUse & (DISUSE_IMMEDIATE16_SX8 | DISUSE_IMMEDIATE32_SX8 | DISUSE_IMMEDIATE64_SX8)) && Dis.Param2.uValue == 1 && Dis.pCurInstr->uOpcode == OP_CMP) { cbCopy = offJmpBack; offJmpBack += cbInstr; rc = DISInstr((void *)((uintptr_t)pfnOrg + offJmpBack), enmCpuMode, &Dis, &cbInstr); AssertFatalRC(rc); if ( Dis.pCurInstr->uOpcode == OP_JNBE && Dis.Param1.uDisp.i8 == 5) { offJmpBack += cbInstr + 5; AssertFatal(offJmpBack >= cbNeeded); break; } } # endif AssertFatal(!(Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */)); # endif offJmpBack += cbInstr; } if (!cbCopy) cbCopy = offJmpBack; /* Assemble the jump back. */ memcpy(pb, (void *)(uintptr_t)pfnOrg, cbCopy); uint32_t off = cbCopy; # ifdef RT_ARCH_AMD64 pb[off++] = 0xff; /* jmp qword [$+8 wrt RIP] */ pb[off++] = 0x25; *(uint32_t *)&pb[off] = 0; off += 4; *(uint64_t *)&pb[off] = (uintptr_t)pfnOrg + offJmpBack; off += 8; off = RT_ALIGN_32(off, 16); # elif defined(RT_ARCH_X86) pb[off++] = 0xe9; /* jmp rel32 */ *(uint32_t *)&pb[off] = (uintptr_t)pfnOrg + offJmpBack - (uintptr_t)&pb[4]; off += 4; off = RT_ALIGN_32(off, 8); # else # error "Port me" # endif *aApis[i].ppfnJumpBack = (PFNRT)(uintptr_t)pb; pb += off; } /* * Modify the APIs. */ for (unsigned i = 0; i < RT_ELEMENTS(aApis); i++) { pb = (uint8_t *)(uintptr_t)aApis[i].pfnOrg; rc = RTMemProtect(pb, 16, RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC); AssertFatalRC(rc); # ifdef RT_ARCH_AMD64 /* Assemble the LdrLoadDll patch. */ *pb++ = 0x48; /* mov rax, qword */ *pb++ = 0xb8; *(uint64_t *)pb = (uintptr_t)aApis[i].pfnReplacement; pb += 8; *pb++ = 0xff; /* jmp rax */ *pb++ = 0xe0; # elif defined(RT_ARCH_X86) *pb++ = 0xe9; /* jmp rel32 */ *(uint32_t *)pb = (uintptr_t)aApis[i].pfnReplacement - (uintptr_t)&pb[4]; # else # error "Port me" # endif } }