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 | |
| 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')
| -rw-r--r-- | emulate/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | emulate/cpustate.cpp | 47 | ||||
| -rw-r--r-- | emulate/cpustate.hpp | 52 | ||||
| -rw-r--r-- | emulate/emulator.cpp | 437 | ||||
| -rw-r--r-- | emulate/emulator.hpp | 393 |
5 files changed, 621 insertions, 310 deletions
diff --git a/emulate/CMakeLists.txt b/emulate/CMakeLists.txt index 824d8c3..df4a04e 100644 --- a/emulate/CMakeLists.txt +++ b/emulate/CMakeLists.txt @@ -1,7 +1,7 @@ add_executable(emulate emulate.cpp - cpustate.hpp + cpustate.hpp cpustate.cpp emulator.hpp emulator.cpp ) diff --git a/emulate/cpustate.cpp b/emulate/cpustate.cpp new file mode 100644 index 0000000..76611b5 --- /dev/null +++ b/emulate/cpustate.cpp @@ -0,0 +1,47 @@ + +#include "cpustate.hpp" + +#include <ostream> +#include <iomanip> + +#define PRINT_REGISTER(NAME, FNAME) #NAME": 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.FNAME() << std::endl + + +std::ostream&operator<<(std::ostream& os, CpuState& cpu) { + os << PRINT_REGISTER(EIP, eip) + << PRINT_REGISTER(EAX, eax) + << PRINT_REGISTER(EBX, ebx) + << PRINT_REGISTER(ECX, ecx) + << PRINT_REGISTER(EDX, edx) + << PRINT_REGISTER(ESP, esp) + << PRINT_REGISTER(EBP, ebp) + << PRINT_REGISTER(ESI, esi) + << PRINT_REGISTER(EDI, edi) + << PRINT_REGISTER( CS, cs) + << PRINT_REGISTER( DS, ds) + << PRINT_REGISTER( ES, es) + << PRINT_REGISTER( SS, ss) + << PRINT_REGISTER( FS, fs) + << PRINT_REGISTER( GS, gs) + << "cf \tpf \taf \tzf \tsf \ttf \tintf \tdf \tof \tnt \trf \tvm \tac \tvif \tvip \tid" << std::endl + << cpu.cf() << "\t" + << cpu.pf() << "\t" + << cpu.af() << "\t" + << cpu.zf() << "\t" + << cpu.sf() << "\t" + << cpu.tf() << "\t" + << cpu.intf() << "\t" + << cpu.df() << "\t" + << cpu.of() << "\t" + << cpu.nt() << "\t" + << cpu.rf() << "\t" + << cpu.vm() << "\t" + << cpu.ac() << "\t" + << cpu.vif() << "\t" + << cpu.vip() << "\t" + << cpu.id() << "\t" + << std::endl; +return os; +} + +#undef PRINT_REGISTER
\ No newline at end of file diff --git a/emulate/cpustate.hpp b/emulate/cpustate.hpp index 6b80551..be9c254 100644 --- a/emulate/cpustate.hpp +++ b/emulate/cpustate.hpp @@ -1,9 +1,7 @@ #pragma once -#include <iostream> #include <array> #include <bitset> -#include <iomanip> #define REGISTER1( NAME ) \ private: \ @@ -12,7 +10,7 @@ public: \ uint32_t& e##NAME##x() { \ return *reinterpret_cast<uint32_t*>(NAME##_storage.data()); \ } \ - \ + \ uint16_t& NAME##x() { \ return *reinterpret_cast<uint16_t*>(NAME##_storage.data()); \ } \ @@ -32,7 +30,15 @@ public: \ uint32_t& e##NAME() { \ return *reinterpret_cast<uint32_t*>(NAME##_storage.data()); \ } \ - \ + \ + uint16_t& NAME() { \ + return *reinterpret_cast<uint16_t*>(NAME##_storage.data()); \ + } \ + +#define SEGMENT_REGISTER( NAME ) \ +private: \ + alignas(2) std::array<uint8_t,2> NAME##_storage = {{0,0}}; \ +public: \ uint16_t& NAME() { \ return *reinterpret_cast<uint16_t*>(NAME##_storage.data()); \ } \ @@ -70,40 +76,18 @@ struct CpuState { REGISTER2(bp) REGISTER2(si) REGISTER2(di) + SEGMENT_REGISTER(cs) + SEGMENT_REGISTER(ds) + SEGMENT_REGISTER(es) + SEGMENT_REGISTER(ss) + SEGMENT_REGISTER(fs) + SEGMENT_REGISTER(gs) EFLAGS }; #undef REGISTER1 #undef REGISTER2 +#undef SEGMENT_REGISTER #undef EFLAGS -std::ostream& operator<<(std::ostream& os, CpuState& cpu) { - os << "EIP: 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.eip() << std::endl - << "EAX: 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.eax() << std::endl - << "EBX: 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.ebx() << std::endl - << "ECX: 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.ecx() << std::endl - << "EDX: 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.edx() << std::endl - << "ESP: 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.esp() << std::endl - << "EBP: 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.ebp() << std::endl - << "ESI: 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.esi() << std::endl - << "EDI: 0x" << std::hex << std::setw(8) << std::setfill('0') << cpu.edi() << std::endl - << "cf \tpf \taf \tzf \tsf \ttf \tintf \tdf \tof \tnt \trf \tvm \tac \tvif \tvip \tid" << std::endl - << cpu.cf() << "\t" - << cpu.pf() << "\t" - << cpu.af() << "\t" - << cpu.zf() << "\t" - << cpu.sf() << "\t" - << cpu.tf() << "\t" - << cpu.intf() << "\t" - << cpu.df() << "\t" - << cpu.of() << "\t" - << cpu.nt() << "\t" - << cpu.rf() << "\t" - << cpu.vm() << "\t" - << cpu.ac() << "\t" - << cpu.vif() << "\t" - << cpu.vip() << "\t" - << cpu.id() << "\t" - << std::endl; - return os; -} +std::ostream& operator<<(std::ostream& os, CpuState& cpu); diff --git a/emulate/emulator.cpp b/emulate/emulator.cpp index e69de29..8c606a5 100644 --- a/emulate/emulator.cpp +++ b/emulate/emulator.cpp @@ -0,0 +1,437 @@ + +#include "emulator.hpp" + +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; +}; + +Emulator::ValueType Emulator::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(); + } + } +} + +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 Emulator::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 or_ : 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 { + std::cout << unsigned(lh) << std::endl; + std::cout << unsigned(rh) << std::endl; + lh = rh; + std::cout << unsigned(lh) << std::endl; + } +}; + +struct add : 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 { + //TODO: flags + + 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; + } +}; + +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); + } else if (inst.ops[0].type == O_SMEM) { + throw UnhandledInstruction(); + } else { + throw UnrecognizedInstruction(); + } +} + +void Emulator::handle_I_JMP(_DInst inst) { + 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 Emulator::handle_I_STI(_DInst) { + cpu.intf() = 1; +} + +void Emulator::handle_I_AND(_DInst inst) { + handle_binary<and_>(inst); +} + +void Emulator::handle_I_MOV(_DInst inst) { + handle_binary<mov>(inst); +} + +void Emulator::handle_I_SUB(_DInst inst) { + handle_binary<sub>(inst); +} + +void Emulator::handle_I_SHR(_DInst inst) { + handle_binary<shr>(inst); +} + +void Emulator::handle_I_CMP(_DInst inst) { + handle_binary<cmp>(inst); +} + +void Emulator::handle_I_OR (_DInst inst) { + handle_binary<or_>(inst); +} + +void Emulator::handle_I_ADD(_DInst inst) { + handle_binary<add>(inst); +} + +void Emulator::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 Emulator::handle_I_JNZ(_DInst inst) { + if(!cpu.zf()) { + cpu.eip() = get_jmp_target(inst); + } +} + +void Emulator::handle_I_JZ(_DInst inst) { + if(cpu.zf()) { + cpu.eip() = get_jmp_target(inst); + } +} + +void Emulator::handle_I_JG(_DInst inst) +{ + if(!cpu.zf() && (cpu.sf() == cpu.of())) { + cpu.eip() = get_jmp_target(inst); + } +} + +void Emulator::handle_I_PUSH(_DInst inst) +{ + switch(inst.ops[0].size) { + case 8: { + auto value = static_cast<uint8_t>(boost::apply_visitor(return_visitor{}, register_getters.at(inst.ops[0].index)())); + auto stack_offset = static_cast<uint8_t>(boost::apply_visitor(return_visitor{}, register_getters.at(R_ESP)())); + + uint8_t& stack_var = *reinterpret_cast<uint8_t*>(&memory[stack_offset]); + stack_var = value; + cpu.esp() += 8; + break; + } + case 16: { + auto value = static_cast<uint16_t>(boost::apply_visitor(return_visitor{}, register_getters.at(inst.ops[0].index)())); + auto stack_offset = static_cast<uint16_t>(boost::apply_visitor(return_visitor{}, register_getters.at(R_ESP)())); + + uint16_t& stack_var = *reinterpret_cast<uint16_t*>(&memory[stack_offset]); + stack_var = value; + cpu.esp() += 16; + break; + } + case 32: { + auto value = static_cast<uint32_t>(boost::apply_visitor(return_visitor{}, register_getters.at(inst.ops[0].index)())); + auto stack_offset = static_cast<uint32_t>(boost::apply_visitor(return_visitor{}, register_getters.at(R_ESP)())); + + uint32_t& stack_var = *reinterpret_cast<uint32_t*>(&memory[stack_offset]); + stack_var = value; + cpu.esp() += 32; + break; + } + } +} + +void Emulator::int_0x21() { + switch(cpu.ah()) { + //Dos Version + case 0x30: { + cpu.al() = 6; + cpu.ah() = 1; + cpu.bh() = 0; + break; + } + //DOS4GW ident + case 0xFF: { + cpu.eax() = 0xFFFF3447; + break; + } + default: { + throw UnhandledInstruction(); + } + } +} + +void Emulator::int_0x31() +{ + switch(cpu.ax()) { + case 0x0006: { + auto entry = ldt.find(cpu.bx()); + if(entry != ldt.end()) { + cpu.cf() = 0; + cpu.cx() = static_cast<uint16_t>(entry->second.offset >> 16); + cpu.dx() = static_cast<uint16_t>(entry->second.offset); + cpu.ax() = 0; + } else { + cpu.cf() = 1; + cpu.ax() = 0x8022; + } + break; + } + default: { + throw UnhandledInstruction(); + } + } +} + +Emulator::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; + + ldt[1] = {0}; + cpu.ds() = ((uint16_t) 1 << 3) | (uint8_t) 1 << 2 | (uint8_t) 3; + cpu.ss() = ((uint16_t) 1 << 3) | (uint8_t) 1 << 2 | (uint8_t) 3; + + ldt[2] = {cpu.esp()}; + cpu.es() = ((uint16_t) 2 << 3) | (uint8_t) 1 << 2 | (uint8_t) 3; + + cpu.fs() = 0; //dos4gw or dos32a specific segment register wont be used anyway + + cpu.gs() = 0; + +#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 ); + REGISTER_HANDLER(I_JZ ); + REGISTER_HANDLER(I_JG ); + REGISTER_HANDLER(I_OR ); + REGISTER_HANDLER(I_ADD ); + REGISTER_HANDLER(I_PUSH); + +#undef REGISTER_HANDLER + +#define REGISTER_GETTER(REGISTER, R_NAME) register_getters[REGISTER] = [this]{return RefValueType(cpu.R_NAME());} + + REGISTER_GETTER(R_AL , al ); + REGISTER_GETTER(R_AH , ah ); + REGISTER_GETTER(R_AX , ax ); + REGISTER_GETTER(R_EAX, eax); + + REGISTER_GETTER(R_BH, bh ); + REGISTER_GETTER(R_BX, bx ); + REGISTER_GETTER(R_EBX, ebx); + + REGISTER_GETTER(R_CX, cx ); + REGISTER_GETTER(R_ECX, ecx); + + REGISTER_GETTER(R_DL , dl ); + REGISTER_GETTER(R_DX , dx ); + REGISTER_GETTER(R_EDX, edx); + + REGISTER_GETTER(R_ESP, esp); + + REGISTER_GETTER(R_ESI, esi); + REGISTER_GETTER(R_EDI, edi); + + REGISTER_GETTER(R_DS , ds ); + REGISTER_GETTER(R_ES , es ); + REGISTER_GETTER(R_GS , gs ); + +#undef REGISTER_GETTER + +#define REGISTER_INTERRUPT_HANDLER(INT) interrupt_handlers[INT] = [this]{return int_##INT();} + + REGISTER_INTERRUPT_HANDLER(0x21); + REGISTER_INTERRUPT_HANDLER(0x31); + +#undef REGISTER_INTERRUPT_HANDLER +} + +void Emulator::set_code_segment(binparse::Offset32 offset) { + ldt[0] = {offset}; + cpu.cs() = ((uint16_t) 0 << 3) | (uint8_t) 1 << 2 | (uint8_t) 3; +} + +void Emulator::set_data_segment(binparse::Offset32 offset) { + ldt[1] = {offset}; + cpu.ds() = ((uint16_t) 1 << 3) | (uint8_t) 1 << 2 | (uint8_t) 3; + cpu.ss() = ((uint16_t) 1 << 3) | (uint8_t) 1 << 2 | (uint8_t) 3; +} + +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); + emulator.set_code_segment(file.object_table.entries.at(1).reloc_base_address); + emulator.set_data_segment(file.object_table.entries.at(2).reloc_base_address); + + 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); + } +} 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); |
