Support for KnownDLLs (#694)

This is my attempt at solving #383

- For KnownDLLs I create the section objects at process setup, later in
NtOpenSection, I check if the root directory is KnownDLL or if it the
path starts with "\\KnownDll", and create a handle to the section
object.

- For handling STATUS_IMAGE_NOT_AT_BASE, I maintain a map of module
filepath -> module load count, at first load the module will get either
the preferred image or the whatever the memory manager gives, and if
load count is greater than 1, I return STATUS_IMAGE_NOT_AT_BASE, and for
that I had to allow loading multiple copies of the same DLL.

- Refactored some stuff, some functions that were used for pe file
parsing are now under winpe namespace.

- Added Dummy handler for NtFlushInstructionCache, WOW64 seems to need
it while loading.

- Remapping of win32u.dll and returning STATUS_IMAGE_NOT_AT_BASE is
problematic because ntdll.dll checks for that, and eventually crashes,
so as a workaround I don't allow remapping of it.
This commit is contained in:
Maurice Heumann
2026-01-14 08:27:21 +01:00
committed by GitHub
19 changed files with 698 additions and 257 deletions

View File

@@ -22,70 +22,72 @@ using NTSTATUS = std::uint32_t;
#define STATUS_INTEGER_DIVIDE_BY_ZERO ((NTSTATUS)0xC0000094L)
#endif
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
#define STATUS_ALERTED ((NTSTATUS)0x00000101L)
#define STATUS_PIPE_LISTENING ((NTSTATUS)0x00000105L)
#define STATUS_PIPE_CONNECTED ((NTSTATUS)0x00000106L)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
#define STATUS_ALERTED ((NTSTATUS)0x00000101L)
#define STATUS_PIPE_LISTENING ((NTSTATUS)0x00000105L)
#define STATUS_PIPE_CONNECTED ((NTSTATUS)0x00000106L)
#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L)
#define STATUS_SERVICE_NOTIFICATION ((NTSTATUS)0x40000018L)
#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L)
#define STATUS_SERVICE_NOTIFICATION ((NTSTATUS)0x40000018L)
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
#define STATUS_NO_MORE_ENTRIES ((NTSTATUS)0x8000001AL)
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
#define STATUS_NO_MORE_ENTRIES ((NTSTATUS)0x8000001AL)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018L)
#define STATUS_NOT_MAPPED_VIEW ((NTSTATUS)0xC0000019L)
#define STATUS_UNABLE_TO_DELETE_SECTION ((NTSTATUS)0xC000001BL)
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
#define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS)0xC0000035L)
#define STATUS_INVALID_PAGE_PROTECTION ((NTSTATUS)0xC0000045L)
#define STATUS_MUTANT_NOT_OWNED ((NTSTATUS)0xC0000046L)
#define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS)0xC0000047L)
#define STATUS_SECTION_NOT_IMAGE ((NTSTATUS)0xC0000049L)
#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)
#define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS)0xC000009FL)
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
#define STATUS_PIPE_BUSY ((NTSTATUS)0xC00000AAL)
#define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS)0xC00000ACL)
#define STATUS_INVALID_PIPE_STATE ((NTSTATUS)0xC00000ADL)
#define STATUS_PIPE_DISCONNECTED ((NTSTATUS)0xC00000B0L)
#define STATUS_PIPE_CLOSING ((NTSTATUS)0xC00000B1L)
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
#define STATUS_PIPE_NOT_CONNECTED ((NTSTATUS)0xC00000BEL)
#define STATUS_PIPE_EMPTY ((NTSTATUS)0xC00000D9L)
#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L)
#define STATUS_INVALID_PARAMETER_1 ((NTSTATUS)0xC00000EFL)
#define STATUS_INVALID_PARAMETER_2 ((NTSTATUS)0xC00000F0L)
#define STATUS_INVALID_PARAMETER_3 ((NTSTATUS)0xC00000F1L)
#define STATUS_INVALID_PARAMETER_4 ((NTSTATUS)0xC00000F2L)
#define STATUS_INVALID_PARAMETER_5 ((NTSTATUS)0xC00000F3L)
#define STATUS_INVALID_PARAMETER_6 ((NTSTATUS)0xC00000F4L)
#define STATUS_INVALID_PARAMETER_7 ((NTSTATUS)0xC00000F5L)
#define STATUS_INVALID_PARAMETER_8 ((NTSTATUS)0xC00000F6L)
#define STATUS_INVALID_PARAMETER_9 ((NTSTATUS)0xC00000F7L)
#define STATUS_INVALID_PARAMETER_10 ((NTSTATUS)0xC00000F8L)
#define STATUS_INVALID_PARAMETER_11 ((NTSTATUS)0xC00000F9L)
#define STATUS_INVALID_PARAMETER_12 ((NTSTATUS)0xC00000FAL)
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
#define STATUS_CONNECTION_RESET ((NTSTATUS)0xC000020DL)
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
#define STATUS_NOT_SAME_OBJECT ((NTSTATUS)0xC00001ACL)
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
#define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS)0xC0000245L)
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
#define STATUS_PORT_NOT_SET ((NTSTATUS)0xC0000353L)
#define STATUS_DEBUGGER_INACTIVE ((NTSTATUS)0xC0000354L)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018L)
#define STATUS_NOT_MAPPED_VIEW ((NTSTATUS)0xC0000019L)
#define STATUS_UNABLE_TO_DELETE_SECTION ((NTSTATUS)0xC000001BL)
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
#define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS)0xC0000035L)
#define STATUS_INVALID_PAGE_PROTECTION ((NTSTATUS)0xC0000045L)
#define STATUS_MUTANT_NOT_OWNED ((NTSTATUS)0xC0000046L)
#define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS)0xC0000047L)
#define STATUS_SECTION_NOT_IMAGE ((NTSTATUS)0xC0000049L)
#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)
#define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS)0xC000009FL)
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
#define STATUS_PIPE_BUSY ((NTSTATUS)0xC00000AAL)
#define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS)0xC00000ACL)
#define STATUS_INVALID_PIPE_STATE ((NTSTATUS)0xC00000ADL)
#define STATUS_PIPE_DISCONNECTED ((NTSTATUS)0xC00000B0L)
#define STATUS_PIPE_CLOSING ((NTSTATUS)0xC00000B1L)
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
#define STATUS_PIPE_NOT_CONNECTED ((NTSTATUS)0xC00000BEL)
#define STATUS_PIPE_EMPTY ((NTSTATUS)0xC00000D9L)
#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L)
#define STATUS_INVALID_PARAMETER_1 ((NTSTATUS)0xC00000EFL)
#define STATUS_INVALID_PARAMETER_2 ((NTSTATUS)0xC00000F0L)
#define STATUS_INVALID_PARAMETER_3 ((NTSTATUS)0xC00000F1L)
#define STATUS_INVALID_PARAMETER_4 ((NTSTATUS)0xC00000F2L)
#define STATUS_INVALID_PARAMETER_5 ((NTSTATUS)0xC00000F3L)
#define STATUS_INVALID_PARAMETER_6 ((NTSTATUS)0xC00000F4L)
#define STATUS_INVALID_PARAMETER_7 ((NTSTATUS)0xC00000F5L)
#define STATUS_INVALID_PARAMETER_8 ((NTSTATUS)0xC00000F6L)
#define STATUS_INVALID_PARAMETER_9 ((NTSTATUS)0xC00000F7L)
#define STATUS_INVALID_PARAMETER_10 ((NTSTATUS)0xC00000F8L)
#define STATUS_INVALID_PARAMETER_11 ((NTSTATUS)0xC00000F9L)
#define STATUS_INVALID_PARAMETER_12 ((NTSTATUS)0xC00000FAL)
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
#define STATUS_CONNECTION_RESET ((NTSTATUS)0xC000020DL)
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
#define STATUS_NOT_SAME_OBJECT ((NTSTATUS)0xC00001ACL)
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
#define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS)0xC0000245L)
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
#define STATUS_PORT_NOT_SET ((NTSTATUS)0xC0000353L)
#define STATUS_DEBUGGER_INACTIVE ((NTSTATUS)0xC0000354L)
#define STATUS_IMAGE_NOT_AT_BASE ((NTSTATUS)0x40000003L)
#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH ((NTSTATUS)0x4000000EL)
#define FILE_DEVICE_NETWORK 0x00000012
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
#define FILE_DEVICE_NETWORK 0x00000012
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK

View File

@@ -4,8 +4,11 @@
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <vector>
#include <system_error>
#include <variant>
#include <array>
#include "../utils/buffer_accessor.hpp"
#include "primitives.hpp"
@@ -480,6 +483,24 @@ struct SECTION_IMAGE_INFORMATION
namespace winpe
{
struct pe_image_basic_info
{
uint64_t entry_point_rva{};
uint64_t image_base{};
uint64_t size_of_stack_reserve{};
uint64_t size_of_stack_commit{};
uint32_t size_of_code{};
uint32_t loader_flags{};
uint32_t checksum{};
uint16_t machine{};
uint16_t subsystem{};
uint16_t subsystem_major_version{};
uint16_t subsystem_minor_version{};
uint16_t image_characteristics{};
uint16_t dll_characteristics{};
bool has_code{false};
std::array<char, 7> _padding{};
};
enum class pe_arch
{
@@ -487,6 +508,51 @@ namespace winpe
pe64
};
template <typename T>
inline 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 =
reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<uintptr_t>(&nt_headers);
const size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader;
const auto* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
return nt_headers_offset + (first_section_absolute - absolute_base);
}
template <typename T>
inline PEDirectory_t2 get_data_directory_by_index(const PENTHeaders_t<T>& nt_headers, uint64_t directory_index)
{
return nt_headers.OptionalHeader.DataDirectory[directory_index];
}
template <typename T>
IMAGE_SECTION_HEADER get_section_header_by_rva(const utils::safe_buffer_accessor<const std::byte>& buffer,
const PENTHeaders_t<T>& nt_headers, uint64_t nt_headers_offset, uint64_t rva)
{
IMAGE_SECTION_HEADER section_header = {};
auto next_section_offset = winpe::get_first_section_offset(nt_headers, nt_headers_offset);
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; i++)
{
const auto section = buffer.as<IMAGE_SECTION_HEADER>(static_cast<size_t>(next_section_offset)).get();
auto section_va_start = section.VirtualAddress;
auto section_va_end = section.VirtualAddress + section.Misc.VirtualSize;
if (section_va_start <= rva && rva <= section_va_end)
{
section_header = section;
break;
}
next_section_offset += sizeof(IMAGE_SECTION_HEADER);
}
return section_header;
}
inline std::variant<pe_arch, std::error_code> get_pe_arch(const std::filesystem::path& file)
{
std::ifstream f(file, std::ios::binary);
@@ -593,6 +659,85 @@ namespace winpe
return std::make_error_code(std::errc::executable_format_error);
}
template <typename T>
inline bool parse_pe_headers(const std::vector<std::byte>& file_data, pe_image_basic_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;
}
if (file_data.size() < dos_header->e_lfanew + sizeof(uint32_t) + sizeof(PEFileHeader_t) + sizeof(uint16_t))
{
return false;
}
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;
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;
}
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;
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;
info.has_code = (optional_header.SizeOfCode > 0) || (optional_header.AddressOfEntryPoint != 0);
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;
}
}
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

