mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-21 20:53:56 +00:00
Prepare exception dispatching
This commit is contained in:
151
src/windows_emulator/context_frame.cpp
Normal file
151
src/windows_emulator/context_frame.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/windows_emulator/context_frame.hpp
Normal file
8
src/windows_emulator/context_frame.hpp
Normal 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);
|
||||
}
|
||||
@@ -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 (...)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user