diff --git a/src/emulator/emulator.hpp b/src/emulator/emulator.hpp index 63dcabf..94c57e5 100644 --- a/src/emulator/emulator.hpp +++ b/src/emulator/emulator.hpp @@ -9,17 +9,31 @@ struct emulator_hook; using memory_operation = memory_permission; -enum class hook_continuation : bool +enum class instruction_hook_continuation : bool { run_instruction = false, skip_instruction = true, }; -using hook_callback = std::function; +enum class memory_violation_continuation : bool +{ + stop = false, + resume = true, +}; + +enum class memory_violation_type : uint8_t +{ + unmapped, + protection, +}; + +using instruction_hook_callback = std::function; using interrupt_hook_callback = std::function; using simple_memory_hook_callback = std::function; using complex_memory_hook_callback = std::function; +using memory_violation_hook_callback = std::function; class emulator : public memory_manager { @@ -38,14 +52,22 @@ class emulator : public memory_manager virtual void read_raw_register(int reg, void* value, size_t size) = 0; virtual void write_raw_register(int reg, const void* value, size_t size) = 0; + virtual emulator_hook* hook_memory_violation(uint64_t address, size_t size, + memory_violation_hook_callback callback) = 0; + virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter, complex_memory_hook_callback callback) = 0; - virtual emulator_hook* hook_instruction(int instruction_type, hook_callback callback) = 0; + virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0; virtual emulator_hook* hook_interrupt(interrupt_hook_callback callback) = 0; virtual void delete_hook(emulator_hook* hook) = 0; + emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) + { + return this->hook_memory_violation(0, std::numeric_limits::max(), std::move(callback)); + } + emulator_hook* hook_memory_read(const uint64_t address, const size_t size, simple_memory_hook_callback callback) { return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::read); diff --git a/src/emulator/typed_emulator.hpp b/src/emulator/typed_emulator.hpp index 36b3611..5f555f2 100644 --- a/src/emulator/typed_emulator.hpp +++ b/src/emulator/typed_emulator.hpp @@ -65,13 +65,30 @@ class typed_emulator : public emulator return result; } - emulator_hook* hook_instruction(hookable_instructions instruction_type, hook_callback callback) + void push_stack(const pointer_type& value) + { + const auto sp = this->read_stack_pointer() - pointer_size; + this->reg(stack_pointer, sp); + this->write_memory(sp, &value, sizeof(value)); + } + + pointer_type pop_stack() + { + pointer_type result{}; + const auto sp = this->read_stack_pointer(); + this->read_memory(sp, &result, sizeof(result)); + this->reg(stack_pointer, sp + pointer_size); + + return result; + } + + emulator_hook* hook_instruction(hookable_instructions instruction_type, instruction_hook_callback callback) { return this->hook_instruction(static_cast(instruction_type), std::move(callback)); } private: - emulator_hook* hook_instruction(int instruction_type, hook_callback callback) override = 0; + emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0; void read_raw_register(int reg, void* value, size_t size) override = 0; void write_raw_register(int reg, const void* value, size_t size) override = 0; diff --git a/src/unicorn_emulator/unicorn_x64_emulator.cpp b/src/unicorn_emulator/unicorn_x64_emulator.cpp index 47a33c0..69bde48 100644 --- a/src/unicorn_emulator/unicorn_x64_emulator.cpp +++ b/src/unicorn_emulator/unicorn_x64_emulator.cpp @@ -29,9 +29,26 @@ namespace unicorn return UC_X86_INS_RDTSC; case x64_hookable_instructions::rdtscp: return UC_X86_INS_RDTSCP; + default: + throw std::runtime_error("Bad instruction for mapping"); } + } - throw std::runtime_error("Bad instruction for mapping"); + memory_violation_type map_memory_violation_type(const uc_mem_type mem_type) + { + switch (mem_type) + { + case UC_MEM_READ_PROT: + case UC_MEM_WRITE_PROT: + case UC_MEM_FETCH_PROT: + return memory_violation_type::protection; + case UC_MEM_READ_UNMAPPED: + case UC_MEM_WRITE_UNMAPPED: + case UC_MEM_FETCH_UNMAPPED: + return memory_violation_type::unmapped; + default: + throw std::runtime_error("Memory type does not constitute a violation"); + } } memory_operation map_memory_operation(const uc_mem_type mem_type) @@ -39,11 +56,19 @@ namespace unicorn switch (mem_type) { case UC_MEM_READ: - return memory_permission::read; + case UC_MEM_READ_PROT: + case UC_MEM_READ_UNMAPPED: + return memory_operation::read; case UC_MEM_WRITE: - return memory_permission::write; + case UC_MEM_WRITE_PROT: + case UC_MEM_WRITE_UNMAPPED: + return memory_operation::write; + case UC_MEM_FETCH: + case UC_MEM_FETCH_PROT: + case UC_MEM_FETCH_UNMAPPED: + return memory_operation::exec; default: - return memory_permission::none; + return memory_operation::none; } } @@ -177,7 +202,7 @@ namespace unicorn size_t result_size = size; uce(uc_reg_write2(*this, reg, value, &result_size)); - if (size != result_size) + if (size < result_size) { throw std::runtime_error( "Register size mismatch: " + std::to_string(size) + " != " + std::to_string(result_size)); @@ -187,9 +212,10 @@ namespace unicorn void read_raw_register(const int reg, void* value, const size_t size) override { size_t result_size = size; + memset(value, 0, size); uce(uc_reg_read2(*this, reg, value, &result_size)); - if (size != result_size) + if (size < result_size) { throw std::runtime_error( "Register size mismatch: " + std::to_string(size) + " != " + std::to_string(result_size)); @@ -250,11 +276,11 @@ namespace unicorn }*/ emulator_hook* hook_instruction(int instruction_type, - hook_callback callback) + instruction_hook_callback callback) { function_wrapper wrapper([c = std::move(callback)](uc_engine*) { - return (c() == hook_continuation::skip_instruction) + return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0; }); @@ -288,10 +314,11 @@ namespace unicorn emulator_hook* hook_interrupt(interrupt_hook_callback callback) override { - function_wrapper wrapper([c = std::move(callback)](uc_engine*, const int interrupt_type) - { - c(interrupt_type); - }); + function_wrapper wrapper( + [c = std::move(callback)](uc_engine*, const int interrupt_type) + { + c(interrupt_type); + }); unicorn_hook hook{*this}; auto container = std::make_unique(); @@ -306,6 +333,33 @@ namespace unicorn return result; } + emulator_hook* hook_memory_violation(uint64_t address, size_t size, + memory_violation_hook_callback callback) override + { + function_wrapper wrapper( + [c = std::move(callback)](uc_engine*, const uc_mem_type type, + const uint64_t address, const int size, const int64_t) + { + assert(size >= 0); + const auto operation = map_memory_operation(type); + const auto violation = map_memory_violation_type(type); + + return c(address, static_cast(size), operation, violation) == memory_violation_continuation::resume; + }); + + unicorn_hook hook{*this}; + auto container = std::make_unique(); + + uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_INVALID, wrapper.get_function(), + wrapper.get_user_data(), address, size)); + + container->add(std::move(wrapper), std::move(hook)); + + auto* result = container->as_opaque_hook(); + this->hooks_.push_back(std::move(container)); + return result; + } + emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter, complex_memory_hook_callback callback) override { diff --git a/src/windows_emulator/main.cpp b/src/windows_emulator/main.cpp index b74ae4d..bf0cbaf 100644 --- a/src/windows_emulator/main.cpp +++ b/src/windows_emulator/main.cpp @@ -24,7 +24,11 @@ #define STACK_ADDRESS (0x80000000000 - STACK_SIZE) #define KUSD_ADDRESS 0x7ffe0000 -bool use_gdb = true; +#define GDT_ADDR 0x30000 +#define GDT_LIMIT 0x1000 +#define GDT_ENTRY_SIZE 0x8 + +bool use_gdb = false; struct breakpoint_key { @@ -274,18 +278,31 @@ namespace return emulator_allocator{emu, base, size}; } + void setup_gdt(x64_emulator& emu) + { + constexpr uint64_t gdtr[4] = {0, GDT_ADDR, GDT_LIMIT, 0}; + emu.write_register(x64_register::gdtr, &gdtr, sizeof(gdtr)); + emu.allocate_memory(GDT_ADDR, GDT_LIMIT, memory_permission::read); + + emu.write_memory(GDT_ADDR + 6 * (sizeof(uint64_t)), 0xEFFE000000FFFF); + emu.reg(x64_register::cs, 0x33); + + emu.write_memory(GDT_ADDR + 5 * (sizeof(uint64_t)), 0xEFF6000000FFFF); + emu.reg(x64_register::ss, 0x2B); + } + process_context setup_context(x64_emulator& emu) { - setup_stack(emu, STACK_ADDRESS, STACK_SIZE); process_context context{}; - context.kusd = setup_kusd(emu); + setup_stack(emu, STACK_ADDRESS, STACK_SIZE); + setup_gdt(emu); + context.kusd = setup_kusd(emu); context.gs_segment = setup_gs_segment(emu, GS_SEGMENT_ADDR, GS_SEGMENT_SIZE); auto allocator = create_allocator(emu, 1 << 20); - auto& gs = context.gs_segment; context.teb = gs.reserve(); @@ -348,6 +365,12 @@ namespace x64_register::r15, x64_register::rip, x64_register::rflags, + /*x64_register::cs, + x64_register::ss, + x64_register::ds, + x64_register::es, + x64_register::fs, + x64_register::gs,*/ }; memory_operation map_breakpoint_type(const breakpoint_type type) @@ -463,7 +486,7 @@ namespace try { - if (regno < 0 || regno >= gdb_registers.size()) + if (static_cast(regno) >= gdb_registers.size()) { return true; } @@ -481,7 +504,7 @@ namespace { try { - if (regno < 0 || regno >= gdb_registers.size()) + if (static_cast(regno) >= gdb_registers.size()) { return true; } @@ -598,40 +621,51 @@ namespace emu->hook_instruction(x64_hookable_instructions::syscall, [&] { dispatcher.dispatch(*emu, context); - return hook_continuation::skip_instruction; + return instruction_hook_continuation::skip_instruction; }); emu->hook_instruction(x64_hookable_instructions::rdtsc, [&] { emu->reg(x64_register::rax, 0x0011223344556677); - return hook_continuation::skip_instruction; + return instruction_hook_continuation::skip_instruction; }); emu->hook_instruction(x64_hookable_instructions::invalid, [&] { const auto ip = emu->read_instruction_pointer(); printf("Invalid instruction at: %llX\n", ip); - return hook_continuation::skip_instruction; + return instruction_hook_continuation::skip_instruction; }); emu->hook_interrupt([&](int interrupt) { printf("Interrupt: %i\n", interrupt); - if (interrupt == 13) - { - const auto sp = emu->reg(x64_register::rsp); - const auto new_ip = emu->read_memory(sp); + }); + + emu->hook_memory_violation([&](const uint64_t address, const size_t size, const memory_operation operation, + const memory_violation_type type) + { + const auto permission = get_permission_string(operation); + const auto ip = emu->read_instruction_pointer(); - emu->reg(x64_register::rsp, sp + 8); - emu->reg(x64_register::rip, new_ip); + if (type == memory_violation_type::protection) + { + printf("Protection violation: %llX (%zX) - %s at %llX\n", address, size, permission.c_str(), ip); + } + else if (type == memory_violation_type::unmapped) + { + printf("Mapping violation: %llX (%zX) - %s at %llX\n", address, size, permission.c_str(), ip); } - }); - watch_object(*emu, context.teb); - watch_object(*emu, context.peb); - watch_object(*emu, context.process_params); - watch_object(*emu, context.kusd); + return memory_violation_continuation::stop; + }); + /* + watch_object(*emu, context.teb); + watch_object(*emu, context.peb); + watch_object(*emu, context.process_params); + watch_object(*emu, context.kusd); + */ /*emu->hook_memory_execution(0, std::numeric_limits::max(), [&](const uint64_t address, const size_t) { if (address == 0x1800D52F4) diff --git a/src/windows_emulator/module_mapper.cpp b/src/windows_emulator/module_mapper.cpp index 14c06a4..f73829d 100644 --- a/src/windows_emulator/module_mapper.cpp +++ b/src/windows_emulator/module_mapper.cpp @@ -227,7 +227,7 @@ std::optional map_file(x64_emulator& emu, const std::filesystem:: auto binary = map_module(emu, data, file.generic_string()); - hook_exports(emu, binary, file); + //hook_exports(emu, binary, file); return binary; } diff --git a/src/windows_emulator/syscalls.cpp b/src/windows_emulator/syscalls.cpp index 8d11518..80b9f1a 100644 --- a/src/windows_emulator/syscalls.cpp +++ b/src/windows_emulator/syscalls.cpp @@ -1156,7 +1156,7 @@ namespace { if (file_handle == STDOUT_HANDLE) { - std::vector temp_buffer{}; + std::string temp_buffer{}; temp_buffer.resize(length); c.emu.read_memory(buffer, temp_buffer.data(), temp_buffer.size());