diff --git a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp index 856dc743..87c0f1bf 100644 --- a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp +++ b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp @@ -178,6 +178,21 @@ namespace icicle } } + pointer_type get_segment_base(const x86_register base) override + { + switch (base) + { + case x86_register::fs: + case x86_register::fs_base: + return this->reg(x86_register::fs_base); + case x86_register::gs: + case x86_register::gs_base: + return this->reg(x86_register::gs_base); + default: + return 0; + } + } + size_t write_raw_register(const int reg, const void* value, const size_t size) override { return icicle_write_register(this->emu_, reg, value, size); @@ -348,7 +363,9 @@ namespace icicle const auto& func = *static_cast(user); const auto res = func(address, 1, static_cast(operation), violation_type); - return res == memory_violation_continuation::resume ? 1 : 0; + const auto restart = res == memory_violation_continuation::restart; + const auto resume = res == memory_violation_continuation::resume || restart; + return resume ? 1 : 0; }; const auto id = icicle_add_violation_hook(this->emu_, wrapper, ptr); diff --git a/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp index e2aaf56a..1219e9e7 100644 --- a/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp @@ -21,6 +21,15 @@ namespace unicorn static_assert(static_cast(x86_register::end) == UC_X86_REG_ENDING); + constexpr auto IA32_FS_BASE_MSR = 0xC0000100; + constexpr auto IA32_GS_BASE_MSR = 0xC0000101; + + struct msr_value + { + uint64_t id{}; + uint64_t value{}; + }; + uc_x86_insn map_hookable_instruction(const x86_hookable_instructions instruction) { switch (instruction) @@ -253,15 +262,6 @@ namespace unicorn void set_segment_base(const x86_register base, const pointer_type value) override { - constexpr auto IA32_FS_BASE_MSR = 0xC0000100; - constexpr auto IA32_GS_BASE_MSR = 0xC0000101; - - struct msr_value - { - uint64_t id{}; - uint64_t value{}; - }; - msr_value msr_val{ .id = 0, .value = value, @@ -272,12 +272,10 @@ namespace unicorn case x86_register::fs: case x86_register::fs_base: msr_val.id = IA32_FS_BASE_MSR; - preserved_fs_base_ = static_cast(value); break; case x86_register::gs: case x86_register::gs_base: msr_val.id = IA32_GS_BASE_MSR; - preserved_gs_base_ = static_cast(value); break; default: return; @@ -286,6 +284,30 @@ namespace unicorn this->write_register(x86_register::msr, &msr_val, sizeof(msr_val)); } + pointer_type get_segment_base(const x86_register base) override + { + msr_value msr_val{}; + + switch (base) + { + case x86_register::fs: + case x86_register::fs_base: + msr_val.id = IA32_FS_BASE_MSR; + break; + case x86_register::gs: + case x86_register::gs_base: + msr_val.id = IA32_GS_BASE_MSR; + break; + default: + return 0; + } + + size_t result_size = sizeof(msr_value); + uce(uc_reg_read2(*this, (int)x86_register::msr, &msr_val, &result_size)); + + return msr_val.value; + } + size_t write_raw_register(const int reg, const void* value, const size_t size) override { auto result_size = size; @@ -495,18 +517,19 @@ namespace unicorn const auto operation = map_memory_operation(type); const auto violation = map_memory_violation_type(type); - const auto resume = - c(address, static_cast(size), operation, violation) == memory_violation_continuation::resume; + const auto result = c(address, static_cast(size), operation, violation); + const auto restart = result == memory_violation_continuation::restart; + const auto resume = result == memory_violation_continuation::resume || restart; const auto new_ip = this->read_instruction_pointer(); - const auto has_ip_changed = ip != new_ip; + const auto set_ip = ip != new_ip || restart; if (!resume) { return false; } - if (resume && has_ip_changed) + if (resume && set_ip) { this->violation_ip_ = new_ip; } @@ -515,7 +538,7 @@ namespace unicorn this->violation_ip_ = std::nullopt; } - if (has_ip_changed) + if (set_ip) { return false; } @@ -538,17 +561,8 @@ namespace unicorn emulator_hook* hook_memory_execution(const uint64_t address, const uint64_t size, memory_execution_hook_callback callback) { - auto exec_wrapper = [c = std::move(callback), this](uc_engine*, const uint64_t address, const uint32_t /*size*/) { + auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address, const uint32_t /*size*/) { c(address); // - - // Fix unicorn bug? - const auto cs_current = this->reg(x86_register::cs); - if (this->current_reg_cs_ != cs_current) - { - this->set_segment_base(x86_register::gs, preserved_gs_base_); - this->set_segment_base(x86_register::fs, preserved_fs_base_); - this->current_reg_cs_ = cs_current; - } }; function_wrapper wrapper(std::move(exec_wrapper)); @@ -657,11 +671,6 @@ namespace unicorn const uc_context_serializer serializer(this->uc_, is_snapshot); serializer.serialize(buffer); - - // Serialize unicorn bug workaround state - buffer.write(this->preserved_gs_base_); - buffer.write(this->preserved_fs_base_); - buffer.write(this->current_reg_cs_); } void deserialize_state(utils::buffer_deserializer& buffer, const bool is_snapshot) override @@ -674,10 +683,6 @@ namespace unicorn const uc_context_serializer serializer(this->uc_, is_snapshot); serializer.deserialize(buffer); - // Deserialize unicorn bug workaround state - buffer.read(this->preserved_gs_base_); - buffer.read(this->preserved_fs_base_); - buffer.read(this->current_reg_cs_); } std::vector save_registers() const override @@ -712,11 +717,6 @@ namespace unicorn std::vector> hooks_{}; std::unordered_map mmio_{}; - // gs & fs base (Fix unicorn Bug?) - mutable uint64_t preserved_gs_base_{0}; - mutable uint64_t preserved_fs_base_{0}; - mutable uint16_t current_reg_cs_{0x33}; - static uint64_t calc_end_address(const uint64_t address, uint64_t size) { if (size == 0) diff --git a/src/common/platform/kernel_mapped.hpp b/src/common/platform/kernel_mapped.hpp index bc1e6310..b1d2b714 100644 --- a/src/common/platform/kernel_mapped.hpp +++ b/src/common/platform/kernel_mapped.hpp @@ -21,6 +21,7 @@ #define WIN32_CLIENT_INFO_LENGTH 62 #define STATIC_UNICODE_BUFFER_LENGTH 261 #define TLS_MINIMUM_AVAILABLE 64 +#define TLS_EXPANSION_SLOTS 1024 #ifndef OS_WINDOWS #define PF_FLOATING_POINT_PRECISION_ERRATA 0 @@ -970,6 +971,13 @@ union TEB_CROSS_TEB_FLAGS_UNION USHORT SpareCrossTebBits : 16; }; +constexpr auto THREAD_CREATE_FLAGS_CREATE_SUSPENDED = 0x1; +constexpr auto THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH = 0x2; +constexpr auto THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER = 0x4; +constexpr auto THREAD_CREATE_FLAGS_LOADER_WORKER = 0x10; +constexpr auto THREAD_CREATE_FLAGS_SKIP_LOADER_INIT = 0x20; +constexpr auto THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE = 0x40; + union TEB_SAME_TEB_FLAGS_UNION { USHORT SameTebFlags; @@ -1671,45 +1679,45 @@ typedef struct _KERNEL_USER_TIMES LARGE_INTEGER UserTime; } KERNEL_USER_TIMES, *PKERNEL_USER_TIMES; -struct THREAD_TLS_INFO +struct THREAD_TLS_INFORMATION { ULONG Flags; uint32_t _Padding; union { - EmulatorTraits::PVOID TlsVector; - EmulatorTraits::PVOID TlsModulePointer; + EmulatorTraits::PVOID NewTlsData; + EmulatorTraits::PVOID OldTlsData; }; - EMULATOR_CAST(std::uint64_t, ULONG_PTR) ThreadId; + uint64_t ThreadId; }; -static_assert(sizeof(THREAD_TLS_INFO) == 0x18); +static_assert(sizeof(THREAD_TLS_INFORMATION) == 0x18); -typedef enum _PROCESS_TLS_INFORMATION_TYPE +enum PROCESS_TLS_INFORMATION_TYPE { ProcessTlsReplaceIndex, ProcessTlsReplaceVector, MaxProcessTlsOperation -} PROCESS_TLS_INFORMATION_TYPE, *PPROCESS_TLS_INFORMATION_TYPE; +}; -struct PROCESS_TLS_INFO +struct PROCESS_TLS_INFORMATION { - ULONG Unknown; - PROCESS_TLS_INFORMATION_TYPE TlsRequest; + ULONG Flags; + PROCESS_TLS_INFORMATION_TYPE OperationType; ULONG ThreadDataCount; union { ULONG TlsIndex; - ULONG TlsVectorLength; + ULONG PreviousCount; }; - THREAD_TLS_INFO ThreadData[1]; + THREAD_TLS_INFORMATION ThreadData[1]; }; -static_assert(sizeof(PROCESS_TLS_INFO) - sizeof(THREAD_TLS_INFO) == 0x10); +static_assert(sizeof(PROCESS_TLS_INFORMATION) == 0x28); struct EMU_GENERIC_MAPPING { diff --git a/src/emulator/arch_emulator.hpp b/src/emulator/arch_emulator.hpp index f20283a1..2177497d 100644 --- a/src/emulator/arch_emulator.hpp +++ b/src/emulator/arch_emulator.hpp @@ -35,6 +35,7 @@ struct x86_emulator : arch_emulator using pointer_type = typename Traits::pointer_type; virtual void set_segment_base(register_type base, pointer_type value) = 0; + virtual pointer_type get_segment_base(register_type base) = 0; virtual void load_gdt(pointer_type address, uint32_t limit) = 0; }; diff --git a/src/emulator/hook_interface.hpp b/src/emulator/hook_interface.hpp index 7c3c6f1b..3edbb08c 100644 --- a/src/emulator/hook_interface.hpp +++ b/src/emulator/hook_interface.hpp @@ -16,10 +16,11 @@ enum class instruction_hook_continuation : bool skip_instruction = true, }; -enum class memory_violation_continuation : bool +enum class memory_violation_continuation : uint8_t { - stop = false, - resume = true, + stop, + resume, + restart, }; enum class memory_violation_type : uint8_t diff --git a/src/windows-emulator/emulator_thread.cpp b/src/windows-emulator/emulator_thread.cpp index 7e578c05..844b2dd3 100644 --- a/src/windows-emulator/emulator_thread.cpp +++ b/src/windows-emulator/emulator_thread.cpp @@ -19,7 +19,7 @@ namespace descriptor |= ((base & 0xFF0000) << 16); // Base[23:16] descriptor |= (0xF3ULL << 40); // P=1, DPL=3, S=1, Type=3 (Data RW Accessed) descriptor |= (static_cast((limit & 0xF0000) >> 16) << 48); // Limit[19:16] - descriptor |= (0x40ULL << 52); // G=0 (byte), D=1 (32-bit), L=0, AVL=0 + descriptor |= (0x40ULL << 48); // G=0 (byte), D=1 (32-bit), L=0, AVL=0 descriptor |= ((base & 0xFF000000) << 32); // Base[31:24] // Write the updated descriptor to GDT index 10 (selector 0x53) @@ -116,15 +116,18 @@ namespace } emulator_thread::emulator_thread(memory_manager& memory, const process_context& context, const uint64_t start_address, - const uint64_t argument, const uint64_t stack_size, const bool suspended, const uint32_t id) + const uint64_t argument, const uint64_t stack_size, const uint32_t create_flags, const uint32_t id, + const bool initial_thread) : memory_ptr(&memory), // stack_size(page_align_up(std::max(stack_size, static_cast(STACK_SIZE)))), start_address(start_address), argument(argument), id(id), - suspended(suspended), + create_flags(create_flags), last_registers(context.default_register_set) { + this->suspended = create_flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED; + // native 64-bit if (!context.is_wow64_process) { @@ -152,6 +155,10 @@ emulator_thread::emulator_thread(memory_manager& memory, const process_context& teb_obj.NtTib.Self = this->teb64->value(); teb_obj.CurrentLocale = 0x409; teb_obj.ProcessEnvironmentBlock = context.peb64.value(); + teb_obj.SameTebFlags.InitialThread = initial_thread; + teb_obj.SameTebFlags.SkipThreadAttach = (create_flags & THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH) ? 1 : 0; + teb_obj.SameTebFlags.LoaderWorker = (create_flags & THREAD_CREATE_FLAGS_LOADER_WORKER) ? 1 : 0; + teb_obj.SameTebFlags.SkipLoaderInit = (create_flags & THREAD_CREATE_FLAGS_SKIP_LOADER_INIT) ? 1 : 0; }); return; @@ -209,6 +216,10 @@ emulator_thread::emulator_thread(memory_manager& memory, const process_context& teb_obj.CurrentLocale = 0x409; teb_obj.ProcessEnvironmentBlock = context.peb64.value(); + teb_obj.SameTebFlags.InitialThread = initial_thread; + teb_obj.SameTebFlags.SkipThreadAttach = (create_flags & THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH) ? 1 : 0; + teb_obj.SameTebFlags.LoaderWorker = (create_flags & THREAD_CREATE_FLAGS_LOADER_WORKER) ? 1 : 0; + teb_obj.SameTebFlags.SkipLoaderInit = (create_flags & THREAD_CREATE_FLAGS_SKIP_LOADER_INIT) ? 1 : 0; teb_obj.StaticUnicodeString.MaximumLength = sizeof(teb_obj.StaticUnicodeBuffer); teb_obj.StaticUnicodeString.Buffer = this->teb64->value() + offsetof(TEB64, StaticUnicodeBuffer); @@ -264,18 +275,18 @@ emulator_thread::emulator_thread(memory_manager& memory, const process_context& } teb32_obj.WowTebOffset = -0x2000; + teb32_obj.InitialThread = initial_thread; + teb32_obj.SkipThreadAttach = (create_flags & THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH) ? 1 : 0; + teb32_obj.LoaderWorker = (create_flags & THREAD_CREATE_FLAGS_LOADER_WORKER) ? 1 : 0; + teb32_obj.SkipLoaderInit = (create_flags & THREAD_CREATE_FLAGS_SKIP_LOADER_INIT) ? 1 : 0; // Note: CurrentLocale and other fields will be initialized by WOW64 runtime }); - // CRITICAL: Setup FS segment (0x53) to point to 32-bit TEB for accurate WOW64 emulation - // This mimics what Windows kernel does during NtCreateUserProcess for WOW64 processes - // Without this, FS:0 won't correctly access the 32-bit TEB - // - // NOTE: We cannot use set_segment_base() here because that sets the FS_BASE MSR - // which is for 64-bit flat addressing. 32-bit code uses actual GDT-based segmentation - // with selector 0x53, so we must modify the GDT entry directly. - setup_wow64_fs_segment(memory, teb32_addr); + this->teb64->access([&](TEB64& teb_obj) { + // teb64.ExceptionList initially points to teb32 + teb_obj.NtTib.ExceptionList = teb32_addr; + }); // Use the allocator to reserve memory for CONTEXT64 this->wow64_cpu_reserved = emulator_object{memory, wow64_cpureserved_base}; @@ -439,6 +450,7 @@ void emulator_thread::setup_registers(x86_64_emulator& emu, const process_contex if (context.rtl_user_thread_start32.has_value()) { ctx.Context.Eip = static_cast(context.rtl_user_thread_start32.value()); + ctx.Context.Ebx = static_cast(this->argument); } }); @@ -473,3 +485,14 @@ void emulator_thread::setup_registers(x86_64_emulator& emu, const process_contex emu.reg(x86_register::rdx, context.ntdll_image_base); emu.reg(x86_register::rip, context.ldr_initialize_thunk); } + +void emulator_thread::refresh_execution_context(x86_64_emulator& emu) const +{ + (void)emu; + + if (this->teb32.has_value()) + { + // Refresh GDT entry for FS selector on context switch + setup_wow64_fs_segment(*this->memory_ptr, this->teb32->value()); + } +} diff --git a/src/windows-emulator/emulator_thread.hpp b/src/windows-emulator/emulator_thread.hpp index d80f2556..3ce60a9d 100644 --- a/src/windows-emulator/emulator_thread.hpp +++ b/src/windows-emulator/emulator_thread.hpp @@ -49,7 +49,7 @@ class emulator_thread : public ref_counted_object } emulator_thread(memory_manager& memory, const process_context& context, uint64_t start_address, uint64_t argument, uint64_t stack_size, - bool suspended, uint32_t id); + uint32_t create_flags, uint32_t id, bool initial_thread); emulator_thread(const emulator_thread&) = delete; emulator_thread& operator=(const emulator_thread&) = delete; @@ -86,6 +86,7 @@ class emulator_thread : public ref_counted_object bool await_any{false}; bool waiting_for_alert{false}; bool alerted{false}; + uint32_t create_flags{0}; uint32_t suspended{0}; std::optional await_time{}; @@ -123,6 +124,7 @@ class emulator_thread : public ref_counted_object void restore(x86_64_emulator& emu) const { emu.restore_registers(this->last_registers); + this->refresh_execution_context(emu); } void setup_if_necessary(x86_64_emulator& emu, const process_context& context) @@ -166,6 +168,7 @@ class emulator_thread : public ref_counted_object buffer.write(this->waiting_for_alert); buffer.write(this->alerted); + buffer.write(this->create_flags); buffer.write(this->suspended); buffer.write_optional(this->await_time); @@ -213,6 +216,7 @@ class emulator_thread : public ref_counted_object buffer.read(this->waiting_for_alert); buffer.read(this->alerted); + buffer.read(this->create_flags); buffer.read(this->suspended); buffer.read_optional(this->await_time); @@ -245,6 +249,7 @@ class emulator_thread : public ref_counted_object private: void setup_registers(x86_64_emulator& emu, const process_context& context) const; + void refresh_execution_context(x86_64_emulator& emu) const; void release() { diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index 7dad2a88..fb6610e4 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -224,12 +224,6 @@ mapped_module* module_manager::map_module_core(const pe_detection_result& detect const auto image_base = mod.image_base; const auto entry = this->modules_.try_emplace(image_base, std::move(mod)); this->last_module_cache_ = this->modules_.end(); - - // TODO: Patch shell32.dll entry point to prevent TLS storage issues - // The shell32.dll module in SysWOW64 has TLS storage that fails, causing crashes - // This is a temporary workaround until the root cause is investigated and fixed - this->patch_shell32_entry_point_if_needed(entry.first->second); - this->callbacks_->on_module_load(entry.first->second); return &entry.first->second; } @@ -573,49 +567,3 @@ bool module_manager::unmap(const uint64_t address) return true; } - -void module_manager::patch_shell32_entry_point_if_needed(mapped_module& mod) -{ - // Only patch shell32.dll in SysWOW64 directory (32-bit) - // Convert module name to lowercase for case-insensitive comparison - std::string module_name_lower = mod.name; - std::transform(module_name_lower.begin(), module_name_lower.end(), module_name_lower.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); - if (module_name_lower != "shell32.dll") - { - return; - } - - // Check if this is the SysWOW64 version by examining if it's a 32-bit module - // Convert path to lowercase for case-insensitive comparison - std::string path_str = mod.path.string(); - std::transform(path_str.begin(), path_str.end(), path_str.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); - if (path_str.find("syswow64") == std::string::npos) - { - return; - } - - if (mod.entry_point == 0) - { - return; - } - - // Get the page containing the entry point - const auto entry_page_start = mod.entry_point & ~0xFFFULL; - const auto page_size = 0x1000; - - // Temporarily change memory protection to writable - nt_memory_permission mem_permisson(memory_permission::none); - if (!this->memory_->protect_memory(entry_page_start, page_size, memory_permission::all, &mem_permisson)) - { - return; // Failed to change protection - } - - // Write the ret 0Ch instruction at the entry point (0xB8, 0x01, 0x00, 0x00, 0x00, 0xC2, 0x0C, 0x00) - // This makes DllMain return immediately without executing CRT startup - constexpr std::array patch_bytes = {0xB8, 0x01, 0x00, 0x00, 0x00, 0xC2, 0x0C, 0x00}; // mov eax, 1 && ret 0Ch - this->memory_->write_memory(mod.entry_point, patch_bytes.data(), patch_bytes.size()); - - // Restore the original memory protection - this->memory_->protect_memory(entry_page_start, page_size, mem_permisson, nullptr); -} diff --git a/src/windows-emulator/module/module_manager.hpp b/src/windows-emulator/module/module_manager.hpp index 5490e14d..dfbaaebe 100644 --- a/src/windows-emulator/module/module_manager.hpp +++ b/src/windows-emulator/module/module_manager.hpp @@ -183,9 +183,6 @@ class module_manager mapped_module* map_module_core(const pe_detection_result& detection_result, const std::function& mapper, const logger& logger, bool is_static); - // Shell32.dll entry point patching to prevent TLS storage issues - void patch_shell32_entry_point_if_needed(mapped_module& mod); - // Execution mode detection execution_mode detect_execution_mode(const windows_path& executable_path, const logger& logger); diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index ea8cfee3..3db7cb0b 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -528,9 +528,9 @@ generic_handle_store* process_context::get_handle_store(const handle handle) } handle process_context::create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument, - const uint64_t stack_size, const bool suspended) + const uint64_t stack_size, const uint32_t create_flags, const bool initial_thread) { - emulator_thread t{memory, *this, start_address, argument, stack_size, suspended, ++this->spawned_thread_count}; + emulator_thread t{memory, *this, start_address, argument, stack_size, create_flags, ++this->spawned_thread_count, initial_thread}; auto [h, thr] = this->threads.store_and_get(std::move(t)); this->callbacks_->on_thread_create(h, *thr); return h; diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 01070a06..25a6c1f5 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -74,7 +74,8 @@ struct process_context const mapped_module& executable, const mapped_module& ntdll, const apiset::container& apiset_container, const mapped_module* ntdll32 = nullptr); - handle create_thread(memory_manager& memory, uint64_t start_address, uint64_t argument, uint64_t stack_size, bool suspended); + handle create_thread(memory_manager& memory, uint64_t start_address, uint64_t argument, uint64_t stack_size, uint32_t create_flags, + bool initial_thread = false); std::optional find_atom(std::u16string_view name); uint16_t add_or_find_atom(std::u16string name); diff --git a/src/windows-emulator/syscalls/process.cpp b/src/windows-emulator/syscalls/process.cpp index edb18355..7f8c78bd 100644 --- a/src/windows-emulator/syscalls/process.cpp +++ b/src/windows-emulator/syscalls/process.cpp @@ -214,15 +214,17 @@ namespace syscalls if (info_class == ProcessTlsInformation) { - constexpr auto thread_data_offset = offsetof(PROCESS_TLS_INFO, ThreadData); - if (process_information_length < thread_data_offset) + if (process_information_length < sizeof(PROCESS_TLS_INFORMATION) || + (process_information_length - (sizeof(PROCESS_TLS_INFORMATION) - sizeof(THREAD_TLS_INFORMATION))) % + sizeof(THREAD_TLS_INFORMATION)) { - return STATUS_BUFFER_OVERFLOW; + return STATUS_INFO_LENGTH_MISMATCH; } - const emulator_object data{c.emu, process_information + thread_data_offset}; + constexpr auto thread_data_offset = offsetof(PROCESS_TLS_INFORMATION, ThreadData); + const emulator_object data{c.emu, process_information + thread_data_offset}; - PROCESS_TLS_INFO tls_info{}; + PROCESS_TLS_INFORMATION tls_info{}; c.emu.read_memory(process_information, &tls_info, thread_data_offset); for (uint32_t i = 0; i < tls_info.ThreadDataCount; ++i) @@ -242,38 +244,78 @@ namespace syscalls entry.Flags = 2; - thread_iterator->second.teb64->access([&](TEB64& teb) { + const auto is_wow64 = c.win_emu.process.is_wow64_process; + const auto& thread = thread_iterator->second; + + thread.teb64->access([&](TEB64& teb) { entry.ThreadId = teb.ClientId.UniqueThread; - const auto tls_vector = teb.ThreadLocalStoragePointer; - constexpr auto ptr_size = sizeof(EmulatorTraits::PVOID); + uint64_t tls_vector = teb.ThreadLocalStoragePointer; + const auto ptr_size = is_wow64 ? sizeof(EmulatorTraits::PVOID) : sizeof(EmulatorTraits::PVOID); + + if (is_wow64) + { + if (!thread.teb32.has_value()) + { + return; + } + + thread.teb32->access([&tls_vector](const TEB32& teb32) { tls_vector = teb32.ThreadLocalStoragePointer; }); + } if (!tls_vector) { return; } - if (tls_info.TlsRequest == ProcessTlsReplaceIndex) + if (tls_info.OperationType == ProcessTlsReplaceIndex) { const auto tls_entry_ptr = tls_vector + (tls_info.TlsIndex * ptr_size); + uint64_t old_entry{}; - const auto old_entry = c.emu.read_memory::PVOID>(tls_entry_ptr); - c.emu.write_memory::PVOID>(tls_entry_ptr, entry.TlsModulePointer); - - entry.TlsModulePointer = old_entry; - } - else if (tls_info.TlsRequest == ProcessTlsReplaceVector) - { - const auto new_tls_vector = entry.TlsVector; - - for (uint32_t index = 0; index < tls_info.TlsVectorLength; ++index) + if (is_wow64) { - const auto old_entry = c.emu.read_memory(tls_vector + index * ptr_size); - c.emu.write_memory(new_tls_vector + index * ptr_size, old_entry); + old_entry = c.emu.read_memory::PVOID>(tls_entry_ptr); + c.emu.write_memory::PVOID>(tls_entry_ptr, static_cast(entry.NewTlsData)); + } + else + { + old_entry = c.emu.read_memory::PVOID>(tls_entry_ptr); + c.emu.write_memory::PVOID>(tls_entry_ptr, entry.NewTlsData); } - teb.ThreadLocalStoragePointer = new_tls_vector; - entry.TlsVector = tls_vector; + entry.OldTlsData = old_entry; + } + else if (tls_info.OperationType == ProcessTlsReplaceVector) + { + const auto new_tls_vector = entry.NewTlsData; + + for (uint32_t index = 0; index < tls_info.PreviousCount; ++index) + { + if (is_wow64) + { + const auto old_entry = c.emu.read_memory(tls_vector + (index * ptr_size)); + c.emu.write_memory(new_tls_vector + (index * ptr_size), old_entry); + } + else + { + const auto old_entry = c.emu.read_memory(tls_vector + (index * ptr_size)); + c.emu.write_memory(new_tls_vector + (index * ptr_size), old_entry); + } + } + + if (is_wow64) + { + thread.teb32->access([&new_tls_vector](TEB32& teb32) { + teb32.ThreadLocalStoragePointer = static_cast(new_tls_vector); + }); + } + else + { + teb.ThreadLocalStoragePointer = new_tls_vector; + } + + entry.OldTlsData = tls_vector; } }); } diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 49d8c4dc..0553152a 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -103,17 +103,44 @@ namespace syscalls for (const auto& t : c.proc.threads | std::views::values) { - t.teb64->access([&](TEB64& teb) { - if (tls_cell < TLS_MINIMUM_AVAILABLE) + if (tls_cell < TLS_MINIMUM_AVAILABLE) + { + if (c.proc.is_wow64_process) { - teb.TlsSlots.arr[tls_cell] = 0; + if (t.teb32.has_value()) + { + t.teb32->access([&](TEB32& teb32) { teb32.TlsSlots.arr[tls_cell] = 0; }); + } } - else if (teb.TlsExpansionSlots) + else { - const emulator_object expansion_slots(c.emu, teb.TlsExpansionSlots); - expansion_slots.write(0, tls_cell - TLS_MINIMUM_AVAILABLE); + t.teb64->access([&](TEB64& teb64) { teb64.TlsSlots.arr[tls_cell] = 0; }); } - }); + } + else if (tls_cell < TLS_MINIMUM_AVAILABLE + TLS_EXPANSION_SLOTS) + { + if (c.proc.is_wow64_process) + { + if (t.teb32.has_value()) + { + t.teb32->access([&](TEB32& teb32) { + if (teb32.TlsExpansionSlots) + { + c.emu.write_memory(teb32.TlsExpansionSlots + (4 * tls_cell) - TLS_MINIMUM_AVAILABLE, 0); + } + }); + } + } + else + { + t.teb64->access([&](TEB64& teb64) { + if (teb64.TlsExpansionSlots) + { + c.emu.write_memory(teb64.TlsExpansionSlots + (8 * tls_cell) - TLS_MINIMUM_AVAILABLE, 0); + } + }); + } + } } return STATUS_SUCCESS; @@ -601,7 +628,7 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - const auto h = c.proc.create_thread(c.win_emu.memory, start_routine, argument, stack_size, create_flags & CREATE_SUSPENDED); + const auto h = c.proc.create_thread(c.win_emu.memory, start_routine, argument, stack_size, create_flags); thread_handle.write(h); if (!attribute_list) diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 2598b5ed..c4057fc4 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -371,7 +371,7 @@ void windows_emulator::setup_process(const application_settings& app_settings) this->dispatcher.setup(ntdll->exports, ntdll_data, win32u->exports, win32u_data); const auto main_thread_id = context.create_thread(this->memory, this->mod_manager.executable->entry_point, 0, - this->mod_manager.executable->size_of_stack_commit, false); + this->mod_manager.executable->size_of_stack_commit, 0, true); switch_to_thread(*this, main_thread_id); } @@ -517,6 +517,18 @@ void windows_emulator::setup_hooks() this->emu().hook_memory_violation( [&](const uint64_t address, const size_t size, const memory_operation operation, const memory_violation_type type) { + if (this->emu().reg(x86_register::cs) == 0x33) + { + // loading gs selector only works in 64-bit mode + const auto required_gs_base = this->current_thread().gs_segment->get_base(); + const auto actual_gs_base = this->emu().get_segment_base(x86_register::gs); + if (actual_gs_base != required_gs_base) + { + this->emu().set_segment_base(x86_register::gs, required_gs_base); + return memory_violation_continuation::restart; + } + } + auto region = this->memory.get_region_info(address); if (region.permissions.is_guarded()) {