From ca642d0c8b0a3dd5f768749b58ba66ac35472610 Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Sat, 16 Jun 2018 15:36:39 +0200 Subject: Fixes a bug with calculating memory offsets and exposes some more registers. --- emulate/emulator.cpp | 437 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) (limited to 'emulate/emulator.cpp') 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 { + typedef int32_t type; +}; + +template <> +struct SignedCounterpart { + typedef int16_t type; +}; + +template <> +struct SignedCounterpart { + 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 +struct op : public boost::static_visitor { + CpuState& cpu; + + op(CpuState& cpu) + : cpu(cpu) + {} +}; + +template +struct binary_op : public op { + using op::op; + + template + Ret operator()(T&, U const&) const { + throw Emulator::IncompatibleArguments(); + } +}; + +struct and_ : public binary_op { + using binary_op::operator(); + using binary_op::binary_op; + + template + void operator()(T& lh, T const& rh) const { + lh &= rh; + } +}; + +struct or_ : public binary_op { + using binary_op::operator(); + using binary_op::binary_op; + + template + void operator()(T& lh, T const& rh) const { + lh |= rh; + } +}; + +struct mov : public binary_op { + using binary_op::operator(); + using binary_op::binary_op; + + template + 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 { + using binary_op::operator(); + using binary_op::binary_op; + + template + void operator()(T& lh, T const& rh) const { + //TODO: flags + + lh += rh; + } +}; + +struct sub : public binary_op { + using binary_op::operator(); + using binary_op::binary_op; + + template + void operator()(T& lh, T const& rh) const { + typedef typename SignedCounterpart::type S; + auto& slh = reinterpret_cast(lh); + auto const& srh = reinterpret_cast(rh); + + cpu.of() = ((srh < 0) && (slh > std::numeric_limits::max() + srh)) || ((rh > 0) && (slh < std::numeric_limits::min() + srh)); + cpu.af() = !(lh & 8) && (rh & 8); + cpu.cf() = !(static_cast(std::numeric_limits::min()) & lh) && (static_cast(std::numeric_limits::min()) & rh); + + lh -= rh; + + cpu.sf() = static_cast(std::numeric_limits::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 { + using binary_op::operator(); + using binary_op::binary_op; + + template + void operator()(T& lh, T const& rh) const { + lh >>= rh; + } +}; + +struct cmp : public binary_op { + using binary_op::operator(); + using binary_op::binary_op; + + template + void operator()(T& lh, T const& rh) const { + typedef typename SignedCounterpart::type S; + auto& slh = reinterpret_cast(lh); + auto const& srh = reinterpret_cast(rh); + + cpu.of() = ((srh < 0) && (slh > std::numeric_limits::max() + srh)) || ((rh > 0) && (slh < std::numeric_limits::min() + srh)); + cpu.af() = !(lh & 8) && (rh & 8); + cpu.cf() = !(static_cast(std::numeric_limits::min()) & lh) && (static_cast(std::numeric_limits::min()) & rh); + + T result = lh - rh; + + cpu.sf() = static_cast(std::numeric_limits::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(inst); +} + +void Emulator::handle_I_MOV(_DInst inst) { + handle_binary(inst); +} + +void Emulator::handle_I_SUB(_DInst inst) { + handle_binary(inst); +} + +void Emulator::handle_I_SHR(_DInst inst) { + handle_binary(inst); +} + +void Emulator::handle_I_CMP(_DInst inst) { + handle_binary(inst); +} + +void Emulator::handle_I_OR (_DInst inst) { + handle_binary(inst); +} + +void Emulator::handle_I_ADD(_DInst inst) { + handle_binary(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(boost::apply_visitor(return_visitor{}, register_getters.at(inst.ops[0].index)())); + auto stack_offset = static_cast(boost::apply_visitor(return_visitor{}, register_getters.at(R_ESP)())); + + uint8_t& stack_var = *reinterpret_cast(&memory[stack_offset]); + stack_var = value; + cpu.esp() += 8; + break; + } + case 16: { + auto value = static_cast(boost::apply_visitor(return_visitor{}, register_getters.at(inst.ops[0].index)())); + auto stack_offset = static_cast(boost::apply_visitor(return_visitor{}, register_getters.at(R_ESP)())); + + uint16_t& stack_var = *reinterpret_cast(&memory[stack_offset]); + stack_var = value; + cpu.esp() += 16; + break; + } + case 32: { + auto value = static_cast(boost::apply_visitor(return_visitor{}, register_getters.at(inst.ops[0].index)())); + auto stack_offset = static_cast(boost::apply_visitor(return_visitor{}, register_getters.at(R_ESP)())); + + uint32_t& stack_var = *reinterpret_cast(&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(entry->second.offset >> 16); + cpu.dx() = static_cast(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& 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); + } +} -- cgit v1.2.3-70-g09d2