From 456dc99fb6e44fec1d650ef599b339108af6a2dd Mon Sep 17 00:00:00 2001 From: brian Date: Fri, 9 Jan 2026 00:38:03 +0800 Subject: [PATCH] Improve Windows version detection and LdrSystemDllInitBlock initialization - Add WINDOWS_VERSION enum and PS_SYSTEM_DLL_INIT_BLOCK sizes for WOW64 support across different Windows builds. - Read system information (SystemRoot, BuildNumber, UpdateBuildRevision) from registry instead of hardcoded paths. - Add build comparison helpers in process_context for precise build checks. --- src/common/platform/kernel_mapped.hpp | 28 ++++++ .../module/module_manager.cpp | 94 ++++++++++++++----- .../module/module_manager.hpp | 16 +++- src/windows-emulator/process_context.cpp | 32 +------ src/windows-emulator/process_context.hpp | 47 +++++++++- src/windows-emulator/windows_emulator.cpp | 5 +- 6 files changed, 161 insertions(+), 61 deletions(-) diff --git a/src/common/platform/kernel_mapped.hpp b/src/common/platform/kernel_mapped.hpp index cad0ecf0..27a8d4e7 100644 --- a/src/common/platform/kernel_mapped.hpp +++ b/src/common/platform/kernel_mapped.hpp @@ -2003,4 +2003,32 @@ typedef enum class _SECTION_INFORMATION_CLASS SectionInternalImageInformation = 4 } SECTION_INFORMATION_CLASS, *PSECTION_INFORMATION_CLASS; +enum WINDOWS_VERSION : uint32_t +{ + WINDOWS_10_1507 = 10240, + WINDOWS_10_1511 = 10586, + WINDOWS_10_1607 = 14393, + WINDOWS_10_1703 = 15063, + WINDOWS_10_1709 = 16299, + WINDOWS_10_1803 = 17134, + WINDOWS_10_1809 = 17763, + WINDOWS_10_1903 = 18362, + WINDOWS_10_1909 = 18363, + WINDOWS_10_2004 = 19041, + WINDOWS_10_20H2 = 19042, + WINDOWS_10_21H1 = 19043, + WINDOWS_10_21H2 = 19044, + WINDOWS_10_22H2 = 19045, + WINDOWS_11_21H2 = 22000, + WINDOWS_11_22H2 = 22621, + WINDOWS_11_23H2 = 22631, + WINDOWS_11_24H2 = 26100, +}; + +constexpr uint64_t PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V1 = sizeof(PS_SYSTEM_DLL_INIT_BLOCK_V1); +constexpr uint64_t PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V2_1703 = offsetof(PS_SYSTEM_DLL_INIT_BLOCK_V2, MitigationAuditOptionsMap); +constexpr uint64_t PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V2 = sizeof(PS_SYSTEM_DLL_INIT_BLOCK_V2); +constexpr uint64_t PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V3_2004 = offsetof(PS_SYSTEM_DLL_INIT_BLOCK_V3, ScpCfgCheckFunction); +constexpr uint64_t PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V3 = sizeof(PS_SYSTEM_DLL_INIT_BLOCK_V3); + // NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index fb6610e4..73d0ea3d 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -4,6 +4,8 @@ #include "platform/win_pefile.hpp" #include "windows-emulator/logger.hpp" #include "../wow64_heaven_gate.hpp" +#include "../registry/registry_manager.hpp" +#include "../process_context.hpp" #include #include @@ -205,6 +207,52 @@ module_manager::module_manager(memory_manager& memory, file_system& file_sys, ca { } +void module_manager::get_system_information_from_registry(registry_manager& registry, system_information& info, const logger& logger) +{ + constexpr auto version_key_path = R"(\Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion)"; + + const auto version_key = registry.get_key({version_key_path}); + if (!version_key) + { + throw std::runtime_error("Failed to get CurrentVersion registry key"); + } + + for (size_t i = 0; const auto value = registry.get_value(*version_key, i); ++i) + { + if (value->name == "SystemRoot" && value->type == REG_SZ && !value->data.empty()) + { + const auto* data_ptr = reinterpret_cast(value->data.data()); + const auto char_count = value->data.size() / sizeof(char16_t); + std::u16string system_root(data_ptr, char_count > 0 && data_ptr[char_count - 1] == u'\0' ? char_count - 1 : char_count); + info.system_root = windows_path{system_root}; + } + else if ((value->name == "CurrentBuildNumber" || value->name == "CurrentBuild") && value->type == REG_SZ) + { + const auto* s = reinterpret_cast(value->data.data()); + info.build_number = static_cast(std::strtoul(u16_to_u8(s).c_str(), nullptr, 10)); + } + else if (value->name == "UBR" && value->type == REG_DWORD && value->data.size() >= sizeof(uint32_t)) + { + info.update_build_revision = *reinterpret_cast(value->data.data()); + } + } + + if (info.system_root.u16string().empty()) + { + throw std::runtime_error("SystemRoot not found in registry"); + } + + if (info.build_number == 0) + { + logger.error("Failed to get CurrentBuildNumber from registry\n"); + } + + if (info.update_build_revision == 0) + { + logger.error("Failed to get UBR from registry\n"); + } +} + // Core mapping logic to eliminate code duplication mapped_module* module_manager::map_module_core(const pe_detection_result& detection_result, const std::function& mapper, const logger& logger, bool is_static) @@ -257,23 +305,19 @@ void module_manager::load_native_64bit_modules(const windows_path& executable_pa this->win32u = this->map_module(win32u_path, logger, true); } -// WOW64 module loading (with TODO placeholders for 32-bit details) +// WOW64 module loading void module_manager::load_wow64_modules(const windows_path& executable_path, const windows_path& ntdll_path, - const windows_path& win32u_path, const windows_path& ntdll32_path, const logger& logger) + const windows_path& win32u_path, const windows_path& ntdll32_path, process_context& context, + const logger& logger) { logger.info("Loading WOW64 modules for 32-bit application\n"); - // Load 32-bit main executable this->executable = this->map_module(executable_path, logger, true); + this->ntdll = this->map_module(ntdll_path, logger, true); + this->win32u = this->map_module(win32u_path, logger, true); - // Load 64-bit system modules for WOW64 subsystem - this->ntdll = this->map_module(ntdll_path, logger, true); // 64-bit ntdll - this->win32u = this->map_module(win32u_path, logger, true); // 64-bit win32u + this->wow64_modules_.ntdll32 = this->map_module(ntdll32_path, logger, true); - // Load 32-bit ntdll module for WOW64 subsystem - this->wow64_modules_.ntdll32 = this->map_module(ntdll32_path, logger, true); // 32-bit ntdll - - // Get original ImageBase values from PE files const auto ntdll32_original_imagebase = this->wow64_modules_.ntdll32->get_image_base_file(); const auto ntdll64_original_imagebase = this->ntdll->get_image_base_file(); @@ -283,12 +327,10 @@ void module_manager::load_wow64_modules(const windows_path& executable_path, con return; } - // Set up LdrSystemDllInitBlock structure PS_SYSTEM_DLL_INIT_BLOCK init_block = {}; - constexpr uint64_t symtem_dll_init_block_fix_size = 0xF0; // Wine or WIN10 + const auto init_block_size = context.get_system_dll_init_block_size(); - // Basic structure initialization - init_block.Size = symtem_dll_init_block_fix_size; + init_block.Size = static_cast(init_block_size); // Calculate relocation values // SystemDllWowRelocation = mapped_base - original_imagebase for 32-bit ntdll @@ -344,8 +386,7 @@ void module_manager::load_wow64_modules(const windows_path& executable_path, con return; } - // Write the initialized structure to the export address - this->memory_->write_memory(ldr_init_block_addr, &init_block, symtem_dll_init_block_fix_size); + this->memory_->write_memory(ldr_init_block_addr, &init_block, init_block_size); logger.info("Successfully initialized LdrSystemDllInitBlock at 0x%" PRIx64 "\n", ldr_init_block_addr); @@ -422,14 +463,20 @@ void module_manager::install_wow64_heaven_gate(const logger& logger) } } -// Refactored map_main_modules with execution mode detection -void module_manager::map_main_modules(const windows_path& executable_path, const windows_path& system32_path, - const windows_path& syswow64_path, const logger& logger) +void module_manager::map_main_modules(const windows_path& executable_path, registry_manager& registry, process_context& context, + const logger& logger) { - // Detect execution mode based on executable architecture - current_execution_mode_ = detect_execution_mode(executable_path, logger); + system_information sys_info{}; + module_manager::get_system_information_from_registry(registry, sys_info, logger); + const auto system32_path = sys_info.system_root / "System32"; + const auto syswow64_path = sys_info.system_root / "SysWOW64"; + + context.windows_build_number = sys_info.build_number; + context.windows_update_build_revision_number = sys_info.update_build_revision; + + current_execution_mode_ = detect_execution_mode(executable_path, logger); + context.is_wow64_process = (current_execution_mode_ == execution_mode::wow64_32bit); - // Load modules based on detected execution mode switch (current_execution_mode_) { case execution_mode::native_64bit: @@ -437,7 +484,8 @@ void module_manager::map_main_modules(const windows_path& executable_path, const break; case execution_mode::wow64_32bit: - load_wow64_modules(executable_path, system32_path / "ntdll.dll", system32_path / "win32u.dll", syswow64_path / "ntdll.dll", logger); + load_wow64_modules(executable_path, system32_path / "ntdll.dll", system32_path / "win32u.dll", syswow64_path / "ntdll.dll", context, + logger); break; case execution_mode::unknown: diff --git a/src/windows-emulator/module/module_manager.hpp b/src/windows-emulator/module/module_manager.hpp index dfbaaebe..402a0955 100644 --- a/src/windows-emulator/module/module_manager.hpp +++ b/src/windows-emulator/module/module_manager.hpp @@ -7,6 +7,8 @@ #include "platform/win_pefile.hpp" class logger; +class registry_manager; +struct process_context; // Execution mode for the emulated process enum class execution_mode @@ -78,6 +80,13 @@ class pe_architecture_detector static execution_mode determine_execution_mode(winpe::pe_arch executable_arch); }; +struct system_information +{ + windows_path system_root{}; + uint32_t build_number{0}; + uint32_t update_build_revision{0}; +}; + class module_manager { public: @@ -91,8 +100,7 @@ class module_manager module_manager(memory_manager& memory, file_system& file_sys, callbacks& cb); - void map_main_modules(const windows_path& executable_path, const windows_path& system32_path, const windows_path& syswow64_path, - const logger& logger); + void map_main_modules(const windows_path& executable_path, registry_manager& registry, process_context& context, const logger& logger); mapped_module* map_module(const windows_path& file, const logger& logger, bool is_static = false); mapped_module* map_local_module(const std::filesystem::path& file, const logger& logger, bool is_static = false); @@ -186,11 +194,13 @@ class module_manager // Execution mode detection execution_mode detect_execution_mode(const windows_path& executable_path, const logger& logger); + static void get_system_information_from_registry(registry_manager& registry, system_information& info, const logger& logger); + // Module loading helpers void load_native_64bit_modules(const windows_path& executable_path, const windows_path& ntdll_path, const windows_path& win32u_path, const logger& logger); void load_wow64_modules(const windows_path& executable_path, const windows_path& ntdll_path, const windows_path& win32u_path, - const windows_path& ntdll32_path, const logger& logger); + const windows_path& ntdll32_path, process_context& context, const logger& logger); void install_wow64_heaven_gate(const logger& logger); diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index d28138fa..c3f5d641 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -176,40 +176,12 @@ namespace return env_map; } - - uint32_t read_windows_build(registry_manager& registry) - { - const auto key = registry.get_key({R"(\Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion)"}); - - if (!key) - { - return 0; - } - - for (size_t i = 0; const auto value = registry.get_value(*key, i); ++i) - { - if (value->type != REG_SZ) - { - continue; - } - - if (value->name == "CurrentBuildNumber" || value->name == "CurrentBuild") - { - const auto* s = reinterpret_cast(value->data.data()); - return static_cast(std::strtoul(u16_to_u8(s).c_str(), nullptr, 10)); - } - } - - return 0; - } } void process_context::setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry, const application_settings& app_settings, const mapped_module& executable, const mapped_module& ntdll, const apiset::container& apiset_container, const mapped_module* ntdll32) { - this->windows_build_number = read_windows_build(registry); - setup_gdt(emu, memory); this->kusd.setup(); @@ -428,7 +400,7 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, regist monitor.hmon = h.bits; monitor.rcMonitor = {.left = 0, .top = 0, .right = 1920, .bottom = 1080}; monitor.rcWork = monitor.rcMonitor; - if (this->is_older_windows_build()) + if (this->is_build_before(26040)) { monitor.b20.monitorDpi = 96; monitor.b20.nativeDpi = monitor.b20.monitorDpi; @@ -465,6 +437,7 @@ void process_context::serialize(utils::buffer_serializer& buffer) const buffer.write(this->is_wow64_process); buffer.write(this->windows_build_number); + buffer.write(this->windows_update_build_revision_number); buffer.write(this->ntdll_image_base); buffer.write(this->ldr_initialize_thunk); buffer.write(this->rtl_user_thread_start); @@ -514,6 +487,7 @@ void process_context::deserialize(utils::buffer_deserializer& buffer) buffer.read(this->is_wow64_process); buffer.read(this->windows_build_number); + buffer.read(this->windows_update_build_revision_number); buffer.read(this->ntdll_image_base); buffer.read(this->ldr_initialize_thunk); buffer.read(this->rtl_user_thread_start); diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index e38cbe5d..b27e8264 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -94,10 +94,53 @@ struct process_context bool is_wow64_process{false}; uint32_t windows_build_number{0}; + uint32_t windows_update_build_revision_number{0}; - bool is_older_windows_build() const + bool is_build_before(uint32_t build, std::optional ubr = std::nullopt) const { - return windows_build_number < 26040; + if (windows_build_number != build) + return windows_build_number < build; + return ubr.has_value() && windows_update_build_revision_number < *ubr; + } + + bool is_build_before_or_equal(uint32_t build, std::optional ubr = std::nullopt) const + { + if (windows_build_number != build) + return windows_build_number < build; + return !ubr.has_value() || windows_update_build_revision_number <= *ubr; + } + + bool is_build_after_or_equal(uint32_t build, std::optional ubr = std::nullopt) const + { + if (windows_build_number != build) + return windows_build_number > build; + return !ubr.has_value() || windows_update_build_revision_number >= *ubr; + } + + bool is_build_after(uint32_t build, std::optional ubr = std::nullopt) const + { + if (windows_build_number != build) + return windows_build_number > build; + return ubr.has_value() && windows_update_build_revision_number > *ubr; + } + + bool is_build_within(uint32_t start_build, uint32_t end_build, std::optional start_ubr = std::nullopt, + std::optional end_ubr = std::nullopt) const + { + return is_build_after_or_equal(start_build, start_ubr) && is_build_before(end_build, end_ubr); + } + + uint64_t get_system_dll_init_block_size() const + { + if (is_build_after_or_equal(WINDOWS_VERSION::WINDOWS_11_24H2)) + return PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V3; + if (is_build_after_or_equal(WINDOWS_VERSION::WINDOWS_10_2004)) + return PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V3_2004; + if (is_build_after_or_equal(WINDOWS_VERSION::WINDOWS_10_1709)) + return PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V2; + if (is_build_after_or_equal(WINDOWS_VERSION::WINDOWS_10_1703)) + return PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V2_1703; + return PS_SYSTEM_DLL_INIT_BLOCK_SIZE_V1; } callbacks* callbacks_{}; diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index c4057fc4..2fc214e2 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -351,10 +351,7 @@ void windows_emulator::setup_process(const application_settings& app_settings) const auto& emu = this->emu(); auto& context = this->process; - this->mod_manager.map_main_modules(app_settings.application, R"(C:\Windows\System32)", R"(C:\Windows\SysWOW64)", this->log); - - // Set WOW64 flag based on the detected execution mode - context.is_wow64_process = (this->mod_manager.get_execution_mode() == execution_mode::wow64_32bit); + this->mod_manager.map_main_modules(app_settings.application, this->registry, context, this->log); const auto* executable = this->mod_manager.executable; const auto* ntdll = this->mod_manager.ntdll;