mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-27 07:11:01 +00:00
Comprehensive WOW64 subsystem implementation
This commit is contained in:
@@ -32,9 +32,17 @@ struct mapped_module
|
||||
std::filesystem::path path{};
|
||||
|
||||
uint64_t image_base{};
|
||||
uint64_t image_base_file{};
|
||||
uint64_t size_of_image{};
|
||||
uint64_t entry_point{};
|
||||
|
||||
// PE header fields
|
||||
uint16_t machine{}; // Machine type from file header
|
||||
uint64_t size_of_stack_reserve{}; // Stack reserve size from optional header
|
||||
uint64_t size_of_stack_commit{}; // Stack commit size from optional header
|
||||
uint64_t size_of_heap_reserve{}; // Heap reserve size from optional header
|
||||
uint64_t size_of_heap_commit{}; // Heap commit size from optional header
|
||||
|
||||
exported_symbols exports{};
|
||||
imported_symbols imports{};
|
||||
imported_module_list imported_modules{};
|
||||
@@ -61,4 +69,9 @@ struct mapped_module
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t get_image_base_file() const
|
||||
{
|
||||
return this->image_base_file;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "module_manager.hpp"
|
||||
#include "module_mapping.hpp"
|
||||
#include "platform/win_pefile.hpp"
|
||||
#include "windows-emulator/logger.hpp"
|
||||
#include "../wow64_heaven_gate.hpp"
|
||||
|
||||
#include <serialization_helper.hpp>
|
||||
#include <cinttypes>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
@@ -55,6 +61,7 @@ namespace utils
|
||||
buffer.write(mod.path);
|
||||
|
||||
buffer.write(mod.image_base);
|
||||
buffer.write(mod.image_base_file);
|
||||
buffer.write(mod.size_of_image);
|
||||
buffer.write(mod.entry_point);
|
||||
|
||||
@@ -72,6 +79,7 @@ namespace utils
|
||||
buffer.read(mod.path);
|
||||
|
||||
buffer.read(mod.image_base);
|
||||
buffer.read(mod.image_base_file);
|
||||
buffer.read(mod.size_of_image);
|
||||
buffer.read(mod.entry_point);
|
||||
|
||||
@@ -84,6 +92,100 @@ namespace utils
|
||||
}
|
||||
}
|
||||
|
||||
// PE Architecture Detector Implementation
|
||||
pe_detection_result pe_architecture_detector::detect_from_file(const std::filesystem::path& file)
|
||||
{
|
||||
auto variant_result = winpe::get_pe_arch(file);
|
||||
|
||||
if (std::holds_alternative<std::error_code>(variant_result))
|
||||
{
|
||||
pe_detection_result result;
|
||||
result.error_message = "Failed to detect PE architecture from file: " + file.string();
|
||||
return result;
|
||||
}
|
||||
|
||||
auto arch = std::get<winpe::pe_arch>(variant_result);
|
||||
pe_detection_result result;
|
||||
result.architecture = arch;
|
||||
result.suggested_mode = determine_execution_mode(arch);
|
||||
return result;
|
||||
}
|
||||
|
||||
pe_detection_result pe_architecture_detector::detect_from_memory(uint64_t base_address, uint64_t image_size)
|
||||
{
|
||||
auto variant_result = winpe::get_pe_arch(base_address, image_size);
|
||||
|
||||
if (std::holds_alternative<std::error_code>(variant_result))
|
||||
{
|
||||
pe_detection_result result;
|
||||
result.error_message = "Failed to detect PE architecture from memory at 0x" + std::to_string(base_address);
|
||||
return result;
|
||||
}
|
||||
|
||||
auto arch = std::get<winpe::pe_arch>(variant_result);
|
||||
pe_detection_result result;
|
||||
result.architecture = arch;
|
||||
result.suggested_mode = determine_execution_mode(arch);
|
||||
return result;
|
||||
}
|
||||
|
||||
execution_mode pe_architecture_detector::determine_execution_mode(winpe::pe_arch executable_arch)
|
||||
{
|
||||
switch (executable_arch)
|
||||
{
|
||||
case winpe::pe_arch::pe32:
|
||||
return execution_mode::wow64_32bit;
|
||||
case winpe::pe_arch::pe64:
|
||||
return execution_mode::native_64bit;
|
||||
default:
|
||||
return execution_mode::unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// PE32 Mapping Strategy Implementation
|
||||
mapped_module pe32_mapping_strategy::map_from_file(memory_manager& memory, std::filesystem::path file)
|
||||
{
|
||||
return map_module_from_file<std::uint32_t>(memory, std::move(file));
|
||||
}
|
||||
|
||||
mapped_module pe32_mapping_strategy::map_from_memory(memory_manager& memory, uint64_t base_address, uint64_t image_size,
|
||||
const std::string& module_name)
|
||||
{
|
||||
return map_module_from_memory<std::uint32_t>(memory, base_address, image_size, module_name);
|
||||
}
|
||||
|
||||
// PE64 Mapping Strategy Implementation
|
||||
mapped_module pe64_mapping_strategy::map_from_file(memory_manager& memory, std::filesystem::path file)
|
||||
{
|
||||
return map_module_from_file<std::uint64_t>(memory, std::move(file));
|
||||
}
|
||||
|
||||
mapped_module pe64_mapping_strategy::map_from_memory(memory_manager& memory, uint64_t base_address, uint64_t image_size,
|
||||
const std::string& module_name)
|
||||
{
|
||||
return map_module_from_memory<std::uint64_t>(memory, base_address, image_size, module_name);
|
||||
}
|
||||
|
||||
// Mapping Strategy Factory Implementation
|
||||
mapping_strategy_factory::mapping_strategy_factory()
|
||||
: pe32_strategy_(std::make_unique<pe32_mapping_strategy>()),
|
||||
pe64_strategy_(std::make_unique<pe64_mapping_strategy>())
|
||||
{
|
||||
}
|
||||
|
||||
module_mapping_strategy& mapping_strategy_factory::get_strategy(winpe::pe_arch arch)
|
||||
{
|
||||
switch (arch)
|
||||
{
|
||||
case winpe::pe_arch::pe32:
|
||||
return *pe32_strategy_;
|
||||
case winpe::pe_arch::pe64:
|
||||
return *pe64_strategy_;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported PE architecture");
|
||||
}
|
||||
}
|
||||
|
||||
module_manager::module_manager(memory_manager& memory, file_system& file_sys, callbacks& cb)
|
||||
: memory_(&memory),
|
||||
file_sys_(&file_sys),
|
||||
@@ -91,23 +193,267 @@ module_manager::module_manager(memory_manager& memory, file_system& file_sys, ca
|
||||
{
|
||||
}
|
||||
|
||||
void module_manager::map_main_modules(const windows_path& executable_path, const windows_path& ntdll_path, const windows_path& win32u_path,
|
||||
const logger& logger)
|
||||
// Core mapping logic to eliminate code duplication
|
||||
mapped_module* module_manager::map_module_core(const pe_detection_result& detection_result, const std::function<mapped_module()>& mapper,
|
||||
const logger& logger, bool is_static)
|
||||
{
|
||||
if (!detection_result.is_valid())
|
||||
{
|
||||
logger.error("%s", detection_result.error_message.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
[[maybe_unused]] auto& strategy = strategy_factory_.get_strategy(detection_result.architecture);
|
||||
mapped_module mod = mapper();
|
||||
mod.is_static = is_static;
|
||||
|
||||
const auto image_base = mod.image_base;
|
||||
const auto entry = this->modules_.try_emplace(image_base, std::move(mod));
|
||||
|
||||
// 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;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
logger.error("Failed to map module: %s", e.what());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Execution mode detection
|
||||
execution_mode module_manager::detect_execution_mode(const windows_path& executable_path, const logger& logger)
|
||||
{
|
||||
auto detection_result = pe_architecture_detector::detect_from_file(this->file_sys_->translate(executable_path));
|
||||
|
||||
if (!detection_result.is_valid())
|
||||
{
|
||||
logger.error("Failed to detect executable architecture: %s", detection_result.error_message.c_str());
|
||||
return execution_mode::unknown;
|
||||
}
|
||||
|
||||
return detection_result.suggested_mode;
|
||||
}
|
||||
|
||||
// Native 64-bit module loading
|
||||
void module_manager::load_native_64bit_modules(const windows_path& executable_path, const windows_path& ntdll_path,
|
||||
const windows_path& win32u_path, const logger& logger)
|
||||
{
|
||||
logger.info("Loading native 64-bit modules\n");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// WOW64 module loading (with TODO placeholders for 32-bit details)
|
||||
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)
|
||||
{
|
||||
logger.info("Loading WOW64 modules for 32-bit application\n");
|
||||
|
||||
// Load 32-bit main executable
|
||||
this->executable = this->map_module(executable_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
|
||||
|
||||
// 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();
|
||||
|
||||
if (ntdll32_original_imagebase == 0 || ntdll64_original_imagebase == 0)
|
||||
{
|
||||
logger.error("Failed to get PE ImageBase values for WOW64 setup\n");
|
||||
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
|
||||
|
||||
// Basic structure initialization
|
||||
init_block.Size = symtem_dll_init_block_fix_size;
|
||||
|
||||
// Calculate relocation values
|
||||
// SystemDllWowRelocation = mapped_base - original_imagebase for 32-bit ntdll
|
||||
init_block.SystemDllWowRelocation = this->wow64_modules_.ntdll32->image_base - ntdll32_original_imagebase;
|
||||
|
||||
// SystemDllNativeRelocation = mapped_base - original_imagebase for 64-bit ntdll
|
||||
init_block.SystemDllNativeRelocation = this->ntdll->image_base - ntdll64_original_imagebase;
|
||||
|
||||
// Fill Wow64SharedInformation array with 32-bit ntdll export addresses
|
||||
init_block.Wow64SharedInformation[static_cast<uint64_t>(WOW64_SHARED_INFORMATION_V5::SharedNtdll32LdrInitializeThunk)] =
|
||||
this->wow64_modules_.ntdll32->find_export("LdrInitializeThunk");
|
||||
init_block.Wow64SharedInformation[static_cast<uint64_t>(WOW64_SHARED_INFORMATION_V5::SharedNtdll32KiUserExceptionDispatcher)] =
|
||||
this->wow64_modules_.ntdll32->find_export("KiUserExceptionDispatcher");
|
||||
init_block.Wow64SharedInformation[static_cast<uint64_t>(WOW64_SHARED_INFORMATION_V5::SharedNtdll32KiUserApcDispatcher)] =
|
||||
this->wow64_modules_.ntdll32->find_export("KiUserApcDispatcher");
|
||||
init_block.Wow64SharedInformation[static_cast<uint64_t>(WOW64_SHARED_INFORMATION_V5::SharedNtdll32KiUserCallbackDispatcher)] =
|
||||
this->wow64_modules_.ntdll32->find_export("KiUserCallbackDispatcher");
|
||||
init_block.Wow64SharedInformation[static_cast<uint64_t>(WOW64_SHARED_INFORMATION_V5::SharedNtdll32RtlUserThreadStart)] =
|
||||
this->wow64_modules_.ntdll32->find_export("RtlUserThreadStart");
|
||||
init_block
|
||||
.Wow64SharedInformation[static_cast<uint64_t>(WOW64_SHARED_INFORMATION_V5::SharedNtdll32pQueryProcessDebugInformationRemote)] =
|
||||
this->wow64_modules_.ntdll32->find_export("RtlpQueryProcessDebugInformationRemote");
|
||||
init_block.Wow64SharedInformation[static_cast<uint64_t>(WOW64_SHARED_INFORMATION_V5::SharedNtdll32BaseAddress)] =
|
||||
this->wow64_modules_.ntdll32->image_base;
|
||||
init_block.Wow64SharedInformation[static_cast<uint64_t>(WOW64_SHARED_INFORMATION_V5::SharedNtdll32LdrSystemDllInitBlock)] =
|
||||
this->wow64_modules_.ntdll32->find_export("LdrSystemDllInitBlock");
|
||||
init_block.Wow64SharedInformation[static_cast<uint64_t>(WOW64_SHARED_INFORMATION_V5::SharedNtdll32RtlpFreezeTimeBias)] =
|
||||
this->wow64_modules_.ntdll32->find_export("RtlpFreezeTimeBias");
|
||||
|
||||
// Set RngData to a random non-zero value for early randomization
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<uint32_t> dis(1, UINT32_MAX);
|
||||
init_block.RngData = dis(gen);
|
||||
|
||||
// Set flags and mitigation options based on WinDbg data
|
||||
init_block.Flags = 0x22222022;
|
||||
init_block.MitigationOptionsMap.Map[0] = 0x20002000;
|
||||
init_block.MitigationOptionsMap.Map[1] = 0x00000002;
|
||||
init_block.MitigationOptionsMap.Map[2] = 0x00000000;
|
||||
|
||||
// CFG and audit options (set to zero as per WinDbg data)
|
||||
init_block.CfgBitMap = 0;
|
||||
init_block.CfgBitMapSize = 0;
|
||||
init_block.Wow64CfgBitMap = 0;
|
||||
init_block.Wow64CfgBitMapSize = 0;
|
||||
init_block.MitigationAuditOptionsMap.Map[0] = 0;
|
||||
init_block.MitigationAuditOptionsMap.Map[1] = 0;
|
||||
init_block.MitigationAuditOptionsMap.Map[2] = 0;
|
||||
|
||||
// Find LdrSystemDllInitBlock export address in 64-bit ntdll and write the structure
|
||||
const auto ldr_init_block_addr = this->ntdll->find_export("LdrSystemDllInitBlock");
|
||||
if (ldr_init_block_addr == 0)
|
||||
{
|
||||
logger.error("Failed to find LdrSystemDllInitBlock export in 64-bit ntdll\n");
|
||||
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);
|
||||
|
||||
logger.info("Successfully initialized LdrSystemDllInitBlock at 0x%" PRIx64 "\n", ldr_init_block_addr);
|
||||
|
||||
// Install the WOW64 Heaven's Gate trampoline used for compat-mode -> 64-bit transitions.
|
||||
this->install_wow64_heaven_gate(logger);
|
||||
}
|
||||
|
||||
void module_manager::install_wow64_heaven_gate(const logger& logger)
|
||||
{
|
||||
using wow64::heaven_gate::kCodeBase;
|
||||
using wow64::heaven_gate::kCodeSize;
|
||||
using wow64::heaven_gate::kStackBase;
|
||||
using wow64::heaven_gate::kStackSize;
|
||||
using wow64::heaven_gate::kTrampolineBytes;
|
||||
|
||||
auto allocate_or_validate = [&](uint64_t base, size_t size, memory_permission perms, const char* name) {
|
||||
if (!this->memory_->allocate_memory(base, size, perms))
|
||||
{
|
||||
const auto region = this->memory_->get_region_info(base);
|
||||
if (!region.is_reserved || region.allocation_length < size)
|
||||
{
|
||||
logger.error("Failed to allocate %s at 0x%" PRIx64 " (size 0x%zx)\n", name, base, size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
bool code_initialized = false;
|
||||
if (allocate_or_validate(kCodeBase, kCodeSize, memory_permission::read_write, "WOW64 heaven gate code"))
|
||||
{
|
||||
if (!this->memory_->protect_memory(kCodeBase, kCodeSize, nt_memory_permission(memory_permission::read_write)))
|
||||
{
|
||||
logger.error("Failed to change protection for WOW64 heaven gate code at 0x%" PRIx64 "\n", kCodeBase);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<uint8_t> buffer(kCodeSize, 0);
|
||||
this->memory_->write_memory(kCodeBase, buffer.data(), buffer.size());
|
||||
this->memory_->write_memory(kCodeBase, kTrampolineBytes.data(), kTrampolineBytes.size());
|
||||
this->memory_->protect_memory(kCodeBase, kCodeSize, nt_memory_permission(memory_permission::read | memory_permission::exec));
|
||||
code_initialized = true;
|
||||
}
|
||||
|
||||
if (code_initialized && this->modules_.find(kCodeBase) == this->modules_.end())
|
||||
{
|
||||
mapped_module module{};
|
||||
module.name = "wow64_heaven_gate";
|
||||
module.path = "<wow64-heaven-gate>";
|
||||
module.image_base = kCodeBase;
|
||||
module.image_base_file = kCodeBase;
|
||||
module.size_of_image = kCodeSize;
|
||||
module.entry_point = kCodeBase;
|
||||
constexpr uint16_t kMachineAmd64 = 0x8664;
|
||||
module.machine = kMachineAmd64;
|
||||
module.is_static = true;
|
||||
|
||||
mapped_section section{};
|
||||
section.name = ".gate";
|
||||
section.region.start = kCodeBase;
|
||||
section.region.length = kCodeSize;
|
||||
section.region.permissions = memory_permission::read | memory_permission::exec;
|
||||
module.sections.emplace_back(std::move(section));
|
||||
|
||||
this->modules_.emplace(module.image_base, std::move(module));
|
||||
}
|
||||
}
|
||||
|
||||
if (allocate_or_validate(kStackBase, kStackSize, memory_permission::read_write, "WOW64 heaven gate stack"))
|
||||
{
|
||||
std::vector<uint8_t> buffer(kStackSize, 0);
|
||||
this->memory_->write_memory(kStackBase, buffer.data(), buffer.size());
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Detect execution mode based on executable architecture
|
||||
current_execution_mode_ = detect_execution_mode(executable_path, logger);
|
||||
|
||||
// Load modules based on detected execution mode
|
||||
switch (current_execution_mode_)
|
||||
{
|
||||
case execution_mode::native_64bit:
|
||||
load_native_64bit_modules(executable_path, system32_path / "ntdll.dll", system32_path / "win32u.dll", logger);
|
||||
break;
|
||||
|
||||
case execution_mode::wow64_32bit:
|
||||
load_wow64_modules(executable_path, system32_path / "ntdll.dll", system32_path / "win32u.dll", syswow64_path / "ntdll.dll", logger);
|
||||
break;
|
||||
|
||||
case execution_mode::unknown:
|
||||
default:
|
||||
throw std::runtime_error("Unknown or unsupported execution mode detected");
|
||||
}
|
||||
}
|
||||
|
||||
mapped_module* module_manager::map_module(const windows_path& file, const logger& logger, const bool is_static)
|
||||
{
|
||||
return this->map_local_module(this->file_sys_->translate(file), logger, is_static);
|
||||
}
|
||||
|
||||
// Refactored map_local_module using the new architecture
|
||||
mapped_module* module_manager::map_local_module(const std::filesystem::path& file, const logger& logger, const bool is_static)
|
||||
{
|
||||
auto local_file = weakly_canonical(absolute(file));
|
||||
|
||||
// Check if module is already loaded
|
||||
for (auto& mod : this->modules_ | std::views::values)
|
||||
{
|
||||
if (mod.path == local_file)
|
||||
@@ -116,31 +462,24 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto mod = map_module_from_file(*this->memory_, std::move(local_file));
|
||||
mod.is_static = is_static;
|
||||
// Detect PE architecture
|
||||
auto detection_result = pe_architecture_detector::detect_from_file(local_file);
|
||||
|
||||
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 %s: %s\n", file.generic_string().c_str(), e.what());
|
||||
return nullptr;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
// Use core mapping logic to eliminate code duplication
|
||||
return map_module_core(
|
||||
detection_result,
|
||||
[&]() {
|
||||
auto& strategy = strategy_factory_.get_strategy(detection_result.architecture);
|
||||
return strategy.map_from_file(*this->memory_, std::move(local_file));
|
||||
},
|
||||
logger, is_static);
|
||||
}
|
||||
|
||||
// Refactored map_memory_module using the new architecture
|
||||
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)
|
||||
{
|
||||
// Check if module is already loaded at this address
|
||||
for (auto& mod : this->modules_ | std::views::values)
|
||||
{
|
||||
if (mod.image_base == base_address)
|
||||
@@ -149,26 +488,17 @@ mapped_module* module_manager::map_memory_module(uint64_t base_address, uint64_t
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto mod = ::map_module_from_memory(*this->memory_, base_address, image_size, module_name);
|
||||
mod.is_static = is_static;
|
||||
// Detect PE architecture from memory
|
||||
auto detection_result = pe_architecture_detector::detect_from_memory(base_address, image_size);
|
||||
|
||||
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%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%016" PRIx64 ": Unknown error\n", module_name.c_str(), base_address);
|
||||
return nullptr;
|
||||
}
|
||||
// Use core mapping logic to eliminate code duplication
|
||||
return map_module_core(
|
||||
detection_result,
|
||||
[&]() {
|
||||
auto& strategy = strategy_factory_.get_strategy(detection_result.architecture);
|
||||
return strategy.map_from_memory(*this->memory_, base_address, image_size, module_name);
|
||||
},
|
||||
logger, is_static);
|
||||
}
|
||||
|
||||
void module_manager::serialize(utils::buffer_serializer& buffer) const
|
||||
@@ -178,6 +508,14 @@ void module_manager::serialize(utils::buffer_serializer& buffer) const
|
||||
buffer.write(this->executable ? this->executable->image_base : 0);
|
||||
buffer.write(this->ntdll ? this->ntdll->image_base : 0);
|
||||
buffer.write(this->win32u ? this->win32u->image_base : 0);
|
||||
|
||||
// Serialize execution mode
|
||||
buffer.write(static_cast<uint32_t>(this->current_execution_mode_));
|
||||
|
||||
// Serialize WOW64 module pointers
|
||||
buffer.write(this->wow64_modules_.ntdll32 ? this->wow64_modules_.ntdll32->image_base : 0);
|
||||
buffer.write(this->wow64_modules_.wow64_dll ? this->wow64_modules_.wow64_dll->image_base : 0);
|
||||
buffer.write(this->wow64_modules_.wow64win_dll ? this->wow64_modules_.wow64win_dll->image_base : 0);
|
||||
}
|
||||
|
||||
void module_manager::deserialize(utils::buffer_deserializer& buffer)
|
||||
@@ -191,6 +529,18 @@ void module_manager::deserialize(utils::buffer_deserializer& buffer)
|
||||
this->executable = executable_base ? this->find_by_address(executable_base) : nullptr;
|
||||
this->ntdll = ntdll_base ? this->find_by_address(ntdll_base) : nullptr;
|
||||
this->win32u = win32u_base ? this->find_by_address(win32u_base) : nullptr;
|
||||
|
||||
// Deserialize execution mode
|
||||
this->current_execution_mode_ = static_cast<execution_mode>(buffer.read<uint32_t>());
|
||||
|
||||
// Deserialize WOW64 module pointers
|
||||
const auto ntdll32_base = buffer.read<uint64_t>();
|
||||
const auto wow64_dll_base = buffer.read<uint64_t>();
|
||||
const auto wow64win_dll_base = buffer.read<uint64_t>();
|
||||
|
||||
this->wow64_modules_.ntdll32 = ntdll32_base ? this->find_by_address(ntdll32_base) : nullptr;
|
||||
this->wow64_modules_.wow64_dll = wow64_dll_base ? this->find_by_address(wow64_dll_base) : nullptr;
|
||||
this->wow64_modules_.wow64win_dll = wow64win_dll_base ? this->find_by_address(wow64win_dll_base) : nullptr;
|
||||
}
|
||||
|
||||
bool module_manager::unmap(const uint64_t address)
|
||||
@@ -212,3 +562,49 @@ 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);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,80 @@
|
||||
#include "mapped_module.hpp"
|
||||
#include "../file_system.hpp"
|
||||
#include <utils/function.hpp>
|
||||
#include "platform/win_pefile.hpp"
|
||||
|
||||
class logger;
|
||||
|
||||
// Execution mode for the emulated process
|
||||
enum class execution_mode
|
||||
{
|
||||
native_64bit, // Native 64-bit execution
|
||||
wow64_32bit, // WOW64 mode for 32-bit applications
|
||||
unknown // Detection failed or unsupported
|
||||
};
|
||||
|
||||
// PE architecture detection result
|
||||
struct pe_detection_result
|
||||
{
|
||||
winpe::pe_arch architecture;
|
||||
execution_mode suggested_mode;
|
||||
std::string error_message;
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return error_message.empty();
|
||||
}
|
||||
};
|
||||
|
||||
// Strategy interface for module mapping
|
||||
class module_mapping_strategy
|
||||
{
|
||||
public:
|
||||
virtual ~module_mapping_strategy() = default;
|
||||
virtual mapped_module map_from_file(memory_manager& memory, std::filesystem::path file) = 0;
|
||||
virtual mapped_module map_from_memory(memory_manager& memory, uint64_t base_address, uint64_t image_size,
|
||||
const std::string& module_name) = 0;
|
||||
};
|
||||
|
||||
// PE32 mapping strategy implementation
|
||||
class pe32_mapping_strategy : public module_mapping_strategy
|
||||
{
|
||||
public:
|
||||
mapped_module map_from_file(memory_manager& memory, std::filesystem::path file) override;
|
||||
mapped_module map_from_memory(memory_manager& memory, uint64_t base_address, uint64_t image_size,
|
||||
const std::string& module_name) override;
|
||||
};
|
||||
|
||||
// PE64 mapping strategy implementation
|
||||
class pe64_mapping_strategy : public module_mapping_strategy
|
||||
{
|
||||
public:
|
||||
mapped_module map_from_file(memory_manager& memory, std::filesystem::path file) override;
|
||||
mapped_module map_from_memory(memory_manager& memory, uint64_t base_address, uint64_t image_size,
|
||||
const std::string& module_name) override;
|
||||
};
|
||||
|
||||
// Factory for creating mapping strategies
|
||||
class mapping_strategy_factory
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<pe32_mapping_strategy> pe32_strategy_;
|
||||
std::unique_ptr<pe64_mapping_strategy> pe64_strategy_;
|
||||
|
||||
public:
|
||||
mapping_strategy_factory();
|
||||
module_mapping_strategy& get_strategy(winpe::pe_arch arch);
|
||||
};
|
||||
|
||||
// PE architecture detector utility class
|
||||
class pe_architecture_detector
|
||||
{
|
||||
public:
|
||||
static pe_detection_result detect_from_file(const std::filesystem::path& file);
|
||||
static pe_detection_result detect_from_memory(uint64_t base_address, uint64_t image_size);
|
||||
static execution_mode determine_execution_mode(winpe::pe_arch executable_arch);
|
||||
};
|
||||
|
||||
class module_manager
|
||||
{
|
||||
public:
|
||||
@@ -20,7 +91,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& ntdll_path, const windows_path& win32u_path,
|
||||
void map_main_modules(const windows_path& executable_path, const windows_path& system32_path, const windows_path& syswow64_path,
|
||||
const logger& logger);
|
||||
|
||||
mapped_module* map_module(const windows_path& file, const logger& logger, bool is_static = false);
|
||||
@@ -72,11 +143,30 @@ class module_manager
|
||||
return modules_;
|
||||
}
|
||||
|
||||
// TODO: These is wrong here. A good mechanism for quick module access is needed.
|
||||
// Execution mode accessors
|
||||
execution_mode get_execution_mode() const
|
||||
{
|
||||
return current_execution_mode_;
|
||||
}
|
||||
bool is_wow64_process() const
|
||||
{
|
||||
return current_execution_mode_ == execution_mode::wow64_32bit;
|
||||
}
|
||||
|
||||
// TODO: These should be properly encapsulated. A good mechanism for quick module access is needed.
|
||||
mapped_module* executable{};
|
||||
mapped_module* ntdll{};
|
||||
mapped_module* win32u{};
|
||||
|
||||
// WOW64-specific modules (for validation and future use)
|
||||
struct wow64_modules
|
||||
{
|
||||
mapped_module* ntdll32 = nullptr; // 32-bit ntdll.dll
|
||||
mapped_module* wow64_dll = nullptr; // wow64.dll (loaded by system)
|
||||
mapped_module* wow64win_dll = nullptr; // wow64win.dll (loaded by system)
|
||||
// Note: wow64cpu.dll is loaded by ntdll via registry lookup, not managed here
|
||||
} wow64_modules_;
|
||||
|
||||
private:
|
||||
memory_manager* memory_{};
|
||||
file_system* file_sys_{};
|
||||
@@ -84,6 +174,28 @@ class module_manager
|
||||
|
||||
module_map modules_{};
|
||||
|
||||
// Strategy pattern components
|
||||
mapping_strategy_factory strategy_factory_;
|
||||
execution_mode current_execution_mode_ = execution_mode::unknown;
|
||||
|
||||
// Core mapping logic to eliminate code duplication
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
void install_wow64_heaven_gate(const logger& logger);
|
||||
|
||||
module_map::iterator get_module(const uint64_t address)
|
||||
{
|
||||
if (this->modules_.empty())
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
uint64_t get_first_section_offset(const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||
template <typename T>
|
||||
uint64_t get_first_section_offset(const PENTHeaders_t<T>& nt_headers, const uint64_t nt_headers_offset)
|
||||
{
|
||||
const auto* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers);
|
||||
const size_t optional_header_offset =
|
||||
@@ -20,6 +21,7 @@ namespace
|
||||
return nt_headers_offset + (first_section_absolute - absolute_base);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<std::byte> read_mapped_memory(const memory_manager& memory, const mapped_module& binary)
|
||||
{
|
||||
std::vector<std::byte> mem{};
|
||||
@@ -29,8 +31,9 @@ namespace
|
||||
return mem;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void collect_imports(mapped_module& binary, const utils::safe_buffer_accessor<const std::byte> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
const PEOptionalHeader_t<T>& optional_header)
|
||||
{
|
||||
const auto& import_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
if (import_directory_entry.VirtualAddress == 0 || import_directory_entry.Size == 0)
|
||||
@@ -48,13 +51,17 @@ namespace
|
||||
break;
|
||||
}
|
||||
|
||||
// Use architecture-specific thunk data type
|
||||
using thunk_traits = thunk_data_traits<T>;
|
||||
using thunk_type = typename thunk_traits::type;
|
||||
|
||||
const auto module_index = binary.imported_modules.size();
|
||||
binary.imported_modules.push_back(buffer.as_string(descriptor.Name));
|
||||
|
||||
auto original_thunk_data = buffer.as<IMAGE_THUNK_DATA64>(descriptor.FirstThunk);
|
||||
auto original_thunk_data = buffer.as<thunk_type>(descriptor.FirstThunk);
|
||||
if (descriptor.OriginalFirstThunk)
|
||||
{
|
||||
original_thunk_data = buffer.as<IMAGE_THUNK_DATA64>(descriptor.OriginalFirstThunk);
|
||||
original_thunk_data = buffer.as<thunk_type>(descriptor.OriginalFirstThunk);
|
||||
}
|
||||
|
||||
for (size_t j = 0;; ++j)
|
||||
@@ -65,16 +72,17 @@ namespace
|
||||
break;
|
||||
}
|
||||
|
||||
static_assert(sizeof(IMAGE_THUNK_DATA64) == sizeof(uint64_t));
|
||||
const auto thunk_rva = descriptor.FirstThunk + sizeof(IMAGE_THUNK_DATA64) * j;
|
||||
static_assert(sizeof(thunk_type) == sizeof(T));
|
||||
const auto thunk_rva = descriptor.FirstThunk + sizeof(thunk_type) * j;
|
||||
const auto thunk_address = thunk_rva + binary.image_base;
|
||||
|
||||
auto& sym = binary.imports[thunk_address];
|
||||
sym.module_index = module_index;
|
||||
|
||||
if (IMAGE_SNAP_BY_ORDINAL64(original_thunk.u1.Ordinal))
|
||||
// Use architecture-specific ordinal checking
|
||||
if (thunk_traits::snap_by_ordinal(original_thunk.u1.Ordinal))
|
||||
{
|
||||
sym.name = "#" + std::to_string(IMAGE_ORDINAL64(original_thunk.u1.Ordinal));
|
||||
sym.name = "#" + std::to_string(thunk_traits::ordinal_mask(original_thunk.u1.Ordinal));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -85,8 +93,9 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const std::byte> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
const PEOptionalHeader_t<T>& optional_header)
|
||||
{
|
||||
const auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
|
||||
@@ -134,8 +143,9 @@ namespace
|
||||
obj.set(new_value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<std::byte> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
const PEOptionalHeader_t<T>& optional_header)
|
||||
{
|
||||
const auto delta = binary.image_base - optional_header.ImageBase;
|
||||
if (delta == 0)
|
||||
@@ -196,8 +206,9 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void map_sections(memory_manager& memory, mapped_module& binary, const utils::safe_buffer_accessor<const std::byte> buffer,
|
||||
const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||
const PENTHeaders_t<T>& nt_headers, const uint64_t nt_headers_offset)
|
||||
{
|
||||
const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset);
|
||||
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(static_cast<size_t>(first_section_offset));
|
||||
@@ -250,6 +261,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
mapped_module map_module_from_data(memory_manager& memory, const std::span<const std::byte> data, std::filesystem::path file)
|
||||
{
|
||||
mapped_module binary{};
|
||||
@@ -261,20 +273,43 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span<const
|
||||
const auto dos_header = buffer.as<PEDosHeader_t>(0).get();
|
||||
const auto nt_headers_offset = dos_header.e_lfanew;
|
||||
|
||||
const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get();
|
||||
const auto nt_headers = buffer.as<PENTHeaders_t<T>>(nt_headers_offset).get();
|
||||
const auto& optional_header = nt_headers.OptionalHeader;
|
||||
|
||||
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
|
||||
if (nt_headers.FileHeader.Machine != PEMachineType::I386 && nt_headers.FileHeader.Machine != PEMachineType::AMD64)
|
||||
{
|
||||
throw std::runtime_error("Unsupported architecture!");
|
||||
}
|
||||
|
||||
binary.image_base = optional_header.ImageBase;
|
||||
binary.image_base_file = optional_header.ImageBase;
|
||||
binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize
|
||||
|
||||
// Store PE header fields
|
||||
binary.machine = static_cast<uint16_t>(nt_headers.FileHeader.Machine);
|
||||
binary.size_of_stack_reserve = optional_header.SizeOfStackReserve;
|
||||
binary.size_of_stack_commit = optional_header.SizeOfStackCommit;
|
||||
binary.size_of_heap_reserve = optional_header.SizeOfHeapReserve;
|
||||
binary.size_of_heap_commit = optional_header.SizeOfHeapCommit;
|
||||
|
||||
if (!memory.allocate_memory(binary.image_base, static_cast<size_t>(binary.size_of_image), memory_permission::all))
|
||||
{
|
||||
binary.image_base = memory.find_free_allocation_base(static_cast<size_t>(binary.size_of_image));
|
||||
// Check if this is a 32-bit module (WOW64)
|
||||
const bool is_32bit = (nt_headers.FileHeader.Machine == PEMachineType::I386);
|
||||
|
||||
if (is_32bit)
|
||||
{
|
||||
// Use 32-bit allocation for WOW64 modules
|
||||
binary.image_base =
|
||||
memory.find_free_allocation_base(static_cast<size_t>(binary.size_of_image), DEFAULT_ALLOCATION_ADDRESS_32BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use 64-bit allocation for native modules
|
||||
binary.image_base =
|
||||
memory.find_free_allocation_base(static_cast<size_t>(binary.size_of_image), DEFAULT_ALLOCATION_ADDRESS_64BIT);
|
||||
}
|
||||
|
||||
const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
|
||||
const auto has_dynamic_base = optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
|
||||
const auto is_relocatable = is_dll || has_dynamic_base;
|
||||
@@ -296,7 +331,7 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span<const
|
||||
|
||||
map_sections(memory, binary, buffer, nt_headers, nt_headers_offset);
|
||||
|
||||
auto mapped_memory = read_mapped_memory(memory, binary);
|
||||
auto mapped_memory = read_mapped_memory<T>(memory, binary);
|
||||
utils::safe_buffer_accessor<std::byte> mapped_buffer{mapped_memory};
|
||||
|
||||
apply_relocations(binary, mapped_buffer, optional_header);
|
||||
@@ -308,6 +343,7 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span<const
|
||||
return binary;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path file)
|
||||
{
|
||||
const auto data = utils::io::read_file(file);
|
||||
@@ -316,18 +352,20 @@ mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path
|
||||
throw std::runtime_error("Bad file data: " + file.string());
|
||||
}
|
||||
|
||||
return map_module_from_data(memory, data, std::move(file));
|
||||
return map_module_from_data<T>(memory, data, std::move(file));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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.image_base_file = base_address;
|
||||
binary.size_of_image = image_size;
|
||||
|
||||
auto mapped_memory = read_mapped_memory(memory, binary);
|
||||
auto mapped_memory = read_mapped_memory<T>(memory, binary);
|
||||
utils::safe_buffer_accessor<const std::byte> buffer{mapped_memory};
|
||||
|
||||
try
|
||||
@@ -339,6 +377,13 @@ mapped_module map_module_from_memory(memory_manager& memory, uint64_t base_addre
|
||||
|
||||
binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint;
|
||||
|
||||
// Store PE header fields
|
||||
binary.machine = static_cast<uint16_t>(nt_headers.FileHeader.Machine);
|
||||
binary.size_of_stack_reserve = optional_header.SizeOfStackReserve;
|
||||
binary.size_of_stack_commit = optional_header.SizeOfStackCommit;
|
||||
binary.size_of_heap_reserve = optional_header.SizeOfHeapReserve;
|
||||
binary.size_of_heap_commit = optional_header.SizeOfHeapCommit;
|
||||
|
||||
const auto section_offset = get_first_section_offset(nt_headers, nt_headers_offset);
|
||||
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(static_cast<size_t>(section_offset));
|
||||
|
||||
@@ -390,3 +435,15 @@ bool unmap_module(memory_manager& memory, const mapped_module& mod)
|
||||
{
|
||||
return memory.release_memory(mod.image_base, static_cast<size_t>(mod.size_of_image));
|
||||
}
|
||||
|
||||
template mapped_module map_module_from_data<std::uint32_t>(memory_manager& memory, const std::span<const std::byte> data,
|
||||
std::filesystem::path file);
|
||||
template mapped_module map_module_from_data<std::uint64_t>(memory_manager& memory, const std::span<const std::byte> data,
|
||||
std::filesystem::path file);
|
||||
template mapped_module map_module_from_file<std::uint32_t>(memory_manager& memory, std::filesystem::path file);
|
||||
template mapped_module map_module_from_file<std::uint64_t>(memory_manager& memory, std::filesystem::path file);
|
||||
|
||||
template mapped_module map_module_from_memory<std::uint32_t>(memory_manager& memory, uint64_t base_address, uint64_t image_size,
|
||||
const std::string& module_name);
|
||||
template mapped_module map_module_from_memory<std::uint64_t>(memory_manager& memory, uint64_t base_address, uint64_t image_size,
|
||||
const std::string& module_name);
|
||||
@@ -4,7 +4,9 @@
|
||||
#include "../memory_manager.hpp"
|
||||
|
||||
mapped_module map_module_from_data(memory_manager& memory, std::span<const uint8_t> data, std::filesystem::path file);
|
||||
template <typename T>
|
||||
mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path file);
|
||||
template <typename T>
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user