summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Brentjes <d.brentjes@gmail.com>2018-06-16 15:36:39 +0200
committerDennis Brentjes <d.brentjes@gmail.com>2018-06-16 15:36:39 +0200
commitca642d0c8b0a3dd5f768749b58ba66ac35472610 (patch)
tree33eb95aee362703eb424fdb93c381acf7111876b
parent44320ada80b08ecf88caf114b2b0be8c8e08e505 (diff)
downloadopenwar-ca642d0c8b0a3dd5f768749b58ba66ac35472610.tar.gz
openwar-ca642d0c8b0a3dd5f768749b58ba66ac35472610.tar.bz2
openwar-ca642d0c8b0a3dd5f768749b58ba66ac35472610.zip
Fixes a bug with calculating memory offsets and exposes some more registers.
-rw-r--r--emulate/CMakeLists.txt2
-rw-r--r--emulate/cpustate.cpp47
-rw-r--r--emulate/cpustate.hpp52
-rw-r--r--emulate/emulator.cpp437
-rw-r--r--emulate/emulator.hpp393
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);