mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Fix static TLS for WOW64
This fix resolves shell32.dll TLS problems. Also it uses correct structure and field names
This commit is contained in:
@@ -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<Emu64>::PVOID TlsVector;
|
||||
EmulatorTraits<Emu64>::PVOID TlsModulePointer;
|
||||
EmulatorTraits<Emu64>::PVOID NewTlsData;
|
||||
EmulatorTraits<Emu64>::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
|
||||
{
|
||||
|
||||
@@ -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<char>(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<char>(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<uint8_t, 8> 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);
|
||||
}
|
||||
|
||||
@@ -183,9 +183,6 @@ class module_manager
|
||||
mapped_module* map_module_core(const pe_detection_result& detection_result, const std::function<mapped_module()>& 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);
|
||||
|
||||
|
||||
@@ -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<THREAD_TLS_INFO> data{c.emu, process_information + thread_data_offset};
|
||||
constexpr auto thread_data_offset = offsetof(PROCESS_TLS_INFORMATION, ThreadData);
|
||||
const emulator_object<THREAD_TLS_INFORMATION> 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<Emu64>::PVOID);
|
||||
uint64_t tls_vector = teb.ThreadLocalStoragePointer;
|
||||
const auto ptr_size = is_wow64 ? sizeof(EmulatorTraits<Emu32>::PVOID) : sizeof(EmulatorTraits<Emu64>::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<EmulatorTraits<Emu64>::PVOID>(tls_entry_ptr);
|
||||
c.emu.write_memory<EmulatorTraits<Emu64>::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<uint64_t>(tls_vector + index * ptr_size);
|
||||
c.emu.write_memory(new_tls_vector + index * ptr_size, old_entry);
|
||||
old_entry = c.emu.read_memory<EmulatorTraits<Emu32>::PVOID>(tls_entry_ptr);
|
||||
c.emu.write_memory<EmulatorTraits<Emu32>::PVOID>(tls_entry_ptr, static_cast<uint32_t>(entry.NewTlsData));
|
||||
}
|
||||
else
|
||||
{
|
||||
old_entry = c.emu.read_memory<EmulatorTraits<Emu64>::PVOID>(tls_entry_ptr);
|
||||
c.emu.write_memory<EmulatorTraits<Emu64>::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<uint32_t>(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<uint64_t>(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<uint32_t>(new_tls_vector);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
teb.ThreadLocalStoragePointer = new_tls_vector;
|
||||
}
|
||||
|
||||
entry.OldTlsData = tls_vector;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user