View File

@@ -196,4 +196,54 @@ namespace utils::string
{
return std::ranges::equal(lhs, rhs, [](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
}
template <class Elem, class Traits, class Alloc>
bool starts_with_ignore_case(const std::basic_string<Elem, Traits, Alloc>& lhs, const std::basic_string<Elem, Traits, Alloc>& rhs)
{
if (lhs.length() < rhs.length())
{
return false;
}
return std::ranges::equal(lhs.substr(0, rhs.length()), rhs,
[](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
}
template <class Elem, class Traits>
bool starts_with_ignore_case(const std::basic_string_view<Elem, Traits>& lhs, const std::basic_string_view<Elem, Traits>& rhs)
{
if (lhs.length() < rhs.length())
{
return false;
}
return std::ranges::equal(lhs.substr(0, rhs.length()), rhs,
[](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
}
template <class Elem, class Traits, class Alloc>
bool ends_with_ignore_case(const std::basic_string<Elem, Traits, Alloc>& lhs, const std::basic_string<Elem, Traits, Alloc>& rhs)
{
if (lhs.length() < rhs.length())
{
return false;
}
auto start = lhs.length() - rhs.length();
return std::ranges::equal(lhs.substr(start, rhs.length()), rhs,
[](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
}
template <class Elem, class Traits>
bool ends_with_ignore_case(const std::basic_string_view<Elem, Traits>& lhs, const std::basic_string_view<Elem, Traits>& rhs)
{
if (lhs.length() < rhs.length())
{
return false;
}
auto start = lhs.length() - rhs.length();
return std::ranges::equal(lhs.substr(start, rhs.length()), rhs,
[](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
}
}

View File

@@ -52,3 +52,8 @@ constexpr uint64_t page_align_up(const uint64_t value, const uint64_t page_size
{
return align_up(value, page_size);
}
constexpr uint64_t rva_to_file_offset(uint64_t va_base, uint64_t raw_base, uint64_t rva)
{
return rva - (va_base - raw_base);
}

View File

@@ -32,7 +32,7 @@ int main()
printf("EntryOffset: %08lX\n", api_set_map->EntryOffset);
printf("HashOffset: %08lX\n", api_set_map->HashOffset);
printf("HashFactor: %08lX\n", api_set_map->HashFactor);
// print_apiset(apiSetMap);
print_apiset(api_set_map);
// Compress the API-SET binary blob
const auto* data_ptr = reinterpret_cast<const std::byte*>(api_set_map);

View File

@@ -175,4 +175,32 @@ namespace apiset
return api_set_map_obj;
}
apiset_map get_namespace_table(const API_SET_NAMESPACE* apiset_ns_data)
{
std::map<std::u16string, std::u16string> apiset;
for (size_t i = 0; i < apiset_ns_data->Count; i++)
{
const auto* entry = reinterpret_cast<const API_SET_NAMESPACE_ENTRY*>(
reinterpret_cast<uint64_t>(apiset_ns_data) + apiset_ns_data->EntryOffset + i * sizeof(API_SET_NAMESPACE_ENTRY));
std::u16string name(reinterpret_cast<const char16_t*>(reinterpret_cast<uint64_t>(apiset_ns_data) + entry->NameOffset),
entry->NameLength / sizeof(char16_t));
if (!entry->ValueCount)
{
continue;
}
const auto* value = reinterpret_cast<const API_SET_VALUE_ENTRY*>(
reinterpret_cast<uint64_t>(apiset_ns_data) + entry->ValueOffset + (entry->ValueCount - 1) * sizeof(API_SET_VALUE_ENTRY));
std::u16string base_name(reinterpret_cast<const char16_t*>(reinterpret_cast<uint64_t>(apiset_ns_data) + value->ValueOffset),
value->ValueLength / sizeof(char16_t));
apiset[name + u".dll"] = base_name;
}
return apiset;
}
}

View File

@@ -3,9 +3,10 @@
#include <vector>
#include <cstdint>
#include <filesystem>
#include <optional>
#include "../emulator_utils.hpp"
using apiset_map = std::map<std::u16string, std::u16string>;
namespace apiset
{
enum class location : uint8_t
@@ -33,4 +34,6 @@ namespace apiset
const API_SET_NAMESPACE& orig_api_set_map);
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator, const container& container);
apiset_map get_namespace_table(const API_SET_NAMESPACE* apiset_ns_data);
}

View File

@@ -247,6 +247,11 @@ mapped_module* module_manager::map_module_core(const pe_detection_result& detect
mapped_module mod = mapper();
mod.is_static = is_static;
if (!mod.path.empty())
{
this->modules_load_count[mod.path]++;
}
const auto image_base = mod.image_base;
const auto entry = this->modules_.try_emplace(image_base, std::move(mod));
this->last_module_cache_ = this->modules_.end();
@@ -468,22 +473,45 @@ void module_manager::map_main_modules(const windows_path& executable_path, windo
}
}
mapped_module* module_manager::map_module(const windows_path& file, const logger& logger, const bool is_static)
std::optional<uint64_t> module_manager::get_module_load_count_by_path(const windows_path& path)
{
return this->map_local_module(this->file_sys_->translate(file), logger, is_static);
auto local_file = std::filesystem::weakly_canonical(std::filesystem::absolute(this->file_sys_->translate(path)));
if (auto load_count_entry = modules_load_count.find(local_file); load_count_entry != modules_load_count.end())
{
return load_count_entry->second;
}
return {};
}
mapped_module* module_manager::map_module(const windows_path& file, const logger& logger, const bool is_static, bool allow_duplicate)
{
auto local_file = this->file_sys_->translate(file);
if (local_file.filename() == "win32u.dll")
{
return this->map_local_module(local_file, logger, is_static, false);
}
return this->map_local_module(local_file, logger, is_static, allow_duplicate);
}
// 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)
mapped_module* module_manager::map_local_module(const std::filesystem::path& file, const logger& logger, const bool is_static,
bool allow_duplicate)
{
auto local_file = weakly_canonical(absolute(file));
// Check if module is already loaded
for (auto& mod : this->modules_ | std::views::values)
if (!allow_duplicate)
{
if (mod.path == local_file)
// Check if module is already loaded
for (auto& mod : this->modules_ | std::views::values)
{
return &mod;
if (mod.path == local_file)
{
return &mod;
}
}
}
@@ -502,14 +530,17 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil
// 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)
const logger& logger, bool is_static, bool allow_duplicate)
{
// Check if module is already loaded at this address
for (auto& mod : this->modules_ | std::views::values)
if (!allow_duplicate)
{
if (mod.image_base == base_address)
// Check if module is already loaded at this address
for (auto& mod : this->modules_ | std::views::values)
{
return &mod;
if (mod.image_base == base_address)
{
return &mod;
}
}
}
@@ -529,6 +560,7 @@ mapped_module* module_manager::map_memory_module(uint64_t base_address, uint64_t
void module_manager::serialize(utils::buffer_serializer& buffer) const
{
buffer.write_map(this->modules_);
buffer.write_map(this->modules_load_count);
buffer.write(this->executable ? this->executable->image_base : 0);
buffer.write(this->ntdll ? this->ntdll->image_base : 0);
@@ -546,6 +578,7 @@ void module_manager::serialize(utils::buffer_serializer& buffer) const
void module_manager::deserialize(utils::buffer_deserializer& buffer)
{
buffer.read_map(this->modules_);
buffer.read_map(this->modules_load_count);
this->last_module_cache_ = this->modules_.end();
const auto executable_base = buffer.read<uint64_t>();
@@ -584,6 +617,17 @@ bool module_manager::unmap(const uint64_t address)
this->callbacks_->on_module_unload(mod->second);
unmap_module(*this->memory_, mod->second);
auto module_load_count = this->modules_load_count[mod->second.path] - 1;
if (module_load_count == 0)
{
this->modules_load_count.erase(mod->second.path);
}
else
{
this->modules_load_count[mod->second.path] = module_load_count;
}
this->modules_.erase(mod);
this->last_module_cache_ = this->modules_.end();

View File

@@ -97,10 +97,12 @@ class module_manager
void map_main_modules(const windows_path& executable_path, windows_version_manager& version, process_context& context,
const logger& logger);
mapped_module* map_module(const windows_path& file, const logger& logger, bool is_static = false);
mapped_module* map_local_module(const std::filesystem::path& file, const logger& logger, bool is_static = false);
std::optional<uint64_t> get_module_load_count_by_path(const windows_path& path);
mapped_module* map_module(const windows_path& file, const logger& logger, bool is_static = false, bool allow_duplicate = false);
mapped_module* map_local_module(const std::filesystem::path& file, const logger& logger, bool is_static = false,
bool allow_duplicate = 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);
bool is_static = false, bool allow_duplicate = false);
mapped_module* find_by_address(const uint64_t address)
{
@@ -160,6 +162,7 @@ class module_manager
mapped_module* executable{};
mapped_module* ntdll{};
mapped_module* win32u{};
std::map<std::filesystem::path, uint64_t> modules_load_count;
// WOW64-specific modules (for validation and future use)
struct wow64_modules

View File

@@ -4,23 +4,10 @@
#include <utils/io.hpp>
#include <utils/buffer_accessor.hpp>
#include <platform/win_pefile.hpp>
namespace
{
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 =
reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<uintptr_t>(&nt_headers);
const size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader;
const auto* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
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)
{
@@ -210,7 +197,7 @@ namespace
void map_sections(memory_manager& memory, mapped_module& binary, const utils::safe_buffer_accessor<const std::byte> buffer,
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 first_section_offset = winpe::get_first_section_offset(nt_headers, nt_headers_offset);
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(static_cast<size_t>(first_section_offset));
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i)
@@ -384,7 +371,7 @@ mapped_module map_module_from_memory(memory_manager& memory, uint64_t base_addre
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 section_offset = winpe::get_first_section_offset(nt_headers, nt_headers_offset);
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(static_cast<size_t>(section_offset));
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i)

View File

@@ -5,6 +5,10 @@
#include "windows_emulator.hpp"
#include "version/windows_version_manager.hpp"
#include <utils/io.hpp>
#include <utils/buffer_accessor.hpp>
#include <regex>
namespace
{
emulator_allocator create_allocator(memory_manager& memory, const size_t size, const bool is_wow64_process)
@@ -179,9 +183,9 @@ namespace
}
}
void process_context::setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry, windows_version_manager& version,
const application_settings& app_settings, const mapped_module& executable, const mapped_module& ntdll,
const apiset::container& apiset_container, const mapped_module* ntdll32)
void process_context::setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry, file_system& file_system,
windows_version_manager& version, const application_settings& app_settings, const mapped_module& executable,
const mapped_module& ntdll, const apiset::container& apiset_container, const mapped_module* ntdll32)
{
setup_gdt(emu, memory);
@@ -386,6 +390,10 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, regist
}
}
this->apiset = apiset::get_namespace_table(reinterpret_cast<const API_SET_NAMESPACE*>(apiset_container.data.data()));
this->build_knowndlls_section_table<uint64_t>(registry, file_system, apiset, false);
this->build_knowndlls_section_table<uint32_t>(registry, file_system, apiset, true);
this->ntdll_image_base = ntdll.image_base;
this->ldr_initialize_thunk = ntdll.find_export("LdrInitializeThunk");
this->rtl_user_thread_start = ntdll.find_export("RtlUserThreadStart");
@@ -520,6 +528,9 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
buffer.write(this->timers);
buffer.write(this->registry_keys);
buffer.write_map(this->atoms);
buffer.write_map(this->apiset);
buffer.write_map(this->knowndlls32_sections);
buffer.write_map(this->knowndlls64_sections);
buffer.write(this->last_extended_params_numa_node);
buffer.write(this->last_extended_params_attributes);
@@ -570,6 +581,9 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
buffer.read(this->timers);
buffer.read(this->registry_keys);
buffer.read_map(this->atoms);
buffer.read_map(this->apiset);
buffer.read_map(this->knowndlls32_sections);
buffer.read_map(this->knowndlls64_sections);
buffer.read(this->last_extended_params_numa_node);
buffer.read(this->last_extended_params_attributes);
@@ -726,3 +740,164 @@ const std::u16string* process_context::get_atom_name(const uint16_t atom_id) con
return &it->second.name;
}
template <typename T>
void process_context::build_knowndlls_section_table(registry_manager& registry, const file_system& file_system, const apiset_map& apiset,
bool is_32bit)
{
windows_path system_root_path;
std::set<std::u16string> visisted;
std::queue<std::u16string> q;
if (is_32bit)
{
system_root_path = "C:\\Windows\\SysWOW64";
}
else
{
system_root_path = "C:\\Windows\\System32";
}
std::optional<registry_key> knowndlls_key =
registry.get_key({R"(\Registry\Machine\System\CurrentControlSet\Control\Session Manager\KnownDLLs)"});
if (!knowndlls_key)
{
return;
}
size_t i = 0;
for (;;)
{
auto known_dll_name_opt = registry.read_u16string(knowndlls_key.value(), i++);
if (!known_dll_name_opt)
{
break;
}
auto known_dll_name = known_dll_name_opt.value();
utils::string::to_lower_inplace(known_dll_name);
q.push(known_dll_name);
visisted.insert(known_dll_name);
}
while (!q.empty())
{
auto knowndll_filename = q.front();
q.pop();
std::vector<std::byte> file;
if (!utils::io::read_file(file_system.translate(system_root_path / knowndll_filename), &file))
{
continue;
}
section s;
s.file_name = (system_root_path / knowndll_filename).u16string();
s.maximum_size = 0;
s.allocation_attributes = SEC_IMAGE;
s.section_page_protection = PAGE_EXECUTE;
s.cache_image_info_from_filedata(file);
add_knowndll_section(knowndll_filename, s, is_32bit);
utils::safe_buffer_accessor<const std::byte> buffer{file};
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<T>>(static_cast<size_t>(nt_headers_offset)).get();
const auto& import_directory_entry = winpe::get_data_directory_by_index(nt_headers, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (!import_directory_entry.VirtualAddress)
{
continue;
}
const auto section_with_import_descs =
winpe::get_section_header_by_rva(buffer, nt_headers, nt_headers_offset, import_directory_entry.VirtualAddress);
auto import_directory_vbase = section_with_import_descs.VirtualAddress;
auto import_directory_rbase = section_with_import_descs.PointerToRawData;
uint64_t import_directory_raw =
rva_to_file_offset(import_directory_vbase, import_directory_rbase, import_directory_entry.VirtualAddress);
auto import_descriptors = buffer.as<IMAGE_IMPORT_DESCRIPTOR>(static_cast<size_t>(import_directory_raw));
for (size_t import_desc_index = 0;; import_desc_index++)
{
const auto descriptor = import_descriptors.get(import_desc_index);
if (!descriptor.Name)
{
break;
}
auto known_dll_dep_name = u8_to_u16(
buffer.as_string(static_cast<size_t>(rva_to_file_offset(import_directory_vbase, import_directory_rbase, descriptor.Name))));
utils::string::to_lower_inplace(known_dll_dep_name);
if (known_dll_dep_name.starts_with(u"api-") || known_dll_dep_name.starts_with(u"ext-"))
{
if (auto apiset_entry = apiset.find(known_dll_dep_name); apiset_entry != apiset.end())
{
known_dll_dep_name = apiset_entry->second;
}
else
{
continue;
}
}
if (!visisted.contains(known_dll_dep_name))
{
q.push(known_dll_dep_name);
visisted.insert(known_dll_dep_name);
}
}
}
}
bool process_context::has_knowndll_section(const std::u16string& name, bool is_32bit) const
{
auto lname = utils::string::to_lower(name);
if (is_32bit)
{
return knowndlls32_sections.contains(lname);
}
return knowndlls64_sections.contains(lname);
}
std::optional<section> process_context::get_knowndll_section_by_name(const std::u16string& name, bool is_32bit) const
{
auto lname = utils::string::to_lower(name);
if (is_32bit)
{
if (auto section = knowndlls32_sections.find(lname); section != knowndlls32_sections.end())
{
return section->second;
}
}
else
{
if (auto section = knowndlls64_sections.find(lname); section != knowndlls64_sections.end())
{
return section->second;
}
}
return {};
}
void process_context::add_knowndll_section(const std::u16string& name, const section& section, bool is_32bit)
{
auto lname = utils::string::to_lower(name);
if (is_32bit)
{
knowndlls32_sections[lname] = section;
}
else
{
knowndlls64_sections[lname] = section;
}
}

View File

@@ -35,6 +35,7 @@ struct emulator_settings;
struct application_settings;
class windows_version_manager;
using knowndlls_map = std::map<std::u16string, section>;
struct process_context
{
struct callbacks
@@ -73,9 +74,9 @@ struct process_context
{
}
void setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry, windows_version_manager& version,
const application_settings& app_settings, const mapped_module& executable, const mapped_module& ntdll,
const apiset::container& apiset_container, const mapped_module* ntdll32 = nullptr);
void setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry, file_system& file_system,
windows_version_manager& version, const application_settings& app_settings, const mapped_module& executable,
const mapped_module& ntdll, const apiset::container& apiset_container, const mapped_module* ntdll32 = nullptr);
void setup_callback_hook(windows_emulator& win_emu, memory_manager& memory);
@@ -88,6 +89,13 @@ struct process_context
bool delete_atom(uint16_t atom_id);
const std::u16string* get_atom_name(uint16_t atom_id) const;
template <typename T>
void build_knowndlls_section_table(registry_manager& registry, const file_system& file_system, const apiset_map& apiset, bool is_32bit);
std::optional<section> get_knowndll_section_by_name(const std::u16string& name, bool is_32bit) const;
void add_knowndll_section(const std::u16string& name, const section& section, bool is_32bit);
bool has_knowndll_section(const std::u16string& name, bool is_32bit) const;
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
@@ -137,6 +145,10 @@ struct process_context
handle_store<handle_types::registry, registry_key, 2> registry_keys{};
std::map<uint16_t, atom_entry> atoms{};
apiset_map apiset;
knowndlls_map knowndlls32_sections;
knowndlls_map knowndlls64_sections;
std::vector<std::byte> default_register_set{};
uint32_t spawned_thread_count{0};

View File

@@ -245,3 +245,34 @@ std::optional<std::string_view> registry_manager::get_sub_key_name(const registr
return *name;
}
std::optional<std::u16string> registry_manager::read_u16string(const registry_key& key, size_t index)
{
const auto value_opt = this->get_value(key, index);
if (!value_opt)
{
return {};
}
const auto& value = value_opt.value();
if (value.type != REG_SZ && value.type != REG_EXPAND_SZ)
{
return {};
}
if (value.data.empty() || value.data.size() % 2 != 0)
{
return {};
}
const auto char_count = value.data.size() / sizeof(char16_t);
const auto* data_ptr = reinterpret_cast<const char16_t*>(value.data.data());
if (data_ptr[char_count - 1] != u'\0')
{
return {};
}
auto s = std::u16string(data_ptr, char_count - 1);
return s;
}

View File

@@ -84,6 +84,8 @@ class registry_manager
std::optional<exposed_hive_key> get_hive_key(const registry_key& key);
std::optional<std::u16string> read_u16string(const registry_key& key, size_t index);
private:
std::filesystem::path hive_path_{};
hive_map hives_{};

View File

@@ -364,6 +364,8 @@ namespace syscalls
emulator_object<ULONG> return_length);
NTSTATUS handle_NtQuerySecurityAttributesToken();
NTSTATUS handle_NtAdjustPrivilegesToken();
NTSTATUS handle_NtFlushInstructionCache(const syscall_context& c, handle process_handle, emulator_object<uint64_t> base_address,
uint64_t region_size);
NTSTATUS handle_NtQueryPerformanceCounter(const syscall_context& c, const emulator_object<LARGE_INTEGER> performance_counter,
const emulator_object<LARGE_INTEGER> performance_frequency)
@@ -1322,6 +1324,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtRemoveProcessDebug);
add_handler(NtNotifyChangeDirectoryFileEx);
add_handler(NtUserGetHDevName);
add_handler(NtFlushInstructionCache);
add_handler(NtUserMapDesktopObject);
#undef add_handler

View File

@@ -421,4 +421,14 @@ namespace syscalls
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtFlushInstructionCache(const syscall_context& c, const handle process_handle,
const emulator_object<uint64_t> base_address, const uint64_t region_size)
{
(void)c;
(void)process_handle;
(void)base_address;
(void)region_size;
return STATUS_SUCCESS;
}
}

View File

@@ -7,93 +7,7 @@
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;
}
using namespace std::string_view_literals;
NTSTATUS handle_NtCreateSection(const syscall_context& c, const emulator_object<handle> section_handle,
const ACCESS_MASK /*desired_access*/,
@@ -141,36 +55,7 @@ namespace syscalls
std::vector<std::byte> file_data;
if (utils::io::read_file(c.win_emu.file_sys.translate(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;
}
s.cache_image_info_from_filedata(file_data);
}
}
@@ -187,9 +72,10 @@ namespace syscalls
const auto attributes = object_attributes.read();
auto filename = read_unicode_string(c.emu, attributes.ObjectName);
auto filename_sv = std::u16string_view(filename);
c.win_emu.callbacks.on_generic_access("Opening section", filename);
if (filename == u"\\Windows\\SharedSection")
if (utils::string::equals_ignore_case(filename_sv, u"\\Windows\\SharedSection"sv))
{
constexpr auto shared_section_size = 0x10000;
@@ -203,7 +89,7 @@ namespace syscalls
return STATUS_SUCCESS;
}
if (filename == u"DBWIN_BUFFER")
if (utils::string::equals_ignore_case(filename_sv, u"DBWIN_BUFFER"sv))
{
constexpr auto dbwin_buffer_section_size = 0x1000;
@@ -217,29 +103,58 @@ namespace syscalls
return STATUS_SUCCESS;
}
if (filename == u"windows_shell_global_counters" //
|| filename == u"Global\\__ComCatalogCache__" //
|| filename == u"{00020000-0000-1005-8005-0000C06B5161}" //
|| filename == u"Global\\{00020000-0000-1005-8005-0000C06B5161}")
if (utils::string::equals_ignore_case(filename_sv, u"windows_shell_global_counters"sv) ||
utils::string::equals_ignore_case(filename_sv, u"Global\\__ComCatalogCache__"sv) ||
utils::string::equals_ignore_case(filename_sv, u"{00020000-0000-1005-8005-0000C06B5161}"sv) ||
utils::string::equals_ignore_case(filename_sv, u"Global\\{00020000-0000-1005-8005-0000C06B5161}"sv))
{
return STATUS_NOT_SUPPORTED;
}
if (attributes.RootDirectory != KNOWN_DLLS_DIRECTORY && attributes.RootDirectory != KNOWN_DLLS32_DIRECTORY &&
attributes.RootDirectory != BASE_NAMED_OBJECTS_DIRECTORY && !filename.starts_with(u"\\KnownDlls"))
bool is_knowndll = (attributes.RootDirectory == KNOWN_DLLS32_DIRECTORY ||
utils::string::starts_with_ignore_case(filename_sv, u"\\KnownDlls32\\"sv)) ||
attributes.RootDirectory == KNOWN_DLLS_DIRECTORY ||
utils::string::starts_with_ignore_case(filename_sv, u"\\KnownDlls\\"sv);
if (!is_knowndll && attributes.RootDirectory != BASE_NAMED_OBJECTS_DIRECTORY)
{
c.win_emu.log.error("Unsupported section\n");
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
utils::string::to_lower_inplace(filename);
for (auto& section_entry : c.proc.sections)
if (is_knowndll)
{
if (section_entry.second.is_image() && section_entry.second.name == filename)
bool is_knowndll32 = attributes.RootDirectory == KNOWN_DLLS32_DIRECTORY ||
utils::string::starts_with_ignore_case(filename_sv, u"\\KnownDlls32\\"sv);
std::u16string knowndll_name = filename;
if (utils::string::starts_with_ignore_case(filename_sv, u"\\KnownDlls32\\"sv))
{
section_handle.write(c.proc.sections.make_handle(section_entry.first));
knowndll_name = filename.substr(13, filename.length() - 13);
}
else if (utils::string::starts_with_ignore_case(filename_sv, u"\\KnownDlls\\"sv))
{
knowndll_name = filename.substr(11, filename.length() - 11);
}
auto section = c.win_emu.process.get_knowndll_section_by_name(knowndll_name, is_knowndll32);
if (!section.has_value())
{
return STATUS_OBJECT_NAME_NOT_FOUND;
}
section_handle.write(c.proc.sections.store(section.value()));
return STATUS_SUCCESS;
}
for (auto& [handle, section] : c.proc.sections)
{
if (section.is_image() && utils::string::equals_ignore_case(section.name, filename))
{
section_handle.write(c.proc.sections.make_handle(handle));
return STATUS_SUCCESS;
}
}
@@ -334,7 +249,7 @@ namespace syscalls
if (section_entry->is_image())
{
const auto* binary = c.win_emu.mod_manager.map_module(section_entry->file_name, c.win_emu.log);
const auto* binary = c.win_emu.mod_manager.map_module(section_entry->file_name, c.win_emu.log, false, true);
if (!binary)
{
return STATUS_FILE_INVALID;
@@ -350,6 +265,17 @@ namespace syscalls
base_address.write(binary->image_base);
// Should return STATUS_IMAGE_MACHINE_TYPE_MISMATCH if a 64-bit process tried to map a 32-bit PE.
if (!c.win_emu.process.is_wow64_process && binary->machine == IMAGE_FILE_MACHINE_I386)
{
return STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
}
if (c.win_emu.mod_manager.get_module_load_count_by_path(section_entry->file_name) > 1)
{
return STATUS_IMAGE_NOT_AT_BASE;
}
return STATUS_SUCCESS;
}

View File

@@ -361,8 +361,8 @@ void windows_emulator::setup_process(const application_settings& app_settings)
const auto apiset_data = apiset::obtain(this->emulation_root);
this->process.setup(this->emu(), this->memory, this->registry, this->version, app_settings, *executable, *ntdll, apiset_data,
this->mod_manager.wow64_modules_.ntdll32);
this->process.setup(this->emu(), this->memory, this->registry, this->file_sys, this->version, app_settings, *executable, *ntdll,
apiset_data, this->mod_manager.wow64_modules_.ntdll32);
const auto ntdll_data = emu.read_memory(ntdll->image_base, static_cast<size_t>(ntdll->size_of_image));
const auto win32u_data = emu.read_memory(win32u->image_base, static_cast<size_t>(win32u->size_of_image));

View File

@@ -5,6 +5,7 @@
#include <serialization_helper.hpp>
#include <utils/file_handle.hpp>
#include <platform/synchronisation.hpp>
#include <platform/win_pefile.hpp>
struct timer : ref_counted_object
{
@@ -248,33 +249,47 @@ struct section : ref_counted_object
uint64_t maximum_size{};
uint32_t section_page_protection{};
uint32_t allocation_attributes{};
// Cached PE image information for image sections
struct image_info
{
uint64_t entry_point_rva{};
uint64_t image_base{};
uint64_t size_of_stack_reserve{};
uint64_t size_of_stack_commit{};
uint32_t size_of_code{};
uint32_t loader_flags{};
uint32_t checksum{};
uint16_t machine{};
uint16_t subsystem{};
uint16_t subsystem_major_version{};
uint16_t subsystem_minor_version{};
uint16_t image_characteristics{};
uint16_t dll_characteristics{};
bool has_code{false};
std::array<char, 7> _padding{};
};
std::optional<image_info> cached_image_info{};
std::optional<winpe::pe_image_basic_info> cached_image_info{};
bool is_image() const
{
return this->allocation_attributes & SEC_IMAGE;
}
void cache_image_info_from_filedata(const std::vector<std::byte>& file_data)
{
winpe::pe_image_basic_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 = winpe::parse_pe_headers<uint32_t>(file_data, info);
}
else if (magic == PEOptionalHeader_t<std::uint64_t>::k_Magic)
{
parsed = winpe::parse_pe_headers<uint64_t>(file_data, info);
}
}
}
if (parsed)
{
this->cached_image_info = info;
}
}
void serialize_object(utils::buffer_serializer& buffer) const override
{
buffer.write(this->name);
@@ -282,7 +297,7 @@ struct section : ref_counted_object
buffer.write(this->maximum_size);
buffer.write(this->section_page_protection);
buffer.write(this->allocation_attributes);
buffer.write_optional<image_info>(this->cached_image_info);
buffer.write_optional<winpe::pe_image_basic_info>(this->cached_image_info);
}
void deserialize_object(utils::buffer_deserializer& buffer) override