Prepare exception dispatching

This commit is contained in:
momo5502
2024-09-05 19:01:55 +02:00
parent 7c6e4a23da
commit e5b3dc953e
4 changed files with 324 additions and 81 deletions

View File

@@ -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<uint16_t>(x64_register::ss, context.SegSs);
emu.reg<uint16_t>(x64_register::cs, context.SegCs);
emu.reg(x64_register::rip, context.Rip);
emu.reg(x64_register::rsp, context.Rsp);
emu.reg<uint32_t>(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<uint16_t>(x64_register::ds, context.SegDs);
emu.reg<uint16_t>(x64_register::es, context.SegEs);
emu.reg<uint16_t>(x64_register::fs, context.SegFs);
emu.reg<uint16_t>(x64_register::gs, context.SegGs);
}*/
if (context.ContextFlags & CONTEXT_FLOATING_POINT)
{
emu.reg<uint16_t>(x64_register::fpcw, context.FltSave.ControlWord);
emu.reg<uint16_t>(x64_register::fpsw, context.FltSave.StatusWord);
emu.reg<uint16_t>(x64_register::fptag, context.FltSave.TagWord);
for (int i = 0; i < 8; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
emu.reg<M128A>(reg, context.FltSave.FloatRegisters[i]);
}
}
if (context.ContextFlags & CONTEXT_XSTATE)
{
emu.reg<uint32_t>(x64_register::mxcsr, context.MxCsr);
for (int i = 0; i < 16; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
emu.reg<M128A>(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<uint16_t>(x64_register::ss);
context.SegCs = emu.reg<uint16_t>(x64_register::cs);
context.Rip = emu.reg(x64_register::rip);
context.Rsp = emu.reg(x64_register::rsp);
context.EFlags = emu.reg<uint32_t>(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<uint16_t>(x64_register::ds);
context.SegEs = emu.reg<uint16_t>(x64_register::es);
context.SegFs = emu.reg<uint16_t>(x64_register::fs);
context.SegGs = emu.reg<uint16_t>(x64_register::gs);
}
if (context.ContextFlags & CONTEXT_FLOATING_POINT)
{
context.FltSave.ControlWord = emu.reg<uint16_t>(x64_register::fpcw);
context.FltSave.StatusWord = emu.reg<uint16_t>(x64_register::fpsw);
context.FltSave.TagWord = static_cast<BYTE>(emu.reg<uint16_t>(x64_register::fptag));
for (int i = 0; i < 8; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
context.FltSave.FloatRegisters[i] = emu.reg<M128A>(reg);
}
}
if (context.ContextFlags & CONTEXT_XSTATE)
{
context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr);
for (int i = 0; i < 16; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
(&context.Xmm0)[i] = emu.reg<M128A>(reg);
}
}
}
}

View File

@@ -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);
}

View File

@@ -9,11 +9,13 @@
#include "reflect_extension.hpp"
#include <reflect>
#include <address_utils.hpp>
#include <unicorn_x64_emulator.hpp>
#include "gdb_stub.hpp"
#include "module_mapper.hpp"
#include <address_utils.hpp>
#include "context_frame.hpp"
#define GS_SEGMENT_ADDR 0x6000000ULL
#define GS_SEGMENT_SIZE (20 << 20) // 20 MB
@@ -122,6 +124,17 @@ namespace
});
}
template <typename T>
emulator_object<T> 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<CONTEXT> 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<CONTEXT>(emu);
ctx_obj.write(ctx);
return ctx_obj;
}
using exception_record_map = std::unordered_map<const EXCEPTION_RECORD*, emulator_object<EXCEPTION_RECORD>>;
emulator_object<EXCEPTION_RECORD> 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<EXCEPTION_RECORD>(emu);
record_obj.write(record);
if (record.ExceptionRecord)
{
record_mapping[&record] = record_obj;
emulator_object<EXCEPTION_RECORD> 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<EXCEPTION_RECORD> 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<void*>(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<uint64_t>(pointers.ExceptionRecord));
emu.reg(x64_register::rdx, reinterpret_cast<uint64_t>(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<CONTEXT>();
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<CONTEXT>(*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 (...)

View File

@@ -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<HANDLE> 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<HANDLE> 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<UNICODE_STRING> uc_string)
{
static_assert(offsetof(UNICODE_STRING, Length) == 0);
@@ -161,10 +123,18 @@ namespace
return resolve_argument<T>(emu, index++);
}
void write_status(const syscall_context& c, const NTSTATUS status)
{
if (c.write_status)
{
c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
}
}
void forward(const syscall_context& c, NTSTATUS (*handler)())
{
const auto ret = handler();
c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(ret));
write_status(c, ret);
}
template <typename... Args>
@@ -178,29 +148,7 @@ namespace
};
const auto ret = std::apply(handler, std::move(func_args));
c.emu.reg<int64_t>(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<CONTEXT> 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
{