diff options
Diffstat (limited to 'emulate')
| -rw-r--r-- | emulate/CMakeLists.txt | 18 | ||||
| -rw-r--r-- | emulate/cpustate.hpp | 77 | ||||
| -rw-r--r-- | emulate/emulate.cpp | 51 | ||||
| -rw-r--r-- | emulate/emulator.cpp | 0 | ||||
| -rw-r--r-- | emulate/emulator.hpp | 113 |
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); + } +} |
