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);