/* * Copyright (c) 2020 Trail of Bits, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "Instruction.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wdocumentation" #pragma clang diagnostic ignored "-Wswitch-enum" #include #include #include #include #include #include #include #include #pragma clang diagnostic pop #include #include #include #include #include #include #include "mcsema/Arch/Arch.h" #include "mcsema/BC/Callback.h" #include "mcsema/BC/Lift.h" #include "mcsema/BC/Util.h" #include "mcsema/CFG/CFG.h" namespace mcsema { InstructionLifter::~InstructionLifter(void) {} InstructionLifter::InstructionLifter(const remill::IntrinsicTable *intrinsics_, TranslationContext &ctx_) : remill::InstructionLifter(gArch.get(), intrinsics_), ctx(ctx_) {} // Lift a single instruction into a basic block. remill::LiftStatus InstructionLifter::LiftIntoBlock(remill::Instruction &inst, llvm::BasicBlock *block_, llvm::Value *state_ptr, bool is_delayed) { inst_ptr = &inst; block = block_; if (ctx.cfg_inst) { mem_ref = GetAddress(ctx.cfg_inst->mem); imm_ref = GetAddress(ctx.cfg_inst->imm); disp_ref = GetAddress(ctx.cfg_inst->disp); } else { mem_ref = nullptr; imm_ref = nullptr; disp_ref = nullptr; } mem_ref_used = false; disp_ref_used = false; imm_ref_used = false; auto status = this->remill::InstructionLifter::LiftIntoBlock( inst, block, state_ptr, is_delayed); // If we have semantics for the instruction, then make sure that we were // able to match cross-reference information to the instruction's operands. if (remill::kLiftedInstruction == status) { if (mem_ref && !mem_ref_used) { LOG(ERROR) << "Unused memory reference operand to " << std::hex << ctx.cfg_inst->mem->target_ea << " in instruction " << inst.Serialize() << std::dec; } if (imm_ref && !imm_ref_used) { LOG(ERROR) << "Unused immediate operand reference to " << std::hex << ctx.cfg_inst->imm->target_ea << " in instruction " << inst.Serialize() << std::dec; } if (disp_ref && !disp_ref_used) { LOG(ERROR) << "Unused displacement operand reference to " << std::hex << ctx.cfg_inst->disp->target_ea << " in instruction " << inst.Serialize() << std::dec; } } return status; } llvm::Value * InstructionLifter::GetAddress(const NativeInstructionXref *cfg_xref) { if (!cfg_xref) { return nullptr; } if (cfg_xref->mask) { return LiftXrefInCode(cfg_xref->target_ea & cfg_xref->mask); } else { return LiftXrefInCode(cfg_xref->target_ea); } } llvm::Value *InstructionLifter::LiftImmediateOperand(remill::Instruction &inst, llvm::BasicBlock *block, llvm::Argument *arg, remill::Operand &op) { auto arg_type = arg->getType(); if (imm_ref && !imm_ref_used) { imm_ref_used = true; llvm::DataLayout data_layout(gModule.get()); auto arg_size = data_layout.getTypeSizeInBits(arg_type); CHECK(arg_size <= gArch->address_size) << "Immediate operand size " << op.size << " of " << op.Serialize() << " in instuction " << std::hex << inst.pc << " is wider than the architecture pointer size (" << std::dec << gArch->address_size << ")."; if (arg_type != imm_ref->getType() && arg_size < gArch->address_size) { llvm::IRBuilder<> ir(block); imm_ref = ir.CreateTrunc(imm_ref, arg_type); } return imm_ref; } else if (op.size == gArch->address_size && 4096 <= op.imm.val) { auto seg = ctx.cfg_module->TryGetSegment(op.imm.val); LOG_IF(WARNING, seg != nullptr) << "Immediate operand '" << op.Serialize() << "' of instruction " << inst.Serialize() << " is a missed cross-reference candidate"; } return this->remill::InstructionLifter::LiftImmediateOperand(inst, block, arg, op); } // Lift an indirect memory operand to a value. llvm::Value *InstructionLifter::LiftAddressOperand(remill::Instruction &inst, llvm::BasicBlock *block, llvm::Value *state_ptr, llvm::Argument *arg, remill::Operand &op) { auto &mem = op.addr; // // A higher layer will resolve any code refs; this is a static address and // // we want to preserve it in the register state structure. // if (mem.IsControlFlowTarget()) { // return this->remill::InstructionLifter::LiftAddressOperand( // inst, block, state_ptr, arg, op); // } if ((mem.base_reg.name.empty() && mem.index_reg.name.empty()) || (mem.base_reg.name == "PC" && mem.index_reg.name.empty())) { if (mem_ref) { mem_ref_used = true; return mem_ref; } else if (disp_ref) { disp_ref_used = true; return disp_ref; } } else if ((mem.base_reg.name.empty() && mem.index_reg.name.empty()) || (mem.base_reg.name == "NEXT_PC" && mem.index_reg.name.empty())) { if (mem_ref) { mem_ref_used = true; return mem_ref; } else if (disp_ref) { disp_ref_used = true; return disp_ref; } } else { // It's a reference located in the displacement. We'll clear out the // displacement, calculate the address operand stuff, then add the address // of the external back in. E.g. `mov rax, [extern_jump_table + rdi]`. if (disp_ref) { disp_ref_used = true; mem.displacement = 0; auto dynamic_addr = this->remill::InstructionLifter::LiftAddressOperand( inst, block, state_ptr, arg, op); llvm::IRBuilder<> ir(block); return ir.CreateAdd(dynamic_addr, disp_ref); } else if (mem_ref && (static_cast(op.addr.displacement) == ctx.cfg_inst->mem->target_ea)) { LOG(ERROR) << "IDA probably incorrectly decoded memory operand " << op.Serialize() << " of instruction " << std::hex << inst.pc << " as an absolute memory reference when it should be treated as a " << "displacement memory reference."; mem_ref_used = true; mem.displacement = 0; auto dynamic_addr = this->remill::InstructionLifter::LiftAddressOperand( inst, block, state_ptr, arg, op); llvm::IRBuilder<> ir(block); return ir.CreateAdd(dynamic_addr, mem_ref); } } return this->remill::InstructionLifter::LiftAddressOperand( inst, block, state_ptr, arg, op); } } // namespace mcsema