diff options
| author | Dennis Brentjes <d.brentjes@gmail.com> | 2018-06-16 15:36:39 +0200 |
|---|---|---|
| committer | Dennis Brentjes <d.brentjes@gmail.com> | 2018-06-16 15:36:39 +0200 |
| commit | ca642d0c8b0a3dd5f768749b58ba66ac35472610 (patch) | |
| tree | 33eb95aee362703eb424fdb93c381acf7111876b /emulate/emulator.hpp | |
| parent | 44320ada80b08ecf88caf114b2b0be8c8e08e505 (diff) | |
| download | openwar-ca642d0c8b0a3dd5f768749b58ba66ac35472610.tar.gz openwar-ca642d0c8b0a3dd5f768749b58ba66ac35472610.tar.bz2 openwar-ca642d0c8b0a3dd5f768749b58ba66ac35472610.zip | |
Fixes a bug with calculating memory offsets and exposes some more registers.
Diffstat (limited to 'emulate/emulator.hpp')
| -rw-r--r-- | emulate/emulator.hpp | 393 |
1 files changed, 118 insertions, 275 deletions
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 <limits> template <typename T> - struct SignedCounterpart {}; - - template <> - struct SignedCounterpart<uint32_t> { - typedef int32_t type; - }; - - template <> - struct SignedCounterpart<uint16_t> { - typedef int16_t type; - }; - - template <> - struct SignedCounterpart<uint8_t> { - typedef int8_t type; +struct SignedCounterpart {}; + +template <> +struct SignedCounterpart<uint32_t>; + +template <> +struct SignedCounterpart<uint16_t>; + +template <> +struct SignedCounterpart<uint8_t>; + +struct LDTEntry { + uint32_t offset; }; class Emulator { public: CpuState cpu; - std::vector<uint8_t> memory; + std::vector<uint8_t>& memory; std::map<int, std::function<void(_DInst)>> opcode_handlers; @@ -44,22 +42,9 @@ public: std::map<int, std::function<void()>> 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<int, LDTEntry> ldt; + + ValueType extract_immediate_of_size(int size, _DInst inst); struct print : public boost::static_visitor<std::ostream&> { template <typename T> @@ -82,105 +67,21 @@ public: } }; + struct to_target_type : public boost::static_visitor<ValueType> { + template <typename Dest, typename Src> + ValueType operator()(Dest const& d, Src const& s) const { + typename SignedCounterpart<Src>::type ss(s); + typename SignedCounterpart<Dest>::type sd(ss); + return static_cast<Dest>(sd); + } + }; + struct IncompatibleArguments : public std::runtime_error { IncompatibleArguments() : std::runtime_error("The two arguments to this instruction are not compatible") {} }; - template <typename Ret> - struct op : public boost::static_visitor<Ret> { - CpuState& cpu; - - op(CpuState& cpu) - : cpu(cpu) - {} - }; - - template <typename Ret> - struct binary_op : public op<Ret> { - using op<Ret>::op; - - template <typename T, typename U> - Ret operator()(T&, U const&) const { - throw IncompatibleArguments(); - } - }; - - struct and_ : public binary_op<void> { - using binary_op::operator(); - using binary_op<void>::binary_op; - - template<typename T> - void operator()(T& lh, T const& rh) const { - lh &= rh; - } - }; - - struct mov : public binary_op<void> { - using binary_op::operator(); - using binary_op<void>::binary_op; - - template<typename T> - void operator()(T& lh, T const& rh) const { - lh = rh; - } - }; - - struct sub : public binary_op<void> { - using binary_op::operator(); - using binary_op<void>::binary_op; - - template<typename T> - void operator()(T& lh, T const& rh) const { - typedef typename SignedCounterpart<T>::type S; - auto& slh = reinterpret_cast<S&>(lh); - auto const& srh = reinterpret_cast<S const&>(rh); - - cpu.of() = ((srh < 0) && (slh > std::numeric_limits<S>::max() + srh)) || ((rh > 0) && (slh < std::numeric_limits<S>::min() + srh)); - cpu.af() = !(lh & 8) && (rh & 8); - cpu.cf() = !(static_cast<T>(std::numeric_limits<S>::min()) & lh) && (static_cast<T>(std::numeric_limits<S>::min()) & rh); - - lh -= rh; - - cpu.sf() = static_cast<T>(std::numeric_limits<S>::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<void> { - using binary_op::operator(); - using binary_op<void>::binary_op; - - template <typename T> - void operator()(T& lh, T const& rh) const { - lh >>= rh; - } - }; - - struct cmp : public binary_op<void> { - using binary_op::operator(); - using binary_op<void>::binary_op; - - template<typename T> - void operator()(T& lh, T const& rh) const { - typedef typename SignedCounterpart<T>::type S; - auto& slh = reinterpret_cast<S&>(lh); - auto const& srh = reinterpret_cast<S const&>(rh); - - cpu.of() = ((srh < 0) && (slh > std::numeric_limits<S>::max() + srh)) || ((rh > 0) && (slh < std::numeric_limits<S>::min() + srh)); - cpu.af() = !(lh & 8) && (rh & 8); - cpu.cf() = !(static_cast<T>(std::numeric_limits<S>::min()) & lh) && (static_cast<T>(std::numeric_limits<S>::min()) & rh); - - T result = lh - rh; - - cpu.sf() = static_cast<T>(std::numeric_limits<S>::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 <typename O> void handle_binary(_DInst inst) { std::unique_ptr<RefValueType> 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<RefValueType>(*reinterpret_cast<uint8_t*>(&memory[memory_offset])); + break; + } + case 16: { + dest = std::make_unique<RefValueType>(*reinterpret_cast<uint16_t*>(&memory[memory_offset])); + break; + } + case 32: { + dest = std::make_unique<RefValueType>(*reinterpret_cast<uint32_t*>(&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<RefValueType>(*reinterpret_cast<uint8_t*>(&memory[inst.disp])); + dest = std::make_unique<RefValueType>(*reinterpret_cast<uint8_t*>(&memory[segment_offset + inst.disp])); break; } case 16: { - dest = std::make_unique<RefValueType>(*reinterpret_cast<uint16_t*>(&memory[inst.disp])); + dest = std::make_unique<RefValueType>(*reinterpret_cast<uint16_t*>(&memory[segment_offset + inst.disp])); break; } case 32: { - dest = std::make_unique<RefValueType>(*reinterpret_cast<uint32_t*>(&memory[inst.disp])); + dest = std::make_unique<RefValueType>(*reinterpret_cast<uint32_t*>(&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<uint8_t>(0); + } else { + src = *reinterpret_cast<uint8_t*>(&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<uint16_t>(0); + } else { + src = *reinterpret_cast<uint16_t*>(&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<uint32_t>(0); + } else { + src = *reinterpret_cast<uint32_t*>(&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<and_>(inst); - } - - void handle_I_MOV(_DInst inst) { - handle_binary<mov>(inst); - } - - void handle_I_SUB(_DInst inst) { - handle_binary<sub>(inst); - } - - void handle_I_SHR(_DInst inst) { - handle_binary<shr>(inst); - } - - void handle_I_CMP(_DInst inst) { - handle_binary<cmp>(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<uint8_t> memory) - : cpu() - , memory(memory) - { - cpu.eip() = init_eip; - cpu.esp() = init_esp; + Emulator(binparse::Offset32 init_eip, binparse::Offset32 init_esp, std::vector<uint8_t>& 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); |
