From e5b3dc953e8e1042b2940fb5c022b99416c8d21a Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 5 Sep 2024 19:01:55 +0200 Subject: [PATCH] Prepare exception dispatching --- src/windows_emulator/context_frame.cpp | 151 +++++++++++++++++++++++ src/windows_emulator/context_frame.hpp | 8 ++ src/windows_emulator/main.cpp | 161 +++++++++++++++++++++++-- src/windows_emulator/syscalls.cpp | 85 +++---------- 4 files changed, 324 insertions(+), 81 deletions(-) create mode 100644 src/windows_emulator/context_frame.cpp create mode 100644 src/windows_emulator/context_frame.hpp diff --git a/src/windows_emulator/context_frame.cpp b/src/windows_emulator/context_frame.cpp new file mode 100644 index 00000000..3f9d7a02 --- /dev/null +++ b/src/windows_emulator/context_frame.cpp @@ -0,0 +1,151 @@ +#include "std_include.hpp" +#include "context_frame.hpp" + +namespace context_frame +{ + void restore(x64_emulator& emu, const CONTEXT& context) + { + if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS) + { + emu.reg(x64_register::dr0, context.Dr0); + emu.reg(x64_register::dr1, context.Dr1); + emu.reg(x64_register::dr2, context.Dr2); + emu.reg(x64_register::dr3, context.Dr3); + emu.reg(x64_register::dr6, context.Dr6); + emu.reg(x64_register::dr7, context.Dr7); + } + + if (context.ContextFlags & CONTEXT_CONTROL) + { + emu.reg(x64_register::ss, context.SegSs); + emu.reg(x64_register::cs, context.SegCs); + + emu.reg(x64_register::rip, context.Rip); + emu.reg(x64_register::rsp, context.Rsp); + + emu.reg(x64_register::eflags, context.EFlags); + } + + if (context.ContextFlags & CONTEXT_INTEGER) + { + emu.reg(x64_register::rax, context.Rax); + emu.reg(x64_register::rbx, context.Rbx); + emu.reg(x64_register::rcx, context.Rcx); + emu.reg(x64_register::rdx, context.Rdx); + emu.reg(x64_register::rbp, context.Rbp); + emu.reg(x64_register::rsi, context.Rsi); + emu.reg(x64_register::rdi, context.Rdi); + emu.reg(x64_register::r8, context.R8); + emu.reg(x64_register::r9, context.R9); + emu.reg(x64_register::r10, context.R10); + emu.reg(x64_register::r11, context.R11); + emu.reg(x64_register::r12, context.R12); + emu.reg(x64_register::r13, context.R13); + emu.reg(x64_register::r14, context.R14); + emu.reg(x64_register::r15, context.R15); + } + + /*if (context.ContextFlags & CONTEXT_SEGMENTS) + { + emu.reg(x64_register::ds, context.SegDs); + emu.reg(x64_register::es, context.SegEs); + emu.reg(x64_register::fs, context.SegFs); + emu.reg(x64_register::gs, context.SegGs); + }*/ + + if (context.ContextFlags & CONTEXT_FLOATING_POINT) + { + emu.reg(x64_register::fpcw, context.FltSave.ControlWord); + emu.reg(x64_register::fpsw, context.FltSave.StatusWord); + emu.reg(x64_register::fptag, context.FltSave.TagWord); + + for (int i = 0; i < 8; i++) + { + const auto reg = static_cast(static_cast(x64_register::st0) + i); + emu.reg(reg, context.FltSave.FloatRegisters[i]); + } + } + + if (context.ContextFlags & CONTEXT_XSTATE) + { + emu.reg(x64_register::mxcsr, context.MxCsr); + + for (int i = 0; i < 16; i++) + { + const auto reg = static_cast(static_cast(x64_register::xmm0) + i); + emu.reg(reg, (&context.Xmm0)[i]); + } + } + } + + void save(x64_emulator& emu, CONTEXT& context) + { + if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS) + { + context.Dr0 = emu.reg(x64_register::dr0); + context.Dr1 = emu.reg(x64_register::dr1); + context.Dr2 = emu.reg(x64_register::dr2); + context.Dr3 = emu.reg(x64_register::dr3); + context.Dr6 = emu.reg(x64_register::dr6); + context.Dr7 = emu.reg(x64_register::dr7); + } + + if (context.ContextFlags & CONTEXT_CONTROL) + { + context.SegSs = emu.reg(x64_register::ss); + context.SegCs = emu.reg(x64_register::cs); + context.Rip = emu.reg(x64_register::rip); + context.Rsp = emu.reg(x64_register::rsp); + context.EFlags = emu.reg(x64_register::eflags); + } + + if (context.ContextFlags & CONTEXT_INTEGER) + { + context.Rax = emu.reg(x64_register::rax); + context.Rbx = emu.reg(x64_register::rbx); + context.Rcx = emu.reg(x64_register::rcx); + context.Rdx = emu.reg(x64_register::rdx); + context.Rbp = emu.reg(x64_register::rbp); + context.Rsi = emu.reg(x64_register::rsi); + context.Rdi = emu.reg(x64_register::rdi); + context.R8 = emu.reg(x64_register::r8); + context.R9 = emu.reg(x64_register::r9); + context.R10 = emu.reg(x64_register::r10); + context.R11 = emu.reg(x64_register::r11); + context.R12 = emu.reg(x64_register::r12); + context.R13 = emu.reg(x64_register::r13); + context.R14 = emu.reg(x64_register::r14); + context.R15 = emu.reg(x64_register::r15); + } + + if (context.ContextFlags & CONTEXT_SEGMENTS) + { + context.SegDs = emu.reg(x64_register::ds); + context.SegEs = emu.reg(x64_register::es); + context.SegFs = emu.reg(x64_register::fs); + context.SegGs = emu.reg(x64_register::gs); + } + + if (context.ContextFlags & CONTEXT_FLOATING_POINT) + { + context.FltSave.ControlWord = emu.reg(x64_register::fpcw); + context.FltSave.StatusWord = emu.reg(x64_register::fpsw); + context.FltSave.TagWord = static_cast(emu.reg(x64_register::fptag)); + for (int i = 0; i < 8; i++) + { + const auto reg = static_cast(static_cast(x64_register::st0) + i); + context.FltSave.FloatRegisters[i] = emu.reg(reg); + } + } + + if (context.ContextFlags & CONTEXT_XSTATE) + { + context.MxCsr = emu.reg(x64_register::mxcsr); + for (int i = 0; i < 16; i++) + { + const auto reg = static_cast(static_cast(x64_register::xmm0) + i); + (&context.Xmm0)[i] = emu.reg(reg); + } + } + } +} diff --git a/src/windows_emulator/context_frame.hpp b/src/windows_emulator/context_frame.hpp new file mode 100644 index 00000000..63f3e36b --- /dev/null +++ b/src/windows_emulator/context_frame.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "x64_emulator.hpp" + +namespace context_frame +{ + void save(x64_emulator& emu, CONTEXT& context); + void restore(x64_emulator& emu, const CONTEXT& context); +} diff --git a/src/windows_emulator/main.cpp b/src/windows_emulator/main.cpp index bf0cbaf8..34420f17 100644 --- a/src/windows_emulator/main.cpp +++ b/src/windows_emulator/main.cpp @@ -9,11 +9,13 @@ #include "reflect_extension.hpp" #include +#include #include #include "gdb_stub.hpp" #include "module_mapper.hpp" -#include +#include "context_frame.hpp" + #define GS_SEGMENT_ADDR 0x6000000ULL #define GS_SEGMENT_SIZE (20 << 20) // 20 MB @@ -122,6 +124,17 @@ namespace }); } + template + emulator_object allocate_object_on_stack(x64_emulator& emu) + { + const auto old_sp = emu.reg(x64_register::rsp); + const auto new_sp = align_down(old_sp - sizeof(CONTEXT), + std::max(alignof(CONTEXT), alignof(x64_emulator::pointer_type))); + emu.reg(x64_register::rsp, new_sp); + + return {emu, new_sp}; + } + void setup_stack(x64_emulator& emu, const uint64_t stack_base, const size_t stack_size) { emu.allocate_memory(stack_base, stack_size, memory_permission::read_write); @@ -598,6 +611,105 @@ namespace return 0; } + emulator_object save_context_on_stack(x64_emulator& emu) + { + CONTEXT ctx{}; + ctx.ContextFlags = CONTEXT_ALL; + context_frame::save(emu, ctx); + + const auto ctx_obj = allocate_object_on_stack(emu); + ctx_obj.write(ctx); + + return ctx_obj; + } + + using exception_record_map = std::unordered_map>; + + emulator_object save_exception_record_on_stack(x64_emulator& emu, const EXCEPTION_RECORD& record, + exception_record_map& record_mapping) + { + const auto record_obj = allocate_object_on_stack(emu); + record_obj.write(record); + + if (record.ExceptionRecord) + { + record_mapping[&record] = record_obj; + + emulator_object nested_record_obj{}; + const auto nested_record = record_mapping.find(record.ExceptionRecord); + + if (nested_record != record_mapping.end()) + { + nested_record_obj = nested_record->second; + } + else + { + nested_record_obj = save_exception_record_on_stack(emu, *record.ExceptionRecord, record_mapping); + } + + record_obj.access([&](EXCEPTION_RECORD& r) + { + r.ExceptionRecord = nested_record_obj.ptr(); + }); + } + + return record_obj; + } + + emulator_object save_exception_record_on_stack(x64_emulator& emu, const EXCEPTION_RECORD& record) + { + exception_record_map record_mapping{}; + return save_exception_record_on_stack(emu, record, record_mapping); + } + + uint32_t map_violation_operation_to_parameter(const memory_operation operation) + { + switch (operation) + { + default: + case memory_operation::read: + return 0; + case memory_operation::write: + return 1; + case memory_operation::exec: + return 1; + } + } + + EXCEPTION_POINTERS create_access_violation_exception_pointers(x64_emulator& emu, const uint64_t address, + const memory_operation operation) + { + EXCEPTION_RECORD record{}; + memset(&record, 0, sizeof(record)); + record.ExceptionCode = STATUS_ACCESS_VIOLATION; + record.ExceptionFlags = 0; + record.ExceptionRecord = nullptr; + record.ExceptionAddress = reinterpret_cast(address); + record.NumberParameters = 2; + record.ExceptionInformation[0] = map_violation_operation_to_parameter(operation); + record.ExceptionInformation[1] = address; + + EXCEPTION_POINTERS pointers{}; + pointers.ContextRecord = save_context_on_stack(emu).ptr(); + pointers.ExceptionRecord = save_exception_record_on_stack(emu, record).ptr(); + + return pointers; + } + + void dispatch_exception_pointers(x64_emulator& emu, uint64_t dispatcher, const EXCEPTION_POINTERS pointers) + { + emu.reg(x64_register::rcx, reinterpret_cast(pointers.ExceptionRecord)); + emu.reg(x64_register::rdx, reinterpret_cast(pointers.ContextRecord)); + emu.reg(x64_register::rip, dispatcher); + } + + void dispatch_access_violation(x64_emulator& emu, uint64_t dispatcher, const uint64_t address, + const memory_operation operation) + { + const auto pointers = create_access_violation_exception_pointers(emu, address, operation); + dispatch_exception_pointers(emu, dispatcher, pointers); + } + void run() { const auto emu = unicorn::create_x64_emulator(); @@ -613,8 +725,10 @@ namespace context.ntdll = *map_file(*emu, R"(C:\Windows\System32\ntdll.dll)"); - const auto entry1 = find_exported_function(context.ntdll.exports, "LdrInitializeThunk"); - const auto entry2 = find_exported_function(context.ntdll.exports, "RtlUserThreadStart"); + const auto ldr_initialize_thunk = find_exported_function(context.ntdll.exports, "LdrInitializeThunk"); + const auto rtl_user_thread_start = find_exported_function(context.ntdll.exports, "RtlUserThreadStart"); + const auto ki_user_exception_dispatcher = find_exported_function( + context.ntdll.exports, "KiUserExceptionDispatcher"); syscall_dispatcher dispatcher{context.ntdll.exports}; @@ -642,6 +756,8 @@ namespace printf("Interrupt: %i\n", interrupt); }); + bool continue_execution = true; + emu->hook_memory_violation([&](const uint64_t address, const size_t size, const memory_operation operation, const memory_violation_type type) { @@ -657,6 +773,8 @@ namespace printf("Mapping violation: %llX (%zX) - %s at %llX\n", address, size, permission.c_str(), ip); } + dispatch_access_violation(*emu, ki_user_exception_dispatcher, address, operation); + continue_execution = true; return memory_violation_continuation::stop; }); @@ -681,17 +799,20 @@ namespace emu->reg(x64_register::rdi), emu->reg(x64_register::rsi)); });*/ - const auto execution_context = context.gs_segment.reserve(); - execution_context.access([&](CONTEXT& c) - { - c.Rip = entry2; - c.Rcx = context.executable.entry_point; - c.Rsp = emu->reg(x64_register::rsp); - }); + CONTEXT ctx{}; + ctx.ContextFlags = CONTEXT_ALL; - emu->reg(x64_register::rcx, execution_context.value()); + context_frame::save(*emu, ctx); + + ctx.Rip = rtl_user_thread_start; + ctx.Rcx = context.executable.entry_point; + + const auto ctx_obj = allocate_object_on_stack(*emu); + ctx_obj.write(ctx); + + emu->reg(x64_register::rcx, ctx_obj.value()); emu->reg(x64_register::rdx, context.ntdll.image_base); - emu->reg(x64_register::rip, entry1); + emu->reg(x64_register::rip, ldr_initialize_thunk); try { @@ -704,7 +825,21 @@ namespace } else { - emu->start_from_ip(); + while (continue_execution) + { + continue_execution = false; + try + { + emu->start_from_ip(); + } + catch (...) + { + if (!continue_execution) + { + throw; + } + } + } } } catch (...) diff --git a/src/windows_emulator/syscalls.cpp b/src/windows_emulator/syscalls.cpp index 80b9f1aa..f58dbb16 100644 --- a/src/windows_emulator/syscalls.cpp +++ b/src/windows_emulator/syscalls.cpp @@ -1,11 +1,13 @@ #include "std_include.hpp" #include "syscalls.hpp" #include "module_mapper.hpp" +#include "context_frame.hpp" struct syscall_context { x64_emulator& emu; process_context& proc; + mutable bool write_status; }; namespace @@ -77,46 +79,6 @@ namespace throw std::runtime_error("Unable to determine syscall id: " + std::string(name)); } - uint32_t store_os_handle(process_context& proc, const HANDLE handle) - { - uint32_t index = 1; - for (;; ++index) - { - if (!proc.os_handles.contains(index)) - { - break; - } - } - - proc.os_handles[index] = handle; - return index; - } - - std::optional get_os_handle(process_context& proc, const uint32_t handle) - { - const auto entry = proc.os_handles.find(handle); - if (entry == proc.os_handles.end()) - { - return {}; - } - - return entry->second; - } - - std::optional remove_os_handle(process_context& proc, const uint32_t handle) - { - const auto entry = proc.os_handles.find(handle); - if (entry == proc.os_handles.end()) - { - return {}; - } - - const auto res = entry->second; - proc.os_handles.erase(entry); - - return res; - } - std::wstring read_unicode_string(emulator& emu, const emulator_object uc_string) { static_assert(offsetof(UNICODE_STRING, Length) == 0); @@ -161,10 +123,18 @@ namespace return resolve_argument(emu, index++); } + void write_status(const syscall_context& c, const NTSTATUS status) + { + if (c.write_status) + { + c.emu.reg(x64_register::rax, static_cast(status)); + } + } + void forward(const syscall_context& c, NTSTATUS (*handler)()) { const auto ret = handler(); - c.emu.reg(x64_register::rax, static_cast(ret)); + write_status(c, ret); } template @@ -178,29 +148,7 @@ namespace }; const auto ret = std::apply(handler, std::move(func_args)); - c.emu.reg(x64_register::rax, ret); - } - - void apply_context(x64_emulator& emu, const CONTEXT& context) - { - emu.reg(x64_register::rax, context.Rax); - emu.reg(x64_register::rbx, context.Rbx); - emu.reg(x64_register::rcx, context.Rcx); - emu.reg(x64_register::rdx, context.Rdx); - emu.reg(x64_register::rsp, context.Rsp); - emu.reg(x64_register::rbp, context.Rbp); - emu.reg(x64_register::rsi, context.Rsi); - emu.reg(x64_register::rdi, context.Rdi); - emu.reg(x64_register::r8, context.R8); - emu.reg(x64_register::r9, context.R9); - emu.reg(x64_register::r10, context.R10); - emu.reg(x64_register::r11, context.R11); - emu.reg(x64_register::r12, context.R12); - emu.reg(x64_register::r13, context.R13); - emu.reg(x64_register::r14, context.R14); - emu.reg(x64_register::r15, context.R15); - - emu.reg(x64_register::rip, context.Rip); + write_status(c, ret); } NTSTATUS handle_NtQueryPerformanceCounter(const syscall_context&, @@ -1122,10 +1070,11 @@ namespace NTSTATUS handle_NtContinue(const syscall_context& c, const emulator_object thread_context, const BOOLEAN /*raise_alert*/) { - const auto context = thread_context.read(); - apply_context(c.emu, context); + c.write_status = false; + + const auto context = thread_context.read(); + context_frame::restore(c.emu, context); - // TODO return STATUS_SUCCESS; } @@ -1261,7 +1210,7 @@ void syscall_dispatcher::dispatch(x64_emulator& emu, process_context& context) printf("Handling syscall: %X (%llX)\n", syscall_id, address); - const syscall_context c{emu, context}; + const syscall_context c{emu, context, true}; try {