From ca642d0c8b0a3dd5f768749b58ba66ac35472610 Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Sat, 16 Jun 2018 15:36:39 +0200 Subject: Fixes a bug with calculating memory offsets and exposes some more registers. --- emulate/emulator.hpp | 393 ++++++++++++++++----------------------------------- 1 file changed, 118 insertions(+), 275 deletions(-) (limited to 'emulate/emulator.hpp') diff --git a/emulate/emulator.hpp b/emulate/emulator.hpp index 1d26f99..ce0e1c6 100644 --- a/emulate/emulator.hpp +++ b/emulate/emulator.hpp @@ -14,27 +14,25 @@ #include template - struct SignedCounterpart {}; - - template <> - struct SignedCounterpart { - typedef int32_t type; - }; - - template <> - struct SignedCounterpart { - typedef int16_t type; - }; - - template <> - struct SignedCounterpart { - typedef int8_t type; +struct SignedCounterpart {}; + +template <> +struct SignedCounterpart; + +template <> +struct SignedCounterpart; + +template <> +struct SignedCounterpart; + +struct LDTEntry { + uint32_t offset; }; class Emulator { public: CpuState cpu; - std::vector memory; + std::vector& memory; std::map> opcode_handlers; @@ -44,22 +42,9 @@ public: std::map> interrupt_handlers; - ValueType extract_immediate_of_size(int size, _DInst inst) { - switch(size) { - case 8: { - return inst.imm.byte; - } - case 16: { - return inst.imm.word; - } - case 32: { - return inst.imm.dword; - } - default: { - throw UnhandledInstruction(); - } - } - } + std::map ldt; + + ValueType extract_immediate_of_size(int size, _DInst inst); struct print : public boost::static_visitor { template @@ -82,105 +67,21 @@ public: } }; + struct to_target_type : public boost::static_visitor { + template + ValueType operator()(Dest const& d, Src const& s) const { + typename SignedCounterpart::type ss(s); + typename SignedCounterpart::type sd(ss); + return static_cast(sd); + } + }; + struct IncompatibleArguments : public std::runtime_error { IncompatibleArguments() : std::runtime_error("The two arguments to this instruction are not compatible") {} }; - template - struct op : public boost::static_visitor { - CpuState& cpu; - - op(CpuState& cpu) - : cpu(cpu) - {} - }; - - template - struct binary_op : public op { - using op::op; - - template - Ret operator()(T&, U const&) const { - throw IncompatibleArguments(); - } - }; - - struct and_ : public binary_op { - using binary_op::operator(); - using binary_op::binary_op; - - template - void operator()(T& lh, T const& rh) const { - lh &= rh; - } - }; - - struct mov : public binary_op { - using binary_op::operator(); - using binary_op::binary_op; - - template - void operator()(T& lh, T const& rh) const { - lh = rh; - } - }; - - struct sub : public binary_op { - using binary_op::operator(); - using binary_op::binary_op; - - template - void operator()(T& lh, T const& rh) const { - typedef typename SignedCounterpart::type S; - auto& slh = reinterpret_cast(lh); - auto const& srh = reinterpret_cast(rh); - - cpu.of() = ((srh < 0) && (slh > std::numeric_limits::max() + srh)) || ((rh > 0) && (slh < std::numeric_limits::min() + srh)); - cpu.af() = !(lh & 8) && (rh & 8); - cpu.cf() = !(static_cast(std::numeric_limits::min()) & lh) && (static_cast(std::numeric_limits::min()) & rh); - - lh -= rh; - - cpu.sf() = static_cast(std::numeric_limits::min()) & lh; - cpu.zf() = lh == 0; - cpu.pf() = ((lh & 1) + (lh >> 1 & 1) + (lh >> 2 & 1) + (lh >> 3 & 1) + (lh >> 4 & 1) + (lh >> 5 & 1) + (lh >> 6 & 1) + (lh >> 7 & 1) % 2) + 1; - } - }; - - struct shr : public binary_op { - using binary_op::operator(); - using binary_op::binary_op; - - template - void operator()(T& lh, T const& rh) const { - lh >>= rh; - } - }; - - struct cmp : public binary_op { - using binary_op::operator(); - using binary_op::binary_op; - - template - void operator()(T& lh, T const& rh) const { - typedef typename SignedCounterpart::type S; - auto& slh = reinterpret_cast(lh); - auto const& srh = reinterpret_cast(rh); - - cpu.of() = ((srh < 0) && (slh > std::numeric_limits::max() + srh)) || ((rh > 0) && (slh < std::numeric_limits::min() + srh)); - cpu.af() = !(lh & 8) && (rh & 8); - cpu.cf() = !(static_cast(std::numeric_limits::min()) & lh) && (static_cast(std::numeric_limits::min()) & rh); - - T result = lh - rh; - - cpu.sf() = static_cast(std::numeric_limits::min()) & result; - cpu.zf() = result == 0; - cpu.pf() = ((result & 1) + (result >> 1 & 1) + (result >> 2 & 1) + (result >> 3 & 1) + (result >> 4 & 1) + (result >> 5 & 1) + (result >> 6 & 1) + (result >> 7 & 1) % 2) + 1; - } - }; - template void handle_binary(_DInst inst) { std::unique_ptr dest; @@ -199,21 +100,47 @@ public: break; } case O_SMEM: { - throw UnhandledInstruction(); + auto segment = register_getters.at(SEGMENT_GET(inst.segment))(); + auto segment_offset = ldt[boost::apply_visitor(return_visitor(), segment) >> 3].offset; + auto register_contents = register_getters.at(dest_op.index)(); + int32_t offset = boost::apply_visitor(return_visitor(), register_contents) + inst.disp; + std::cout << segment_offset << std::endl << offset << std::endl << inst.disp << std::endl; + std::cout << segment_offset + offset + inst.disp << std::endl; + auto memory_offset = segment_offset + offset + inst.disp; + switch(dest_op.size) { + case 8: { + dest = std::make_unique(*reinterpret_cast(&memory[memory_offset])); + break; + } + case 16: { + dest = std::make_unique(*reinterpret_cast(&memory[memory_offset])); + break; + } + case 32: { + dest = std::make_unique(*reinterpret_cast(&memory[memory_offset])); + break; + } + default: { + throw UnhandledInstruction(); + } + } + break; } case O_DISP: { + auto segment = register_getters.at(SEGMENT_GET(inst.segment))(); + auto segment_offset = ldt[boost::apply_visitor(return_visitor(), segment) >> 3].offset; switch(dest_op.size) { case 8: { - dest = std::make_unique(*reinterpret_cast(&memory[inst.disp])); + dest = std::make_unique(*reinterpret_cast(&memory[segment_offset + inst.disp])); break; } case 16: { - dest = std::make_unique(*reinterpret_cast(&memory[inst.disp])); + dest = std::make_unique(*reinterpret_cast(&memory[segment_offset + inst.disp])); break; } case 32: { - dest = std::make_unique(*reinterpret_cast(&memory[inst.disp])); + dest = std::make_unique(*reinterpret_cast(&memory[segment_offset + inst.disp])); break; } default: { @@ -234,7 +161,7 @@ public: switch(src_op.type) { case O_REG: { auto x = register_getters.at(src_op.index)(); - src = register_getters.at(src_op.index)(); + src = boost::apply_visitor(to_target_type(), *dest, x); break; } case O_IMM: { @@ -242,6 +169,43 @@ public: src = extract_immediate_of_size(boost::apply_visitor(dest_size(), *dest), inst); break; } + case O_DISP: { + auto segment = register_getters.at(SEGMENT_GET(inst.segment))(); + auto segment_offset = ldt[boost::apply_visitor(return_visitor(), segment) >> 3].offset; + switch(boost::apply_visitor(dest_size(), *dest)) { + case 8: { + if(segment_offset + inst.disp > memory.size()) { + std::cerr << "Warning reading outside of memory region" << std::endl; + src = static_cast(0); + } else { + src = *reinterpret_cast(&memory[segment_offset + inst.disp]); + } + break; + } + case 16: { + if(segment_offset + inst.disp + 1 > memory.size()) { + std::cerr << "Warning reading outside of memory region" << std::endl; + src = static_cast(0); + } else { + src = *reinterpret_cast(&memory[segment_offset + inst.disp]); + } + break; + } + case 32: { + if(segment_offset + inst.disp + 3 > memory.size()) { + std::cerr << "Warning reading outside of memory region" << std::endl; + src = static_cast(0); + } else { + src = *reinterpret_cast(&memory[segment_offset + inst.disp]); + } + break; + } + default: { + throw UnhandledInstruction(); + } + } + break; + } default: { throw UnhandledInstruction(); } @@ -251,110 +215,31 @@ public: boost::apply_visitor(O(cpu), *dest, src); } - void handle_I_JMP(_DInst inst) { - if(inst.ops[0].type == O_PC || inst.ops[0].type == O_PTR || inst.ops[0].type == O_DISP) { - binparse::Offset32 target = binparse::Offset32(INSTRUCTION_GET_TARGET(&inst)); - cpu.eip() = target; - } else if (inst.ops[0].type == O_SMEM) { - throw UnhandledInstruction(); - } else { - throw UnrecognizedInstruction(); - } - } - void handle_I_STI(_DInst) { - cpu.intf() = 1; - } - - void handle_I_AND(_DInst inst) { - handle_binary(inst); - } - - void handle_I_MOV(_DInst inst) { - handle_binary(inst); - } - - void handle_I_SUB(_DInst inst) { - handle_binary(inst); - } - - void handle_I_SHR(_DInst inst) { - handle_binary(inst); - } - - void handle_I_CMP(_DInst inst) { - handle_binary(inst); - } - - void handle_I_INT(_DInst inst) { - _Operand int_op = inst.ops[0]; - - ValueType imm = extract_immediate_of_size(int_op.size, inst); - - interrupt_handlers.at(boost::apply_visitor(return_visitor(), imm))(); - } - - void handle_I_JNZ(_DInst inst) { - if(!cpu.zf()) { - if(inst.ops[0].type == O_PC || inst.ops[0].type == O_PTR || inst.ops[0].type == O_DISP) { - cpu.eip() = INSTRUCTION_GET_TARGET(&inst); - } else if (inst.ops[0].type == O_SMEM) { - throw UnhandledInstruction(); - } else { - throw UnrecognizedInstruction(); - } - } - } - - void int_0x21() { - if(cpu.ah() == 0x30) { - cpu.al() = 6; - cpu.ah() = 0; - cpu.bh() = 0; - } - } - + uint32_t get_jmp_target(_DInst inst); + + void handle_I_JMP (_DInst inst); + void handle_I_STI (_DInst); + void handle_I_AND (_DInst inst); + void handle_I_MOV (_DInst inst); + void handle_I_SUB (_DInst inst); + void handle_I_SHR (_DInst inst); + void handle_I_CMP (_DInst inst); + void handle_I_INT (_DInst inst); + void handle_I_JNZ (_DInst inst); + void handle_I_JZ (_DInst inst); + void handle_I_JG (_DInst inst); + void handle_I_OR (_DInst inst); + void handle_I_ADD (_DInst inst); + void handle_I_PUSH(_DInst inst); + + void int_0x21(); + void int_0x31(); + public: - Emulator(binparse::Offset32 init_eip, binparse::Offset32 init_esp, std::vector memory) - : cpu() - , memory(memory) - { - cpu.eip() = init_eip; - cpu.esp() = init_esp; + Emulator(binparse::Offset32 init_eip, binparse::Offset32 init_esp, std::vector& memory); - #define REGISTER_HANDLER(OPCODE) opcode_handlers[OPCODE] = std::bind(&Emulator::handle_##OPCODE, this, std::placeholders::_1) - - REGISTER_HANDLER(I_JMP); - REGISTER_HANDLER(I_STI); - REGISTER_HANDLER(I_AND); - REGISTER_HANDLER(I_MOV); - REGISTER_HANDLER(I_SUB); - REGISTER_HANDLER(I_INT); - REGISTER_HANDLER(I_SHR); - REGISTER_HANDLER(I_CMP); - REGISTER_HANDLER(I_JNZ); - - #undef REGISTER_HANDLER - - #define REGISTER_GETTER(REGISTER, R_NAME) register_getters[REGISTER] = [this]{return RefValueType(cpu.R_NAME());} - - REGISTER_GETTER(R_ESP, esp); - REGISTER_GETTER(R_AL , al ); - REGISTER_GETTER(R_AH , ah ); - REGISTER_GETTER(R_AX , ax ); - REGISTER_GETTER(R_EAX, eax); - REGISTER_GETTER(R_EBX, ebx); - REGISTER_GETTER(R_ECX, ecx); - REGISTER_GETTER(R_ESI, esi); - REGISTER_GETTER(R_EDI, edi); - - #undef REGISTER_GETTER - - #define REGISTER_INTERRUPT_HANDLER(INT) interrupt_handlers[INT] = [this]{return int_##INT();} - - REGISTER_INTERRUPT_HANDLER(0x21); - - #undef REGISTER_INTERRUPT_HANDLER - } + void set_code_segment(binparse::Offset32 offset); + void set_data_segment(binparse::Offset32 offset); struct UnhandledInstruction : public std::runtime_error { UnhandledInstruction() @@ -374,46 +259,4 @@ public: }; -void emulate(std::string file_path) { - std::ifstream file_stream(file_path, std::ios::binary); - file_stream.unsetf(std::ios::skipws); - auto file = le::parse_file(file_stream); - auto binary = load_binary(file); - - _CodeInfo ci; - _DecodeType dt = Decode32Bits; - - auto code_object = file.object_table.entries.at(file.le_header.EIP_object); - auto initial_eip = code_object.reloc_base_address + file.le_header.EIP; - - auto data_object = file.object_table.entries.at(file.le_header.ESP_object); - auto initial_esp = data_object.reloc_base_address + file.le_header.ESP; - - Emulator emulator(initial_eip, initial_esp, binary); - - unsigned int decodedInstructionsCount; - - bool run = true; - while(run) { - ci.code = binary.data() + emulator.cpu.eip(); - ci.nextOffset = emulator.cpu.eip(); - ci.codeLen = binary.size() - emulator.cpu.eip(); - ci.codeOffset = emulator.cpu.eip(); - ci.dt = dt; - ci.features = DF_NONE; - - _DInst decinst; - distorm_decompose(&ci, &decinst, 1, &decodedInstructionsCount); - - _DecodedInst inst; - distorm_format64(&ci, &decinst, &inst); - - std::cout << emulator.cpu << std::endl; - std::cout << "CurrentInstruction: " << std::hex << std::setw(8) << std::setfill('0') << inst.offset << ":\t" << inst.mnemonic.p << " " << inst.operands.p << std::endl; - std::cout << std::endl << std::endl; - - emulator.cpu.eip() += decinst.size; - - emulator.handle_instruction(decinst); - } -} +void emulate(std::string file_path); -- cgit v1.2.3-70-g09d2