Skip to content

Commit

Permalink
Prepare exception dispatching
Browse files Browse the repository at this point in the history
  • Loading branch information
momo5502 committed Sep 5, 2024
1 parent 7c6e4a2 commit e5b3dc9
Show file tree
Hide file tree
Showing 4 changed files with 323 additions and 80 deletions.
151 changes: 151 additions & 0 deletions src/windows_emulator/context_frame.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
8 changes: 8 additions & 0 deletions src/windows_emulator/context_frame.hpp
Original file line number Diff line number Diff line change
@@ -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);
}
161 changes: 148 additions & 13 deletions src/windows_emulator/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -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};

Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
});

Expand All @@ -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;

context_frame::save(*emu, ctx);

ctx.Rip = rtl_user_thread_start;
ctx.Rcx = context.executable.entry_point;

emu->reg(x64_register::rcx, execution_context.value());
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
{
Expand All @@ -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 (...)
Expand Down
Loading

0 comments on commit e5b3dc9

Please sign in to comment.