From 7cb1abb529e9c330f572cb161c5397221a96cc5e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 16 Oct 2024 19:19:36 +0200 Subject: [PATCH 1/7] Prepare thread support --- src/analyzer/main.cpp | 39 +----- src/windows-emulator/emulator_utils.hpp | 10 ++ src/windows-emulator/handles.hpp | 1 + src/windows-emulator/process_context.hpp | 158 ++++++++++++++++++++-- src/windows-emulator/syscalls.cpp | 30 +++- src/windows-emulator/windows_emulator.cpp | 130 +++++++++++------- src/windows-emulator/windows_emulator.hpp | 10 ++ 7 files changed, 277 insertions(+), 101 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index bbb56e02..a600880c 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -11,7 +11,7 @@ namespace { void watch_system_objects(windows_emulator& win_emu) { - watch_object(win_emu, win_emu.process().teb); + //watch_object(win_emu, *win_emu.current_thread().teb); watch_object(win_emu, win_emu.process().peb); watch_object(win_emu, win_emu.process().kusd); auto* params_hook = watch_object(win_emu, win_emu.process().process_params); @@ -86,43 +86,6 @@ namespace } }); - win_emu.add_syscall_hook([&] - { - // Read syscall id and name - - const auto syscall_id = win_emu.emu().reg(x64_register::eax); - const auto syscall_name = win_emu.dispatcher().get_syscall_name(syscall_id); - - - // Check if desired syscall - - if (syscall_name != "NtQueryInformationProcess") - { - return instruction_hook_continuation::run_instruction; - } - - // Check if image file name is read - - const auto info_class = win_emu.emu().reg(x64_register::rdx); - if (info_class != ProcessImageFileNameWin32) - { - return instruction_hook_continuation::run_instruction; - } - - // Patch result and feed expected filename - - win_emu.logger.print(color::pink, "Patching NtQueryInformationProcess...\n"); - - const auto data = win_emu.emu().reg(x64_register::r8); - - emulator_allocator data_allocator{win_emu.emu(), data, 0x100}; - data_allocator.make_unicode_string(L"C:\\Users\\Maurice\\Desktop\\protected.exe"); - - win_emu.emu().reg(x64_register::rax, STATUS_SUCCESS); - - return instruction_hook_continuation::skip_instruction; - }); - run_emulation(win_emu); } } diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index c5dd9ea1..a05c5881 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -181,6 +181,16 @@ public: buffer.read(this->active_address_); } + void release() + { + if (this->emu_ && this->address_ && this->size_) + { + this->emu_->release_memory(this->address_, this->size_); + this->address_ = 0; + this->size_ = 0; + } + } + private: emulator* emu_{}; uint64_t address_{}; diff --git a/src/windows-emulator/handles.hpp b/src/windows-emulator/handles.hpp index 97d16683..a1a8b1ad 100644 --- a/src/windows-emulator/handles.hpp +++ b/src/windows-emulator/handles.hpp @@ -11,6 +11,7 @@ struct handle_types directory, semaphore, port, + thread, }; }; diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 3e6d686b..b553db23 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -106,15 +106,141 @@ struct port } }; +struct process_context; + +class moved_marker +{ +public: + moved_marker() = default; + + moved_marker(const moved_marker& copy) = default; + moved_marker& operator=(const moved_marker&) = default; + + moved_marker(moved_marker&& obj) noexcept + : moved_marker() + { + this->operator=(std::move(obj)); + } + + moved_marker& operator=(moved_marker&& obj) noexcept + { + if (this != &obj) + { + this->was_moved_ = obj.was_moved_; + obj.was_moved_ = true; + } + + return *this; + } + + ~moved_marker() = default; + + bool was_moved() const + { + return this->was_moved_; + } + +private: + bool was_moved_{false}; +}; + +class emulator_thread +{ +public: + emulator_thread() = default; + + emulator_thread(x64_emulator& emu, std::vector default_register_set, const uint64_t start_address, + const uint64_t argument, + const uint64_t stack_size) + : emu_ptr(&emu) + , stack_size(page_align_up(stack_size)) + , start_address(start_address) + , argument(argument) + , last_registers(std::move(default_register_set)) + { + } + + emulator_thread(const emulator_thread&) = delete; + emulator_thread& operator=(const emulator_thread&) = delete; + + emulator_thread(emulator_thread&& obj) noexcept = default; + emulator_thread& operator=(emulator_thread&& obj) noexcept = default; + + ~emulator_thread() + { + if (marker.was_moved()) + { + return; + } + + if (this->stack_base) + { + this->emu_ptr->release_memory(this->stack_base, this->stack_size); + } + + if (this->gs_segment) + { + this->gs_segment->release(); + } + } + + moved_marker marker{}; + + x64_emulator* emu_ptr{}; + + uint32_t id{}; + + uint64_t stack_base{}; + uint64_t stack_size{}; + uint64_t start_address{}; + uint64_t argument{}; + uint64_t executed_instructions{0}; + + std::optional gs_segment; + std::optional> teb; + + std::vector last_registers{}; + + void save(x64_emulator& emu) + { + this->last_registers = emu.save_registers(); + } + + void restore(x64_emulator& emu) const + { + emu.restore_registers(this->last_registers); + } + + void setup_if_necessary(x64_emulator& emu, const process_context& context) + { + if (!this->teb.has_value()) + { + this->setup(emu, context); + } + } + + void serialize(utils::buffer_serializer&) const + { + // TODO + } + + void deserialize(utils::buffer_deserializer&) + { + // TODO + } + +private: + void setup(x64_emulator& emu, const process_context& context); +}; + struct process_context { process_context(x64_emulator& emu) - : teb(emu) + : base_allocator(emu) , peb(emu) , process_params(emu) , kusd(emu) , module_manager(emu) - , gs_segment(emu) { } @@ -124,7 +250,8 @@ struct process_context std::optional exception_rip{}; - emulator_object teb; + emulator_allocator base_allocator; + emulator_object peb; emulator_object process_params; emulator_object kusd; @@ -135,6 +262,8 @@ struct process_context mapped_module* ntdll{}; mapped_module* win32u{}; + uint64_t ldr_initialize_thunk{}; + uint64_t rtl_user_thread_start{}; uint64_t ki_user_exception_dispatcher{}; uint64_t shared_section_size{}; @@ -144,7 +273,11 @@ struct process_context handle_store semaphores{}; handle_store ports{}; std::map atoms{}; - emulator_allocator gs_segment; + + std::vector default_register_set{}; + + handle_store threads{}; + emulator_thread* active_thread{nullptr}; void serialize(utils::buffer_serializer& buffer) const { @@ -152,7 +285,6 @@ struct process_context buffer.write(this->current_ip); buffer.write(this->previous_ip); buffer.write_optional(this->exception_rip); - buffer.write(this->teb); buffer.write(this->peb); buffer.write(this->process_params); buffer.write(this->kusd); @@ -170,7 +302,8 @@ struct process_context buffer.write(this->semaphores); buffer.write(this->ports); buffer.write_map(this->atoms); - buffer.write(this->gs_segment); + + // TODO: Serialize/deserialize threads } void deserialize(utils::buffer_deserializer& buffer) @@ -179,7 +312,6 @@ struct process_context buffer.read(this->current_ip); buffer.read(this->previous_ip); buffer.read_optional(this->exception_rip); - buffer.read(this->teb); buffer.read(this->peb); buffer.read(this->process_params); buffer.read(this->kusd); @@ -201,6 +333,16 @@ struct process_context buffer.read(this->semaphores); buffer.read(this->ports); buffer.read_map(this->atoms); - buffer.read(this->gs_segment); + } + + handle create_thread(x64_emulator& emu, const uint64_t start_address, const uint64_t argument, + const uint64_t stack_size) + { + emulator_thread t{emu, default_register_set, start_address, argument, stack_size}; + + const handle h = this->threads.store(std::move(t)); + this->threads.get(h)->id = h.value.id; + + return h; } }; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 0cc71f3b..f954595e 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -494,7 +494,7 @@ namespace const emulator_object sysdir_obj{c.emu, obj_address + windir_obj.size()}; sysdir_obj.access([&](UNICODE_STRING& ucs) { - c.proc.gs_segment.make_unicode_string(ucs, L"C:\\WINDOWS\\System32"); + c.proc.base_allocator.make_unicode_string(ucs, L"C:\\WINDOWS\\System32"); ucs.Buffer = reinterpret_cast(reinterpret_cast(ucs.Buffer) - obj_address); }); @@ -1136,8 +1136,8 @@ namespace const emulator_object info{c.emu, thread_information}; info.access([&](THREAD_BASIC_INFORMATION& i) { - i.TebBaseAddress = c.proc.teb.ptr(); - i.ClientId = c.proc.teb.read().ClientId; + i.TebBaseAddress = c.win_emu.current_thread().teb->ptr(); + i.ClientId = c.win_emu.current_thread().teb->read().ClientId; }); return STATUS_SUCCESS; @@ -1556,7 +1556,7 @@ namespace { if (!peb.GdiSharedHandleTable) { - peb.GdiSharedHandleTable = c.proc.gs_segment.reserve().ptr(); + peb.GdiSharedHandleTable = c.proc.base_allocator.reserve().ptr(); } }); @@ -1881,8 +1881,8 @@ namespace return STATUS_SUCCESS; } - NTSTATUS handle_NtUnmapViewOfSection(const syscall_context& c, uint64_t process_handle, uint64_t base_address - ) + NTSTATUS handle_NtUnmapViewOfSection(const syscall_context& c, const uint64_t process_handle, + const uint64_t base_address) { if (process_handle != ~0ULL) { @@ -1902,6 +1902,23 @@ namespace c.emu.stop(); return STATUS_NOT_SUPPORTED; } + + NTSTATUS handle_NtCreateThreadEx(const syscall_context& c, const emulator_object thread_handle, + const ACCESS_MASK /*desired_access*/, + const emulator_object object_attributes, + const uint64_t process_handle, const uint64_t start_routine, + const uint64_t argument, const ULONG create_flags, const SIZE_T zero_bits, + const SIZE_T stack_size, const SIZE_T maximum_stack_size) + { + if (process_handle != ~0ULL) + { + return STATUS_NOT_SUPPORTED; + } + + const auto h = c.proc.create_thread(c.emu, start_routine, argument, stack_size); + thread_handle.write(h.bits); + return STATUS_SUCCESS; + } } void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports) @@ -2000,6 +2017,7 @@ void syscall_dispatcher::add_handlers() add_handler(NtQueryInformationJobObject); add_handler(NtSetSystemInformation); add_handler(NtQueryInformationFile); + add_handler(NtCreateThreadEx); #undef add_handler diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 9b44eaef..e5b09a0c 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -4,13 +4,12 @@ #include -#define GS_SEGMENT_ADDR 0x6000000ULL -#define GS_SEGMENT_SIZE (20 << 20) // 20 MB +#define PEB_SEGMENT_SIZE (1 << 20) // 1 MB + +#define GS_SEGMENT_SIZE (1 << 20) // 1 MB #define IA32_GS_BASE_MSR 0xC0000101 -#define STACK_SIZE 0x40000 -#define STACK_ADDRESS (0x80000000000 - STACK_SIZE) #define KUSD_ADDRESS 0x7ffe0000 #define GDT_ADDR 0x30000 @@ -37,15 +36,16 @@ namespace emu.reg(x64_register::rsp, sp); } - void setup_stack(x64_emulator& emu, const uint64_t stack_base, const size_t stack_size) + uint64_t setup_stack(x64_emulator& emu, const size_t stack_size) { - emu.allocate_memory(stack_base, stack_size, memory_permission::read_write); + const auto stack_base = emu.allocate_memory(stack_size, memory_permission::read_write); const uint64_t stack_end = stack_base + stack_size; emu.reg(x64_register::rsp, stack_end); + return stack_base; } - emulator_allocator setup_gs_segment(x64_emulator& emu, const uint64_t segment_base, const uint64_t size) + emulator_allocator setup_gs_segment(x64_emulator& emu, const uint64_t size) { struct msr_value { @@ -53,13 +53,14 @@ namespace uint64_t value; }; + const auto segment_base = emu.allocate_memory(size, memory_permission::read_write); + const msr_value value{ IA32_GS_BASE_MSR, segment_base }; emu.write_register(x64_register::msr, &value, sizeof(value)); - emu.allocate_memory(segment_base, size, memory_permission::read_write); return {emu, segment_base, size}; } @@ -174,8 +175,6 @@ namespace hash_entries_obj.write(hash_entry, i); } - //watch_object(emu, api_set_map_obj); - return api_set_map_obj; } @@ -209,28 +208,14 @@ namespace void setup_context(process_context& context, x64_emulator& emu, const std::filesystem::path& file, const std::vector& arguments) { - setup_stack(emu, STACK_ADDRESS, STACK_SIZE); setup_gdt(emu); context.kusd = setup_kusd(emu); - context.gs_segment = setup_gs_segment(emu, GS_SEGMENT_ADDR, GS_SEGMENT_SIZE); - auto allocator = create_allocator(emu, 1 << 20); + context.base_allocator = create_allocator(emu, PEB_SEGMENT_SIZE); + auto& allocator = context.base_allocator; - auto& gs = context.gs_segment; - - context.teb = gs.reserve(); - context.peb = gs.reserve(); - - context.teb.access([&](TEB& teb) - { - teb.ClientId.UniqueProcess = reinterpret_cast(1); - teb.ClientId.UniqueThread = reinterpret_cast(2); - teb.NtTib.StackLimit = reinterpret_cast(STACK_ADDRESS); - teb.NtTib.StackBase = reinterpret_cast((STACK_ADDRESS + STACK_SIZE)); - teb.NtTib.Self = &context.teb.ptr()->NtTib; - teb.ProcessEnvironmentBlock = context.peb.ptr(); - }); + context.peb = allocator.reserve(); /* Values of the following fields must be * allocated relative to the process_params themselves. @@ -247,7 +232,7 @@ namespace * RedirectionDllName */ - context.process_params = gs.reserve(); + context.process_params = allocator.reserve(); context.process_params.access([&](RTL_USER_PROCESS_PARAMETERS& proc_params) { @@ -267,11 +252,11 @@ namespace command_line.append(arg); } - gs.make_unicode_string(proc_params.CommandLine, command_line); + allocator.make_unicode_string(proc_params.CommandLine, command_line); //gs.make_unicode_string(proc_params.CurrentDirectory.DosPath, file.parent_path().wstring()); - gs.make_unicode_string(proc_params.ImagePathName, file.wstring()); + allocator.make_unicode_string(proc_params.ImagePathName, file.wstring()); - const auto total_length = gs.get_next_address() - context.process_params.value(); + const auto total_length = allocator.get_next_address() - context.process_params.value(); proc_params.Length = static_cast(std::max(sizeof(proc_params), total_length)); proc_params.MaximumLength = proc_params.Length; @@ -449,6 +434,66 @@ namespace dispatch_exception_pointers(emu, dispatcher, pointers); } + + void switch_to_thread(x64_emulator& emu, process_context& context, emulator_thread& thread) + { + auto* active_thread = context.active_thread; + if (active_thread) + { + active_thread->save(emu); + } + + context.active_thread = &thread; + thread.restore(emu); + thread.setup_if_necessary(emu, context); + } + + void switch_to_thread(x64_emulator& emu, process_context& context, const handle thread_handle) + { + auto* thread = context.threads.get(thread_handle); + if (!thread) + { + throw std::runtime_error("Bad thread handle"); + } + + switch_to_thread(emu, context, *thread); + } +} + +void emulator_thread::setup(x64_emulator& emu, const process_context& context) +{ + this->stack_base = setup_stack(emu, this->stack_size); + this->gs_segment = setup_gs_segment(emu, GS_SEGMENT_SIZE); + + this->teb = this->gs_segment->reserve(); + + this->teb->access([&](TEB& teb_obj) + { + teb_obj.ClientId.UniqueProcess = reinterpret_cast(1); + teb_obj.ClientId.UniqueThread = reinterpret_cast(static_cast(this->id)); + teb_obj.NtTib.StackLimit = reinterpret_cast(this->stack_base); + teb_obj.NtTib.StackBase = reinterpret_cast(this->stack_base + this->stack_size); + teb_obj.NtTib.Self = &this->teb->ptr()->NtTib; + teb_obj.ProcessEnvironmentBlock = context.peb.ptr(); + }); + + CONTEXT ctx{}; + ctx.ContextFlags = CONTEXT_ALL; + + unalign_stack(emu); + context_frame::save(emu, ctx); + + ctx.Rip = context.rtl_user_thread_start; + ctx.Rcx = this->start_address; + + const auto ctx_obj = allocate_object_on_stack(emu); + ctx_obj.write(ctx); + + unalign_stack(emu); + + emu.reg(x64_register::rcx, ctx_obj.value()); + emu.reg(x64_register::rdx, context.ntdll->image_base); + emu.reg(x64_register::rip, context.ldr_initialize_thunk); } std::unique_ptr create_default_x64_emulator() @@ -492,27 +537,14 @@ void windows_emulator::setup_process(const std::filesystem::path& application, this->dispatcher_.setup(context.ntdll->exports, context.win32u->exports); - const auto ldr_initialize_thunk = context.ntdll->find_export("LdrInitializeThunk"); - const auto rtl_user_thread_start = context.ntdll->find_export("RtlUserThreadStart"); + context.ldr_initialize_thunk = context.ntdll->find_export("LdrInitializeThunk"); + context.rtl_user_thread_start = context.ntdll->find_export("RtlUserThreadStart"); context.ki_user_exception_dispatcher = context.ntdll->find_export("KiUserExceptionDispatcher"); - CONTEXT ctx{}; - ctx.ContextFlags = CONTEXT_ALL; + context.default_register_set = emu.save_registers(); - unalign_stack(emu); - 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(emu); - ctx_obj.write(ctx); - - unalign_stack(emu); - - emu.reg(x64_register::rcx, ctx_obj.value()); - emu.reg(x64_register::rdx, context.ntdll->image_base); - emu.reg(x64_register::rip, ldr_initialize_thunk); + const auto main_thread_id = context.create_thread(emu, context.executable->entry_point, 0, 0x4000); + switch_to_thread(emu, context, main_thread_id); } void windows_emulator::setup_hooks() diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index e02ccb5e..11f70878 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -51,6 +51,16 @@ public: return this->dispatcher_; } + emulator_thread& current_thread() const + { + if (!this->process_.active_thread) + { + throw std::runtime_error("No active thread!"); + } + + return *this->process_.active_thread; + } + void serialize(utils::buffer_serializer& buffer) const; void deserialize(utils::buffer_deserializer& buffer); From 933bfcaaf3c0bb2f45868a9fe256421b0ddaa7f3 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 17 Oct 2024 19:02:14 +0200 Subject: [PATCH 2/7] Apply thread attributes --- src/windows-emulator/process_context.hpp | 46 +++++++++-------- src/windows-emulator/syscalls.cpp | 61 ++++++++++++++++++++++- src/windows-emulator/windows_emulator.cpp | 57 ++++++++++----------- 3 files changed, 113 insertions(+), 51 deletions(-) diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index b553db23..f8ba9e18 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -8,6 +8,19 @@ #include +#define PEB_SEGMENT_SIZE (1 << 20) // 1 MB +#define GS_SEGMENT_SIZE (1 << 20) // 1 MB + +#define IA32_GS_BASE_MSR 0xC0000101 + +#define KUSD_ADDRESS 0x7ffe0000 + +#define STACK_SIZE 0x40000ULL + +#define GDT_ADDR 0x30000 +#define GDT_LIMIT 0x1000 +#define GDT_ENTRY_SIZE 0x8 + struct event { bool signaled{}; @@ -149,16 +162,8 @@ class emulator_thread public: emulator_thread() = default; - emulator_thread(x64_emulator& emu, std::vector default_register_set, const uint64_t start_address, - const uint64_t argument, - const uint64_t stack_size) - : emu_ptr(&emu) - , stack_size(page_align_up(stack_size)) - , start_address(start_address) - , argument(argument) - , last_registers(std::move(default_register_set)) - { - } + emulator_thread(x64_emulator& emu, const process_context& context, uint64_t start_address, uint64_t argument, + uint64_t stack_size, uint32_t id); emulator_thread(const emulator_thread&) = delete; emulator_thread& operator=(const emulator_thread&) = delete; @@ -188,14 +193,14 @@ public: x64_emulator* emu_ptr{}; - uint32_t id{}; - uint64_t stack_base{}; uint64_t stack_size{}; uint64_t start_address{}; uint64_t argument{}; uint64_t executed_instructions{0}; + uint32_t id{}; + std::optional gs_segment; std::optional> teb; @@ -211,11 +216,11 @@ public: emu.restore_registers(this->last_registers); } - void setup_if_necessary(x64_emulator& emu, const process_context& context) + void setup_if_necessary(x64_emulator& emu, const process_context& context) const { - if (!this->teb.has_value()) + if (!this->executed_instructions) { - this->setup(emu, context); + this->setup_registers(emu, context); } } @@ -230,7 +235,7 @@ public: } private: - void setup(x64_emulator& emu, const process_context& context); + void setup_registers(x64_emulator& emu, const process_context& context) const; }; struct process_context @@ -276,6 +281,7 @@ struct process_context std::vector default_register_set{}; + uint32_t current_thread_id{0}; handle_store threads{}; emulator_thread* active_thread{nullptr}; @@ -338,11 +344,7 @@ struct process_context handle create_thread(x64_emulator& emu, const uint64_t start_address, const uint64_t argument, const uint64_t stack_size) { - emulator_thread t{emu, default_register_set, start_address, argument, stack_size}; - - const handle h = this->threads.store(std::move(t)); - this->threads.get(h)->id = h.value.id; - - return h; + emulator_thread t{emu, *this, start_address, argument, stack_size, ++this->current_thread_id}; + return this->threads.store(std::move(t)); } }; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index f954595e..0ebdda8c 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -161,6 +161,20 @@ namespace }; } + template + void write_attribute(emulator& emu, const PS_ATTRIBUTE& attribute, const T& value) + { + if (attribute.ReturnLength) + { + emulator_object{emu, attribute.ReturnLength}.write(sizeof(T)); + } + + if (attribute.Size >= sizeof(T)) + { + emulator_object{emu, attribute.Value}.write(value); + } + } + NTSTATUS handle_NtQueryPerformanceCounter(const syscall_context&, const emulator_object performance_counter, const emulator_object performance_frequency) @@ -866,6 +880,7 @@ namespace { if (info_class == SystemFlushInformation || info_class == SystemFeatureConfigurationInformation + || info_class == SystemSupportedProcessorArchitectures2 || info_class == SystemFeatureConfigurationSectionInformation) { //printf("Unsupported, but allowed system info class: %X\n", info_class); @@ -1195,6 +1210,7 @@ namespace || info_class == ProcessTlsInformation || info_class == ProcessConsoleHostProcess || info_class == ProcessFaultInformation + || info_class == ProcessDefaultHardErrorMode || info_class == ProcessRaiseUMExceptionOnInvalidHandleClose) { return STATUS_SUCCESS; @@ -1908,7 +1924,8 @@ namespace const emulator_object object_attributes, const uint64_t process_handle, const uint64_t start_routine, const uint64_t argument, const ULONG create_flags, const SIZE_T zero_bits, - const SIZE_T stack_size, const SIZE_T maximum_stack_size) + const SIZE_T stack_size, const SIZE_T maximum_stack_size, + const emulator_object attribute_list) { if (process_handle != ~0ULL) { @@ -1917,8 +1934,49 @@ namespace const auto h = c.proc.create_thread(c.emu, start_routine, argument, stack_size); thread_handle.write(h.bits); + + if (!attribute_list) + { + return STATUS_SUCCESS; + } + + const auto* thread = c.proc.threads.get(h); + + const emulator_object attributes{ + c.emu, attribute_list.value() + offsetof(PS_ATTRIBUTE_LIST, Attributes) + }; + + const auto total_length = attribute_list.read().TotalLength; + + constexpr auto entry_size = sizeof(PS_ATTRIBUTE); + constexpr auto header_size = sizeof(PS_ATTRIBUTE_LIST) - entry_size; + const auto attribute_count = (total_length - header_size) / entry_size; + + for (size_t i = 0; i < attribute_count; ++i) + { + attributes.access([&](const PS_ATTRIBUTE& attribute) + { + const auto type = attribute.Attribute & ~PS_ATTRIBUTE_THREAD; + + if (type == PsAttributeClientId) + { + const auto client_id = thread->teb->read().ClientId; + write_attribute(c.emu, attribute, client_id); + } + else if (type == PsAttributeTebAddress) + { + write_attribute(c.emu, attribute, thread->teb->ptr()); + } + }, i); + } + return STATUS_SUCCESS; } + + NTSTATUS handle_NtQueryDebugFilterState() + { + return FALSE; + } } void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports) @@ -2018,6 +2076,7 @@ void syscall_dispatcher::add_handlers() add_handler(NtSetSystemInformation); add_handler(NtQueryInformationFile); add_handler(NtCreateThreadEx); + add_handler(NtQueryDebugFilterState); #undef add_handler diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index e5b09a0c..1307bb06 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -4,18 +4,6 @@ #include -#define PEB_SEGMENT_SIZE (1 << 20) // 1 MB - -#define GS_SEGMENT_SIZE (1 << 20) // 1 MB - -#define IA32_GS_BASE_MSR 0xC0000101 - -#define KUSD_ADDRESS 0x7ffe0000 - -#define GDT_ADDR 0x30000 -#define GDT_LIMIT 0x1000 -#define GDT_ENTRY_SIZE 0x8 - namespace { template @@ -36,16 +24,13 @@ namespace emu.reg(x64_register::rsp, sp); } - uint64_t setup_stack(x64_emulator& emu, const size_t stack_size) + void setup_stack(x64_emulator& emu, const uint64_t stack_base, const size_t stack_size) { - const auto stack_base = emu.allocate_memory(stack_size, memory_permission::read_write); - const uint64_t stack_end = stack_base + stack_size; emu.reg(x64_register::rsp, stack_end); - return stack_base; } - emulator_allocator setup_gs_segment(x64_emulator& emu, const uint64_t size) + void setup_gs_segment(x64_emulator& emu, const emulator_allocator& allocator) { struct msr_value { @@ -53,16 +38,12 @@ namespace uint64_t value; }; - const auto segment_base = emu.allocate_memory(size, memory_permission::read_write); - const msr_value value{ IA32_GS_BASE_MSR, - segment_base + allocator.get_base() }; emu.write_register(x64_register::msr, &value, sizeof(value)); - - return {emu, segment_base, size}; } emulator_object setup_kusd(x64_emulator& emu) @@ -218,7 +199,7 @@ namespace context.peb = allocator.reserve(); /* Values of the following fields must be - * allocated relative to the process_params themselves. + * allocated relative to the process_params themselves * and included in the length: * * CurrentDirectory @@ -329,7 +310,6 @@ namespace case memory_operation::read: return 0; case memory_operation::write: - return 1; case memory_operation::exec: return 1; } @@ -460,10 +440,24 @@ namespace } } -void emulator_thread::setup(x64_emulator& emu, const process_context& context) +emulator_thread::emulator_thread(x64_emulator& emu, const process_context& context, + const uint64_t start_address, + const uint64_t argument, + const uint64_t stack_size, const uint32_t id) + : emu_ptr(&emu) + , stack_size(page_align_up(std::max(stack_size, STACK_SIZE))) + , start_address(start_address) + , argument(argument) + , last_registers(context.default_register_set) + , id(id) { - this->stack_base = setup_stack(emu, this->stack_size); - this->gs_segment = setup_gs_segment(emu, GS_SEGMENT_SIZE); + this->stack_base = emu.allocate_memory(this->stack_size, memory_permission::read_write); + + this->gs_segment = emulator_allocator{ + emu, + emu.allocate_memory(GS_SEGMENT_SIZE, memory_permission::read_write), + GS_SEGMENT_SIZE, + }; this->teb = this->gs_segment->reserve(); @@ -476,6 +470,12 @@ void emulator_thread::setup(x64_emulator& emu, const process_context& context) teb_obj.NtTib.Self = &this->teb->ptr()->NtTib; teb_obj.ProcessEnvironmentBlock = context.peb.ptr(); }); +} + +void emulator_thread::setup_registers(x64_emulator& emu, const process_context& context) const +{ + setup_stack(emu, this->stack_base, this->stack_size); + setup_gs_segment(emu, *this->gs_segment); CONTEXT ctx{}; ctx.ContextFlags = CONTEXT_ALL; @@ -485,6 +485,7 @@ void emulator_thread::setup(x64_emulator& emu, const process_context& context) ctx.Rip = context.rtl_user_thread_start; ctx.Rcx = this->start_address; + ctx.Rdx = this->argument; const auto ctx_obj = allocate_object_on_stack(emu); ctx_obj.write(ctx); @@ -543,7 +544,7 @@ void windows_emulator::setup_process(const std::filesystem::path& application, context.default_register_set = emu.save_registers(); - const auto main_thread_id = context.create_thread(emu, context.executable->entry_point, 0, 0x4000); + const auto main_thread_id = context.create_thread(emu, context.executable->entry_point, 0, 0); switch_to_thread(emu, context, main_thread_id); } From 71c8177ee30573a0e890db74b56eb4d2e2233a0d Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 17 Oct 2024 20:05:47 +0200 Subject: [PATCH 3/7] Quick & dirty thread switching support --- src/analyzer/main.cpp | 12 ++++++- src/windows-emulator/process_context.hpp | 2 ++ src/windows-emulator/syscalls.cpp | 37 +++++++++++++++++++ src/windows-emulator/windows_emulator.cpp | 43 ++++++++++++++++++++++- src/windows-emulator/windows_emulator.hpp | 3 ++ 5 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index a600880c..2d5f8421 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -48,7 +48,17 @@ namespace } else { - win_emu.emu().start_from_ip(); + while (true) + { + win_emu.emu().start_from_ip(); + if (win_emu.switch_thread) + { + win_emu.perform_thread_switch(); + continue; + } + + break; + } } } catch (...) diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index f8ba9e18..84fceb1e 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -201,6 +201,8 @@ public: uint32_t id{}; + std::optional await_object{}; + std::optional gs_segment; std::optional> teb; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 0ebdda8c..37bf6fd3 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -1967,6 +1967,10 @@ namespace { write_attribute(c.emu, attribute, thread->teb->ptr()); } + else + { + printf("Unsupported thread attribute type: %llX\n", type); + } }, i); } @@ -1977,6 +1981,38 @@ namespace { return FALSE; } + + NTSTATUS handle_NtWaitForSingleObject(const syscall_context& c, const uint64_t handle_value, + const BOOLEAN alertable, + const emulator_object timeout) + { + if (timeout.value()) + { + puts("NtWaitForSingleObject timeout not supported yet!"); + return STATUS_NOT_SUPPORTED; + } + + if (alertable) + { + puts("Alertable NtWaitForSingleObject not supported yet!"); + return STATUS_NOT_SUPPORTED; + } + + handle h{}; + h.bits = handle_value; + + if (h.value.type != handle_types::thread) + { + puts("NtWaitForSingleObject only supported with thread handles yet!"); + return STATUS_NOT_SUPPORTED; + } + + c.win_emu.current_thread().await_object = h; + c.win_emu.switch_thread = true; + c.emu.stop(); + + return STATUS_WAIT_0; + } } void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports) @@ -2077,6 +2113,7 @@ void syscall_dispatcher::add_handlers() add_handler(NtQueryInformationFile); add_handler(NtCreateThreadEx); add_handler(NtQueryDebugFilterState); + add_handler(NtWaitForSingleObject); #undef add_handler diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 1307bb06..e42aa53c 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -4,6 +4,8 @@ #include +constexpr auto MAX_INSTRUCTIONS_PER_TIME_SLICE = 100; + namespace { template @@ -438,6 +440,28 @@ namespace switch_to_thread(emu, context, *thread); } + + void switch_to_next_thread(x64_emulator& emu, process_context& context) + { + bool next_thread = false; + + + for (auto& thread : context.threads) + { + if (next_thread) + { + switch_to_thread(emu, context, thread.second); + return; + } + + if (&thread.second == context.active_thread) + { + next_thread = true; + } + } + + switch_to_thread(emu, context, context.threads.begin()->second); + } } emulator_thread::emulator_thread(x64_emulator& emu, const process_context& context, @@ -448,8 +472,8 @@ emulator_thread::emulator_thread(x64_emulator& emu, const process_context& conte , stack_size(page_align_up(std::max(stack_size, STACK_SIZE))) , start_address(start_address) , argument(argument) - , last_registers(context.default_register_set) , id(id) + , last_registers(context.default_register_set) { this->stack_base = emu.allocate_memory(this->stack_size, memory_permission::read_write); @@ -548,6 +572,13 @@ void windows_emulator::setup_process(const std::filesystem::path& application, switch_to_thread(emu, context, main_thread_id); } +void windows_emulator::perform_thread_switch() +{ + this->logger.print(color::green, "Performing thread switch...\n"); + switch_to_next_thread(this->emu(), this->process()); + this->switch_thread = false; +} + void windows_emulator::setup_hooks() { this->emu().hook_instruction(x64_hookable_instructions::syscall, [&] @@ -619,6 +650,16 @@ void windows_emulator::setup_hooks() ++process.executed_instructions; + auto& thread = this->current_thread(); + if (thread.executed_instructions == MAX_INSTRUCTIONS_PER_TIME_SLICE) + { + this->switch_thread = true; + this->emu().stop(); + } + + ++thread.executed_instructions; + thread.executed_instructions %= MAX_INSTRUCTIONS_PER_TIME_SLICE; + process.previous_ip = process.current_ip; process.current_ip = this->emu().read_instruction_pointer(); diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 11f70878..b221b2f4 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -77,6 +77,9 @@ public: bool verbose_calls{false}; bool buffer_stdout{false}; bool fuzzing{false}; + bool switch_thread{false}; + + void perform_thread_switch(); private: std::unique_ptr emu_{}; From d3ce5de9de3566d97ad4be9e75cb5c4d9f4f9d86 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 17 Oct 2024 20:22:04 +0200 Subject: [PATCH 4/7] More thread progress --- src/windows-emulator/handles.hpp | 5 ++++ src/windows-emulator/process_context.hpp | 1 + src/windows-emulator/syscalls.cpp | 38 ++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/windows-emulator/handles.hpp b/src/windows-emulator/handles.hpp index a1a8b1ad..05f26a82 100644 --- a/src/windows-emulator/handles.hpp +++ b/src/windows-emulator/handles.hpp @@ -133,6 +133,11 @@ public: return this->get(hh); } + size_t size() const + { + return this->store_.size(); + } + bool erase(const handle_value h) { const auto entry = this->get_iterator(h); diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 84fceb1e..98c70ce6 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -201,6 +201,7 @@ public: uint32_t id{}; + std::optional exit_status{}; std::optional await_object{}; std::optional gs_segment; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 37bf6fd3..e08aa44b 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -1158,6 +1158,24 @@ namespace return STATUS_SUCCESS; } + if (info_class == ThreadAmILastThread) + { + if (return_length) + { + return_length.write(sizeof(ULONG)); + } + + if (thread_information_length != sizeof(ULONG)) + { + return STATUS_BUFFER_OVERFLOW; + } + + const emulator_object info{c.emu, thread_information}; + info.write(c.proc.threads.size() <= 1); + + return STATUS_SUCCESS; + } + printf("Unsupported thread info class: %X\n", info_class); c.emu.stop(); @@ -2013,6 +2031,25 @@ namespace return STATUS_WAIT_0; } + + NTSTATUS handle_NtTerminateThread(const syscall_context& c, const uint64_t thread_handle, + const NTSTATUS exit_status) + { + auto* thread = c.proc.threads.get(thread_handle); + if (!thread) + { + return STATUS_INVALID_HANDLE; + } + + thread->exit_status = exit_status; + if (thread == c.proc.active_thread) + { + c.win_emu.switch_thread = true; + c.emu.stop(); + } + + return STATUS_SUCCESS; + } } void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports) @@ -2114,6 +2151,7 @@ void syscall_dispatcher::add_handlers() add_handler(NtCreateThreadEx); add_handler(NtQueryDebugFilterState); add_handler(NtWaitForSingleObject); + add_handler(NtTerminateThread); #undef add_handler From 669bf73fb6abdba7e22327a1cae063eddab1da1c Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Oct 2024 10:59:50 +0200 Subject: [PATCH 5/7] Basic working thread support --- src/windows-emulator/handles.hpp | 51 ++++++++++++++++++++--- src/windows-emulator/process_context.hpp | 34 +++++++++++---- src/windows-emulator/syscalls.cpp | 13 ++++-- src/windows-emulator/windows_emulator.cpp | 35 +++++++++++++++- 4 files changed, 114 insertions(+), 19 deletions(-) diff --git a/src/windows-emulator/handles.hpp b/src/windows-emulator/handles.hpp index 05f26a82..b0f7dd22 100644 --- a/src/windows-emulator/handles.hpp +++ b/src/windows-emulator/handles.hpp @@ -138,9 +138,8 @@ public: return this->store_.size(); } - bool erase(const handle_value h) + bool erase(const typename value_map::iterator& entry) { - const auto entry = this->get_iterator(h); if (entry == this->store_.end()) { return false; @@ -158,6 +157,12 @@ public: return true; } + bool erase(const handle_value h) + { + const auto entry = this->get_iterator(h); + return this->erase(entry); + } + bool erase(const handle h) { return this->erase(h.value); @@ -171,6 +176,12 @@ public: return this->erase(hh); } + bool erase(const T& value) + { + const auto entry = this->find(value); + return this->erase(entry); + } + void serialize(utils::buffer_serializer& buffer) const { buffer.write_map(this->store_); @@ -181,22 +192,50 @@ public: buffer.read_map(this->store_); } - value_map::iterator begin() + typename value_map::iterator find(const T& value) + { + auto i = this->store_.begin(); + for (; i != this->store_.end(); ++i) + { + if (&i->second == &value) + { + break; + } + } + + return i; + } + + typename value_map::const_iterator find(const T& value) const + { + auto i = this->store_.begin(); + for (; i != this->store_.end(); ++i) + { + if (&i->second == &value) + { + break; + } + } + + return i; + } + + typename value_map::iterator begin() { return this->store_.begin(); } - value_map::const_iterator begin() const + typename value_map::const_iterator begin() const { return this->store_.begin(); } - value_map::iterator end() + typename value_map::iterator end() { return this->store_.end(); } - value_map::const_iterator end() const + typename value_map::const_iterator end() const { return this->store_.end(); } diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 98c70ce6..0bd0e6e9 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -21,12 +21,31 @@ #define GDT_LIMIT 0x1000 #define GDT_ENTRY_SIZE 0x8 -struct event +struct ref_counted_object +{ + uint32_t ref_count{1}; + + void serialize(utils::buffer_serializer& buffer) const + { + buffer.write(this->ref_count); + } + + void deserialize(utils::buffer_deserializer& buffer) + { + buffer.read(this->ref_count); + } + + static bool deleter(ref_counted_object& e) + { + return --e.ref_count == 0; + } +}; + +struct event : ref_counted_object { bool signaled{}; EVENT_TYPE type{}; std::wstring name{}; - uint32_t ref_count{0}; bool is_signaled() { @@ -45,7 +64,8 @@ struct event buffer.write(this->signaled); buffer.write(this->type); buffer.write(this->name); - buffer.write(this->ref_count); + + ref_counted_object::serialize(buffer); } void deserialize(utils::buffer_deserializer& buffer) @@ -53,12 +73,8 @@ struct event buffer.read(this->signaled); buffer.read(this->type); buffer.read(this->name); - buffer.read(this->ref_count); - } - static bool deleter(event& e) - { - return --e.ref_count == 0; + ref_counted_object::deserialize(buffer); } }; @@ -157,7 +173,7 @@ private: bool was_moved_{false}; }; -class emulator_thread +class emulator_thread : ref_counted_object { public: emulator_thread() = default; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index e08aa44b..6b910e14 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -273,6 +273,11 @@ namespace return STATUS_SUCCESS; } + if (value.type == handle_types::thread && c.proc.threads.erase(handle)) + { + return STATUS_SUCCESS; + } + if (value.type == handle_types::event && c.proc.events.erase(handle)) { return STATUS_SUCCESS; @@ -319,7 +324,6 @@ namespace event e{}; e.type = event_type; e.signaled = initial_state != FALSE; - e.ref_count = 1; e.name = std::move(name); const auto handle = c.proc.events.store(std::move(e)); @@ -2032,10 +2036,13 @@ namespace return STATUS_WAIT_0; } - NTSTATUS handle_NtTerminateThread(const syscall_context& c, const uint64_t thread_handle, + NTSTATUS handle_NtTerminateThread(const syscall_context& c, uint64_t thread_handle, const NTSTATUS exit_status) { - auto* thread = c.proc.threads.get(thread_handle); + auto* thread = !thread_handle + ? c.proc.active_thread + : c.proc.threads.get(thread_handle); + if (!thread) { return STATUS_INVALID_HANDLE; diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index e42aa53c..0b9ddd68 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -430,6 +430,33 @@ namespace thread.setup_if_necessary(emu, context); } + void cleanup_threads(process_context& context) + { + while (true) + { + bool has_changed = false; + for (auto i = context.threads.begin(); i != context.threads.end(); ++i) + { + if (i->second.exit_status.has_value()) + { + if (&i->second == context.active_thread) + { + context.active_thread = nullptr; + } + + context.threads.erase(i); + has_changed = true; + break; + } + } + + if (!has_changed) + { + break; + } + } + } + void switch_to_thread(x64_emulator& emu, process_context& context, const handle thread_handle) { auto* thread = context.threads.get(thread_handle); @@ -443,13 +470,19 @@ namespace void switch_to_next_thread(x64_emulator& emu, process_context& context) { - bool next_thread = false; + //cleanup_threads(context); + bool next_thread = false; for (auto& thread : context.threads) { if (next_thread) { + if (thread.second.exit_status.has_value()) + { + continue; + } + switch_to_thread(emu, context, thread.second); return; } From 2a090821cdebc22ba9fc0f6251fe16c3f9f2f097 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Oct 2024 11:33:15 +0200 Subject: [PATCH 6/7] Don't buffer stdout --- src/analyzer/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 2d5f8421..554838c4 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -78,7 +78,7 @@ namespace (void)&watch_system_objects; //watch_system_objects(win_emu); - win_emu.buffer_stdout = true; + win_emu.buffer_stdout = false; //win_emu.verbose_calls = true; const auto& exe = *win_emu.process().executable; From 55237cc91e30dbfac63c189a7bf58495144c3d97 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Oct 2024 11:42:13 +0200 Subject: [PATCH 7/7] Fix warnings --- src/windows-emulator/syscalls.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 6b910e14..0b76d5b5 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -1943,10 +1943,10 @@ namespace NTSTATUS handle_NtCreateThreadEx(const syscall_context& c, const emulator_object thread_handle, const ACCESS_MASK /*desired_access*/, - const emulator_object object_attributes, + const emulator_object /*object_attributes*/, const uint64_t process_handle, const uint64_t start_routine, - const uint64_t argument, const ULONG create_flags, const SIZE_T zero_bits, - const SIZE_T stack_size, const SIZE_T maximum_stack_size, + const uint64_t argument, const ULONG /*create_flags*/, const SIZE_T /*zero_bits*/, + const SIZE_T stack_size, const SIZE_T /*maximum_stack_size*/, const emulator_object attribute_list) { if (process_handle != ~0ULL)