From 5d9dd122d26a660cdea4435115763005c06e5f25 Mon Sep 17 00:00:00 2001 From: redthing1 Date: Mon, 9 Jun 2025 23:12:45 -0700 Subject: [PATCH 01/12] minidump support: dump loading and process reconstruction --- .gitmodules | 3 + deps/CMakeLists.txt | 4 + deps/minidump_cpp | 1 + src/analyzer/main.cpp | 36 +- src/common/CMakeLists.txt | 1 + src/windows-emulator/CMakeLists.txt | 2 +- src/windows-emulator/minidump_loader.cpp | 744 ++++++++++++++++++ src/windows-emulator/minidump_loader.hpp | 17 + .../module/module_manager.cpp | 35 + .../module/module_manager.hpp | 2 + .../module/module_mapping.cpp | 63 ++ .../module/module_mapping.hpp | 2 + src/windows-emulator/windows_emulator.cpp | 8 + src/windows-emulator/windows_emulator.hpp | 2 + 14 files changed, 914 insertions(+), 6 deletions(-) create mode 160000 deps/minidump_cpp create mode 100644 src/windows-emulator/minidump_loader.cpp create mode 100644 src/windows-emulator/minidump_loader.hpp diff --git a/.gitmodules b/.gitmodules index 385ca7f2..254c45b0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -29,3 +29,6 @@ path = deps/base64 url = https://github.com/tobiaslocker/base64.git shallow = true +[submodule "deps/minidump_cpp"] + path = deps/minidump_cpp + url = https://github.com/redthing1/minidump_cpp diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 9bd3ba8d..3d13f896 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -25,5 +25,9 @@ target_include_directories(reflect INTERFACE ########################################## +add_subdirectory(minidump_cpp) + +########################################## + include(googletest.cmake) include(zlib.cmake) diff --git a/deps/minidump_cpp b/deps/minidump_cpp new file mode 160000 index 00000000..df9ec209 --- /dev/null +++ b/deps/minidump_cpp @@ -0,0 +1 @@ +Subproject commit df9ec209ced694405dfde0b8b714e4c43f093d81 diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 88b3b08d..6e5d73bd 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -18,6 +18,7 @@ namespace { mutable bool use_gdb{false}; std::filesystem::path dump{}; + std::filesystem::path minidump_path{}; std::string registry_path{"./registry"}; std::string emulation_root{}; std::unordered_map path_mappings{}; @@ -142,6 +143,12 @@ namespace win_x64_gdb_stub_handler handler{win_emu, should_stop}; gdb_stub::run_gdb_stub(network::address{"0.0.0.0:28960", AF_INET}, handler); } + else if (!options.minidump_path.empty()) + { + // For minidumps, don't start execution automatically; just report ready state + win_emu.log.print(color::green, "Minidump loaded successfully. Process state ready for analysis.\n"); + return true; // Return success without starting emulation + } else { win_emu.start(); @@ -244,14 +251,23 @@ namespace std::unique_ptr setup_emulator(const analysis_options& options, const std::span args) { - if (options.dump.empty()) + if (!options.dump.empty()) { - return create_application_emulator(options, args); + // load snapshot + auto win_emu = create_empty_emulator(options); + snapshot::load_emulator_snapshot(*win_emu, options.dump); + return win_emu; + } + if (!options.minidump_path.empty()) + { + // load minidump + auto win_emu = create_empty_emulator(options); + win_emu->load_minidump(options.minidump_path); + return win_emu; } - auto win_emu = create_empty_emulator(options); - snapshot::load_emulator_snapshot(*win_emu, options.dump); - return win_emu; + // default: load application + return create_application_emulator(options, args); } bool run(const analysis_options& options, const std::span args) @@ -355,6 +371,7 @@ namespace printf(" -m, --module Specify module to track\n"); printf(" -e, --emulation Set emulation root path\n"); printf(" -a, --snapshot Load snapshot dump from path\n"); + printf(" --minidump Load minidump from path\n"); printf(" -i, --ignore Comma-separated list of functions to ignore\n"); printf(" -p, --path Map Windows path to host path\n"); printf(" -r, --registry Set registry path (default: ./registry)\n\n"); @@ -425,6 +442,15 @@ namespace arg_it = args.erase(arg_it); options.dump = args[0]; } + else if (arg == "--minidump") + { + if (args.size() < 2) + { + throw std::runtime_error("No minidump path provided after --minidump"); + } + arg_it = args.erase(arg_it); + options.minidump_path = args[0]; + } else if (arg == "-i" || arg == "--ignore") { if (args.size() < 2) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 9423e8f1..b3b2e89a 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -15,6 +15,7 @@ find_package(Threads REQUIRED) target_link_libraries(emulator-common PUBLIC Threads::Threads zlibstatic + minidump::minidump ) if(WIN) diff --git a/src/windows-emulator/CMakeLists.txt b/src/windows-emulator/CMakeLists.txt index 93a25074..6a585bca 100644 --- a/src/windows-emulator/CMakeLists.txt +++ b/src/windows-emulator/CMakeLists.txt @@ -14,7 +14,7 @@ if(NOT MOMO_ENABLE_CLANG_TIDY) target_precompile_headers(windows-emulator PRIVATE std_include.hpp) endif() -target_link_libraries(windows-emulator PUBLIC emulator) +target_link_libraries(windows-emulator PUBLIC emulator minidump) target_include_directories(windows-emulator INTERFACE "${CMAKE_CURRENT_LIST_DIR}") diff --git a/src/windows-emulator/minidump_loader.cpp b/src/windows-emulator/minidump_loader.cpp new file mode 100644 index 00000000..4c710f91 --- /dev/null +++ b/src/windows-emulator/minidump_loader.cpp @@ -0,0 +1,744 @@ +#include "std_include.hpp" +#include "minidump_loader.hpp" +#include "windows_emulator.hpp" +#include "windows_objects.hpp" +#include "emulator_thread.hpp" + +#include + +namespace +{ + struct dump_statistics + { + size_t thread_count = 0; + size_t module_count = 0; + size_t memory_region_count = 0; + size_t memory_segment_count = 0; + size_t handle_count = 0; + uint64_t total_memory_size = 0; + bool has_exception = false; + bool has_system_info = false; + }; + + std::string get_architecture_string(const minidump::minidump_file* dump_file) + { + if (!dump_file) + { + return "Unknown"; + } + + const auto* sys_info = dump_file->get_system_info(); + if (!sys_info) + { + return "Unknown"; + } + + const auto arch = static_cast(sys_info->processor_architecture); + switch (arch) + { + case minidump::processor_architecture::amd64: + return "x64 (AMD64)"; + case minidump::processor_architecture::intel: + return "x86 (Intel)"; + case minidump::processor_architecture::arm64: + return "ARM64"; + default: + return "Unknown (" + std::to_string(static_cast(arch)) + ")"; + } + } + + bool parse_minidump_file(windows_emulator& win_emu, const std::filesystem::path& minidump_path, + std::unique_ptr& dump_file, + std::unique_ptr& dump_reader) + { + win_emu.log.info("Parsing minidump file\n"); + + if (!std::filesystem::exists(minidump_path)) + { + win_emu.log.error("Minidump file does not exist: %s\n", minidump_path.string().c_str()); + return false; + } + + const auto file_size = std::filesystem::file_size(minidump_path); + win_emu.log.info("File size: %zu bytes\n", file_size); + + auto parsed_file = minidump::minidump_file::parse(minidump_path.string()); + if (!parsed_file) + { + win_emu.log.error("Failed to parse minidump file\n"); + return false; + } + + win_emu.log.info("Minidump header parsed successfully\n"); + + auto reader = parsed_file->get_reader(); + if (!reader) + { + win_emu.log.error("Failed to create minidump reader\n"); + return false; + } + + dump_file = std::move(parsed_file); + dump_reader = std::move(reader); + + win_emu.log.info("Minidump reader created successfully\n"); + return true; + } + + bool validate_dump_compatibility(windows_emulator& win_emu, const minidump::minidump_file* dump_file) + { + win_emu.log.info("Validating dump compatibility\n"); + + if (!dump_file) + { + win_emu.log.error("Dump file not loaded\n"); + return false; + } + + const auto& header = dump_file->header(); + + if (!header.is_valid()) + { + win_emu.log.error("Invalid minidump signature or header\n"); + return false; + } + + win_emu.log.info("Minidump signature: 0x%08X (valid)\n", header.signature); + win_emu.log.info("Version: %u.%u\n", header.version, header.implementation_version); + win_emu.log.info("Number of streams: %u\n", header.number_of_streams); + win_emu.log.info("Flags: 0x%016llX\n", header.flags); + + const auto* sys_info = dump_file->get_system_info(); + if (sys_info) + { + const auto arch = static_cast(sys_info->processor_architecture); + const bool is_x64 = (arch == minidump::processor_architecture::amd64); + + win_emu.log.info("Processor architecture: %s\n", get_architecture_string(dump_file).c_str()); + + if (!is_x64) + { + win_emu.log.error("Only x64 minidumps are currently supported\n"); + return false; + } + + win_emu.log.info("Architecture compatibility: OK (x64)\n"); + } + else + { + win_emu.log.warn("No system info stream found - proceeding with caution\n"); + } + + return true; + } + + void log_dump_summary(windows_emulator& win_emu, const minidump::minidump_file* dump_file, dump_statistics& stats) + { + win_emu.log.info("Generating dump summary\n"); + + stats = {}; + + if (!dump_file) + { + win_emu.log.error("Dump file not loaded\n"); + return; + } + + stats.thread_count = dump_file->threads().size(); + stats.module_count = dump_file->modules().size(); + stats.memory_region_count = dump_file->memory_regions().size(); + stats.memory_segment_count = dump_file->memory_segments().size(); + stats.handle_count = dump_file->handles().size(); + stats.has_exception = (dump_file->get_exception_info() != nullptr); + stats.has_system_info = (dump_file->get_system_info() != nullptr); + + for (const auto& segment : dump_file->memory_segments()) + { + stats.total_memory_size += segment.size; + } + + win_emu.log.info( + "Summary: %s, %zu threads, %zu modules, %zu regions, %zu segments, %zu handles, %" PRIu64 " bytes memory\n", + get_architecture_string(dump_file).c_str(), stats.thread_count, stats.module_count, + stats.memory_region_count, stats.memory_segment_count, stats.handle_count, stats.total_memory_size); + } + + void process_streams(windows_emulator& win_emu, const minidump::minidump_file* dump_file) + { + if (!dump_file) + { + return; + } + + // Process system info + const auto* sys_info = dump_file->get_system_info(); + if (sys_info) + { + win_emu.log.info("System: OS %u.%u.%u, %u processors, type %u, platform %u\n", sys_info->major_version, + sys_info->minor_version, sys_info->build_number, sys_info->number_of_processors, + sys_info->product_type, sys_info->platform_id); + } + + // Process memory info + const auto& memory_regions = dump_file->memory_regions(); + uint64_t total_reserved = 0, total_committed = 0; + size_t guard_pages = 0; + for (const auto& region : memory_regions) + { + total_reserved += region.region_size; + if (region.state & MEM_COMMIT) + total_committed += region.region_size; + if (region.protect & PAGE_GUARD) + guard_pages++; + } + win_emu.log.info("Memory: %zu regions, %" PRIu64 " bytes reserved, %" PRIu64 + " bytes committed, %zu guard pages\n", + memory_regions.size(), total_reserved, total_committed, guard_pages); + + // Process memory content + const auto& memory_segments = dump_file->memory_segments(); + uint64_t min_addr = UINT64_MAX, max_addr = 0; + for (const auto& segment : memory_segments) + { + min_addr = std::min(min_addr, segment.start_virtual_address); + max_addr = std::max(max_addr, segment.end_virtual_address()); + } + if (!memory_segments.empty()) + { + win_emu.log.info("Content: %zu segments, range 0x%" PRIx64 "-0x%" PRIx64 " (%" PRIu64 " bytes span)\n", + memory_segments.size(), min_addr, max_addr, max_addr - min_addr); + } + + // Process modules + const auto& modules = dump_file->modules(); + for (const auto& module : modules) + { + win_emu.log.info("Module: %s at 0x%" PRIx64 " (%u bytes)\n", module.module_name.c_str(), + module.base_of_image, module.size_of_image); + } + + // Process threads + const auto& threads = dump_file->threads(); + for (const auto& thread : threads) + { + win_emu.log.info("Thread %u: TEB 0x%" PRIx64 ", stack 0x%" PRIx64 " (%u bytes), context %u bytes\n", + thread.thread_id, thread.teb, thread.stack_start_of_memory_range, thread.stack_data_size, + thread.context_data_size); + } + + // Process handles + const auto& handles = dump_file->handles(); + if (!handles.empty()) + { + std::map handle_type_counts; + for (const auto& handle : handles) + handle_type_counts[handle.type_name]++; + win_emu.log.info("Handles: %zu total\n", handles.size()); + for (const auto& [type, count] : handle_type_counts) + { + win_emu.log.info(" %s: %zu\n", type.c_str(), count); + } + } + + // Process exception info + const auto* exception = dump_file->get_exception_info(); + if (exception) + { + win_emu.log.info("Exception: thread %u, code 0x%08X at 0x%" PRIx64 "\n", exception->thread_id, + exception->exception_record.exception_code, exception->exception_record.exception_address); + } + } + + void reconstruct_memory_state(windows_emulator& win_emu, const minidump::minidump_file* dump_file, + minidump::minidump_reader* dump_reader) + { + if (!dump_file || !dump_reader) + { + win_emu.log.error("Dump file or reader not loaded\n"); + return; + } + + const auto& memory_regions = dump_file->memory_regions(); + const auto& memory_segments = dump_file->memory_segments(); + + win_emu.log.info("Reconstructing memory: %zu regions, %zu data segments\n", memory_regions.size(), + memory_segments.size()); + size_t reserved_count = 0; + size_t committed_count = 0; + size_t failed_count = 0; + + for (const auto& region : memory_regions) + { + const bool is_reserved = (region.state & MEM_RESERVE) != 0; + const bool is_committed = (region.state & MEM_COMMIT) != 0; + const bool is_free = (region.state & MEM_FREE) != 0; + + if (is_free) + continue; + + memory_permission perms = memory_permission::none; + + // Strip modifier flags like PAGE_GUARD, PAGE_NOCACHE, etc. + const uint32_t base_protect = region.protect & 0xFF; + + switch (base_protect) + { + case PAGE_READWRITE: + perms = memory_permission::read_write; + break; + case PAGE_READONLY: + perms = memory_permission::read; + break; + case PAGE_EXECUTE_READ: + perms = memory_permission::read | memory_permission::exec; + break; + case PAGE_EXECUTE_READWRITE: + perms = memory_permission::all; + break; + case PAGE_NOACCESS: + perms = memory_permission::none; + break; + default: + // For any other protection, default to read-only as safest fallback + perms = memory_permission::read; + break; + } + + try + { + if (is_committed) + { + if (win_emu.memory.allocate_memory(region.base_address, region.region_size, perms, false)) + { + committed_count++; + win_emu.log.info(" Allocated committed 0x%" PRIx64 ": size=%" PRIu64 + ", state=0x%08X, protect=0x%08X\n", + region.base_address, region.region_size, region.state, region.protect); + } + else + { + failed_count++; + win_emu.log.warn(" Failed to allocate committed 0x%" PRIx64 ": size=%" PRIu64 "\n", + region.base_address, region.region_size); + } + } + else if (is_reserved) + { + if (win_emu.memory.allocate_memory(region.base_address, region.region_size, perms, true)) + { + reserved_count++; + win_emu.log.info(" Reserved 0x%" PRIx64 ": size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n", + region.base_address, region.region_size, region.state, region.protect); + } + else + { + failed_count++; + win_emu.log.warn(" Failed to reserve 0x%" PRIx64 ": size=%" PRIu64 "\n", region.base_address, + region.region_size); + } + } + } + catch (const std::exception& e) + { + failed_count++; + win_emu.log.error(" Exception allocating 0x%" PRIx64 ": %s\n", region.base_address, e.what()); + } + } + + win_emu.log.info("Regions: %zu reserved, %zu committed, %zu failed\n", reserved_count, committed_count, + failed_count); + size_t written_count = 0; + size_t write_failed_count = 0; + uint64_t total_bytes_written = 0; + + for (const auto& segment : memory_segments) + { + try + { + auto memory_data = dump_reader->read_memory(segment.start_virtual_address, segment.size); + win_emu.memory.write_memory(segment.start_virtual_address, memory_data.data(), memory_data.size()); + written_count++; + total_bytes_written += memory_data.size(); + win_emu.log.info(" Written segment 0x%" PRIx64 ": %zu bytes\n", segment.start_virtual_address, + memory_data.size()); + } + catch (const std::exception& e) + { + write_failed_count++; + win_emu.log.error(" Failed to write segment 0x%" PRIx64 ": %s\n", segment.start_virtual_address, + e.what()); + } + } + + win_emu.log.info("Content: %zu segments written (%" PRIu64 " bytes), %zu failed\n", written_count, + total_bytes_written, write_failed_count); + } + + bool is_main_executable(const minidump::module_info& module) + { + const auto name = module.module_name; + return name.find(".exe") != std::string::npos; + } + + bool is_ntdll(const minidump::module_info& module) + { + const auto name = module.module_name; + return name == "ntdll.dll" || name == "NTDLL.DLL"; + } + + bool is_win32u(const minidump::module_info& module) + { + const auto name = module.module_name; + return name == "win32u.dll" || name == "WIN32U.DLL"; + } + + void reconstruct_module_state(windows_emulator& win_emu, const minidump::minidump_file* dump_file) + { + if (!dump_file) + { + win_emu.log.error("Dump file not loaded\n"); + return; + } + + const auto& modules = dump_file->modules(); + win_emu.log.info("Reconstructing module state: %zu modules\n", modules.size()); + + size_t mapped_count = 0; + size_t failed_count = 0; + size_t identified_count = 0; + + for (const auto& module : modules) + { + try + { + auto* mapped_module = win_emu.mod_manager.map_memory_module(module.base_of_image, module.size_of_image, + module.module_name, win_emu.log); + + if (mapped_module) + { + mapped_count++; + win_emu.log.info(" Mapped %s at 0x%" PRIx64 " (%u bytes, %zu sections, %zu exports)\n", + module.module_name.c_str(), module.base_of_image, module.size_of_image, + mapped_module->sections.size(), mapped_module->exports.size()); + + if (is_main_executable(module)) + { + win_emu.mod_manager.executable = mapped_module; + identified_count++; + win_emu.log.info(" Identified as main executable\n"); + } + else if (is_ntdll(module)) + { + win_emu.mod_manager.ntdll = mapped_module; + identified_count++; + win_emu.log.info(" Identified as ntdll\n"); + } + else if (is_win32u(module)) + { + win_emu.mod_manager.win32u = mapped_module; + identified_count++; + win_emu.log.info(" Identified as win32u\n"); + } + } + else + { + failed_count++; + win_emu.log.warn(" Failed to map %s at 0x%" PRIx64 "\n", module.module_name.c_str(), + module.base_of_image); + } + } + catch (const std::exception& e) + { + failed_count++; + win_emu.log.error(" Exception mapping %s: %s\n", module.module_name.c_str(), e.what()); + } + } + + win_emu.log.info("Module reconstruction: %zu mapped, %zu failed, %zu system modules identified\n", mapped_count, + failed_count, identified_count); + } + + void setup_kusd_from_dump(windows_emulator& win_emu, const minidump::minidump_file* dump_file) + { + const auto* sys_info = dump_file->get_system_info(); + if (!sys_info) + { + win_emu.log.warn("No system info available - using default KUSD\n"); + return; + } + + win_emu.log.info("Setting up KUSER_SHARED_DATA from dump system info\n"); + + auto& kusd = win_emu.process.kusd.get(); + kusd.NtMajorVersion = sys_info->major_version; + kusd.NtMinorVersion = sys_info->minor_version; + kusd.NtBuildNumber = sys_info->build_number; + kusd.NativeProcessorArchitecture = sys_info->processor_architecture; + kusd.ActiveProcessorCount = sys_info->number_of_processors; + kusd.UnparkedProcessorCount = sys_info->number_of_processors; + kusd.NtProductType = static_cast(sys_info->product_type); + kusd.ProductTypeIsValid = 1; + + win_emu.log.info("KUSD updated: Windows %u.%u.%u, %u processors, product type %u\n", sys_info->major_version, + sys_info->minor_version, sys_info->build_number, sys_info->number_of_processors, + sys_info->product_type); + } + + bool load_thread_context(const std::filesystem::path& minidump_path, const minidump::thread_info& thread_info, + std::vector& context_buffer) + { + if (thread_info.context_data_size == 0) + { + return false; + } + + std::ifstream context_file(minidump_path, std::ios::binary); + if (!context_file.is_open()) + { + return false; + } + + context_file.seekg(thread_info.context_rva); + context_buffer.resize(thread_info.context_data_size); + context_file.read(reinterpret_cast(context_buffer.data()), thread_info.context_data_size); + + return context_file.good(); + } + + void reconstruct_threads(windows_emulator& win_emu, const minidump::minidump_file* dump_file, + const std::filesystem::path& minidump_path) + { + const auto& threads = dump_file->threads(); + if (threads.empty()) + { + win_emu.log.warn("No threads found in minidump\n"); + return; + } + + win_emu.log.info("Reconstructing threads: %zu threads\n", threads.size()); + + size_t success_count = 0; + size_t context_loaded_count = 0; + + for (const auto& thread_info : threads) + { + try + { + emulator_thread thread(win_emu.memory); + thread.id = thread_info.thread_id; + thread.stack_base = thread_info.stack_start_of_memory_range; + thread.stack_size = thread_info.stack_data_size; + + // Load CPU context if available + const bool context_loaded = load_thread_context(minidump_path, thread_info, thread.last_registers); + if (context_loaded) + { + context_loaded_count++; + } + + // Set TEB address if valid + if (thread_info.teb != 0) + { + thread.teb = emulator_object(win_emu.memory); + thread.teb->set_address(thread_info.teb); + } + + win_emu.log.info(" Thread %u: TEB=0x%" PRIx64 ", stack=0x%" PRIx64 " (%u bytes), context=%s\n", + thread_info.thread_id, thread_info.teb, thread.stack_base, thread_info.stack_data_size, + context_loaded ? "loaded" : "unavailable"); + + win_emu.process.threads.store(std::move(thread)); + success_count++; + } + catch (const std::exception& e) + { + win_emu.log.error(" Failed to reconstruct thread %u: %s\n", thread_info.thread_id, e.what()); + } + } + + // Set active thread to first available thread + if (success_count > 0) + { + auto& first_thread = win_emu.process.threads.begin()->second; + win_emu.process.active_thread = &first_thread; + } + + win_emu.log.info("Thread reconstruction: %zu/%zu threads created, %zu with context\n", success_count, + threads.size(), context_loaded_count); + } + + void setup_peb_from_teb(windows_emulator& win_emu, const minidump::minidump_file* dump_file) + { + const auto& threads = dump_file->threads(); + if (threads.empty()) + { + win_emu.log.warn("No threads available for PEB setup\n"); + return; + } + + const auto& first_thread = threads[0]; + if (first_thread.teb == 0) + { + win_emu.log.warn("Thread %u has null TEB address\n", first_thread.thread_id); + return; + } + + try + { + // Read PEB address from TEB+0x60 (standard x64 TEB layout) + constexpr uint64_t teb_peb_offset = 0x60; + uint64_t peb_address = 0; + + win_emu.memory.read_memory(first_thread.teb + teb_peb_offset, &peb_address, sizeof(peb_address)); + + if (peb_address == 0) + { + win_emu.log.warn("PEB address is null in TEB at 0x%" PRIx64 "\n", first_thread.teb); + return; + } + + win_emu.process.peb.set_address(peb_address); + win_emu.log.info("PEB address: 0x%" PRIx64 " (from TEB 0x%" PRIx64 ")\n", peb_address, first_thread.teb); + } + catch (const std::exception& e) + { + win_emu.log.error("Failed to read PEB from TEB: %s\n", e.what()); + } + } + + std::u16string utf8_to_utf16(const std::string& str) + { + std::u16string result; + result.reserve(str.size()); + for (const char c : str) + { + result.push_back(static_cast(static_cast(c))); + } + return result; + } + + void reconstruct_handle_table(windows_emulator& win_emu, const minidump::minidump_file* dump_file) + { + const auto& handles = dump_file->handles(); + if (handles.empty()) + { + return; + } + + win_emu.log.info("Reconstructing handle table: %zu handles\n", handles.size()); + + std::map handle_type_counts; + size_t created_count = 0; + + for (const auto& handle_info : handles) + { + handle_type_counts[handle_info.type_name]++; + + try + { + if (handle_info.type_name == "Event") + { + event evt{}; + evt.name = utf8_to_utf16(handle_info.object_name); + win_emu.process.events.store(std::move(evt)); + created_count++; + } + else if (handle_info.type_name == "File") + { + file f{}; + f.name = utf8_to_utf16(handle_info.object_name); + win_emu.process.files.store(std::move(f)); + created_count++; + } + else if (handle_info.type_name == "Mutant") + { + mutant m{}; + m.name = utf8_to_utf16(handle_info.object_name); + win_emu.process.mutants.store(std::move(m)); + created_count++; + } + // Other handle types can be added here as needed + } + catch (const std::exception& e) + { + win_emu.log.error(" Failed to create %s handle '%s': %s\n", handle_info.type_name.c_str(), + handle_info.object_name.c_str(), e.what()); + } + } + + // Log summary by type + for (const auto& [type, count] : handle_type_counts) + { + win_emu.log.info(" %s: %zu handles\n", type.c_str(), count); + } + + win_emu.log.info("Handle table: %zu/%zu handles reconstructed\n", created_count, handles.size()); + } + + void setup_exception_context(windows_emulator& win_emu, const minidump::minidump_file* dump_file) + { + const auto* exception_info = dump_file->get_exception_info(); + if (!exception_info) + { + return; + } + + win_emu.process.current_ip = exception_info->exception_record.exception_address; + win_emu.log.info("Exception context: address=0x%" PRIx64 ", code=0x%08X, thread=%u\n", + exception_info->exception_record.exception_address, + exception_info->exception_record.exception_code, exception_info->thread_id); + } +} + +minidump_loader::minidump_loader(windows_emulator& win_emu, const std::filesystem::path& minidump_path) + : win_emu_(win_emu), + minidump_path_(minidump_path) +{ +} + +minidump_loader::~minidump_loader() = default; + +void minidump_loader::load_into_emulator() +{ + win_emu_.log.info("Starting minidump loading process\n"); + win_emu_.log.info("Minidump file: %s\n", minidump_path_.string().c_str()); + + try + { + std::unique_ptr dump_file; + std::unique_ptr dump_reader; + + if (!parse_minidump_file(win_emu_, minidump_path_, dump_file, dump_reader)) + { + throw std::runtime_error("Failed to parse minidump file"); + } + + if (!validate_dump_compatibility(win_emu_, dump_file.get())) + { + throw std::runtime_error("Minidump compatibility validation failed"); + } + + setup_kusd_from_dump(win_emu_, dump_file.get()); + + dump_statistics stats; + log_dump_summary(win_emu_, dump_file.get(), stats); + process_streams(win_emu_, dump_file.get()); + + // Existing phases + reconstruct_memory_state(win_emu_, dump_file.get(), dump_reader.get()); + reconstruct_module_state(win_emu_, dump_file.get()); + + // Process state reconstruction phases + setup_peb_from_teb(win_emu_, dump_file.get()); + reconstruct_threads(win_emu_, dump_file.get(), minidump_path_); + reconstruct_handle_table(win_emu_, dump_file.get()); + setup_exception_context(win_emu_, dump_file.get()); + + win_emu_.log.info("Process state reconstruction completed\n"); + } + catch (const std::exception& e) + { + win_emu_.log.error("Minidump loading failed: %s\n", e.what()); + throw; + } +} \ No newline at end of file diff --git a/src/windows-emulator/minidump_loader.hpp b/src/windows-emulator/minidump_loader.hpp new file mode 100644 index 00000000..0507f45a --- /dev/null +++ b/src/windows-emulator/minidump_loader.hpp @@ -0,0 +1,17 @@ +#pragma once +#include + +class windows_emulator; + +class minidump_loader +{ + public: + minidump_loader(windows_emulator& win_emu, const std::filesystem::path& minidump_path); + ~minidump_loader(); + + void load_into_emulator(); + + private: + windows_emulator& win_emu_; + std::filesystem::path minidump_path_; +}; \ No newline at end of file diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index 00eb45ab..ae263350 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -139,6 +139,41 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil } } +mapped_module* module_manager::map_memory_module(uint64_t base_address, uint64_t image_size, + const std::string& module_name, const logger& logger, bool is_static) +{ + for (auto& mod : this->modules_ | std::views::values) + { + if (mod.image_base == base_address) + { + return &mod; + } + } + + try + { + auto mod = ::map_module_from_memory(*this->memory_, base_address, image_size, module_name); + mod.is_static = is_static; + + const auto image_base = mod.image_base; + const auto entry = this->modules_.try_emplace(image_base, std::move(mod)); + this->callbacks_->on_module_load(entry.first->second); + return &entry.first->second; + } + catch (const std::exception& e) + { + logger.error("Failed to map module from memory %s at 0x%016llX: %s\n", module_name.c_str(), base_address, + e.what()); + return nullptr; + } + catch (...) + { + logger.error("Failed to map module from memory %s at 0x%016llX: Unknown error\n", module_name.c_str(), + base_address); + return nullptr; + } +} + void module_manager::serialize(utils::buffer_serializer& buffer) const { buffer.write_map(this->modules_); diff --git a/src/windows-emulator/module/module_manager.hpp b/src/windows-emulator/module/module_manager.hpp index 4123f733..927d5e67 100644 --- a/src/windows-emulator/module/module_manager.hpp +++ b/src/windows-emulator/module/module_manager.hpp @@ -25,6 +25,8 @@ class module_manager 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); + mapped_module* map_memory_module(uint64_t base_address, uint64_t image_size, + const std::string& module_name, const logger& logger, bool is_static = false); mapped_module* find_by_address(const uint64_t address) { diff --git a/src/windows-emulator/module/module_mapping.cpp b/src/windows-emulator/module/module_mapping.cpp index 8f433809..65423921 100644 --- a/src/windows-emulator/module/module_mapping.cpp +++ b/src/windows-emulator/module/module_mapping.cpp @@ -265,6 +265,69 @@ mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path return map_module_from_data(memory, data, std::move(file)); } +mapped_module map_module_from_memory(memory_manager& memory, uint64_t base_address, uint64_t image_size, + const std::string& module_name) +{ + mapped_module binary{}; + binary.name = module_name; + binary.path = module_name; + binary.image_base = base_address; + binary.size_of_image = image_size; + + auto mapped_memory = read_mapped_memory(memory, binary); + utils::safe_buffer_accessor buffer{mapped_memory}; + + try + { + const auto dos_header = buffer.as(0).get(); + const auto nt_headers_offset = dos_header.e_lfanew; + const auto nt_headers = buffer.as>(nt_headers_offset).get(); + const auto& optional_header = nt_headers.OptionalHeader; + + binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint; + + const auto section_offset = get_first_section_offset(nt_headers, nt_headers_offset); + const auto sections = buffer.as(section_offset); + + for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) + { + const auto section = sections.get(i); + + mapped_section section_info{}; + section_info.region.start = binary.image_base + section.VirtualAddress; + section_info.region.length = + static_cast(page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize))); + + auto permissions = memory_permission::none; + if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE) + permissions |= memory_permission::exec; + if (section.Characteristics & IMAGE_SCN_MEM_READ) + permissions |= memory_permission::read; + if (section.Characteristics & IMAGE_SCN_MEM_WRITE) + permissions |= memory_permission::write; + + section_info.region.permissions = permissions; + + for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j) + { + section_info.name.push_back(static_cast(section.Name[j])); + } + + binary.sections.push_back(std::move(section_info)); + } + + collect_exports(binary, buffer, optional_header); + } + catch (const std::exception&) + { + // bad! + throw std::runtime_error("Failed to map module from memory at " + std::to_string(base_address) + " with size " + + std::to_string(image_size) + " for module " + module_name); + } + + return binary; +} + bool unmap_module(memory_manager& memory, const mapped_module& mod) { return memory.release_memory(mod.image_base, static_cast(mod.size_of_image)); diff --git a/src/windows-emulator/module/module_mapping.hpp b/src/windows-emulator/module/module_mapping.hpp index 60f0a553..43595956 100644 --- a/src/windows-emulator/module/module_mapping.hpp +++ b/src/windows-emulator/module/module_mapping.hpp @@ -5,5 +5,7 @@ mapped_module map_module_from_data(memory_manager& memory, std::span data, std::filesystem::path file); mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path file); +mapped_module map_module_from_memory(memory_manager& memory, uint64_t base_address, uint64_t image_size, + const std::string& module_name); bool unmap_module(memory_manager& memory, const mapped_module& mod); diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 44175c43..eb019de5 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -12,6 +12,8 @@ #include "network/static_socket_factory.hpp" +#include "minidump_loader.hpp" + constexpr auto MAX_INSTRUCTIONS_PER_TIME_SLICE = 0x20000; namespace @@ -662,3 +664,9 @@ void windows_emulator::restore_snapshot() this->process.deserialize(buffer); // this->process = *this->process_snapshot_; } + +void windows_emulator::load_minidump(const std::filesystem::path& minidump_path) +{ + minidump_loader mdmp_loader(*this, minidump_path); + mdmp_loader.load_into_emulator(); +} diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index c6732a30..34d17068 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -164,6 +164,8 @@ class windows_emulator void save_snapshot(); void restore_snapshot(); + void load_minidump(const std::filesystem::path& minidump_path); + uint16_t get_host_port(const uint16_t emulator_port) const { const auto entry = this->port_mappings_.find(emulator_port); From 12646566ca33b50e958db6258054879ef1772ee8 Mon Sep 17 00:00:00 2001 From: redthing1 Date: Tue, 10 Jun 2025 00:27:13 -0700 Subject: [PATCH 02/12] minidump loader: fix PRIx64 format specifier --- src/windows-emulator/minidump_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows-emulator/minidump_loader.cpp b/src/windows-emulator/minidump_loader.cpp index 4c710f91..23c10af9 100644 --- a/src/windows-emulator/minidump_loader.cpp +++ b/src/windows-emulator/minidump_loader.cpp @@ -106,7 +106,7 @@ namespace win_emu.log.info("Minidump signature: 0x%08X (valid)\n", header.signature); win_emu.log.info("Version: %u.%u\n", header.version, header.implementation_version); win_emu.log.info("Number of streams: %u\n", header.number_of_streams); - win_emu.log.info("Flags: 0x%016llX\n", header.flags); + win_emu.log.info("Flags: 0x%016" PRIx64 "\n", header.flags); const auto* sys_info = dump_file->get_system_info(); if (sys_info) From aa966826a29b90e25b4e51610b3fd840bf26bf00 Mon Sep 17 00:00:00 2001 From: redthing1 Date: Tue, 10 Jun 2025 00:28:43 -0700 Subject: [PATCH 03/12] fixup! minidump loader: fix PRIx64 format specifier --- src/windows-emulator/module/module_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index ae263350..c8549218 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -162,13 +162,13 @@ mapped_module* module_manager::map_memory_module(uint64_t base_address, uint64_t } catch (const std::exception& e) { - logger.error("Failed to map module from memory %s at 0x%016llX: %s\n", module_name.c_str(), base_address, + logger.error("Failed to map module from memory %s at 0x%016" PRIx64 ": %s\n", module_name.c_str(), base_address, e.what()); return nullptr; } catch (...) { - logger.error("Failed to map module from memory %s at 0x%016llX: Unknown error\n", module_name.c_str(), + logger.error("Failed to map module from memory %s at 0x%016" PRIx64 ": Unknown error\n", module_name.c_str(), base_address); return nullptr; } From dae4f07f700bf41abffc02c0ce8f148ddbcc2026 Mon Sep 17 00:00:00 2001 From: redthing1 Date: Tue, 10 Jun 2025 00:31:17 -0700 Subject: [PATCH 04/12] module manager: apply clang format --- src/windows-emulator/module/module_manager.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/module/module_manager.hpp b/src/windows-emulator/module/module_manager.hpp index 927d5e67..6155a039 100644 --- a/src/windows-emulator/module/module_manager.hpp +++ b/src/windows-emulator/module/module_manager.hpp @@ -25,8 +25,8 @@ class module_manager 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); - mapped_module* map_memory_module(uint64_t base_address, uint64_t image_size, - const std::string& module_name, const logger& logger, bool is_static = false); + mapped_module* map_memory_module(uint64_t base_address, uint64_t image_size, const std::string& module_name, + const logger& logger, bool is_static = false); mapped_module* find_by_address(const uint64_t address) { From 3fa9fa9c157dc2369ebeef3a78c67886f1673ef9 Mon Sep 17 00:00:00 2001 From: redthing1 Date: Tue, 10 Jun 2025 00:35:52 -0700 Subject: [PATCH 05/12] minidump: fix uintmax_t format specifier --- src/windows-emulator/minidump_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows-emulator/minidump_loader.cpp b/src/windows-emulator/minidump_loader.cpp index 23c10af9..2c96f46a 100644 --- a/src/windows-emulator/minidump_loader.cpp +++ b/src/windows-emulator/minidump_loader.cpp @@ -60,7 +60,7 @@ namespace } const auto file_size = std::filesystem::file_size(minidump_path); - win_emu.log.info("File size: %zu bytes\n", file_size); + win_emu.log.info("File size: %ju bytes\n", file_size); auto parsed_file = minidump::minidump_file::parse(minidump_path.string()); if (!parsed_file) From 47f45897749ad507bfe259bed45cd6984bbaf42a Mon Sep 17 00:00:00 2001 From: redthing1 Date: Tue, 10 Jun 2025 00:47:01 -0700 Subject: [PATCH 06/12] minidump: satisfy clang tidy --- src/windows-emulator/minidump_loader.cpp | 18 ++++++++++++++---- src/windows-emulator/minidump_loader.hpp | 2 +- src/windows-emulator/module/module_mapping.cpp | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/windows-emulator/minidump_loader.cpp b/src/windows-emulator/minidump_loader.cpp index 2c96f46a..d69a02f5 100644 --- a/src/windows-emulator/minidump_loader.cpp +++ b/src/windows-emulator/minidump_loader.cpp @@ -181,15 +181,20 @@ namespace // Process memory info const auto& memory_regions = dump_file->memory_regions(); - uint64_t total_reserved = 0, total_committed = 0; + uint64_t total_reserved = 0; + uint64_t total_committed = 0; size_t guard_pages = 0; for (const auto& region : memory_regions) { total_reserved += region.region_size; if (region.state & MEM_COMMIT) + { total_committed += region.region_size; + } if (region.protect & PAGE_GUARD) + { guard_pages++; + } } win_emu.log.info("Memory: %zu regions, %" PRIu64 " bytes reserved, %" PRIu64 " bytes committed, %zu guard pages\n", @@ -197,7 +202,8 @@ namespace // Process memory content const auto& memory_segments = dump_file->memory_segments(); - uint64_t min_addr = UINT64_MAX, max_addr = 0; + uint64_t min_addr = UINT64_MAX; + uint64_t max_addr = 0; for (const auto& segment : memory_segments) { min_addr = std::min(min_addr, segment.start_virtual_address); @@ -232,7 +238,9 @@ namespace { std::map handle_type_counts; for (const auto& handle : handles) + { handle_type_counts[handle.type_name]++; + } win_emu.log.info("Handles: %zu total\n", handles.size()); for (const auto& [type, count] : handle_type_counts) { @@ -274,7 +282,9 @@ namespace const bool is_free = (region.state & MEM_FREE) != 0; if (is_free) + { continue; + } memory_permission perms = memory_permission::none; @@ -690,9 +700,9 @@ namespace } } -minidump_loader::minidump_loader(windows_emulator& win_emu, const std::filesystem::path& minidump_path) +minidump_loader::minidump_loader(windows_emulator& win_emu, std::filesystem::path minidump_path) : win_emu_(win_emu), - minidump_path_(minidump_path) + minidump_path_(std::move(minidump_path)) { } diff --git a/src/windows-emulator/minidump_loader.hpp b/src/windows-emulator/minidump_loader.hpp index 0507f45a..dc172e91 100644 --- a/src/windows-emulator/minidump_loader.hpp +++ b/src/windows-emulator/minidump_loader.hpp @@ -6,7 +6,7 @@ class windows_emulator; class minidump_loader { public: - minidump_loader(windows_emulator& win_emu, const std::filesystem::path& minidump_path); + minidump_loader(windows_emulator& win_emu, std::filesystem::path minidump_path); ~minidump_loader(); void load_into_emulator(); diff --git a/src/windows-emulator/module/module_mapping.cpp b/src/windows-emulator/module/module_mapping.cpp index 65423921..bdc3748c 100644 --- a/src/windows-emulator/module/module_mapping.cpp +++ b/src/windows-emulator/module/module_mapping.cpp @@ -287,7 +287,7 @@ mapped_module map_module_from_memory(memory_manager& memory, uint64_t base_addre binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint; const auto section_offset = get_first_section_offset(nt_headers, nt_headers_offset); - const auto sections = buffer.as(section_offset); + const auto sections = buffer.as(static_cast(section_offset)); for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) { From ca214a539a70027e188a3161d99ab0bf69e9df94 Mon Sep 17 00:00:00 2001 From: redthing1 Date: Tue, 10 Jun 2025 00:57:19 -0700 Subject: [PATCH 07/12] minidump: more clang tidy --- src/windows-emulator/minidump_loader.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/windows-emulator/minidump_loader.cpp b/src/windows-emulator/minidump_loader.cpp index d69a02f5..1004a9aa 100644 --- a/src/windows-emulator/minidump_loader.cpp +++ b/src/windows-emulator/minidump_loader.cpp @@ -318,7 +318,8 @@ namespace { if (is_committed) { - if (win_emu.memory.allocate_memory(region.base_address, region.region_size, perms, false)) + if (win_emu.memory.allocate_memory(region.base_address, static_cast(region.region_size), + perms, false)) { committed_count++; win_emu.log.info(" Allocated committed 0x%" PRIx64 ": size=%" PRIu64 @@ -334,7 +335,8 @@ namespace } else if (is_reserved) { - if (win_emu.memory.allocate_memory(region.base_address, region.region_size, perms, true)) + if (win_emu.memory.allocate_memory(region.base_address, static_cast(region.region_size), + perms, true)) { reserved_count++; win_emu.log.info(" Reserved 0x%" PRIx64 ": size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n", @@ -365,8 +367,10 @@ namespace { try { - auto memory_data = dump_reader->read_memory(segment.start_virtual_address, segment.size); - win_emu.memory.write_memory(segment.start_virtual_address, memory_data.data(), memory_data.size()); + auto memory_data = + dump_reader->read_memory(segment.start_virtual_address, static_cast(segment.size)); + win_emu.memory.write_memory(segment.start_virtual_address, memory_data.data(), + static_cast(memory_data.size())); written_count++; total_bytes_written += memory_data.size(); win_emu.log.info(" Written segment 0x%" PRIx64 ": %zu bytes\n", segment.start_virtual_address, From 21a0d3c4bc875d14a34ace8fd6df7085427615c9 Mon Sep 17 00:00:00 2001 From: redthing1 Date: Tue, 10 Jun 2025 01:08:45 -0700 Subject: [PATCH 08/12] fix tidy --- src/windows-emulator/module/module_mapping.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/windows-emulator/module/module_mapping.cpp b/src/windows-emulator/module/module_mapping.cpp index bdc3748c..036088ed 100644 --- a/src/windows-emulator/module/module_mapping.cpp +++ b/src/windows-emulator/module/module_mapping.cpp @@ -300,11 +300,17 @@ mapped_module map_module_from_memory(memory_manager& memory, uint64_t base_addre auto permissions = memory_permission::none; if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE) + { permissions |= memory_permission::exec; + } if (section.Characteristics & IMAGE_SCN_MEM_READ) + { permissions |= memory_permission::read; + } if (section.Characteristics & IMAGE_SCN_MEM_WRITE) + { permissions |= memory_permission::write; + } section_info.region.permissions = permissions; From f8b163f55624d67bcf8b461354b085893bc110df Mon Sep 17 00:00:00 2001 From: redthing1 Date: Thu, 12 Jun 2025 15:04:54 -0700 Subject: [PATCH 09/12] refactor to address comments --- src/windows-emulator/minidump_loader.cpp | 135 ++++++++-------------- src/windows-emulator/minidump_loader.hpp | 13 +-- src/windows-emulator/windows_emulator.cpp | 3 +- 3 files changed, 50 insertions(+), 101 deletions(-) diff --git a/src/windows-emulator/minidump_loader.cpp b/src/windows-emulator/minidump_loader.cpp index 1004a9aa..31e7f1ab 100644 --- a/src/windows-emulator/minidump_loader.cpp +++ b/src/windows-emulator/minidump_loader.cpp @@ -3,6 +3,9 @@ #include "windows_emulator.hpp" #include "windows_objects.hpp" #include "emulator_thread.hpp" +#include "common/platform/unicode.hpp" +#include "common/platform/kernel_mapped.hpp" +#include "memory_utils.hpp" #include @@ -217,10 +220,10 @@ namespace // Process modules const auto& modules = dump_file->modules(); - for (const auto& module : modules) + for (const auto& mod : modules) { - win_emu.log.info("Module: %s at 0x%" PRIx64 " (%u bytes)\n", module.module_name.c_str(), - module.base_of_image, module.size_of_image); + win_emu.log.info("Module: %s at 0x%" PRIx64 " (%u bytes)\n", mod.module_name.c_str(), mod.base_of_image, + mod.size_of_image); } // Process threads @@ -277,6 +280,10 @@ namespace for (const auto& region : memory_regions) { + // Log the memory region details + win_emu.log.info("Region: 0x%" PRIx64 ", size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n", + region.base_address, region.region_size, region.state, region.protect); + const bool is_reserved = (region.state & MEM_RESERVE) != 0; const bool is_committed = (region.state & MEM_COMMIT) != 0; const bool is_free = (region.state & MEM_FREE) != 0; @@ -286,33 +293,7 @@ namespace continue; } - memory_permission perms = memory_permission::none; - - // Strip modifier flags like PAGE_GUARD, PAGE_NOCACHE, etc. - const uint32_t base_protect = region.protect & 0xFF; - - switch (base_protect) - { - case PAGE_READWRITE: - perms = memory_permission::read_write; - break; - case PAGE_READONLY: - perms = memory_permission::read; - break; - case PAGE_EXECUTE_READ: - perms = memory_permission::read | memory_permission::exec; - break; - case PAGE_EXECUTE_READWRITE: - perms = memory_permission::all; - break; - case PAGE_NOACCESS: - perms = memory_permission::none; - break; - default: - // For any other protection, default to read-only as safest fallback - perms = memory_permission::read; - break; - } + memory_permission perms = map_nt_to_emulator_protection(region.protect); try { @@ -388,21 +369,21 @@ namespace total_bytes_written, write_failed_count); } - bool is_main_executable(const minidump::module_info& module) + bool is_main_executable(const minidump::module_info& mod) { - const auto name = module.module_name; + const auto name = mod.module_name; return name.find(".exe") != std::string::npos; } - bool is_ntdll(const minidump::module_info& module) + bool is_ntdll(const minidump::module_info& mod) { - const auto name = module.module_name; + const auto name = mod.module_name; return name == "ntdll.dll" || name == "NTDLL.DLL"; } - bool is_win32u(const minidump::module_info& module) + bool is_win32u(const minidump::module_info& mod) { - const auto name = module.module_name; + const auto name = mod.module_name; return name == "win32u.dll" || name == "WIN32U.DLL"; } @@ -421,33 +402,33 @@ namespace size_t failed_count = 0; size_t identified_count = 0; - for (const auto& module : modules) + for (const auto& mod : modules) { try { - auto* mapped_module = win_emu.mod_manager.map_memory_module(module.base_of_image, module.size_of_image, - module.module_name, win_emu.log); + auto* mapped_module = win_emu.mod_manager.map_memory_module(mod.base_of_image, mod.size_of_image, + mod.module_name, win_emu.log); if (mapped_module) { mapped_count++; win_emu.log.info(" Mapped %s at 0x%" PRIx64 " (%u bytes, %zu sections, %zu exports)\n", - module.module_name.c_str(), module.base_of_image, module.size_of_image, + mod.module_name.c_str(), mod.base_of_image, mod.size_of_image, mapped_module->sections.size(), mapped_module->exports.size()); - if (is_main_executable(module)) + if (is_main_executable(mod)) { win_emu.mod_manager.executable = mapped_module; identified_count++; win_emu.log.info(" Identified as main executable\n"); } - else if (is_ntdll(module)) + else if (is_ntdll(mod)) { win_emu.mod_manager.ntdll = mapped_module; identified_count++; win_emu.log.info(" Identified as ntdll\n"); } - else if (is_win32u(module)) + else if (is_win32u(mod)) { win_emu.mod_manager.win32u = mapped_module; identified_count++; @@ -457,14 +438,14 @@ namespace else { failed_count++; - win_emu.log.warn(" Failed to map %s at 0x%" PRIx64 "\n", module.module_name.c_str(), - module.base_of_image); + win_emu.log.warn(" Failed to map %s at 0x%" PRIx64 "\n", mod.module_name.c_str(), + mod.base_of_image); } } catch (const std::exception& e) { failed_count++; - win_emu.log.error(" Exception mapping %s: %s\n", module.module_name.c_str(), e.what()); + win_emu.log.error(" Exception mapping %s: %s\n", mod.module_name.c_str(), e.what()); } } @@ -599,8 +580,7 @@ namespace try { - // Read PEB address from TEB+0x60 (standard x64 TEB layout) - constexpr uint64_t teb_peb_offset = 0x60; + constexpr uint64_t teb_peb_offset = offsetof(TEB64, ProcessEnvironmentBlock); uint64_t peb_address = 0; win_emu.memory.read_memory(first_thread.teb + teb_peb_offset, &peb_address, sizeof(peb_address)); @@ -620,17 +600,6 @@ namespace } } - std::u16string utf8_to_utf16(const std::string& str) - { - std::u16string result; - result.reserve(str.size()); - for (const char c : str) - { - result.push_back(static_cast(static_cast(c))); - } - return result; - } - void reconstruct_handle_table(windows_emulator& win_emu, const minidump::minidump_file* dump_file) { const auto& handles = dump_file->handles(); @@ -653,21 +622,21 @@ namespace if (handle_info.type_name == "Event") { event evt{}; - evt.name = utf8_to_utf16(handle_info.object_name); + evt.name = u8_to_u16(handle_info.object_name); win_emu.process.events.store(std::move(evt)); created_count++; } else if (handle_info.type_name == "File") { file f{}; - f.name = utf8_to_utf16(handle_info.object_name); + f.name = u8_to_u16(handle_info.object_name); win_emu.process.files.store(std::move(f)); created_count++; } else if (handle_info.type_name == "Mutant") { mutant m{}; - m.name = utf8_to_utf16(handle_info.object_name); + m.name = u8_to_u16(handle_info.object_name); win_emu.process.mutants.store(std::move(m)); created_count++; } @@ -702,57 +671,49 @@ namespace exception_info->exception_record.exception_address, exception_info->exception_record.exception_code, exception_info->thread_id); } -} +} // namespace -minidump_loader::minidump_loader(windows_emulator& win_emu, std::filesystem::path minidump_path) - : win_emu_(win_emu), - minidump_path_(std::move(minidump_path)) +void load_minidump_into_emulator(windows_emulator& win_emu, std::filesystem::path minidump_path) { -} - -minidump_loader::~minidump_loader() = default; - -void minidump_loader::load_into_emulator() -{ - win_emu_.log.info("Starting minidump loading process\n"); - win_emu_.log.info("Minidump file: %s\n", minidump_path_.string().c_str()); + win_emu.log.info("Starting minidump loading process\n"); + win_emu.log.info("Minidump file: %s\n", minidump_path.string().c_str()); try { std::unique_ptr dump_file; std::unique_ptr dump_reader; - if (!parse_minidump_file(win_emu_, minidump_path_, dump_file, dump_reader)) + if (!parse_minidump_file(win_emu, minidump_path, dump_file, dump_reader)) { throw std::runtime_error("Failed to parse minidump file"); } - if (!validate_dump_compatibility(win_emu_, dump_file.get())) + if (!validate_dump_compatibility(win_emu, dump_file.get())) { throw std::runtime_error("Minidump compatibility validation failed"); } - setup_kusd_from_dump(win_emu_, dump_file.get()); + setup_kusd_from_dump(win_emu, dump_file.get()); dump_statistics stats; - log_dump_summary(win_emu_, dump_file.get(), stats); - process_streams(win_emu_, dump_file.get()); + log_dump_summary(win_emu, dump_file.get(), stats); + process_streams(win_emu, dump_file.get()); // Existing phases - reconstruct_memory_state(win_emu_, dump_file.get(), dump_reader.get()); - reconstruct_module_state(win_emu_, dump_file.get()); + reconstruct_memory_state(win_emu, dump_file.get(), dump_reader.get()); + reconstruct_module_state(win_emu, dump_file.get()); // Process state reconstruction phases - setup_peb_from_teb(win_emu_, dump_file.get()); - reconstruct_threads(win_emu_, dump_file.get(), minidump_path_); - reconstruct_handle_table(win_emu_, dump_file.get()); - setup_exception_context(win_emu_, dump_file.get()); + setup_peb_from_teb(win_emu, dump_file.get()); + reconstruct_threads(win_emu, dump_file.get(), minidump_path); + reconstruct_handle_table(win_emu, dump_file.get()); + setup_exception_context(win_emu, dump_file.get()); - win_emu_.log.info("Process state reconstruction completed\n"); + win_emu.log.info("Process state reconstruction completed\n"); } catch (const std::exception& e) { - win_emu_.log.error("Minidump loading failed: %s\n", e.what()); + win_emu.log.error("Minidump loading failed: %s\n", e.what()); throw; } } \ No newline at end of file diff --git a/src/windows-emulator/minidump_loader.hpp b/src/windows-emulator/minidump_loader.hpp index dc172e91..770ad094 100644 --- a/src/windows-emulator/minidump_loader.hpp +++ b/src/windows-emulator/minidump_loader.hpp @@ -3,15 +3,4 @@ class windows_emulator; -class minidump_loader -{ - public: - minidump_loader(windows_emulator& win_emu, std::filesystem::path minidump_path); - ~minidump_loader(); - - void load_into_emulator(); - - private: - windows_emulator& win_emu_; - std::filesystem::path minidump_path_; -}; \ No newline at end of file +void load_minidump_into_emulator(windows_emulator& win_emu, std::filesystem::path minidump_path); \ No newline at end of file diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index eb019de5..e4d6f61e 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -667,6 +667,5 @@ void windows_emulator::restore_snapshot() void windows_emulator::load_minidump(const std::filesystem::path& minidump_path) { - minidump_loader mdmp_loader(*this, minidump_path); - mdmp_loader.load_into_emulator(); + load_minidump_into_emulator(*this, minidump_path); } From 01d2656189445ef1e2fc4809788fbf5b8b3c3ede Mon Sep 17 00:00:00 2001 From: redthing1 Date: Thu, 12 Jun 2025 15:08:07 -0700 Subject: [PATCH 10/12] minidump loader: handle zero protection --- src/windows-emulator/minidump_loader.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/windows-emulator/minidump_loader.cpp b/src/windows-emulator/minidump_loader.cpp index 31e7f1ab..f052e4dc 100644 --- a/src/windows-emulator/minidump_loader.cpp +++ b/src/windows-emulator/minidump_loader.cpp @@ -293,7 +293,15 @@ namespace continue; } - memory_permission perms = map_nt_to_emulator_protection(region.protect); + auto protect_value = region.protect; + if (protect_value == 0) + { + protect_value = PAGE_READONLY; + win_emu.log.warn(" Region 0x%" PRIx64 " has zero protection, using PAGE_READONLY\n", + region.base_address); + } + + memory_permission perms = map_nt_to_emulator_protection(protect_value); try { From afd80aa9afcd3d6b9667b6a0ea7afba77da0eb6c Mon Sep 17 00:00:00 2001 From: redthing1 Date: Thu, 12 Jun 2025 15:13:45 -0700 Subject: [PATCH 11/12] make minidump loader fully standalone --- src/analyzer/main.cpp | 3 +- src/windows-emulator/minidump_loader.cpp | 78 +++++++++++------------ src/windows-emulator/minidump_loader.hpp | 5 +- src/windows-emulator/windows_emulator.cpp | 7 -- src/windows-emulator/windows_emulator.hpp | 2 - 5 files changed, 45 insertions(+), 50 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 6e5d73bd..5515c533 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "object_watching.hpp" #include "snapshot.hpp" @@ -262,7 +263,7 @@ namespace { // load minidump auto win_emu = create_empty_emulator(options); - win_emu->load_minidump(options.minidump_path); + minidump_loader::load_minidump_into_emulator(*win_emu, options.minidump_path); return win_emu; } diff --git a/src/windows-emulator/minidump_loader.cpp b/src/windows-emulator/minidump_loader.cpp index f052e4dc..05cf2042 100644 --- a/src/windows-emulator/minidump_loader.cpp +++ b/src/windows-emulator/minidump_loader.cpp @@ -9,7 +9,7 @@ #include -namespace +namespace minidump_loader { struct dump_statistics { @@ -679,49 +679,49 @@ namespace exception_info->exception_record.exception_address, exception_info->exception_record.exception_code, exception_info->thread_id); } -} // namespace -void load_minidump_into_emulator(windows_emulator& win_emu, std::filesystem::path minidump_path) -{ - win_emu.log.info("Starting minidump loading process\n"); - win_emu.log.info("Minidump file: %s\n", minidump_path.string().c_str()); - - try + void load_minidump_into_emulator(windows_emulator& win_emu, std::filesystem::path minidump_path) { - std::unique_ptr dump_file; - std::unique_ptr dump_reader; + win_emu.log.info("Starting minidump loading process\n"); + win_emu.log.info("Minidump file: %s\n", minidump_path.string().c_str()); - if (!parse_minidump_file(win_emu, minidump_path, dump_file, dump_reader)) + try { - throw std::runtime_error("Failed to parse minidump file"); - } + std::unique_ptr dump_file; + std::unique_ptr dump_reader; - if (!validate_dump_compatibility(win_emu, dump_file.get())) + if (!parse_minidump_file(win_emu, minidump_path, dump_file, dump_reader)) + { + throw std::runtime_error("Failed to parse minidump file"); + } + + if (!validate_dump_compatibility(win_emu, dump_file.get())) + { + throw std::runtime_error("Minidump compatibility validation failed"); + } + + setup_kusd_from_dump(win_emu, dump_file.get()); + + dump_statistics stats; + log_dump_summary(win_emu, dump_file.get(), stats); + process_streams(win_emu, dump_file.get()); + + // Existing phases + reconstruct_memory_state(win_emu, dump_file.get(), dump_reader.get()); + reconstruct_module_state(win_emu, dump_file.get()); + + // Process state reconstruction phases + setup_peb_from_teb(win_emu, dump_file.get()); + reconstruct_threads(win_emu, dump_file.get(), minidump_path); + reconstruct_handle_table(win_emu, dump_file.get()); + setup_exception_context(win_emu, dump_file.get()); + + win_emu.log.info("Process state reconstruction completed\n"); + } + catch (const std::exception& e) { - throw std::runtime_error("Minidump compatibility validation failed"); + win_emu.log.error("Minidump loading failed: %s\n", e.what()); + throw; } - - setup_kusd_from_dump(win_emu, dump_file.get()); - - dump_statistics stats; - log_dump_summary(win_emu, dump_file.get(), stats); - process_streams(win_emu, dump_file.get()); - - // Existing phases - reconstruct_memory_state(win_emu, dump_file.get(), dump_reader.get()); - reconstruct_module_state(win_emu, dump_file.get()); - - // Process state reconstruction phases - setup_peb_from_teb(win_emu, dump_file.get()); - reconstruct_threads(win_emu, dump_file.get(), minidump_path); - reconstruct_handle_table(win_emu, dump_file.get()); - setup_exception_context(win_emu, dump_file.get()); - - win_emu.log.info("Process state reconstruction completed\n"); } - catch (const std::exception& e) - { - win_emu.log.error("Minidump loading failed: %s\n", e.what()); - throw; - } -} \ No newline at end of file +} // namespace minidump_loader \ No newline at end of file diff --git a/src/windows-emulator/minidump_loader.hpp b/src/windows-emulator/minidump_loader.hpp index 770ad094..351299cd 100644 --- a/src/windows-emulator/minidump_loader.hpp +++ b/src/windows-emulator/minidump_loader.hpp @@ -3,4 +3,7 @@ class windows_emulator; -void load_minidump_into_emulator(windows_emulator& win_emu, std::filesystem::path minidump_path); \ No newline at end of file +namespace minidump_loader +{ + void load_minidump_into_emulator(windows_emulator& win_emu, std::filesystem::path minidump_path); +} \ No newline at end of file diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index e4d6f61e..44175c43 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -12,8 +12,6 @@ #include "network/static_socket_factory.hpp" -#include "minidump_loader.hpp" - constexpr auto MAX_INSTRUCTIONS_PER_TIME_SLICE = 0x20000; namespace @@ -664,8 +662,3 @@ void windows_emulator::restore_snapshot() this->process.deserialize(buffer); // this->process = *this->process_snapshot_; } - -void windows_emulator::load_minidump(const std::filesystem::path& minidump_path) -{ - load_minidump_into_emulator(*this, minidump_path); -} diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 34d17068..c6732a30 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -164,8 +164,6 @@ class windows_emulator void save_snapshot(); void restore_snapshot(); - void load_minidump(const std::filesystem::path& minidump_path); - uint16_t get_host_port(const uint16_t emulator_port) const { const auto entry = this->port_mappings_.find(emulator_port); From 1e63fe381ff7ba99026a020f37c4967f9d7887e6 Mon Sep 17 00:00:00 2001 From: redthing1 Date: Thu, 12 Jun 2025 15:25:37 -0700 Subject: [PATCH 12/12] minidump: tidy const refs --- src/windows-emulator/minidump_loader.cpp | 2 +- src/windows-emulator/minidump_loader.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/minidump_loader.cpp b/src/windows-emulator/minidump_loader.cpp index 05cf2042..7e9c7e31 100644 --- a/src/windows-emulator/minidump_loader.cpp +++ b/src/windows-emulator/minidump_loader.cpp @@ -680,7 +680,7 @@ namespace minidump_loader exception_info->exception_record.exception_code, exception_info->thread_id); } - void load_minidump_into_emulator(windows_emulator& win_emu, std::filesystem::path minidump_path) + void load_minidump_into_emulator(windows_emulator& win_emu, const std::filesystem::path& minidump_path) { win_emu.log.info("Starting minidump loading process\n"); win_emu.log.info("Minidump file: %s\n", minidump_path.string().c_str()); diff --git a/src/windows-emulator/minidump_loader.hpp b/src/windows-emulator/minidump_loader.hpp index 351299cd..c7b2fe1b 100644 --- a/src/windows-emulator/minidump_loader.hpp +++ b/src/windows-emulator/minidump_loader.hpp @@ -5,5 +5,5 @@ class windows_emulator; namespace minidump_loader { - void load_minidump_into_emulator(windows_emulator& win_emu, std::filesystem::path minidump_path); + void load_minidump_into_emulator(windows_emulator& win_emu, const std::filesystem::path& minidump_path); } \ No newline at end of file