summaryrefslogtreecommitdiff
path: root/emulate
diff options
context:
space:
mode:
authorDennis Brentjes <d.brentjes@gmail.com>2016-09-19 17:38:23 +0200
committerDennis Brentjes <d.brentjes@gmail.com>2016-10-04 22:18:23 +0200
commitc29ae7a65c636b8d1fa37c6589278dcdee97658f (patch)
treea0ad6583a1f954a179349e0b27d58d7c945c59b5 /emulate
parentabd5cfdd4a6c8e9979b2deb3c48304fb3d63a44c (diff)
downloadopenwar-c29ae7a65c636b8d1fa37c6589278dcdee97658f.tar.gz
openwar-c29ae7a65c636b8d1fa37c6589278dcdee97658f.tar.bz2
openwar-c29ae7a65c636b8d1fa37c6589278dcdee97658f.zip
Started working on a x86 emulator.
Diffstat (limited to 'emulate')
-rw-r--r--emulate/CMakeLists.txt18
-rw-r--r--emulate/cpustate.hpp77
-rw-r--r--emulate/emulate.cpp51
-rw-r--r--emulate/emulator.cpp0
-rw-r--r--emulate/emulator.hpp113
5 files changed, 259 insertions, 0 deletions
diff --git a/emulate/CMakeLists.txt b/emulate/CMakeLists.txt
new file mode 100644
index 0000000..824d8c3
--- /dev/null
+++ b/emulate/CMakeLists.txt
@@ -0,0 +1,18 @@
+
+add_executable(emulate
+ emulate.cpp
+ cpustate.hpp
+ emulator.hpp emulator.cpp
+)
+
+find_package(Boost COMPONENTS filesystem program_options system REQUIRED)
+
+find_package(distorm3 REQUIRED CONFIG)
+
+target_link_libraries(emulate
+ PRIVATE Boost::program_options
+ PRIVATE Boost::system
+ PRIVATE Boost::filesystem
+ PRIVATE distorm3
+ PRIVATE le
+) \ No newline at end of file
diff --git a/emulate/cpustate.hpp b/emulate/cpustate.hpp
new file mode 100644
index 0000000..fb9c377
--- /dev/null
+++ b/emulate/cpustate.hpp
@@ -0,0 +1,77 @@
+#pragma once
+
+#include <iostream>
+#include <array>
+#include <bitset>
+
+#define REGISTER1( NAME ) \
+private: \
+ alignas(4) std::array<uint8_t,4> NAME##_storage = {{0,0,0,0}}; \
+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()); \
+ } \
+ \
+ uint8_t& NAME##h() { \
+ return *reinterpret_cast<uint8_t*>(NAME##_storage.data()+1); \
+ } \
+ \
+ uint8_t& NAME##l() { \
+ return *reinterpret_cast<uint8_t*>(NAME##_storage.data()); \
+ } \
+
+#define REGISTER2( NAME ) \
+private: \
+ alignas(4) std::array<uint8_t,4> NAME##_storage = {{0,0,0,0}}; \
+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 EFLAGS \
+private: \
+ alignas(8) std::bitset<32> storage = 2; \
+public: \
+ using ref = std::bitset<32>::reference; \
+ ref cf() { return storage[0]; } \
+ ref pf() { return storage[2]; } \
+ ref af() { return storage[4]; } \
+ ref zf() { return storage[6]; } \
+ ref sf() { return storage[7]; } \
+ ref tf() { return storage[8]; } \
+ ref intf() { return storage[9]; } \
+ ref df() { return storage[10]; } \
+ ref of() { return storage[11]; } \
+ /*TODO: iopl*/ \
+ ref nt() { return storage[14]; } \
+ ref rf() { return storage[16]; } \
+ ref vm() { return storage[17]; } \
+ ref ac() { return storage[18]; } \
+ ref vif() { return storage[19]; } \
+ ref vip() { return storage[20]; } \
+ ref id() { return storage[21]; } \
+
+struct CpuState {
+ REGISTER2(ip)
+ REGISTER1(a)
+ REGISTER1(c)
+ REGISTER1(d)
+ REGISTER1(b)
+ REGISTER2(sp)
+ REGISTER2(bp)
+ REGISTER2(si)
+ REGISTER2(di)
+ EFLAGS
+};
+
+#undef REGISTER1
+#undef REGISTER2
+#undef EFLAGS \ No newline at end of file
diff --git a/emulate/emulate.cpp b/emulate/emulate.cpp
new file mode 100644
index 0000000..4254dcf
--- /dev/null
+++ b/emulate/emulate.cpp
@@ -0,0 +1,51 @@
+
+#include "emulator.hpp"
+
+#include <boost/program_options.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/operations.hpp>
+
+int main(int argc, char* argv[]) {
+ boost::program_options::options_description description;
+ description.add_options()
+ ("help,h", "produces this help message")
+ ("exe,e", boost::program_options::value<std::string>(), "The LE executable to parse the header for.")
+ ;
+
+ boost::program_options::variables_map vm;
+ boost::program_options::store(boost::program_options::parse_command_line(argc, argv, description), vm);
+ boost::program_options::notify(vm);
+
+ if(vm.count("help")) {
+ std::cout << description << std::endl;
+ return 0;
+ }
+
+ boost::filesystem::path file_path;
+ if(vm.count("exe")) {
+ std::string exe_file = vm["exe"].as<std::string>();
+
+ if(boost::filesystem::exists(exe_file)) {
+ if(!boost::filesystem::is_directory(exe_file)) {
+ file_path = exe_file;
+ } else {
+ std::cerr << exe_file << " is a folder" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << description << std::endl;
+ return -1;
+ }
+ } else {
+ std::cerr << "file: " << exe_file << " does not exist" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << description << std::endl;
+ return -1;
+ }
+ } else {
+ std::cerr << "Option \"exe_file\" is required";
+ std::cerr << std::endl;
+ std::cerr << description << std::endl;
+ return -1;
+ }
+
+ emulate(file_path.string());
+} \ No newline at end of file
diff --git a/emulate/emulator.cpp b/emulate/emulator.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emulate/emulator.cpp
diff --git a/emulate/emulator.hpp b/emulate/emulator.hpp
new file mode 100644
index 0000000..e894042
--- /dev/null
+++ b/emulate/emulator.hpp
@@ -0,0 +1,113 @@
+#pragma once
+
+#include "cpustate.hpp"
+
+#include "le_parse_util.hpp"
+
+#include <distorm.h>
+#include <mnemonics.h>
+
+#include <fstream>
+#include <iomanip>
+
+class Emulator {
+public:
+ CpuState cpu;
+
+ std::map<int, std::function<void(_DInst)>> opcode_handlers;
+
+
+
+ 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) {
+ std::cout << inst.ops[0].size << std::endl;
+ //get_op(0, inst) &= get_op(1, inst);
+ }
+
+public:
+ Emulator(binparse::Offset32 init_eip, binparse::Offset32 init_esp)
+ : cpu()
+ {
+ cpu.eip() = init_eip;
+ cpu.esp() = init_esp;
+
+ #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);
+
+ #undef REGISTER_HANDLER
+ }
+
+ struct UnhandledInstruction : public std::runtime_error {
+ UnhandledInstruction()
+ : std::runtime_error("Encountered unhandled instruction")
+ {}
+ };
+
+ struct UnrecognizedInstruction : public std::runtime_error {
+ UnrecognizedInstruction()
+ : std::runtime_error("Encountered unhandled instruction")
+ {}
+ };
+
+ void handle_instruction(_DInst inst) {
+ opcode_handlers.at(inst.opcode)(inst);
+ }
+
+};
+
+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);
+
+ 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);
+
+ emulator.cpu.eip() += decinst.size;
+
+ _DecodedInst inst;
+ distorm_format64(&ci, &decinst, &inst);
+ std::cout << "CurrentInstruction: " << std::hex << std::setw(8) << std::setfill('0') << inst.offset << ":\t" << inst.mnemonic.p << " " << inst.operands.p << std::endl;
+
+ emulator.handle_instruction(decinst);
+ }
+}