Comprehensive WOW64 subsystem implementation

This commit is contained in:
brian
2025-10-13 19:53:14 +08:00
parent 9453123db0
commit 65eecf1cfd
51 changed files with 3772 additions and 283 deletions

View File

@@ -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);