mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-28 15:31:02 +00:00
Comprehensive WOW64 subsystem implementation
This commit is contained in:
@@ -853,6 +853,28 @@ namespace syscalls
|
||||
const auto attributes = object_attributes.read();
|
||||
auto filename = read_unicode_string(c.emu, attributes.ObjectName);
|
||||
|
||||
// Check for console device paths
|
||||
// Convert to uppercase for case-insensitive comparison
|
||||
std::u16string filename_upper = filename;
|
||||
std::transform(filename_upper.begin(), filename_upper.end(), filename_upper.begin(), ::towupper);
|
||||
|
||||
// Handle console output device
|
||||
if (filename_upper == u"\\??\\CONOUT$" || filename_upper == u"\\DEVICE\\CONOUT$" || filename_upper == u"CONOUT$" ||
|
||||
filename_upper == u"\\??\\CON" || filename_upper == u"\\DEVICE\\CONSOLE" || filename_upper == u"CON")
|
||||
{
|
||||
c.win_emu.callbacks.on_generic_access("Opening console output", filename);
|
||||
file_handle.write(STDOUT_HANDLE);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Handle console input device
|
||||
if (filename_upper == u"\\??\\CONIN$" || filename_upper == u"\\DEVICE\\CONIN$" || filename_upper == u"CONIN$")
|
||||
{
|
||||
c.win_emu.callbacks.on_generic_access("Opening console input", filename);
|
||||
file_handle.write(STDIN_HANDLE);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (is_named_pipe_path(filename))
|
||||
{
|
||||
return handle_named_pipe_create(c, file_handle, filename, attributes, desired_access);
|
||||
@@ -1074,6 +1096,12 @@ namespace syscalls
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (object_name == u"\\KnownDlls32")
|
||||
{
|
||||
directory_handle.write(KNOWN_DLLS32_DIRECTORY);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (object_name == u"\\Sessions\\1\\BaseNamedObjects")
|
||||
{
|
||||
directory_handle.write(BASE_NAMED_OBJECTS_DIRECTORY);
|
||||
@@ -1132,6 +1160,29 @@ namespace syscalls
|
||||
return too_small ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (link_handle == KNOWN_DLLS32_SYMLINK)
|
||||
{
|
||||
constexpr std::u16string_view syswow64 = u"C:\\WINDOWS\\SysWOW64";
|
||||
constexpr auto str_length = syswow64.size() * 2;
|
||||
constexpr auto max_length = str_length + 2;
|
||||
|
||||
returned_length.write(max_length);
|
||||
|
||||
bool too_small = false;
|
||||
link_target.access([&](UNICODE_STRING<EmulatorTraits<Emu64>>& str) {
|
||||
if (str.MaximumLength < max_length)
|
||||
{
|
||||
too_small = true;
|
||||
return;
|
||||
}
|
||||
|
||||
str.Length = str_length;
|
||||
c.emu.write_memory(str.Buffer, syswow64.data(), max_length);
|
||||
});
|
||||
|
||||
return too_small ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,10 @@ namespace syscalls
|
||||
std::u16string device_path;
|
||||
switch (handle.value.type)
|
||||
{
|
||||
case handle_types::reserved: {
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
case handle_types::file: {
|
||||
const auto* file = c.proc.files.get(handle);
|
||||
if (!file)
|
||||
@@ -118,6 +122,50 @@ namespace syscalls
|
||||
device_path = device->get_device_path();
|
||||
break;
|
||||
}
|
||||
case handle_types::directory: {
|
||||
// Directory handles are pseudo handles representing specific object directories
|
||||
if (handle == KNOWN_DLLS_DIRECTORY)
|
||||
{
|
||||
device_path = u"\\KnownDlls";
|
||||
}
|
||||
else if (handle == KNOWN_DLLS32_DIRECTORY)
|
||||
{
|
||||
device_path = u"\\KnownDlls32";
|
||||
}
|
||||
else if (handle == BASE_NAMED_OBJECTS_DIRECTORY)
|
||||
{
|
||||
device_path = u"\\Sessions\\1\\BaseNamedObjects";
|
||||
}
|
||||
else if (handle == RPC_CONTROL_DIRECTORY)
|
||||
{
|
||||
device_path = u"\\RPC Control";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown directory handle
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case handle_types::registry: {
|
||||
const auto* registry = c.proc.registry_keys.get(handle);
|
||||
if (!registry)
|
||||
{
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
// Build the full registry path in device format
|
||||
auto registry_path = (registry->hive.get() / registry->path.get()).u16string();
|
||||
|
||||
// Convert backslashes to forward slashes for consistency
|
||||
std::ranges::replace(registry_path, u'/', u'\\');
|
||||
|
||||
// Convert to uppercase as Windows registry paths are case-insensitive
|
||||
std::ranges::transform(registry_path, registry_path.begin(), std::towupper);
|
||||
|
||||
device_path = registry_path;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
c.win_emu.log.error("Unsupported handle type for name information query: %X\n", handle.value.type);
|
||||
c.emu.stop();
|
||||
|
||||
@@ -17,8 +17,36 @@ namespace syscalls
|
||||
|
||||
switch (info_class)
|
||||
{
|
||||
case ProcessExecuteFlags:
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
case ProcessGroupInformation:
|
||||
case ProcessMitigationPolicy:
|
||||
case ProcessMitigationPolicy: {
|
||||
// ProcessMitigationPolicy requires special handling because the caller
|
||||
// specifies which policy to query via the Policy field in the input buffer.
|
||||
// We need to read this field first to determine what's being queried.
|
||||
|
||||
// Ensure we have at least enough space to read the Policy field
|
||||
if (process_information_length < sizeof(PROCESS_MITIGATION_POLICY))
|
||||
{
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
// Read the policy type from the input buffer using safe emulator memory access
|
||||
const emulator_object<PROCESS_MITIGATION_POLICY> policy_obj{c.emu, process_information};
|
||||
const auto policy = policy_obj.read();
|
||||
|
||||
// We only support querying ProcessDynamicCodePolicy
|
||||
if (policy != ProcessDynamicCodePolicy)
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return handle_query<PROCESS_MITIGATION_POLICY_RAW_DATA>(c.emu, process_information, process_information_length, return_length,
|
||||
[policy](PROCESS_MITIGATION_POLICY_RAW_DATA& policy_data) {
|
||||
policy_data.Policy = policy;
|
||||
policy_data.Value = 0;
|
||||
});
|
||||
}
|
||||
case ProcessEnclaveInformation:
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
@@ -61,7 +89,7 @@ namespace syscalls
|
||||
case ProcessBasicInformation:
|
||||
return handle_query<PROCESS_BASIC_INFORMATION64>(c.emu, process_information, process_information_length, return_length,
|
||||
[&](PROCESS_BASIC_INFORMATION64& basic_info) {
|
||||
basic_info.PebBaseAddress = c.proc.peb.value();
|
||||
basic_info.PebBaseAddress = c.proc.peb64.value();
|
||||
basic_info.UniqueProcessId = 1;
|
||||
});
|
||||
|
||||
@@ -99,7 +127,7 @@ namespace syscalls
|
||||
});
|
||||
|
||||
case ProcessImageFileNameWin32: {
|
||||
const auto peb = c.proc.peb.read();
|
||||
const auto peb = c.proc.peb64.read();
|
||||
emulator_object<RTL_USER_PROCESS_PARAMETERS64> proc_params{c.emu, peb.ProcessParameters};
|
||||
const auto params = proc_params.read();
|
||||
const auto length = params.ImagePathName.Length + sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) + 2;
|
||||
@@ -154,6 +182,11 @@ namespace syscalls
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (info_class == ProcessExecuteFlags)
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (info_class == ProcessTlsInformation)
|
||||
{
|
||||
constexpr auto thread_data_offset = offsetof(PROCESS_TLS_INFO, ThreadData);
|
||||
@@ -184,7 +217,7 @@ namespace syscalls
|
||||
|
||||
entry.Flags = 2;
|
||||
|
||||
thread_iterator->second.teb->access([&](TEB64& teb) {
|
||||
thread_iterator->second.teb64->access([&](TEB64& teb) {
|
||||
entry.ThreadId = teb.ClientId.UniqueThread;
|
||||
|
||||
const auto tls_vector = teb.ThreadLocalStoragePointer;
|
||||
|
||||
@@ -6,6 +6,94 @@
|
||||
|
||||
namespace syscalls
|
||||
{
|
||||
// Helper function to parse PE headers and extract image information
|
||||
template <typename T>
|
||||
static bool parse_pe_headers(const std::vector<std::byte>& file_data, section::image_info& info)
|
||||
{
|
||||
if (file_data.size() < sizeof(PEDosHeader_t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* dos_header = reinterpret_cast<const PEDosHeader_t*>(file_data.data());
|
||||
if (dos_header->e_magic != PEDosHeader_t::k_Magic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// First check if we can read up to the optional header magic
|
||||
if (file_data.size() < dos_header->e_lfanew + sizeof(uint32_t) + sizeof(PEFileHeader_t) + sizeof(uint16_t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the magic number from the optional header
|
||||
const auto* magic_ptr =
|
||||
reinterpret_cast<const uint16_t*>(file_data.data() + dos_header->e_lfanew + sizeof(uint32_t) + sizeof(PEFileHeader_t));
|
||||
const uint16_t magic = *magic_ptr;
|
||||
|
||||
// Check if the magic matches the expected type
|
||||
constexpr uint16_t expected_magic = (sizeof(T) == sizeof(uint32_t))
|
||||
? static_cast<uint16_t>(PEOptionalHeader_t<std::uint32_t>::k_Magic)
|
||||
: static_cast<uint16_t>(PEOptionalHeader_t<std::uint64_t>::k_Magic);
|
||||
|
||||
if (magic != expected_magic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now check the full NT headers size
|
||||
if (file_data.size() < dos_header->e_lfanew + sizeof(PENTHeaders_t<T>))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* nt_headers = reinterpret_cast<const PENTHeaders_t<T>*>(file_data.data() + dos_header->e_lfanew);
|
||||
if (nt_headers->Signature != PENTHeaders_t<T>::k_Signature)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& file_header = nt_headers->FileHeader;
|
||||
const auto& optional_header = nt_headers->OptionalHeader;
|
||||
|
||||
// Extract information from headers
|
||||
info.machine = static_cast<uint16_t>(file_header.Machine);
|
||||
info.image_characteristics = file_header.Characteristics;
|
||||
|
||||
info.entry_point_rva = optional_header.AddressOfEntryPoint;
|
||||
info.image_base = optional_header.ImageBase;
|
||||
info.subsystem = optional_header.Subsystem;
|
||||
info.subsystem_major_version = optional_header.MajorSubsystemVersion;
|
||||
info.subsystem_minor_version = optional_header.MinorSubsystemVersion;
|
||||
info.dll_characteristics = optional_header.DllCharacteristics;
|
||||
info.size_of_stack_reserve = optional_header.SizeOfStackReserve;
|
||||
info.size_of_stack_commit = optional_header.SizeOfStackCommit;
|
||||
info.size_of_code = optional_header.SizeOfCode;
|
||||
info.loader_flags = optional_header.LoaderFlags;
|
||||
info.checksum = optional_header.CheckSum;
|
||||
|
||||
// Check if image contains code
|
||||
info.has_code = (optional_header.SizeOfCode > 0) || (optional_header.AddressOfEntryPoint != 0);
|
||||
|
||||
// Also check section characteristics for code sections
|
||||
const auto sections_offset = dos_header->e_lfanew + sizeof(uint32_t) + sizeof(PEFileHeader_t) + file_header.SizeOfOptionalHeader;
|
||||
if (file_data.size() >= sections_offset + sizeof(IMAGE_SECTION_HEADER) * file_header.NumberOfSections)
|
||||
{
|
||||
const auto* sections = reinterpret_cast<const IMAGE_SECTION_HEADER*>(file_data.data() + sections_offset);
|
||||
for (uint16_t i = 0; i < file_header.NumberOfSections; ++i)
|
||||
{
|
||||
if (sections[i].Characteristics & IMAGE_SCN_CNT_CODE)
|
||||
{
|
||||
info.has_code = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtCreateSection(const syscall_context& c, const emulator_object<handle> section_handle,
|
||||
const ACCESS_MASK /*desired_access*/,
|
||||
const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes,
|
||||
@@ -46,6 +134,45 @@ namespace syscalls
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// If this is an image section, parse PE headers
|
||||
if ((allocation_attributes & SEC_IMAGE) && !s.file_name.empty())
|
||||
{
|
||||
std::vector<std::byte> file_data;
|
||||
if (utils::io::read_file(s.file_name, &file_data))
|
||||
{
|
||||
section::image_info info{};
|
||||
|
||||
// Read the PE magic to determine if it's 32-bit or 64-bit
|
||||
bool parsed = false;
|
||||
if (file_data.size() >= sizeof(PEDosHeader_t))
|
||||
{
|
||||
const auto* dos_header = reinterpret_cast<const PEDosHeader_t*>(file_data.data());
|
||||
if (dos_header->e_magic == PEDosHeader_t::k_Magic &&
|
||||
file_data.size() >= dos_header->e_lfanew + sizeof(uint32_t) + sizeof(PEFileHeader_t) + sizeof(uint16_t))
|
||||
{
|
||||
const auto* magic_ptr = reinterpret_cast<const uint16_t*>(file_data.data() + dos_header->e_lfanew +
|
||||
sizeof(uint32_t) + sizeof(PEFileHeader_t));
|
||||
const uint16_t magic = *magic_ptr;
|
||||
|
||||
// Parse based on the actual PE type
|
||||
if (magic == PEOptionalHeader_t<std::uint32_t>::k_Magic)
|
||||
{
|
||||
parsed = parse_pe_headers<uint32_t>(file_data, info);
|
||||
}
|
||||
else if (magic == PEOptionalHeader_t<std::uint64_t>::k_Magic)
|
||||
{
|
||||
parsed = parse_pe_headers<uint64_t>(file_data, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed)
|
||||
{
|
||||
s.cached_image_info = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto h = c.proc.sections.store(std::move(s));
|
||||
section_handle.write(h);
|
||||
|
||||
@@ -95,7 +222,8 @@ namespace syscalls
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (attributes.RootDirectory != KNOWN_DLLS_DIRECTORY && attributes.RootDirectory != BASE_NAMED_OBJECTS_DIRECTORY)
|
||||
if (attributes.RootDirectory != KNOWN_DLLS_DIRECTORY && attributes.RootDirectory != KNOWN_DLLS32_DIRECTORY &&
|
||||
attributes.RootDirectory != BASE_NAMED_OBJECTS_DIRECTORY)
|
||||
{
|
||||
c.win_emu.log.error("Unsupported section\n");
|
||||
c.emu.stop();
|
||||
@@ -253,6 +381,182 @@ namespace syscalls
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtMapViewOfSectionEx(const syscall_context& c, const handle section_handle, const handle process_handle,
|
||||
const emulator_object<uint64_t> base_address, const emulator_object<LARGE_INTEGER> section_offset,
|
||||
const emulator_object<EMULATOR_CAST(EmulatorTraits<Emu64>::SIZE_T, SIZE_T)> view_size,
|
||||
const ULONG allocation_type, const ULONG page_protection,
|
||||
const uint64_t extended_parameters, // PMEM_EXTENDED_PARAMETER
|
||||
const ULONG extended_parameter_count)
|
||||
{
|
||||
// Process extended parameters if present
|
||||
struct ExtendedParamsInfo
|
||||
{
|
||||
uint64_t numa_node = 0;
|
||||
uint64_t lowest_address = 0;
|
||||
uint64_t highest_address = UINT64_MAX;
|
||||
uint64_t alignment = 0;
|
||||
uint32_t attribute_flags = 0;
|
||||
uint16_t image_machine = IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
bool has_address_requirements = false;
|
||||
bool has_numa_node = false;
|
||||
bool has_attributes = false;
|
||||
bool has_image_machine = false;
|
||||
} ext_info;
|
||||
|
||||
if (extended_parameters && extended_parameter_count > 0)
|
||||
{
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: Processing %u extended parameters\n", extended_parameter_count);
|
||||
|
||||
// Read and process each extended parameter
|
||||
for (ULONG i = 0; i < extended_parameter_count; i++)
|
||||
{
|
||||
const auto param_addr = extended_parameters + (i * sizeof(MEM_EXTENDED_PARAMETER64));
|
||||
MEM_EXTENDED_PARAMETER64 param{};
|
||||
|
||||
// Read the extended parameter structure
|
||||
if (!c.emu.try_read_memory(param_addr, ¶m, sizeof(param)))
|
||||
{
|
||||
c.win_emu.log.error("NtMapViewOfSectionEx: Failed to read extended parameter %u\n", i);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Extract the type (lower 8 bits)
|
||||
const auto param_type = static_cast<MEM_EXTENDED_PARAMETER_TYPE>(param.Type & 0xFF);
|
||||
|
||||
switch (param_type)
|
||||
{
|
||||
case MemExtendedParameterAddressRequirements: {
|
||||
// Read the MEM_ADDRESS_REQUIREMENTS structure
|
||||
MEM_ADDRESS_REQUIREMENTS64 addr_req{};
|
||||
if (!c.emu.try_read_memory(param.Pointer, &addr_req, sizeof(addr_req)))
|
||||
{
|
||||
c.win_emu.log.error("NtMapViewOfSectionEx: Failed to read address requirements\n");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ext_info.lowest_address = addr_req.LowestStartingAddress;
|
||||
ext_info.highest_address = addr_req.HighestEndingAddress;
|
||||
ext_info.alignment = addr_req.Alignment;
|
||||
ext_info.has_address_requirements = true;
|
||||
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: Address requirements - Low: 0x%" PRIX64 ", High: 0x%" PRIX64
|
||||
", Align: 0x%" PRIX64 "\n",
|
||||
ext_info.lowest_address, ext_info.highest_address, ext_info.alignment);
|
||||
}
|
||||
break;
|
||||
|
||||
case MemExtendedParameterNumaNode:
|
||||
ext_info.numa_node = param.ULong64;
|
||||
ext_info.has_numa_node = true;
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: NUMA node: %" PRIu64 "\n", ext_info.numa_node);
|
||||
break;
|
||||
|
||||
case MemExtendedParameterAttributeFlags:
|
||||
ext_info.attribute_flags = static_cast<uint32_t>(param.ULong64);
|
||||
ext_info.has_attributes = true;
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: Attribute flags: 0x%X\n", ext_info.attribute_flags);
|
||||
|
||||
// Log specific attribute flags
|
||||
if (ext_info.attribute_flags & MEM_EXTENDED_PARAMETER_GRAPHICS)
|
||||
{
|
||||
c.win_emu.log.info(" - Graphics memory requested\n");
|
||||
}
|
||||
if (ext_info.attribute_flags & MEM_EXTENDED_PARAMETER_NONPAGED)
|
||||
{
|
||||
c.win_emu.log.info(" - Non-paged memory requested\n");
|
||||
}
|
||||
if (ext_info.attribute_flags & MEM_EXTENDED_PARAMETER_EC_CODE)
|
||||
{
|
||||
c.win_emu.log.info(" - EC code memory requested\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case MemExtendedParameterImageMachine:
|
||||
ext_info.image_machine = static_cast<uint16_t>(param.ULong);
|
||||
ext_info.has_image_machine = true;
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: Image machine: 0x%X\n", ext_info.image_machine);
|
||||
break;
|
||||
|
||||
case MemExtendedParameterPartitionHandle:
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: Partition handle parameter (not supported)\n");
|
||||
break;
|
||||
|
||||
case MemExtendedParameterUserPhysicalHandle:
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: User physical handle parameter (not supported)\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
c.win_emu.log.warn("NtMapViewOfSectionEx: Unknown extended parameter type: %u\n", param_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Store extended parameters info in process context for other syscalls to use
|
||||
// This allows NtAllocateVirtualMemoryEx and other functions to access the same info
|
||||
c.proc.last_extended_params_numa_node = ext_info.numa_node;
|
||||
c.proc.last_extended_params_attributes = ext_info.attribute_flags;
|
||||
}
|
||||
|
||||
// Call the existing NtMapViewOfSection implementation
|
||||
// For WOW64 processes with image machine parameter, validate architecture compatibility
|
||||
if (ext_info.has_image_machine && c.proc.is_wow64_process)
|
||||
{
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: WOW64 process mapping with machine type 0x%X\n", ext_info.image_machine);
|
||||
|
||||
// Special handling for IMAGE_FILE_MACHINE_I386 (0x014c) on WOW64
|
||||
if (ext_info.image_machine == IMAGE_FILE_MACHINE_I386)
|
||||
{
|
||||
// This indicates the caller wants to map a 32-bit view
|
||||
// Store this for the module manager to use
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: Mapping 32-bit view for WOW64 process\n");
|
||||
}
|
||||
else if (ext_info.image_machine == IMAGE_FILE_MACHINE_AMD64)
|
||||
{
|
||||
// This indicates the caller wants to map a 64-bit view
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: Mapping 64-bit view for WOW64 process\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Store extended parameters for other syscalls to use
|
||||
if (ext_info.has_numa_node)
|
||||
{
|
||||
c.proc.last_extended_params_numa_node = ext_info.numa_node;
|
||||
}
|
||||
if (ext_info.has_attributes)
|
||||
{
|
||||
c.proc.last_extended_params_attributes = ext_info.attribute_flags;
|
||||
}
|
||||
if (ext_info.has_image_machine)
|
||||
{
|
||||
c.proc.last_extended_params_image_machine = ext_info.image_machine;
|
||||
}
|
||||
|
||||
// Perform the actual mapping
|
||||
const auto status = handle_NtMapViewOfSection(c, section_handle, process_handle, base_address,
|
||||
0, // zero_bits (not in Ex)
|
||||
0, // commit_size (not in Ex)
|
||||
section_offset, // section_offset
|
||||
view_size, // view_size
|
||||
ViewUnmap, // inherit_disposition (default)
|
||||
allocation_type, // allocation_type
|
||||
page_protection); // page_protection
|
||||
|
||||
// If mapping succeeded and this is a WOW64 image section with specific machine type
|
||||
if (NT_SUCCESS(status) && ext_info.has_image_machine && c.proc.is_wow64_process)
|
||||
{
|
||||
// Check if this was an image section (DLL/EXE)
|
||||
auto* section_entry = c.proc.sections.get(section_handle);
|
||||
if (section_entry && section_entry->is_image())
|
||||
{
|
||||
c.win_emu.log.info("NtMapViewOfSectionEx: Successfully mapped image section for WOW64 with machine type 0x%X\n",
|
||||
ext_info.image_machine);
|
||||
// Note: In a full WOW64 implementation, we would check for Wow64Transition export here
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtUnmapViewOfSection(const syscall_context& c, const handle process_handle, const uint64_t base_address)
|
||||
{
|
||||
if (process_handle != CURRENT_PROCESS)
|
||||
@@ -310,4 +614,222 @@ namespace syscalls
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtQuerySection(const syscall_context& c, const handle section_handle,
|
||||
const SECTION_INFORMATION_CLASS section_information_class, const uint64_t section_information,
|
||||
const EmulatorTraits<Emu64>::SIZE_T section_information_length,
|
||||
const emulator_object<EmulatorTraits<Emu64>::SIZE_T> result_length)
|
||||
{
|
||||
// Check if section handle is valid
|
||||
auto* section_entry = c.proc.sections.get(section_handle);
|
||||
|
||||
// Handle special sections
|
||||
if (section_handle == SHARED_SECTION || section_handle == DBWIN_BUFFER)
|
||||
{
|
||||
// These special sections don't support querying
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (!section_entry)
|
||||
{
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
switch (section_information_class)
|
||||
{
|
||||
case SECTION_INFORMATION_CLASS::SectionBasicInformation: {
|
||||
// Check buffer size
|
||||
if (section_information_length < sizeof(SECTION_BASIC_INFORMATION<EmulatorTraits<Emu64>>))
|
||||
{
|
||||
return STATUS_INFO_LENGTH_MISMATCH;
|
||||
}
|
||||
|
||||
SECTION_BASIC_INFORMATION<EmulatorTraits<Emu64>> info{};
|
||||
|
||||
// BaseAddress - typically NULL unless SEC_BASED is used
|
||||
info.BaseAddress = 0;
|
||||
|
||||
// Attributes - combine the SEC_ flags
|
||||
info.Attributes = section_entry->allocation_attributes;
|
||||
|
||||
// If it's an image section, ensure SEC_IMAGE is set
|
||||
if (section_entry->is_image())
|
||||
{
|
||||
info.Attributes |= SEC_IMAGE;
|
||||
}
|
||||
|
||||
// If it's file-backed, ensure SEC_FILE is set
|
||||
if (!section_entry->file_name.empty())
|
||||
{
|
||||
info.Attributes |= SEC_FILE;
|
||||
}
|
||||
|
||||
// Size - maximum size of the section
|
||||
info.Size.QuadPart = static_cast<LONGLONG>(section_entry->maximum_size);
|
||||
|
||||
// Write the structure to user buffer
|
||||
c.emu.write_memory(section_information, &info, sizeof(info));
|
||||
|
||||
// Set return length if requested
|
||||
if (result_length)
|
||||
{
|
||||
result_length.write(sizeof(SECTION_BASIC_INFORMATION<EmulatorTraits<Emu64>>));
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
case SECTION_INFORMATION_CLASS::SectionImageInformation: {
|
||||
// Only image sections support this query
|
||||
if (!section_entry->is_image())
|
||||
{
|
||||
return STATUS_SECTION_NOT_IMAGE;
|
||||
}
|
||||
|
||||
// Check buffer size
|
||||
if (section_information_length < sizeof(SECTION_IMAGE_INFORMATION<EmulatorTraits<Emu64>>))
|
||||
{
|
||||
return STATUS_INFO_LENGTH_MISMATCH;
|
||||
}
|
||||
|
||||
SECTION_IMAGE_INFORMATION<EmulatorTraits<Emu64>> info{};
|
||||
|
||||
// First check if we have cached PE information
|
||||
if (section_entry->cached_image_info.has_value())
|
||||
{
|
||||
const auto& cached = section_entry->cached_image_info.value();
|
||||
|
||||
// TransferAddress - entry point address (image base + RVA)
|
||||
info.TransferAddress = static_cast<std::uint64_t>(cached.image_base + cached.entry_point_rva);
|
||||
|
||||
// Machine type
|
||||
info.Machine = static_cast<PEMachineType>(cached.machine);
|
||||
|
||||
// Subsystem information
|
||||
info.SubSystemType = cached.subsystem;
|
||||
info.SubSystemMajorVersion = cached.subsystem_major_version;
|
||||
info.SubSystemMinorVersion = cached.subsystem_minor_version;
|
||||
|
||||
// Stack sizes
|
||||
info.MaximumStackSize = cached.size_of_stack_reserve;
|
||||
info.CommittedStackSize = cached.size_of_stack_commit;
|
||||
|
||||
// Image characteristics
|
||||
info.ImageCharacteristics = cached.image_characteristics;
|
||||
info.DllCharacteristics = cached.dll_characteristics;
|
||||
|
||||
// Image contains code
|
||||
info.ImageContainsCode = cached.has_code ? TRUE : FALSE;
|
||||
|
||||
// Image flags
|
||||
info.ImageMappedFlat = 1;
|
||||
info.ImageDynamicallyRelocated = (cached.dll_characteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) ? 1 : 0;
|
||||
|
||||
// Other fields
|
||||
info.ZeroBits = 0;
|
||||
info.LoaderFlags = cached.loader_flags;
|
||||
info.CheckSum = cached.checksum;
|
||||
info.ImageFileSize = static_cast<ULONG>(section_entry->maximum_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to get the mapped module to extract PE information
|
||||
// Convert u16string to string for find_by_name
|
||||
std::string narrow_name;
|
||||
if (!section_entry->name.empty())
|
||||
{
|
||||
narrow_name = u16_to_u8(section_entry->name);
|
||||
}
|
||||
else if (!section_entry->file_name.empty())
|
||||
{
|
||||
narrow_name = u16_to_u8(section_entry->file_name);
|
||||
}
|
||||
|
||||
const mapped_module* module = nullptr;
|
||||
if (!narrow_name.empty())
|
||||
{
|
||||
module = c.win_emu.mod_manager.find_by_name(narrow_name);
|
||||
}
|
||||
|
||||
if (module)
|
||||
{
|
||||
// TransferAddress - entry point address
|
||||
info.TransferAddress = static_cast<std::uint64_t>(module->entry_point);
|
||||
|
||||
// Machine type and other fields would need to be extracted from PE headers
|
||||
// For now, set reasonable defaults for x64
|
||||
info.Machine = PEMachineType::AMD64;
|
||||
info.SubSystemType = 3; // IMAGE_SUBSYSTEM_WINDOWS_CUI
|
||||
info.SubSystemMajorVersion = 10;
|
||||
info.SubSystemMinorVersion = 0;
|
||||
|
||||
// Stack sizes - typical defaults
|
||||
info.MaximumStackSize = 0x100000; // 1MB
|
||||
info.CommittedStackSize = 0x10000; // 64KB
|
||||
|
||||
// Image characteristics
|
||||
info.ImageCharacteristics = 0x0022; // IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE
|
||||
info.DllCharacteristics = 0x8160; // Common DLL characteristics including ASLR and DEP
|
||||
|
||||
// Check if it's a DLL
|
||||
if (section_entry->name.find(u".dll") != std::u16string::npos)
|
||||
{
|
||||
info.ImageCharacteristics |= IMAGE_FILE_DLL;
|
||||
}
|
||||
|
||||
// Image contains code
|
||||
info.ImageContainsCode = TRUE;
|
||||
|
||||
// Image flags
|
||||
info.ImageMappedFlat = 1;
|
||||
info.ImageDynamicallyRelocated = 1; // ASLR enabled
|
||||
|
||||
// File size
|
||||
info.ImageFileSize = static_cast<ULONG>(module->size_of_image);
|
||||
|
||||
// Other fields
|
||||
info.ZeroBits = 0;
|
||||
info.LoaderFlags = 0;
|
||||
info.CheckSum = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If module is not mapped yet and no cached info, return minimal information
|
||||
info.Machine = PEMachineType::AMD64;
|
||||
info.SubSystemType = 3;
|
||||
info.SubSystemMajorVersion = 10;
|
||||
info.SubSystemMinorVersion = 0;
|
||||
info.MaximumStackSize = 0x100000;
|
||||
info.CommittedStackSize = 0x10000;
|
||||
info.ImageCharacteristics = 0x0022;
|
||||
info.DllCharacteristics = 0x8160;
|
||||
info.ImageContainsCode = TRUE;
|
||||
info.ImageMappedFlat = 1;
|
||||
info.ImageDynamicallyRelocated = 1;
|
||||
info.ImageFileSize = static_cast<ULONG>(section_entry->maximum_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the structure to user buffer
|
||||
c.emu.write_memory(section_information, &info, sizeof(info));
|
||||
|
||||
// Set return length if requested
|
||||
if (result_length)
|
||||
{
|
||||
result_length.write(sizeof(SECTION_IMAGE_INFORMATION<EmulatorTraits<Emu64>>));
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
case SECTION_INFORMATION_CLASS::SectionRelocationInformation:
|
||||
case SECTION_INFORMATION_CLASS::SectionOriginalBaseInformation:
|
||||
case SECTION_INFORMATION_CLASS::SectionInternalImageInformation:
|
||||
// These information classes are not implemented
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
default:
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,39 @@ namespace syscalls
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (info_class == ThreadWow64Context)
|
||||
{
|
||||
// ThreadWow64Context is only valid for WOW64 processes
|
||||
if (!c.proc.is_wow64_process)
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (thread_information_length != sizeof(WOW64_CONTEXT))
|
||||
{
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
// Check if thread has persistent WOW64 context
|
||||
if (!thread->wow64_cpu_reserved.has_value())
|
||||
{
|
||||
c.win_emu.log.print(color::red, "Error: WOW64 saved context not initialized for thread %d\n", thread->id);
|
||||
return STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
const emulator_object<WOW64_CONTEXT> context_obj{c.emu, thread_information};
|
||||
const auto new_wow64_context = context_obj.read();
|
||||
|
||||
// Update the persistent context for future queries
|
||||
thread->wow64_cpu_reserved->access([&](WOW64_CPURESERVED& ctx) {
|
||||
ctx.Flags |= WOW64_CPURESERVED_FLAG_RESET_STATE;
|
||||
ctx.Context = new_wow64_context;
|
||||
// c.win_emu.callbacks.on_suspicious_activity("WOW64 CONTEXT");
|
||||
});
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (info_class == ThreadSchedulerSharedDataSlot || info_class == ThreadBasePriority || info_class == ThreadAffinityMask)
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
@@ -68,7 +101,7 @@ namespace syscalls
|
||||
|
||||
for (const auto& t : c.proc.threads | std::views::values)
|
||||
{
|
||||
t.teb->access([&](TEB64& teb) {
|
||||
t.teb64->access([&](TEB64& teb) {
|
||||
if (tls_cell < TLS_MINIMUM_AVAILABLE)
|
||||
{
|
||||
teb.TlsSlots.arr[tls_cell] = 0;
|
||||
@@ -100,6 +133,42 @@ namespace syscalls
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (info_class == ThreadWow64Context)
|
||||
{
|
||||
// ThreadWow64Context is only valid for WOW64 processes
|
||||
if (!c.proc.is_wow64_process)
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (return_length)
|
||||
{
|
||||
return_length.write(sizeof(WOW64_CONTEXT));
|
||||
}
|
||||
|
||||
if (thread_information_length < sizeof(WOW64_CONTEXT))
|
||||
{
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
const emulator_object<WOW64_CONTEXT> context_obj{c.emu, thread_information};
|
||||
|
||||
// Check if thread has persistent WOW64 context
|
||||
if (!thread->wow64_cpu_reserved.has_value())
|
||||
{
|
||||
c.win_emu.log.print(color::red, "Error: WOW64 saved context not initialized for thread %d\n", thread->id);
|
||||
return STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
// Return the saved context (which was set by NtSetInformationThread)
|
||||
thread->wow64_cpu_reserved->access([&](const WOW64_CPURESERVED& ctx) {
|
||||
const auto wow64_context = ctx.Context;
|
||||
context_obj.write(wow64_context);
|
||||
});
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (info_class == ThreadTebInformation)
|
||||
{
|
||||
if (return_length)
|
||||
@@ -113,7 +182,7 @@ namespace syscalls
|
||||
}
|
||||
|
||||
const auto teb_info = c.emu.read_memory<THREAD_TEB_INFORMATION>(thread_information);
|
||||
const auto data = c.emu.read_memory(thread->teb->value() + teb_info.TebOffset, teb_info.BytesToRead);
|
||||
const auto data = c.emu.read_memory(thread->teb64->value() + teb_info.TebOffset, teb_info.BytesToRead);
|
||||
c.emu.write_memory(teb_info.TebInformation, data.data(), data.size());
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
@@ -133,8 +202,8 @@ namespace syscalls
|
||||
|
||||
const emulator_object<THREAD_BASIC_INFORMATION64> info{c.emu, thread_information};
|
||||
info.access([&](THREAD_BASIC_INFORMATION64& i) {
|
||||
i.TebBaseAddress = thread->teb->value();
|
||||
i.ClientId = thread->teb->read().ClientId;
|
||||
i.TebBaseAddress = thread->teb64->value();
|
||||
i.ClientId = thread->teb64->read().ClientId;
|
||||
});
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
@@ -555,12 +624,12 @@ namespace syscalls
|
||||
|
||||
if (type == PsAttributeClientId)
|
||||
{
|
||||
const auto client_id = thread->teb->read().ClientId;
|
||||
const auto client_id = thread->teb64->read().ClientId;
|
||||
write_attribute(c.emu, attribute, client_id);
|
||||
}
|
||||
else if (type == PsAttributeTebAddress)
|
||||
{
|
||||
write_attribute(c.emu, attribute, thread->teb->value());
|
||||
write_attribute(c.emu, attribute, thread->teb64->value());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -366,4 +366,4 @@ namespace syscalls
|
||||
// puts("NtQuerySecurityAttributesToken not supported");
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user