From f58dec38a2165e6584a8c2785d79cfc1de821e0e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 09:48:02 +0200 Subject: [PATCH 01/13] Prepare APC support --- src/windows-emulator/emulator_thread.hpp | 3 +++ src/windows-emulator/syscalls.cpp | 6 +++--- src/windows-emulator/syscalls/object.cpp | 14 ++------------ src/windows-emulator/syscalls/thread.cpp | 17 +++++++---------- src/windows-emulator/windows_emulator.cpp | 3 ++- src/windows-emulator/windows_emulator.hpp | 2 +- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/windows-emulator/emulator_thread.hpp b/src/windows-emulator/emulator_thread.hpp index b0cc364b..d3e34c3a 100644 --- a/src/windows-emulator/emulator_thread.hpp +++ b/src/windows-emulator/emulator_thread.hpp @@ -54,6 +54,7 @@ class emulator_thread : public ref_counted_object bool await_any{false}; bool waiting_for_alert{false}; bool alerted{false}; + bool apc_alertable{false}; uint32_t suspended{0}; std::optional await_time{}; @@ -123,6 +124,7 @@ class emulator_thread : public ref_counted_object buffer.write(this->waiting_for_alert); buffer.write(this->alerted); + buffer.write(this->apc_alertable); buffer.write(this->suspended); @@ -158,6 +160,7 @@ class emulator_thread : public ref_counted_object buffer.read(this->waiting_for_alert); buffer.read(this->alerted); + buffer.read(this->apc_alertable); buffer.read(this->suspended); diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 5a40a921..2d285295 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -413,10 +413,10 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - NTSTATUS handle_NtTestAlert() + NTSTATUS handle_NtTestAlert(const syscall_context& c) { - // puts("NtTestAlert not supported"); - return STATUS_NOT_SUPPORTED; + c.win_emu.yield_thread(true); + return STATUS_SUCCESS; } NTSTATUS handle_NtUserSystemParametersInfo() diff --git a/src/windows-emulator/syscalls/object.cpp b/src/windows-emulator/syscalls/object.cpp index 76ca46e4..7420e43e 100644 --- a/src/windows-emulator/syscalls/object.cpp +++ b/src/windows-emulator/syscalls/object.cpp @@ -116,11 +116,6 @@ namespace syscalls const emulator_object handles, const WAIT_TYPE wait_type, const BOOLEAN alertable, const emulator_object timeout) { - if (alertable) - { - c.win_emu.log.print(color::gray, "Alertable NtWaitForMultipleObjects not supported yet!\n"); - } - if (wait_type != WaitAny && wait_type != WaitAll) { c.win_emu.log.error("Wait type not supported!\n"); @@ -151,18 +146,13 @@ namespace syscalls t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), timeout.read()); } - c.win_emu.yield_thread(); + c.win_emu.yield_thread(alertable); return STATUS_SUCCESS; } NTSTATUS handle_NtWaitForSingleObject(const syscall_context& c, const handle h, const BOOLEAN alertable, const emulator_object timeout) { - if (alertable) - { - c.win_emu.log.print(color::gray, "Alertable NtWaitForSingleObject not supported yet!\n"); - } - if (!is_awaitable_object_type(h)) { c.win_emu.log.print(color::gray, "Unsupported handle type for NtWaitForSingleObject: %d!\n", h.value.type); @@ -178,7 +168,7 @@ namespace syscalls t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), timeout.read()); } - c.win_emu.yield_thread(); + c.win_emu.yield_thread(alertable); return STATUS_SUCCESS; } diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 8c65a5a1..f9b98484 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -282,17 +282,9 @@ namespace syscalls NTSTATUS handle_NtDelayExecution(const syscall_context& c, const BOOLEAN alertable, const emulator_object delay_interval) { - if (alertable) - { - c.win_emu.log.error("Alertable NtDelayExecution not supported yet!\n"); - c.emu.stop(); - return STATUS_NOT_SUPPORTED; - } - auto& t = c.win_emu.current_thread(); t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), delay_interval.read()); - - c.win_emu.yield_thread(); + c.win_emu.yield_thread(alertable); return STATUS_SUCCESS; } @@ -370,10 +362,15 @@ namespace syscalls } NTSTATUS handle_NtContinue(const syscall_context& c, const emulator_object thread_context, - const BOOLEAN /*raise_alert*/) + const BOOLEAN raise_alert) { c.write_status = false; + if (raise_alert) + { + c.win_emu.current_thread().apc_alertable = false; + } + const auto context = thread_context.read(); cpu_context::restore(c.emu, context); diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 8bdb30bb..787afb16 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -298,9 +298,10 @@ void windows_emulator::setup_process(const application_settings& app_settings) switch_to_thread(*this, main_thread_id); } -void windows_emulator::yield_thread() +void windows_emulator::yield_thread(const bool alertable) { this->switch_thread_ = true; + this->current_thread().apc_alertable = alertable; this->emu().stop(); } diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 986f0be7..5a68f614 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -183,7 +183,7 @@ class windows_emulator bool buffer_stdout{false}; bool fuzzing{false}; - void yield_thread(); + void yield_thread(bool alertable = false); void perform_thread_switch(); bool activate_thread(uint32_t id); From c0252a5f7146d7a5cb5eb4195da6208e53a1559f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 10:04:42 +0200 Subject: [PATCH 02/13] Add partial NtContinueEx support --- src/common/platform/threading.hpp | 19 +++++++++++++++ src/windows-emulator/syscalls.cpp | 5 +++- src/windows-emulator/syscalls/thread.cpp | 31 ++++++++++++++++++------ 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/common/platform/threading.hpp b/src/common/platform/threading.hpp index 96e61816..32fa1c75 100644 --- a/src/common/platform/threading.hpp +++ b/src/common/platform/threading.hpp @@ -90,4 +90,23 @@ typedef struct _THREAD_TEB_INFORMATION ULONG BytesToRead; // Number of bytes to read. } THREAD_TEB_INFORMATION, *PTHREAD_TEB_INFORMATION; +typedef enum _KCONTINUE_TYPE +{ + KCONTINUE_UNWIND, + KCONTINUE_RESUME, + KCONTINUE_LONGJUMP, + KCONTINUE_SET, + KCONTINUE_LAST, +} KCONTINUE_TYPE; + +typedef struct _KCONTINUE_ARGUMENT +{ + KCONTINUE_TYPE ContinueType; + ULONG ContinueFlags; + ULONGLONG Reserved[2]; +} KCONTINUE_ARGUMENT, *PKCONTINUE_ARGUMENT; + +#define KCONTINUE_FLAG_TEST_ALERT 0x00000001 +#define KCONTINUE_FLAG_DELIVER_APC 0x00000002 + // NOLINTEND(modernize-use-using) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 2d285295..0ee6e89e 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -270,7 +270,9 @@ namespace syscalls NTSTATUS handle_NtResumeThread(const syscall_context& c, handle thread_handle, emulator_object previous_suspend_count); NTSTATUS handle_NtContinue(const syscall_context& c, emulator_object thread_context, - BOOLEAN /*raise_alert*/); + BOOLEAN raise_alert); + NTSTATUS handle_NtContinueEx(const syscall_context& c, emulator_object thread_context, + uint64_t continue_argument); NTSTATUS handle_NtGetNextThread(const syscall_context& c, handle process_handle, handle thread_handle, ACCESS_MASK /*desired_access*/, ULONG /*handle_attributes*/, ULONG flags, emulator_object new_thread_handle); @@ -664,6 +666,7 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtQueryLicenseValue); add_handler(NtTestAlert); add_handler(NtContinue); + add_handler(NtContinueEx); add_handler(NtTerminateProcess); add_handler(NtWriteFile); add_handler(NtRaiseHardError); diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index f9b98484..12446f8a 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -361,22 +361,39 @@ namespace syscalls return STATUS_SUCCESS; } - NTSTATUS handle_NtContinue(const syscall_context& c, const emulator_object thread_context, - const BOOLEAN raise_alert) + NTSTATUS handle_NtContinueEx(const syscall_context& c, const emulator_object thread_context, + const uint64_t continue_argument) { c.write_status = false; - if (raise_alert) - { - c.win_emu.current_thread().apc_alertable = false; - } - const auto context = thread_context.read(); cpu_context::restore(c.emu, context); + KCONTINUE_ARGUMENT argument{}; + + if (continue_argument <= 0xFF) + { + argument.ContinueFlags = KCONTINUE_FLAG_TEST_ALERT; + } + else + { + argument = c.emu.read_memory(continue_argument); + } + + if (argument.ContinueFlags & KCONTINUE_FLAG_TEST_ALERT) + { + c.win_emu.yield_thread(true); + } + return STATUS_SUCCESS; } + 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); + } + NTSTATUS handle_NtGetNextThread(const syscall_context& c, const handle process_handle, const handle thread_handle, const ACCESS_MASK /*desired_access*/, const ULONG /*handle_attributes*/, const ULONG flags, const emulator_object new_thread_handle) From 71e8aab91319f2af248ee70095bc5e612d3e78ab Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 10:22:15 +0200 Subject: [PATCH 03/13] Ignore linter warnings --- src/common/platform/threading.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/platform/threading.hpp b/src/common/platform/threading.hpp index 32fa1c75..256a6ef7 100644 --- a/src/common/platform/threading.hpp +++ b/src/common/platform/threading.hpp @@ -1,6 +1,6 @@ #pragma once -// NOLINTBEGIN(modernize-use-using) +// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) typedef enum _THREADINFOCLASS { @@ -109,4 +109,4 @@ typedef struct _KCONTINUE_ARGUMENT #define KCONTINUE_FLAG_TEST_ALERT 0x00000001 #define KCONTINUE_FLAG_DELIVER_APC 0x00000002 -// NOLINTEND(modernize-use-using) +// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) From 9609a550e01b116cd1b4ca26ae0aa596182e7f9e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 10:22:46 +0200 Subject: [PATCH 04/13] Add APC test --- src/samples/test-sample/test.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/samples/test-sample/test.cpp b/src/samples/test-sample/test.cpp index 62254120..4fc7c567 100644 --- a/src/samples/test-sample/test.cpp +++ b/src/samples/test-sample/test.cpp @@ -431,6 +431,26 @@ namespace const auto epoch_time = std::chrono::system_clock::now().time_since_epoch(); printf("Time: %lld\n", std::chrono::duration_cast(epoch_time).count()); } + + bool test_apc() + { + bool executed = false; + QueueUserAPC( + +[](ULONG_PTR param) { + *reinterpret_cast(param) = true; // + }, + GetCurrentThread(), reinterpret_cast(&executed)); + + Sleep(1); + + if (executed) + { + return false; + } + + SleepEx(1, TRUE); + return executed; + } } #define RUN_TEST(func, name) \ @@ -461,8 +481,7 @@ int main(const int argc, const char* argv[]) RUN_TEST(test_native_exceptions, "Native Exceptions") RUN_TEST(test_tls, "TLS") RUN_TEST(test_socket, "Socket") - - Sleep(1); + RUN_TEST(test_apc, "APC") return valid ? 0 : 1; } From 82227e4d4158a468e1aa00ca6413e0231a84e51f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 10:32:22 +0200 Subject: [PATCH 05/13] Prepare NtQueueApcThreadEx2 support --- src/windows-emulator/syscalls.cpp | 4 ++++ src/windows-emulator/syscalls/thread.cpp | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 0ee6e89e..480b4834 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -291,6 +291,9 @@ namespace syscalls emulator_object>> attribute_list); NTSTATUS handle_NtGetCurrentProcessorNumberEx(const syscall_context&, emulator_object processor_number); + NTSTATUS handle_NtQueueApcThreadEx2(const syscall_context& c, handle thread_handle, handle reserve_handle, + ULONG apc_flags, uint64_t apc_routine, uint64_t apc_argument1, + uint64_t apc_argument2, uint64_t apc_argument3); // syscalls/timer.cpp: NTSTATUS handle_NtQueryTimerResolution(const syscall_context&, emulator_object maximum_time, @@ -752,6 +755,7 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtClearEvent); add_handler(NtTraceControl); add_handler(NtUserGetProcessUIContextInformation); + add_handler(NtQueueApcThreadEx2); #undef add_handler } \ No newline at end of file diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 12446f8a..db30c708 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -563,4 +563,12 @@ namespace syscalls processor_number.write(number); return STATUS_SUCCESS; } + + NTSTATUS handle_NtQueueApcThreadEx2(const syscall_context& c, const handle thread_handle, + const handle reserve_handle, const ULONG apc_flags, const uint64_t apc_routine, + const uint64_t apc_argument1, const uint64_t apc_argument2, + const uint64_t apc_argument3) + { + return STATUS_NOT_SUPPORTED; + } } \ No newline at end of file From fdab8cd5ecdf3b35a1bd4dbb0511241aa37ee57f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 10:37:39 +0200 Subject: [PATCH 06/13] Extend APC test --- src/samples/test-sample/test.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/samples/test-sample/test.cpp b/src/samples/test-sample/test.cpp index 4fc7c567..a3b03a34 100644 --- a/src/samples/test-sample/test.cpp +++ b/src/samples/test-sample/test.cpp @@ -434,22 +434,24 @@ namespace bool test_apc() { - bool executed = false; - QueueUserAPC( - +[](ULONG_PTR param) { - *reinterpret_cast(param) = true; // - }, - GetCurrentThread(), reinterpret_cast(&executed)); + int executions = 0; + + auto* apc_func = +[](ULONG_PTR param) { + *reinterpret_cast(param) += 1; // + }; + + QueueUserAPC(apc_func, GetCurrentThread(), reinterpret_cast(&executions)); + QueueUserAPC(apc_func, GetCurrentThread(), reinterpret_cast(&executions)); Sleep(1); - if (executed) + if (executions != 0) { return false; } SleepEx(1, TRUE); - return executed; + return executions == 2; } } From bb4b159850c595b0118ba68ad0355f9d28e8ed37 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 10:56:28 +0200 Subject: [PATCH 07/13] Implement NtQueueApcThreadEx2 --- src/windows-emulator/emulator_thread.hpp | 43 +++++++++++++++++++++--- src/windows-emulator/syscalls/thread.cpp | 21 ++++++++++-- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/windows-emulator/emulator_thread.hpp b/src/windows-emulator/emulator_thread.hpp index d3e34c3a..0a646092 100644 --- a/src/windows-emulator/emulator_thread.hpp +++ b/src/windows-emulator/emulator_thread.hpp @@ -8,6 +8,33 @@ struct process_context; +struct pending_apc +{ + ULONG flags{}; + uint64_t apc_routine{}; + uint64_t apc_argument1{}; + uint64_t apc_argument2{}; + uint64_t apc_argument3{}; + + void serialize(utils::buffer_serializer& buffer) const + { + buffer.write(this->flags); + buffer.write(this->apc_routine); + buffer.write(this->apc_argument1); + buffer.write(this->apc_argument2); + buffer.write(this->apc_argument3); + } + + void deserialize(utils::buffer_deserializer& buffer) + { + buffer.read(this->flags); + buffer.read(this->apc_routine); + buffer.read(this->apc_argument1); + buffer.read(this->apc_argument2); + buffer.read(this->apc_argument3); + } +}; + class emulator_thread : public ref_counted_object { public: @@ -54,10 +81,12 @@ class emulator_thread : public ref_counted_object bool await_any{false}; bool waiting_for_alert{false}; bool alerted{false}; - bool apc_alertable{false}; uint32_t suspended{0}; std::optional await_time{}; + bool apc_alertable{false}; + std::vector pending_apcs{}; + std::optional pending_status{}; std::optional gs_segment; @@ -124,11 +153,13 @@ class emulator_thread : public ref_counted_object buffer.write(this->waiting_for_alert); buffer.write(this->alerted); - buffer.write(this->apc_alertable); buffer.write(this->suspended); - buffer.write_optional(this->await_time); + + buffer.write(this->apc_alertable); + buffer.write_vector(this->pending_apcs); + buffer.write_optional(this->pending_status); buffer.write_optional(this->gs_segment); buffer.write_optional(this->teb); @@ -160,11 +191,13 @@ class emulator_thread : public ref_counted_object buffer.read(this->waiting_for_alert); buffer.read(this->alerted); - buffer.read(this->apc_alertable); buffer.read(this->suspended); - buffer.read_optional(this->await_time); + + buffer.read(this->apc_alertable); + buffer.read_vector(this->pending_apcs); + buffer.read_optional(this->pending_status); buffer.read_optional(this->gs_segment, [this] { return emulator_allocator(*this->memory_ptr); }); buffer.read_optional(this->teb, [this] { return emulator_object(*this->memory_ptr); }); diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index db30c708..25ebd1ba 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -565,10 +565,25 @@ namespace syscalls } NTSTATUS handle_NtQueueApcThreadEx2(const syscall_context& c, const handle thread_handle, - const handle reserve_handle, const ULONG apc_flags, const uint64_t apc_routine, - const uint64_t apc_argument1, const uint64_t apc_argument2, - const uint64_t apc_argument3) + const handle /*reserve_handle*/, const ULONG apc_flags, + const uint64_t apc_routine, const uint64_t apc_argument1, + const uint64_t apc_argument2, const uint64_t apc_argument3) { + auto* thread = thread_handle == CURRENT_THREAD ? c.proc.active_thread : c.proc.threads.get(thread_handle); + + if (!thread) + { + return STATUS_INVALID_HANDLE; + } + + thread->pending_apcs.push_back({ + .flags = apc_flags, + .apc_routine = apc_routine, + .apc_argument1 = apc_argument1, + .apc_argument2 = apc_argument2, + .apc_argument3 = apc_argument3, + }); + return STATUS_NOT_SUPPORTED; } } \ No newline at end of file From a8926592bcb548aa4cba0f00d95f87087e4da01b Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 11:07:32 +0200 Subject: [PATCH 08/13] Prepare APC dispatching --- src/windows-emulator/emulator_thread.hpp | 2 +- src/windows-emulator/process_context.cpp | 3 +++ src/windows-emulator/process_context.hpp | 1 + src/windows-emulator/syscalls.cpp | 2 +- src/windows-emulator/syscalls/thread.cpp | 9 ++++++++- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/windows-emulator/emulator_thread.hpp b/src/windows-emulator/emulator_thread.hpp index 0a646092..eab80c8c 100644 --- a/src/windows-emulator/emulator_thread.hpp +++ b/src/windows-emulator/emulator_thread.hpp @@ -10,7 +10,7 @@ struct process_context; struct pending_apc { - ULONG flags{}; + uint32_t flags{}; uint64_t apc_routine{}; uint64_t apc_argument1{}; uint64_t apc_argument2{}; diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index f604eacd..bb720ec4 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -119,6 +119,7 @@ void process_context::setup(x64_emulator& emu, memory_manager& memory, const app this->ntdll_image_base = ntdll.image_base; this->ldr_initialize_thunk = ntdll.find_export("LdrInitializeThunk"); this->rtl_user_thread_start = ntdll.find_export("RtlUserThreadStart"); + this->ki_user_apc_dispatcher = ntdll.find_export("KiUserApcDispatcher"); this->ki_user_exception_dispatcher = ntdll.find_export("KiUserExceptionDispatcher"); this->default_register_set = emu.save_registers(); @@ -139,6 +140,7 @@ void process_context::serialize(utils::buffer_serializer& buffer) const buffer.write(this->ntdll_image_base); buffer.write(this->ldr_initialize_thunk); buffer.write(this->rtl_user_thread_start); + buffer.write(this->ki_user_apc_dispatcher); buffer.write(this->ki_user_exception_dispatcher); buffer.write(this->events); @@ -173,6 +175,7 @@ void process_context::deserialize(utils::buffer_deserializer& buffer) buffer.read(this->ntdll_image_base); buffer.read(this->ldr_initialize_thunk); buffer.read(this->rtl_user_thread_start); + buffer.read(this->ki_user_apc_dispatcher); buffer.read(this->ki_user_exception_dispatcher); buffer.read(this->events); diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index f01647b4..1be428b3 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -75,6 +75,7 @@ struct process_context uint64_t ntdll_image_base{}; uint64_t ldr_initialize_thunk{}; uint64_t rtl_user_thread_start{}; + uint64_t ki_user_apc_dispatcher{}; uint64_t ki_user_exception_dispatcher{}; handle_store events{}; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 480b4834..5675d0da 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -292,7 +292,7 @@ namespace syscalls NTSTATUS handle_NtGetCurrentProcessorNumberEx(const syscall_context&, emulator_object processor_number); NTSTATUS handle_NtQueueApcThreadEx2(const syscall_context& c, handle thread_handle, handle reserve_handle, - ULONG apc_flags, uint64_t apc_routine, uint64_t apc_argument1, + uint32_t apc_flags, uint64_t apc_routine, uint64_t apc_argument1, uint64_t apc_argument2, uint64_t apc_argument3); // syscalls/timer.cpp: diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 25ebd1ba..3c7a90b5 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -565,7 +565,7 @@ namespace syscalls } NTSTATUS handle_NtQueueApcThreadEx2(const syscall_context& c, const handle thread_handle, - const handle /*reserve_handle*/, const ULONG apc_flags, + const handle /*reserve_handle*/, const uint32_t apc_flags, const uint64_t apc_routine, const uint64_t apc_argument1, const uint64_t apc_argument2, const uint64_t apc_argument3) { @@ -576,6 +576,13 @@ namespace syscalls return STATUS_INVALID_HANDLE; } + if (apc_flags) + { + c.win_emu.log.error("Unsupported APC flags: %X\n", apc_flags); + c.emu.stop(); + return STATUS_NOT_SUPPORTED; + } + thread->pending_apcs.push_back({ .flags = apc_flags, .apc_routine = apc_routine, From c1e621c2cd7613c1b9f5bc71db9d30e50d281892 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 12:15:57 +0200 Subject: [PATCH 09/13] Basic hacky working APC dispatching --- src/windows-emulator/emulator_utils.hpp | 5 ++ src/windows-emulator/syscalls/thread.cpp | 11 +-- src/windows-emulator/windows_emulator.cpp | 81 +++++++++++++++++++---- 3 files changed, 79 insertions(+), 18 deletions(-) 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; } From a3f811a466febf66886f2c912397f0d097f48a6b Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 13:38:19 +0200 Subject: [PATCH 10/13] Support all APC queue syscalls --- src/windows-emulator/syscalls.cpp | 7 +++++++ src/windows-emulator/syscalls/thread.cpp | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 5675d0da..622cd4b9 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -294,6 +294,11 @@ namespace syscalls NTSTATUS handle_NtQueueApcThreadEx2(const syscall_context& c, handle thread_handle, handle reserve_handle, uint32_t apc_flags, uint64_t apc_routine, uint64_t apc_argument1, uint64_t apc_argument2, uint64_t apc_argument3); + NTSTATUS handle_NtQueueApcThreadEx(const syscall_context& c, handle thread_handle, handle reserve_handle, + uint64_t apc_routine, uint64_t apc_argument1, uint64_t apc_argument2, + uint64_t apc_argument3); + NTSTATUS handle_NtQueueApcThread(const syscall_context& c, handle thread_handle, uint64_t apc_routine, + uint64_t apc_argument1, uint64_t apc_argument2, uint64_t apc_argument3); // syscalls/timer.cpp: NTSTATUS handle_NtQueryTimerResolution(const syscall_context&, emulator_object maximum_time, @@ -756,6 +761,8 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtTraceControl); add_handler(NtUserGetProcessUIContextInformation); add_handler(NtQueueApcThreadEx2); + add_handler(NtQueueApcThreadEx); + add_handler(NtQueueApcThread); #undef add_handler } \ No newline at end of file diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 29550928..d11511ce 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -594,4 +594,21 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } + + NTSTATUS handle_NtQueueApcThreadEx(const syscall_context& c, const handle thread_handle, + const handle reserve_handle, const uint64_t apc_routine, + const uint64_t apc_argument1, const uint64_t apc_argument2, + const uint64_t apc_argument3) + { + return handle_NtQueueApcThreadEx2(c, thread_handle, reserve_handle, 0, apc_routine, apc_argument1, + apc_argument2, apc_argument3); + } + + NTSTATUS handle_NtQueueApcThread(const syscall_context& c, const handle thread_handle, const uint64_t apc_routine, + const uint64_t apc_argument1, const uint64_t apc_argument2, + const uint64_t apc_argument3) + { + return handle_NtQueueApcThreadEx(c, thread_handle, make_handle(0), apc_routine, apc_argument1, apc_argument2, + apc_argument3); + } } \ No newline at end of file From 30e77f5f9a4ec12467c8a4880a380eb6581cb714 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 13:50:44 +0200 Subject: [PATCH 11/13] Fix NtQueueApcThreadEx --- src/common/platform/threading.hpp | 9 +++++++++ src/windows-emulator/syscalls/thread.cpp | 13 +++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/common/platform/threading.hpp b/src/common/platform/threading.hpp index 256a6ef7..de430220 100644 --- a/src/common/platform/threading.hpp +++ b/src/common/platform/threading.hpp @@ -109,4 +109,13 @@ typedef struct _KCONTINUE_ARGUMENT #define KCONTINUE_FLAG_TEST_ALERT 0x00000001 #define KCONTINUE_FLAG_DELIVER_APC 0x00000002 +#ifndef OS_WINDOWS +typedef enum _QUEUE_USER_APC_FLAGS +{ + QUEUE_USER_APC_FLAGS_NONE, + QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC, + QUEUE_USER_APC_CALLBACK_DATA_CONTEXT +} QUEUE_USER_APC_FLAGS; +#endif + // NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index d11511ce..6c72d506 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -600,7 +600,16 @@ namespace syscalls const uint64_t apc_argument1, const uint64_t apc_argument2, const uint64_t apc_argument3) { - return handle_NtQueueApcThreadEx2(c, thread_handle, reserve_handle, 0, apc_routine, apc_argument1, + uint32_t flags{0}; + auto real_reserve_handle = reserve_handle; + if (reserve_handle.bits == QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC) + { + real_reserve_handle.bits = 0; + flags = QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC; + static_assert(QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC == 1); + } + + return handle_NtQueueApcThreadEx2(c, thread_handle, real_reserve_handle, flags, apc_routine, apc_argument1, apc_argument2, apc_argument3); } @@ -611,4 +620,4 @@ namespace syscalls return handle_NtQueueApcThreadEx(c, thread_handle, make_handle(0), apc_routine, apc_argument1, apc_argument2, apc_argument3); } -} \ No newline at end of file +} From 7910021124685b448fc5c7f02da0b13d25aff60b Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 14:28:35 +0200 Subject: [PATCH 12/13] Fix apc dispatching --- src/common/platform/process.hpp | 14 ++++++++++++++ src/windows-emulator/syscalls/thread.cpp | 6 ++---- src/windows-emulator/windows_emulator.cpp | 15 ++++++--------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/common/platform/process.hpp b/src/common/platform/process.hpp index ecc9ce2f..cd512b9d 100644 --- a/src/common/platform/process.hpp +++ b/src/common/platform/process.hpp @@ -670,6 +670,20 @@ typedef struct DECLSPEC_ALIGN(16) _CONTEXT64 DWORD64 LastExceptionFromRip; } CONTEXT64, *PCONTEXT64; +typedef struct _CONTEXT_CHUNK +{ + LONG Offset; // Offset may be negative. + ULONG Length; +} CONTEXT_CHUNK, *PCONTEXT_CHUNK; + +typedef struct _CONTEXT_EX +{ + CONTEXT_CHUNK All; + CONTEXT_CHUNK Legacy; + CONTEXT_CHUNK XState; + CONTEXT_CHUNK KernelCet; +} CONTEXT_EX, *PCONTEXT_EX; + template struct EMU_EXCEPTION_RECORD { diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 6c72d506..5a6b1f4c 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -361,14 +361,12 @@ namespace syscalls return STATUS_SUCCESS; } - NTSTATUS handle_NtContinueEx(const syscall_context& c, emulator_object thread_context, + NTSTATUS handle_NtContinueEx(const syscall_context& c, const emulator_object thread_context, const uint64_t continue_argument) { c.write_status = false; KCONTINUE_ARGUMENT argument{}; - thread_context = thread_context.shift(0x20); // TODO: Figure out what that is? Extended context? - if (continue_argument <= 0xFF) { argument.ContinueFlags = KCONTINUE_FLAG_TEST_ALERT; @@ -392,7 +390,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.shift(-0x20), raise_alert ? 1 : 0); + return handle_NtContinueEx(c, thread_context, 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 713b720d..b913f721 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -90,27 +90,24 @@ namespace return; } - win_emu.log.print(color::dark_gray, "Dispatching APC..."); + win_emu.log.print(color::dark_gray, "Dispatching APC...\n"); 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{}; + CONTEXT_EX context_ex{}; 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.context.P1Home = next_apx.apc_argument1; + stack_layout.context.P2Home = next_apx.apc_argument2; + stack_layout.context.P3Home = next_apx.apc_argument3; + stack_layout.context.P4Home = next_apx.apc_routine; stack_layout.continue_argument.ContinueFlags |= KCONTINUE_FLAG_TEST_ALERT; From 2ed35d94b92f461306dac56ff996655cd246b491 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Apr 2025 15:00:39 +0200 Subject: [PATCH 13/13] Set correct status --- src/common/platform/status.hpp | 1 + src/windows-emulator/windows_emulator.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/common/platform/status.hpp b/src/common/platform/status.hpp index 79d97a3b..7c2fd78e 100644 --- a/src/common/platform/status.hpp +++ b/src/common/platform/status.hpp @@ -6,6 +6,7 @@ using NTSTATUS = std::uint32_t; #ifndef OS_WINDOWS #define STATUS_WAIT_0 ((NTSTATUS)0x00000000L) +#define STATUS_USER_APC ((NTSTATUS)0x000000C0L) #define STATUS_TIMEOUT ((NTSTATUS)0x00000102L) #define STATUS_PENDING ((NTSTATUS)0x00000103L) diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index b913f721..1178b449 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -162,6 +162,7 @@ namespace if (can_dispatch_apcs) { + thread.mark_as_ready(STATUS_USER_APC); dispatch_next_apc(win_emu, thread); }