summaryrefslogtreecommitdiff
path: root/emulate/emulator.hpp
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 /emulate/emulator.hpp
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.
Diffstat (limited to 'emulate/emulator.hpp')
-rw-r--r--emulate/emulator.hpp393
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);