diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index 42a36cdb..b0e71ece 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -161,6 +161,11 @@ class emulator_object this->address_ = address; } + emulator_object shift(const int64_t offset) const + { + return emulator_object(*this->memory_, this->address_ + offset); + } + private: memory_interface* memory_{}; uint64_t address_{}; diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 3c7a90b5..29550928 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -361,15 +361,13 @@ namespace syscalls return STATUS_SUCCESS; } - NTSTATUS handle_NtContinueEx(const syscall_context& c, const emulator_object thread_context, + NTSTATUS handle_NtContinueEx(const syscall_context& c, emulator_object 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(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 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, diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 787afb16..713b720d 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -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; }