summaryrefslogtreecommitdiff
path: root/emulate
diff options
context:
space:
mode:
authorDennis Brentjes <d.brentjes@gmail.com>2018-06-16 19:26:59 +0200
committerDennis Brentjes <d.brentjes@gmail.com>2018-06-16 19:26:59 +0200
commit90c2782d0b78d64a19e7236c7dd6d8cc7a2e8396 (patch)
treeb9a781b69621ac0cdc0c8654dc3b93a088a5fd42 /emulate
parent01e7e033e6254f141e44c4e3fc5fe8fc9d0b0c59 (diff)
downloadopenwar-90c2782d0b78d64a19e7236c7dd6d8cc7a2e8396.tar.gz
openwar-90c2782d0b78d64a19e7236c7dd6d8cc7a2e8396.tar.bz2
openwar-90c2782d0b78d64a19e7236c7dd6d8cc7a2e8396.zip
Rewrites the binary operation function to have code reuse between unary and binary operations.
Diffstat (limited to 'emulate')
-rw-r--r--emulate/emulator.cpp233
-rw-r--r--emulate/emulator.hpp172
2 files changed, 245 insertions, 160 deletions
diff --git a/emulate/emulator.cpp b/emulate/emulator.cpp
index 669684f..d30081e 100644
--- a/emulate/emulator.cpp
+++ b/emulate/emulator.cpp
@@ -35,12 +35,190 @@ Emulator::ValueType Emulator::extract_immediate_of_size(int size, _DInst inst) {
}
}
+std::unique_ptr<Emulator::RefValueType> Emulator::get_first_operand(_DInst inst) {
+ _Operand dest_op = inst.ops[0];
+ std::unique_ptr<RefValueType> dest;
+
+ switch(dest_op.type) {
+ case O_REG: {
+ dest = std::make_unique<RefValueType>(register_getters.at(dest_op.index)());
+ break;
+ }
+ case O_MEM: {
+ throw UnhandledInstruction();
+ break;
+ }
+ case O_SMEM: {
+ 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[segment_offset + inst.disp]));
+ break;
+ }
+ case 16: {
+ 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[segment_offset + inst.disp]));
+ break;
+ }
+ default: {
+ throw UnhandledInstruction();
+ }
+ }
+ break;
+ }
+ default: {
+ throw UnhandledInstruction();
+ }
+ }
+ return dest;;
+}
+
+Emulator::ValueType Emulator::get_second_operand(_DInst inst, RefValueType dest) {
+ ValueType src;
+ _Operand src_op = inst.ops[1];
+
+ switch(src_op.type) {
+ case O_REG: {
+ auto x = register_getters.at(src_op.index)();
+ src = boost::apply_visitor(to_target_type(), dest, x);
+ break;
+ }
+ case O_IMM: {
+ //Do not rely on the imm size as we need to sign extend to dest size.
+ 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;
+ }
+ case O_SMEM: {
+ 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(src_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(src_op.size) {
+ case 8: {
+ src = *reinterpret_cast<uint8_t*>(&memory[memory_offset]);
+ break;
+ }
+ case 16: {
+ src = *reinterpret_cast<uint16_t*>(&memory[memory_offset]);
+ break;
+ }
+ case 32: {
+ src = *reinterpret_cast<uint32_t*>(&memory[memory_offset]);
+ break;
+ }
+ case 0: {
+ assert(inst.opcode == I_LEA);
+ switch(boost::apply_visitor(dest_size(), dest)) {
+ case 8: {
+ throw UnrecognizedInstruction();
+ }
+ case 16: {
+ src = static_cast<uint16_t>(memory_offset);
+ break;
+ }
+ case 32: {
+ src = static_cast<uint32_t>(memory_offset);
+ break;
+ }
+ default: {
+ throw UnrecognizedInstruction();
+ }
+ }
+
+ break;
+ }
+ default: {
+ throw UnhandledInstruction();
+ }
+ }
+ break;
+ }
+ default: {
+ throw UnhandledInstruction();
+
+ }
+ }
+ return src;
+}
+
template <typename Ret>
struct op : public boost::static_visitor<Ret> {
CpuState& cpu;
op(CpuState& cpu)
- : cpu(cpu)
+ : cpu(cpu)
{}
};
@@ -54,6 +232,11 @@ struct binary_op : public op<Ret> {
}
};
+template <typename Ret>
+struct unary_op : public op<Ret> {
+ using op<Ret>::op;
+};
+
struct and_ : public binary_op<void> {
using binary_op::operator();
using binary_op<void>::binary_op;
@@ -80,10 +263,7 @@ struct mov : public binary_op<void> {
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;
}
};
@@ -146,13 +326,32 @@ struct cmp : public binary_op<void> {
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;
}
};
+struct lea : 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 dec : public unary_op<void> {
+ using unary_op<void>::unary_op;
+
+ template<typename T>
+ void operator()(T& op) const {
+ op--;
+ }
+};
+
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);
@@ -270,6 +469,27 @@ void Emulator::handle_I_CLD(_DInst inst)
cpu.df() = 0;
}
+void Emulator::handle_I_SCAS(_DInst inst)
+{
+ if(FLAG_GET_PREFIX(inst.flags) == FLAG_REP) {
+ while(cpu.ecx() != 0 && cpu.zf() != 0) {
+ throw UnhandledInstruction();
+ }
+ } else {
+ throw UnhandledInstruction();
+ }
+}
+
+void Emulator::handle_I_LEA(_DInst inst)
+{
+ handle_binary<lea>(inst);
+}
+
+void Emulator::handle_I_DEC(_DInst inst)
+{
+ handle_unary<dec>(inst);
+}
+
void Emulator::int_0x21() {
switch(cpu.ah()) {
//Dos Version
@@ -347,6 +567,9 @@ Emulator::Emulator(binparse::Offset32 init_eip, binparse::Offset32 init_esp, std
REGISTER_HANDLER(I_ADD );
REGISTER_HANDLER(I_PUSH);
REGISTER_HANDLER(I_CLD );
+ REGISTER_HANDLER(I_SCAS);
+ REGISTER_HANDLER(I_LEA );
+ REGISTER_HANDLER(I_DEC );
#undef REGISTER_HANDLER
diff --git a/emulate/emulator.hpp b/emulate/emulator.hpp
index 025c823..eb48ee8 100644
--- a/emulate/emulator.hpp
+++ b/emulate/emulator.hpp
@@ -82,166 +82,25 @@ public:
{}
};
+ std::unique_ptr<RefValueType> get_first_operand(_DInst inst);
+
+ ValueType get_second_operand(_DInst inst, RefValueType dest);
+
template <typename O>
void handle_binary(_DInst inst) {
- std::unique_ptr<RefValueType> dest;
- ValueType src;
-
- {
- _Operand dest_op = inst.ops[0];
-
- switch(dest_op.type) {
- case O_REG: {
- dest = std::make_unique<RefValueType>(register_getters.at(dest_op.index)());
- break;
- }
- case O_MEM: {
- throw UnhandledInstruction();
- break;
- }
- case O_SMEM: {
- 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[segment_offset + inst.disp]));
- break;
- }
- case 16: {
- 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[segment_offset + inst.disp]));
- break;
- }
- default: {
- throw UnhandledInstruction();
- }
- }
- break;
- }
- default: {
- throw UnhandledInstruction();
- }
- }
- }
-
- {
- _Operand src_op = inst.ops[1];
-
- switch(src_op.type) {
- case O_REG: {
- auto x = register_getters.at(src_op.index)();
- src = boost::apply_visitor(to_target_type(), *dest, x);
- break;
- }
- case O_IMM: {
- //Do not rely on the imm size as we need to sign extend to dest size.
- 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;
- }
- case O_SMEM: {
- 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(src_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(src_op.size) {
- case 8: {
- src = *reinterpret_cast<uint8_t*>(&memory[memory_offset]);
- break;
- }
- case 16: {
- src = *reinterpret_cast<uint16_t*>(&memory[memory_offset]);
- break;
- }
- case 32: {
- src = *reinterpret_cast<uint32_t*>(&memory[memory_offset]);
- break;
- }
- default: {
- throw UnhandledInstruction();
- }
- }
- break;
- }
- default: {
- throw UnhandledInstruction();
- }
- }
- }
+ std::unique_ptr<RefValueType> dest = get_first_operand(inst);
+ ValueType src = get_second_operand(inst, *dest);
boost::apply_visitor(O(cpu), *dest, src);
}
+ template<typename O>
+ void handle_unary(_DInst inst) {
+ std::unique_ptr<RefValueType> op = get_first_operand(inst);
+
+ boost::apply_visitor(O(cpu), *op);
+ }
+
uint32_t get_jmp_target(_DInst inst);
void handle_I_JMP (_DInst inst);
@@ -259,6 +118,9 @@ public:
void handle_I_ADD (_DInst inst);
void handle_I_PUSH(_DInst inst);
void handle_I_CLD (_DInst inst);
+ void handle_I_SCAS(_DInst inst);
+ void handle_I_LEA (_DInst inst);
+ void handle_I_DEC (_DInst inst);
void int_0x21();
void int_0x31();
@@ -277,7 +139,7 @@ public:
struct UnrecognizedInstruction : public std::runtime_error {
UnrecognizedInstruction()
- : std::runtime_error("Encountered unhandled instruction")
+ : std::runtime_error("Encountered unrecognized instruction")
{}
};