diff options
| author | Dennis Brentjes <d.brentjes@gmail.com> | 2018-06-16 19:26:59 +0200 |
|---|---|---|
| committer | Dennis Brentjes <d.brentjes@gmail.com> | 2018-06-16 19:26:59 +0200 |
| commit | 90c2782d0b78d64a19e7236c7dd6d8cc7a2e8396 (patch) | |
| tree | b9a781b69621ac0cdc0c8654dc3b93a088a5fd42 | |
| parent | 01e7e033e6254f141e44c4e3fc5fe8fc9d0b0c59 (diff) | |
| download | openwar-90c2782d0b78d64a19e7236c7dd6d8cc7a2e8396.tar.gz openwar-90c2782d0b78d64a19e7236c7dd6d8cc7a2e8396.tar.bz2 openwar-90c2782d0b78d64a19e7236c7dd6d8cc7a2e8396.zip | |
Rewrites the binary operation function to have code reuse between unary and binary operations.
| -rw-r--r-- | emulate/emulator.cpp | 233 | ||||
| -rw-r--r-- | emulate/emulator.hpp | 172 |
2 files changed, 245 insertions, 160 deletions
diff --git a/emulate/emulator.cpp b/emulate/emulator.cpp index 669684f..d30081e 100644 --- a/emulate/emulator.cpp +++ b/emulate/emulator.cpp @@ -35,12 +35,190 @@ Emulator::ValueType Emulator::extract_immediate_of_size(int size, _DInst inst) { } } +std::unique_ptr<Emulator::RefValueType> Emulator::get_first_operand(_DInst inst) { + _Operand dest_op = inst.ops[0]; + std::unique_ptr<RefValueType> dest; + + switch(dest_op.type) { + case O_REG: { + dest = std::make_unique<RefValueType>(register_getters.at(dest_op.index)()); + break; + } + case O_MEM: { + throw UnhandledInstruction(); + break; + } + case O_SMEM: { + 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[segment_offset + inst.disp])); + break; + } + case 16: { + 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[segment_offset + inst.disp])); + break; + } + default: { + throw UnhandledInstruction(); + } + } + break; + } + default: { + throw UnhandledInstruction(); + } + } + return dest;; +} + +Emulator::ValueType Emulator::get_second_operand(_DInst inst, RefValueType dest) { + ValueType src; + _Operand src_op = inst.ops[1]; + + switch(src_op.type) { + case O_REG: { + auto x = register_getters.at(src_op.index)(); + src = boost::apply_visitor(to_target_type(), dest, x); + break; + } + case O_IMM: { + //Do not rely on the imm size as we need to sign extend to dest size. + 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; + } + case O_SMEM: { + 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(src_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(src_op.size) { + case 8: { + src = *reinterpret_cast<uint8_t*>(&memory[memory_offset]); + break; + } + case 16: { + src = *reinterpret_cast<uint16_t*>(&memory[memory_offset]); + break; + } + case 32: { + src = *reinterpret_cast<uint32_t*>(&memory[memory_offset]); + break; + } + case 0: { + assert(inst.opcode == I_LEA); + switch(boost::apply_visitor(dest_size(), dest)) { + case 8: { + throw UnrecognizedInstruction(); + } + case 16: { + src = static_cast<uint16_t>(memory_offset); + break; + } + case 32: { + src = static_cast<uint32_t>(memory_offset); + break; + } + default: { + throw UnrecognizedInstruction(); + } + } + + break; + } + default: { + throw UnhandledInstruction(); + } + } + break; + } + default: { + throw UnhandledInstruction(); + + } + } + return src; +} + template <typename Ret> struct op : public boost::static_visitor<Ret> { CpuState& cpu; op(CpuState& cpu) - : cpu(cpu) + : cpu(cpu) {} }; @@ -54,6 +232,11 @@ struct binary_op : public op<Ret> { } }; +template <typename Ret> +struct unary_op : public op<Ret> { + using op<Ret>::op; +}; + struct and_ : public binary_op<void> { using binary_op::operator(); using binary_op<void>::binary_op; @@ -80,10 +263,7 @@ struct mov : public binary_op<void> { template<typename T> void operator()(T& lh, T const& rh) const { - std::cout << unsigned(lh) << std::endl; - std::cout << unsigned(rh) << std::endl; lh = rh; - std::cout << unsigned(lh) << std::endl; } }; @@ -146,13 +326,32 @@ struct cmp : public binary_op<void> { 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; } }; +struct lea : 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 dec : public unary_op<void> { + using unary_op<void>::unary_op; + + template<typename T> + void operator()(T& op) const { + op--; + } +}; + uint32_t Emulator::get_jmp_target(_DInst inst) { if(inst.ops[0].type == O_PC || inst.ops[0].type == O_PTR || inst.ops[0].type == O_DISP) { return INSTRUCTION_GET_TARGET(&inst); @@ -270,6 +469,27 @@ void Emulator::handle_I_CLD(_DInst inst) cpu.df() = 0; } +void Emulator::handle_I_SCAS(_DInst inst) +{ + if(FLAG_GET_PREFIX(inst.flags) == FLAG_REP) { + while(cpu.ecx() != 0 && cpu.zf() != 0) { + throw UnhandledInstruction(); + } + } else { + throw UnhandledInstruction(); + } +} + +void Emulator::handle_I_LEA(_DInst inst) +{ + handle_binary<lea>(inst); +} + +void Emulator::handle_I_DEC(_DInst inst) +{ + handle_unary<dec>(inst); +} + void Emulator::int_0x21() { switch(cpu.ah()) { //Dos Version @@ -347,6 +567,9 @@ Emulator::Emulator(binparse::Offset32 init_eip, binparse::Offset32 init_esp, std REGISTER_HANDLER(I_ADD ); REGISTER_HANDLER(I_PUSH); REGISTER_HANDLER(I_CLD ); + REGISTER_HANDLER(I_SCAS); + REGISTER_HANDLER(I_LEA ); + REGISTER_HANDLER(I_DEC ); #undef REGISTER_HANDLER diff --git a/emulate/emulator.hpp b/emulate/emulator.hpp index 025c823..eb48ee8 100644 --- a/emulate/emulator.hpp +++ b/emulate/emulator.hpp @@ -82,166 +82,25 @@ public: {} }; + std::unique_ptr<RefValueType> get_first_operand(_DInst inst); + + ValueType get_second_operand(_DInst inst, RefValueType dest); + template <typename O> void handle_binary(_DInst inst) { - std::unique_ptr<RefValueType> dest; - ValueType src; - - { - _Operand dest_op = inst.ops[0]; - - switch(dest_op.type) { - case O_REG: { - dest = std::make_unique<RefValueType>(register_getters.at(dest_op.index)()); - break; - } - case O_MEM: { - throw UnhandledInstruction(); - break; - } - case O_SMEM: { - 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[segment_offset + inst.disp])); - break; - } - case 16: { - 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[segment_offset + inst.disp])); - break; - } - default: { - throw UnhandledInstruction(); - } - } - break; - } - default: { - throw UnhandledInstruction(); - } - } - } - - { - _Operand src_op = inst.ops[1]; - - switch(src_op.type) { - case O_REG: { - auto x = register_getters.at(src_op.index)(); - src = boost::apply_visitor(to_target_type(), *dest, x); - break; - } - case O_IMM: { - //Do not rely on the imm size as we need to sign extend to dest size. - 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; - } - case O_SMEM: { - 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(src_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(src_op.size) { - case 8: { - src = *reinterpret_cast<uint8_t*>(&memory[memory_offset]); - break; - } - case 16: { - src = *reinterpret_cast<uint16_t*>(&memory[memory_offset]); - break; - } - case 32: { - src = *reinterpret_cast<uint32_t*>(&memory[memory_offset]); - break; - } - default: { - throw UnhandledInstruction(); - } - } - break; - } - default: { - throw UnhandledInstruction(); - } - } - } + std::unique_ptr<RefValueType> dest = get_first_operand(inst); + ValueType src = get_second_operand(inst, *dest); boost::apply_visitor(O(cpu), *dest, src); } + template<typename O> + void handle_unary(_DInst inst) { + std::unique_ptr<RefValueType> op = get_first_operand(inst); + + boost::apply_visitor(O(cpu), *op); + } + uint32_t get_jmp_target(_DInst inst); void handle_I_JMP (_DInst inst); @@ -259,6 +118,9 @@ public: void handle_I_ADD (_DInst inst); void handle_I_PUSH(_DInst inst); void handle_I_CLD (_DInst inst); + void handle_I_SCAS(_DInst inst); + void handle_I_LEA (_DInst inst); + void handle_I_DEC (_DInst inst); void int_0x21(); void int_0x31(); @@ -277,7 +139,7 @@ public: struct UnrecognizedInstruction : public std::runtime_error { UnrecognizedInstruction() - : std::runtime_error("Encountered unhandled instruction") + : std::runtime_error("Encountered unrecognized instruction") {} }; |
