From 04ff582fa97be7922577208f0e7e229e5692d1a7 Mon Sep 17 00:00:00 2001 From: ssvine <79405160+ssvine@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:18:18 +0300 Subject: [PATCH] Fix static TLS for WOW64 This fix resolves shell32.dll TLS problems. Also it uses correct structure and field names --- src/common/platform/kernel_mapped.hpp | 26 +++--- .../module/module_manager.cpp | 52 ----------- .../module/module_manager.hpp | 3 - src/windows-emulator/syscalls/process.cpp | 88 ++++++++++++++----- 4 files changed, 78 insertions(+), 91 deletions(-) diff --git a/src/common/platform/kernel_mapped.hpp b/src/common/platform/kernel_mapped.hpp index bc1e6310..4bcbada7 100644 --- a/src/common/platform/kernel_mapped.hpp +++ b/src/common/platform/kernel_mapped.hpp @@ -1671,45 +1671,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/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/syscalls/process.cpp b/src/windows-emulator/syscalls/process.cpp index 0d9105e6..3931bb8f 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; } }); }