status_t DisassemblerX8664::GetNextInstructionInfo(InstructionInfo& _info, CpuState* state) { unsigned int size = ud_disassemble(fUdisData); if (size < 1) return B_ENTRY_NOT_FOUND; target_addr_t address = ud_insn_off(fUdisData); instruction_type type = INSTRUCTION_TYPE_OTHER; target_addr_t targetAddress = 0; ud_mnemonic_code mnemonic = ud_insn_mnemonic(fUdisData); if (mnemonic == UD_Icall) type = INSTRUCTION_TYPE_SUBROUTINE_CALL; else if (mnemonic == UD_Ijmp) type = INSTRUCTION_TYPE_JUMP; if (state != NULL) targetAddress = GetInstructionTargetAddress(state); char buffer[256]; snprintf(buffer, sizeof(buffer), "0x%016" B_PRIx64 ": %16.16s %s", address, ud_insn_hex(fUdisData), ud_insn_asm(fUdisData)); // TODO: Resolve symbols! if (!_info.SetTo(address, targetAddress, size, type, true, buffer)) return B_NO_MEMORY; return B_OK; }
void LLVMState::show_machine_code(void* buffer, size_t size) { ud_t ud; ud_init(&ud); #ifdef IS_X8664 ud_set_mode(&ud, 64); #else ud_set_mode(&ud, 32); #endif ud_set_syntax(&ud, UD_SYN_ATT); ud_set_input_buffer(&ud, reinterpret_cast<uint8_t*>(buffer), size); while(ud_disassemble(&ud)) { void* address = reinterpret_cast<void*>( reinterpret_cast<uintptr_t>(buffer) + ud_insn_off(&ud)); std::cout << std::setw(10) << std::right << address << " "; std::cout << std::setw(24) << std::left << ud_insn_asm(&ud); if(ud.operand[0].type == UD_OP_JIMM) { const void* addr = (const void*)((uintptr_t)buffer + ud.pc + (int)ud.operand[0].lval.udword); std::cout << " ; " << addr; if(ud.mnemonic == UD_Icall) { Dl_info info; if(dladdr(addr, &info)) { int status = 0; char* cpp_name = abi::__cxa_demangle(info.dli_sname, 0, 0, &status); if(status >= 0) { // Chop off the arg info from the signature output char *paren = strstr(cpp_name, "("); *paren = 0; std::cout << " " << cpp_name; free(cpp_name); } else { std::cout << " " << info.dli_sname; } } } } for(uint8_t i = 0; i < 2; i++) { if(ud.operand[i].type == UD_OP_IMM) { Dl_info info; if(dladdr((void*)ud.operand[i].lval.uqword, &info)) { std::cout << " ; " << info.dli_sname; break; // only do one } } } std::cout << "\n"; } }
int ludis86_ud_insn_hex (lua_State * L) { ud_t * ud_obj; ud_obj = ludis86_check_ud_t(L, 1); lua_pop(L, 1); lua_pushinteger(L, ud_insn_off(ud_obj)); return 1; }
static int check_dynamic_opr(ud_t *udptr) { //obtain the operand of current instruction const ud_operand_t *opr = ud_insn_opr(udptr, 0); if (opr == NULL) { fprintf(stderr, "Bad jmp instruction at address %p.\n", (void *)ud_insn_off(udptr)); exit(1); } //if it is a dynamic operand if (opr->type == UD_OP_MEM || opr->type == UD_OP_PTR || opr->type == UD_OP_REG) return 1; return 0; }
status_t DisassemblerX8664::GetPreviousInstruction(target_addr_t nextAddress, target_addr_t& _address, target_size_t& _size) { if (nextAddress < fAddress || nextAddress > fAddress + fCodeSize) return B_BAD_VALUE; // loop until hitting the last instruction while (true) { target_size_t size = ud_disassemble(fUdisData); if (size < 1) return B_ENTRY_NOT_FOUND; target_addr_t address = ud_insn_off(fUdisData); if (address + size == nextAddress) { _address = address; _size = size; return B_OK; } } }
status_t DisassemblerX8664::GetNextInstruction(BString& line, target_addr_t& _address, target_size_t& _size, bool& _breakpointAllowed) { unsigned int size = ud_disassemble(fUdisData); if (size < 1) return B_ENTRY_NOT_FOUND; target_addr_t address = ud_insn_off(fUdisData); char buffer[256]; snprintf(buffer, sizeof(buffer), "0x%016" B_PRIx64 ": %16.16s %s", address, ud_insn_hex(fUdisData), ud_insn_asm(fUdisData)); // TODO: Resolve symbols! line = buffer; _address = address; _size = size; _breakpointAllowed = true; // TODO: Implement (rep!)! return B_OK; }
void LLVMState::show_machine_code(void* buffer, size_t size) { #if defined(IS_X86) || defined(IS_X8664) #ifndef RBX_WINDOWS ud_t ud; ud_init(&ud); #ifdef IS_64BIT_ARCH ud_set_mode(&ud, 64); #else ud_set_mode(&ud, 32); #endif ud_set_syntax(&ud, UD_SYN_ATT); ud_set_input_buffer(&ud, reinterpret_cast<uint8_t*>(buffer), size); while(ud_disassemble(&ud)) { void* address = reinterpret_cast<void*>( reinterpret_cast<uintptr_t>(buffer) + ud_insn_off(&ud)); llvm::outs() << format("%10p", address) << " "; llvm::outs() << format("%-24s", ud_insn_asm(&ud)); if(ud.operand[0].type == UD_OP_JIMM) { const void* addr = (const void*)((uintptr_t)buffer + ud.pc + (int)ud.operand[0].lval.udword); llvm::outs() << " ; " << addr; if(ud.mnemonic == UD_Icall) { Dl_info info; if(dladdr((void*)addr, &info)) { int status = 0; char* cpp_name = abi::__cxa_demangle(info.dli_sname, 0, 0, &status); if(status >= 0) { // Chop off the arg info from the signature output char *paren = strstr(cpp_name, "("); *paren = 0; llvm::outs() << " " << cpp_name; free(cpp_name); } else { llvm::outs() << " " << info.dli_sname; } } } } for(uint8_t i = 0; i < 2; i++) { if(ud.operand[i].type == UD_OP_IMM) { Dl_info info; if(dladdr((void*)ud.operand[i].lval.uqword, &info)) { llvm::outs() << " ; " << info.dli_sname; break; // only do one } } } llvm::outs() << "\n"; } #endif // !RBX_WINDOWS #else JITDisassembler disassembler(buffer, size); std::string output = disassembler.print_machine_code(); std::cout << output; #endif // !IS_X86 }
virtual void Apply() override { if (applied_) { return; } if (detached_) { HADESMEM_DETAIL_ASSERT(false); return; } // Reset the trampolines here because we don't do it in remove, otherwise // there's a potential race condition where we want to unhook and unload // safely, so we unhook the function, then try waiting on our ref count to // become zero, but we haven't actually called the trampoline yet, so we end // up jumping to the memory we just free'd! trampoline_ = nullptr; trampolines_.clear(); stub_gate_ = nullptr; SuspendedProcess const suspended_process{process_->GetId()}; std::uint32_t const kMaxInstructionLen = 15; std::uint32_t const kTrampSize = kMaxInstructionLen * 3; trampoline_ = std::make_unique<Allocator>(*process_, kTrampSize); auto tramp_cur = static_cast<std::uint8_t*>(trampoline_->GetBase()); auto const detour_raw = detour_.target<DetourFuncRawT>(); if (detour_raw || detour_) { HADESMEM_DETAIL_TRACE_FORMAT_A( "Target = %p, Detour = %p, Trampoline = %p.", target_, detour_raw, trampoline_->GetBase()); } else { HADESMEM_DETAIL_TRACE_FORMAT_A( "Target = %p, Detour = INVALID, Trampoline = %p.", target_, trampoline_->GetBase()); } auto const buffer = ReadVector<std::uint8_t>(*process_, target_, kTrampSize); ud_t ud_obj; ud_init(&ud_obj); ud_set_input_buffer(&ud_obj, buffer.data(), buffer.size()); ud_set_syntax(&ud_obj, UD_SYN_INTEL); ud_set_pc(&ud_obj, reinterpret_cast<std::uint64_t>(target_)); #if defined(HADESMEM_DETAIL_ARCH_X64) ud_set_mode(&ud_obj, 64); #elif defined(HADESMEM_DETAIL_ARCH_X86) ud_set_mode(&ud_obj, 32); #else #error "[HadesMem] Unsupported architecture." #endif stub_gate_ = detail::AllocatePageNear(*process_, target_); std::size_t const patch_size = GetPatchSize(); std::uint32_t instr_size = 0; do { std::uint32_t const len = ud_disassemble(&ud_obj); if (len == 0) { HADESMEM_DETAIL_THROW_EXCEPTION(Error{} << ErrorString{"Disassembly failed."}); } #if !defined(HADESMEM_NO_TRACE) char const* const asm_str = ud_insn_asm(&ud_obj); char const* const asm_bytes_str = ud_insn_hex(&ud_obj); HADESMEM_DETAIL_TRACE_FORMAT_A( "%s. [%s].", (asm_str ? asm_str : "Invalid."), (asm_bytes_str ? asm_bytes_str : "Invalid.")); #endif ud_operand_t const* const op = ud_insn_opr(&ud_obj, 0); bool is_jimm = op && op->type == UD_OP_JIMM; // Handle JMP QWORD PTR [RIP+Rel32]. Necessary for hook chain support. bool is_jmem = op && op->type == UD_OP_MEM && op->base == UD_R_RIP && op->index == UD_NONE && op->scale == 0 && op->size == 0x40; if ((ud_obj.mnemonic == UD_Ijmp || ud_obj.mnemonic == UD_Icall) && op && (is_jimm || is_jmem)) { std::uint16_t const size = is_jimm ? op->size : op->offset; HADESMEM_DETAIL_TRACE_FORMAT_A("Operand/offset size is %hu.", size); std::int64_t const insn_target = [&]() -> std::int64_t { switch (size) { case sizeof(std::int8_t) * CHAR_BIT: return op->lval.sbyte; case sizeof(std::int16_t) * CHAR_BIT: return op->lval.sword; case sizeof(std::int32_t) * CHAR_BIT: return op->lval.sdword; case sizeof(std::int64_t) * CHAR_BIT: return op->lval.sqword; default: HADESMEM_DETAIL_ASSERT(false); HADESMEM_DETAIL_THROW_EXCEPTION( Error{} << ErrorString{"Unknown instruction size."}); } }(); auto const resolve_rel = [](std::uint64_t base, std::int64_t target, std::uint32_t insn_len) { return reinterpret_cast<std::uint8_t*>( static_cast<std::uintptr_t>(base)) + target + insn_len; }; std::uint64_t const insn_base = ud_insn_off(&ud_obj); std::uint32_t const insn_len = ud_insn_len(&ud_obj); auto const resolved_target = resolve_rel(insn_base, insn_target, insn_len); void* const jump_target = is_jimm ? resolved_target : Read<void*>(*process_, resolved_target); HADESMEM_DETAIL_TRACE_FORMAT_A("Jump/call target = %p.", jump_target); if (ud_obj.mnemonic == UD_Ijmp) { HADESMEM_DETAIL_TRACE_A("Writing resolved jump."); tramp_cur += detail::WriteJump( *process_, tramp_cur, jump_target, true, &trampolines_); } else { HADESMEM_DETAIL_ASSERT(ud_obj.mnemonic == UD_Icall); HADESMEM_DETAIL_TRACE_A("Writing resolved call."); tramp_cur += detail::WriteCall(*process_, tramp_cur, jump_target, trampolines_); } } else { std::uint8_t const* const raw = ud_insn_ptr(&ud_obj); Write(*process_, tramp_cur, raw, raw + len); tramp_cur += len; } instr_size += len; } while (instr_size < patch_size); HADESMEM_DETAIL_TRACE_A("Writing jump back to original code."); tramp_cur += detail::WriteJump(*process_, tramp_cur, reinterpret_cast<std::uint8_t*>(target_) + instr_size, true, &trampolines_); FlushInstructionCache( *process_, trampoline_->GetBase(), trampoline_->GetSize()); detail::WriteStubGate<TargetFuncT>(*process_, stub_gate_->GetBase(), &*stub_, &GetOriginalArbitraryUserPtrPtr); orig_ = ReadVector<std::uint8_t>(*process_, target_, patch_size); detail::VerifyPatchThreads(process_->GetId(), target_, orig_.size()); WritePatch(); FlushInstructionCache(*process_, target_, instr_size); applied_ = true; }
target_addr_t DisassemblerX8664::GetInstructionTargetAddress(CpuState* state) const { ud_mnemonic_code mnemonic = ud_insn_mnemonic(fUdisData); if (mnemonic != UD_Icall && mnemonic != UD_Ijmp) return 0; CpuStateX8664* x64State = dynamic_cast<CpuStateX8664*>(state); if (x64State == NULL) return 0; target_addr_t targetAddress = 0; const struct ud_operand* op = ud_insn_opr(fUdisData, 0); switch (op->type) { case UD_OP_REG: { targetAddress = x64State->IntRegisterValue( RegisterNumberFromUdisIndex(op->base)); targetAddress += op->offset; } break; case UD_OP_MEM: { targetAddress = x64State->IntRegisterValue( RegisterNumberFromUdisIndex(op->base)); targetAddress += x64State->IntRegisterValue( RegisterNumberFromUdisIndex(op->index)) * op->scale; off_t offset = 0; switch (op->offset) { case 8: offset = op->lval.sbyte; break; case 16: offset = op->lval.sword; break; case 32: offset = op->lval.sdword; break; case 64: offset = op->lval.sqword; break; } targetAddress += offset; } break; case UD_OP_JIMM: { targetAddress = ud_insn_off(fUdisData) + ud_insn_len(fUdisData); if (op->size == 32) targetAddress += op->lval.sdword; else targetAddress += op->lval.sqword; } break; case UD_OP_IMM: case UD_OP_CONST: { if (op->size == 32) targetAddress = op->lval.udword; else if (op->size == 64) targetAddress = op->lval.uqword; } break; default: break; } return targetAddress; }
void lp_disassemble(const void* func) { #ifdef HAVE_UDIS86 ud_t ud_obj; uint64_t max_jmp_pc; uint inst_no; boolean emit_addrs = TRUE, emit_line_nos = FALSE; ud_init(&ud_obj); ud_set_input_buffer(&ud_obj, (void*)func, 0xffff); max_jmp_pc = (uint64_t) (uintptr_t) func; ud_set_pc(&ud_obj, max_jmp_pc); #ifdef PIPE_ARCH_X86 ud_set_mode(&ud_obj, 32); #endif #ifdef PIPE_ARCH_X86_64 ud_set_mode(&ud_obj, 64); #endif ud_set_syntax(&ud_obj, UD_SYN_ATT); while (ud_disassemble(&ud_obj)) { if (emit_addrs) { #ifdef PIPE_ARCH_X86 debug_printf("0x%08lx:\t", (unsigned long)ud_insn_off(&ud_obj)); #endif #ifdef PIPE_ARCH_X86_64 debug_printf("0x%016llx:\t", (unsigned long long)ud_insn_off(&ud_obj)); #endif } else if (emit_line_nos) { debug_printf("%6d:\t", inst_no); inst_no++; } #if 0 debug_printf("%-16s ", ud_insn_hex(&ud_obj)); #endif debug_printf("%s\n", ud_insn_asm(&ud_obj)); if(ud_obj.mnemonic != UD_Icall) { unsigned i; for(i = 0; i < 3; ++i) { const struct ud_operand *op = &ud_obj.operand[i]; if (op->type == UD_OP_JIMM){ uint64_t pc = ud_obj.pc; switch (op->size) { case 8: pc += op->lval.sbyte; break; case 16: pc += op->lval.sword; break; case 32: pc += op->lval.sdword; break; default: break; } if(pc > max_jmp_pc) max_jmp_pc = pc; } } } if (ud_obj.mnemonic == UD_Iinvalid || (ud_insn_off(&ud_obj) >= max_jmp_pc && (ud_obj.mnemonic == UD_Iret || ud_obj.mnemonic == UD_Ijmp))) break; } #if 0 /* Print GDB command, useful to verify udis86 output */ debug_printf("disassemble %p %p\n", func, (void*)(uintptr_t)ud_obj.pc); #endif debug_printf("\n"); #else (void)func; #endif }