mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-18 11:13:57 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_{};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user