int ludis86_ud_insn_ptr (lua_State * L) { ud_t * ud_obj; ud_obj = ludis86_check_ud_t(L, 1); lua_pop(L, 1); lua_pushlstring(L, ud_insn_ptr(ud_obj), ud_insn_len(ud_obj)); return 1; }
/* ============================================================================= * ud_insn_oct() - Returns octal form of disassembled instruction. * ============================================================================= */ const char* ud_insn_oct(struct ud* u) { u->insn_octcode[0] = 0; if (!u->error) { unsigned int i; const unsigned char *src_ptr = ud_insn_ptr(u); char* src_oct; src_oct = (char*) u->insn_octcode; /* for each byte used to decode instruction */ for (i = 0; i < ud_insn_len(u) && i < sizeof(u->insn_octcode) / 4; ++i, ++src_ptr) { sprintf(src_oct, (i > 0 ? " %03o" : "%03o"), *src_ptr & 0xFF); src_oct += (i > 0 ? 4 : 3); } } return u->insn_octcode; }
/* ============================================================================= * ud_insn_hex() - Returns hex form of disassembled instruction. * ============================================================================= */ const char* ud_insn_hex(struct ud* u) { u->insn_hexcode[0] = 0; if (!u->error) { unsigned int i; const unsigned char *src_ptr = ud_insn_ptr(u); char* src_hex; src_hex = (char*) u->insn_hexcode; /* for each byte used to decode instruction */ for (i = 0; i < ud_insn_len(u) && i < sizeof(u->insn_hexcode) / 2; ++i, ++src_ptr) { sprintf(src_hex, "%02x", *src_ptr & 0xFF); src_hex += 2; } } return u->insn_hexcode; }
struct _ins * redis_x86_create_ins (struct _redis_x86 * redis_x86) { ud_t ud_obj; ud_init(&ud_obj); ud_set_mode(&ud_obj, 32); ud_set_syntax(&ud_obj, UD_SYN_INTEL); ud_set_input_buffer(&ud_obj, redis_x86->ins_bytes, redis_x86->ins_size); if (ud_disassemble(&ud_obj) == 0) { fprintf(stderr, "disassembly error %p %d\n", redis_x86->ins_bytes, (int) redis_x86->ins_size); return NULL; } struct _ins * ins = ins_create(redis_x86->ins_addr, ud_insn_ptr(&ud_obj), ud_insn_len(&ud_obj), ud_insn_asm(&ud_obj), NULL); return ins; }
struct _ins * x8664_ins (uint64_t address, ud_t * ud_obj) { struct _ins * ins; ins = ins_create(address, ud_insn_ptr(ud_obj), ud_insn_len(ud_obj), ud_insn_asm(ud_obj), NULL); if (udis86_target(address, &(ud_obj->operand[0])) != -1) { char * mnemonic_str = NULL; switch (ud_obj->mnemonic) { case UD_Ijo : mnemonic_str = "jo"; break; case UD_Ijno : mnemonic_str = "jno"; break; case UD_Ijb : mnemonic_str = "jb"; break; case UD_Ijae : mnemonic_str = "jae"; break; case UD_Ijz : mnemonic_str = "jz"; break; case UD_Ijnz : mnemonic_str = "jnz"; break; case UD_Ijbe : mnemonic_str = "jbe"; break; case UD_Ija : mnemonic_str = "ja"; break; case UD_Ijs : mnemonic_str = "js"; break; case UD_Ijns : mnemonic_str = "jns"; break; case UD_Ijp : mnemonic_str = "jp"; break; case UD_Ijnp : mnemonic_str = "jnp"; break; case UD_Ijl : mnemonic_str = "jl"; break; case UD_Ijge : mnemonic_str = "jge"; break; case UD_Ijle : mnemonic_str = "jle"; break; case UD_Ijg : mnemonic_str = "jg"; break; case UD_Ijmp : mnemonic_str = "jmp"; break; case UD_Iloop : mnemonic_str = "loop"; break; case UD_Icall : mnemonic_str = "call"; break; default : break; } uint64_t destination; destination = ud_insn_len(ud_obj); destination += udis86_target(address, &(ud_obj->operand[0])); if (mnemonic_str != NULL) { char tmp[64]; snprintf(tmp, 64, "%s %llx", mnemonic_str, (unsigned long long) destination); ins_s_description(ins, tmp); ins_s_target(ins, destination); } else { struct _reference * reference; reference = reference_create(REFERENCE_STORE, address, destination); ins_add_reference(ins, reference); object_delete(reference); } } else if (udis86_target(address, &(ud_obj->operand[1])) != -1) { uint64_t destination; destination = ud_insn_len(ud_obj); destination += udis86_target(address, &(ud_obj->operand[1])); struct _reference * reference; reference = reference_create(REFERENCE_LOAD, address, destination); ins_add_reference(ins, reference); object_delete(reference); } else if ( (ud_obj->operand[1].type == UD_OP_IMM) && (ud_obj->operand[1].size >= 32)) { int64_t tmp = udis86_sign_extend_lval(&(ud_obj->operand[1])); if (tmp > 0x1000) { struct _reference * reference; reference = reference_create(REFERENCE_CONSTANT, address, udis86_sign_extend_lval(&(ud_obj->operand[1]))); ins_add_reference(ins, reference); object_delete(reference); } } if (ud_obj->mnemonic == UD_Icall) ins_s_call(ins); return ins; }
struct _ins * x86_disassemble_ins_ (const struct _map * mem_map, const uint64_t address, uint8_t mode) { struct _buffer * buf = map_fetch_max(mem_map, address); uint64_t buf_addr = map_fetch_max_key(mem_map, address); if (buf == NULL) return NULL; size_t offset = address - buf_addr; ud_t ud_obj; ud_init(&ud_obj); ud_set_mode(&ud_obj, mode); ud_set_syntax(&ud_obj, UD_SYN_INTEL); ud_set_input_buffer(&ud_obj, &(buf->bytes[offset]), buf->size - offset); if (ud_disassemble(&ud_obj) == 0) return NULL; struct _ins * ins = ins_create(address, ud_insn_ptr(&ud_obj), ud_insn_len(&ud_obj), ud_insn_asm(&ud_obj), NULL); switch (ud_obj.mnemonic) { case UD_Ijo : case UD_Ijno : case UD_Ijb : case UD_Ijae : case UD_Ijz : case UD_Ijnz : case UD_Ijbe : case UD_Ija : case UD_Ijs : case UD_Ijns : case UD_Ijp : case UD_Ijnp : case UD_Ijl : case UD_Ijge : case UD_Ijle : case UD_Ijg : case UD_Iloop : ins_add_successor(ins, address + ud_insn_len(&ud_obj), INS_SUC_JCC_FALSE); if (ud_obj.operand[0].type == UD_OP_JIMM) { ins_add_successor(ins, address + ud_insn_len(&ud_obj) + x86_sign_extend_lval(&(ud_obj.operand[0])), INS_SUC_JCC_TRUE); } break; case UD_Ijmp : if (ud_obj.operand[0].type == UD_OP_JIMM) { ins_add_successor(ins, address + ud_insn_len(&ud_obj) + x86_sign_extend_lval(&(ud_obj.operand[0])), INS_SUC_JUMP); } break; case UD_Icall : ins_add_successor(ins, address + ud_insn_len(&ud_obj), INS_SUC_NORMAL); if (ud_obj.operand[0].type == UD_OP_JIMM) { ins_add_successor(ins, address + ud_insn_len(&ud_obj) + x86_sign_extend_lval(&(ud_obj.operand[0])), INS_SUC_CALL); } break; case UD_Iret : case UD_Ihlt : break; default : ins_add_successor(ins, address + ud_insn_len(&ud_obj), INS_SUC_NORMAL); } return ins; }
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; }