Basic hacky working APC dispatching

This commit is contained in:
momo5502
2025-04-13 12:15:57 +02:00
parent a8926592bc
commit c1e621c2cd
3 changed files with 79 additions and 18 deletions

View File

@@ -161,6 +161,11 @@ class emulator_object
this->address_ = address;
}
emulator_object<T> shift(const int64_t offset) const
{
return emulator_object<T>(*this->memory_, this->address_ + offset);
}
private:
memory_interface* memory_{};
uint64_t address_{};

View File

@@ -361,15 +361,13 @@ namespace syscalls
return STATUS_SUCCESS;
}
NTSTATUS handle_NtContinueEx(const syscall_context& c, const emulator_object<CONTEXT64> thread_context,
NTSTATUS handle_NtContinueEx(const syscall_context& c, emulator_object<CONTEXT64> thread_context,
const uint64_t continue_argument)
{
c.write_status = false;
const auto context = thread_context.read();
cpu_context::restore(c.emu, context);
KCONTINUE_ARGUMENT argument{};
thread_context = thread_context.shift(0x20); // TODO: Figure out what that is? Extended context?
if (continue_argument <= 0xFF)
{
@@ -380,6 +378,9 @@ namespace syscalls
argument = c.emu.read_memory<KCONTINUE_ARGUMENT>(continue_argument);
}
const auto context = thread_context.read();
cpu_context::restore(c.emu, context);
if (argument.ContinueFlags & KCONTINUE_FLAG_TEST_ALERT)
{
c.win_emu.yield_thread(true);
@@ -391,7 +392,7 @@ namespace syscalls
NTSTATUS handle_NtContinue(const syscall_context& c, const emulator_object<CONTEXT64> thread_context,
const BOOLEAN raise_alert)
{
return handle_NtContinueEx(c, thread_context, raise_alert ? 1 : 0);
return handle_NtContinueEx(c, thread_context.shift(-0x20), raise_alert ? 1 : 0);
}
NTSTATUS handle_NtGetNextThread(const syscall_context& c, const handle process_handle, const handle thread_handle,

View File

@@ -79,6 +79,54 @@ namespace
return nullptr;
}
void dispatch_next_apc(windows_emulator& win_emu, emulator_thread& thread)
{
assert(&win_emu.current_thread() == &thread);
auto& emu = win_emu.emu();
auto& apcs = thread.pending_apcs;
if (apcs.empty())
{
return;
}
win_emu.log.print(color::dark_gray, "Dispatching APC...");
const auto next_apx = apcs.front();
apcs.erase(apcs.begin());
struct
{
uint64_t apc_argument1{};
uint64_t apc_argument2{};
uint64_t apc_argument3{};
uint64_t apc_routine{};
CONTEXT64 context{};
KCONTINUE_ARGUMENT continue_argument{};
} stack_layout;
static_assert(offsetof(decltype(stack_layout), continue_argument) == 0x4F0);
stack_layout.apc_routine = next_apx.apc_routine;
stack_layout.apc_argument1 = next_apx.apc_argument1;
stack_layout.apc_argument2 = next_apx.apc_argument2;
stack_layout.apc_argument3 = next_apx.apc_argument3;
stack_layout.continue_argument.ContinueFlags |= KCONTINUE_FLAG_TEST_ALERT;
auto& ctx = stack_layout.context;
ctx.ContextFlags = CONTEXT64_ALL;
cpu_context::save(emu, ctx);
const auto initial_sp = emu.reg(x64_register::rsp);
const auto new_sp = align_down(initial_sp - sizeof(stack_layout), 0x100);
emu.write_memory(new_sp, stack_layout);
emu.reg(x64_register::rsp, new_sp);
emu.reg(x64_register::rip, win_emu.process.ki_user_apc_dispatcher);
}
bool switch_to_thread(windows_emulator& win_emu, emulator_thread& thread, const bool force = false)
{
if (thread.is_terminated())
@@ -90,30 +138,37 @@ namespace
auto& context = win_emu.process;
const auto is_ready = thread.is_thread_ready(context, win_emu.clock());
const auto can_dispatch_apcs = thread.apc_alertable && !thread.pending_apcs.empty();
if (!is_ready && !force)
if (!is_ready && !force && !can_dispatch_apcs)
{
return false;
}
auto* active_thread = context.active_thread;
if (active_thread == &thread)
if (active_thread != &thread)
{
thread.setup_if_necessary(emu, context);
return true;
if (active_thread)
{
win_emu.log.print(color::dark_gray, "Performing thread switch: %X -> %X\n", active_thread->id,
thread.id);
active_thread->save(emu);
}
context.active_thread = &thread;
thread.restore(emu);
}
if (active_thread)
{
win_emu.log.print(color::dark_gray, "Performing thread switch: %X -> %X\n", active_thread->id, thread.id);
active_thread->save(emu);
}
context.active_thread = &thread;
thread.restore(emu);
thread.setup_if_necessary(emu, context);
if (can_dispatch_apcs)
{
dispatch_next_apc(win_emu, thread);
}
thread.apc_alertable = false;
return true;
}