From ceadcbc3e8b41bd55c08ac7a449bd0d89d1f0f78 Mon Sep 17 00:00:00 2001 From: 3fault Date: Tue, 1 Jul 2025 17:50:45 -0400 Subject: [PATCH 01/17] Add dispatch_guard_page_violation to exception_dispatch --- src/windows-emulator/exception_dispatch.cpp | 11 +++++++++++ src/windows-emulator/exception_dispatch.hpp | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/windows-emulator/exception_dispatch.cpp b/src/windows-emulator/exception_dispatch.cpp index e4626cf2..df4164b1 100644 --- a/src/windows-emulator/exception_dispatch.cpp +++ b/src/windows-emulator/exception_dispatch.cpp @@ -2,6 +2,7 @@ #include "exception_dispatch.hpp" #include "process_context.hpp" #include "cpu_context.hpp" +#include namespace { @@ -182,6 +183,16 @@ void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc }); } +void dispatch_guard_page_violation(x86_64_emulator& emu, const process_context& proc, const uint64_t address, + const memory_operation operation) +{ + dispatch_exception(emu, proc, STATUS_GUARD_PAGE_VIOLATION, + { + map_violation_operation_to_parameter(operation), + address, + }); +} + void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context& proc) { dispatch_exception(emu, proc, STATUS_ILLEGAL_INSTRUCTION, {}); diff --git a/src/windows-emulator/exception_dispatch.hpp b/src/windows-emulator/exception_dispatch.hpp index 4a717c3d..428c55ef 100644 --- a/src/windows-emulator/exception_dispatch.hpp +++ b/src/windows-emulator/exception_dispatch.hpp @@ -19,6 +19,8 @@ void dispatch_exception(x86_64_emulator& emu, const process_context& proc, const void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, uint64_t address, memory_operation operation); +void dispatch_guard_page_violation(x86_64_emulator& emu, const process_context& proc, uint64_t address, + memory_operation operation); void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context& proc); void dispatch_integer_division_by_zero(x86_64_emulator& emu, const process_context& proc); void dispatch_single_step(x86_64_emulator& emu, const process_context& proc); From a8ae617e50d0be3d5d1f679d751d171abd7bec34 Mon Sep 17 00:00:00 2001 From: 3fault Date: Tue, 1 Jul 2025 18:14:02 -0400 Subject: [PATCH 02/17] Add memory_permission_ext to windows-emulator --- .../memory_permission_ext.cpp | 0 .../memory_permission_ext.hpp | 160 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 src/windows-emulator/memory_permission_ext.cpp create mode 100644 src/windows-emulator/memory_permission_ext.hpp diff --git a/src/windows-emulator/memory_permission_ext.cpp b/src/windows-emulator/memory_permission_ext.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/windows-emulator/memory_permission_ext.hpp b/src/windows-emulator/memory_permission_ext.hpp new file mode 100644 index 00000000..ca410475 --- /dev/null +++ b/src/windows-emulator/memory_permission_ext.hpp @@ -0,0 +1,160 @@ +#pragma once +#include "memory_permission.hpp" + +enum class memory_permission_ext : uint8_t +{ + none = 0, + guard = 1 << 0, +}; + +/***************************************************************************** + * + ****************************************************************************/ + +constexpr memory_permission_ext operator&(const memory_permission_ext x, const memory_permission_ext y) +{ + return static_cast(static_cast(x) & static_cast(y)); +} + +constexpr memory_permission_ext operator|(const memory_permission_ext x, const memory_permission_ext y) +{ + return static_cast(static_cast(x) | static_cast(y)); +} + +constexpr memory_permission_ext operator^(const memory_permission_ext x, const memory_permission_ext y) +{ + return static_cast(static_cast(x) ^ static_cast(y)); +} + +constexpr memory_permission_ext operator~(memory_permission_ext x) +{ + return static_cast(~static_cast(x)); +} + +inline memory_permission_ext& operator&=(memory_permission_ext& x, const memory_permission_ext y) +{ + x = x & y; + return x; +} + +inline memory_permission_ext& operator|=(memory_permission_ext& x, const memory_permission_ext y) +{ + x = x | y; + return x; +} + +inline memory_permission_ext& operator^=(memory_permission_ext& x, const memory_permission_ext y) +{ + x = x ^ y; + return x; +} + +/***************************************************************************** + * + ****************************************************************************/ + +struct nt_memory_permission +{ + memory_permission common; + memory_permission_ext extended; + + constexpr nt_memory_permission() : common(memory_permission::none), extended(memory_permission_ext::none) {} + constexpr nt_memory_permission(memory_permission common) : common(common), extended(memory_permission_ext::none) {} + constexpr nt_memory_permission(memory_permission common, memory_permission_ext ext) : common(common), extended(ext) {} + + // Implicit coercions + operator memory_permission() const { return common; } + operator memory_permission_ext() const { return extended; } + + // This just does memberwise equality on each of the members in declaration order + bool operator==(nt_memory_permission const&) const = default; + + nt_memory_permission& operator=(memory_permission const& y) + { + this->common = y; + return *this; + } + + constexpr bool is_guarded() const { + return (this->extended & memory_permission_ext::guard) == memory_permission_ext::guard; + } +}; + +/***************************************************************************** + * + ****************************************************************************/ + +constexpr nt_memory_permission operator&(const nt_memory_permission x, const memory_permission y) +{ + return nt_memory_permission { x.common & y, x.extended }; +} + +constexpr nt_memory_permission operator&(const nt_memory_permission x, const memory_permission_ext y) +{ + return nt_memory_permission { x.common, x.extended & y }; +} + +constexpr nt_memory_permission operator|(const nt_memory_permission x, const memory_permission y) +{ + return nt_memory_permission { x.common | y, x.extended }; +} + +constexpr nt_memory_permission operator|(const nt_memory_permission x, const memory_permission_ext y) +{ + return nt_memory_permission { x.common, x.extended | y }; +} + +constexpr nt_memory_permission operator^(const nt_memory_permission x, const memory_permission y) +{ + return nt_memory_permission { x.common ^ y, x.extended }; +} + +constexpr nt_memory_permission operator^(const nt_memory_permission x, const memory_permission_ext y) +{ + return nt_memory_permission { x.common, x.extended ^ y }; +} + +inline nt_memory_permission& operator&=(nt_memory_permission& x, const memory_permission y) +{ + x = x & y; + return x; +} + +inline nt_memory_permission& operator&=(nt_memory_permission& x, const memory_permission_ext y) +{ + x = x & y; + return x; +} + +inline nt_memory_permission& operator|=(nt_memory_permission& x, const memory_permission y) +{ + x.common | y; + return x; +} + +inline nt_memory_permission& operator|=(nt_memory_permission& x, const nt_memory_permission y) +{ + x.extended | y; + return x; +} + +inline nt_memory_permission& operator^=(nt_memory_permission& x, const memory_permission y) +{ + x.common ^ y; + return x; +} + +inline nt_memory_permission& operator^=(nt_memory_permission& x, const nt_memory_permission y) +{ + x.extended ^ y; + return x; +} + +/***************************************************************************** + * + ****************************************************************************/ + +inline bool is_guarded(const memory_permission_ext permission) +{ + return (permission & memory_permission_ext::guard) != memory_permission_ext::none; +} From 7d8daf7c686cd34c314eaa866846a1336ae055fa Mon Sep 17 00:00:00 2001 From: 3fault Date: Tue, 1 Jul 2025 18:28:54 -0400 Subject: [PATCH 03/17] Update map_nt_to_emulator_protection to support PAGE_GUARD flag --- src/emulator/memory_interface.hpp | 2 +- src/windows-emulator/memory_utils.hpp | 49 +++++++++++++++++---------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/emulator/memory_interface.hpp b/src/emulator/memory_interface.hpp index 2f735e9a..3f472040 100644 --- a/src/emulator/memory_interface.hpp +++ b/src/emulator/memory_interface.hpp @@ -2,7 +2,7 @@ #include #include -#include "memory_permission.hpp" +#include "windows-emulator/memory_permission_ext.hpp" using mmio_read_callback = std::function; using mmio_write_callback = std::function; diff --git a/src/windows-emulator/memory_utils.hpp b/src/windows-emulator/memory_utils.hpp index 70b1a1f8..d524ffd3 100644 --- a/src/windows-emulator/memory_utils.hpp +++ b/src/windows-emulator/memory_utils.hpp @@ -19,28 +19,43 @@ inline std::string get_permission_string(const memory_permission permission) return res; } -inline memory_permission map_nt_to_emulator_protection(uint32_t nt_protection) +inline nt_memory_permission map_nt_to_emulator_protection(uint32_t nt_protection) { - nt_protection &= ~static_cast(PAGE_GUARD); // TODO: Implement that + memory_permission_ext ext = memory_permission_ext::none; + // TODO: Check for invalid combinations + if (nt_protection & PAGE_GUARD) + { + // Unset the guard flag so the following switch statement will still work + nt_protection &= ~static_cast(PAGE_GUARD); + ext = memory_permission_ext::guard; + } + memory_permission common = memory_permission::none; switch (nt_protection) { - case PAGE_NOACCESS: - return memory_permission::none; - case PAGE_READONLY: - return memory_permission::read; - case PAGE_READWRITE: - case PAGE_WRITECOPY: - return memory_permission::read | memory_permission::write; - case PAGE_EXECUTE: - case PAGE_EXECUTE_READ: - return memory_permission::read | memory_permission::exec; - case PAGE_EXECUTE_READWRITE: - return memory_permission::all; - case PAGE_EXECUTE_WRITECOPY: - default: - throw std::runtime_error("Failed to map protection"); + case PAGE_NOACCESS: + common = memory_permission::none; + break; + case PAGE_READONLY: + common = memory_permission::read; + break; + case PAGE_READWRITE: + case PAGE_WRITECOPY: + common = memory_permission::read | memory_permission::write; + break; + case PAGE_EXECUTE: + case PAGE_EXECUTE_READ: + common = memory_permission::read | memory_permission::exec; + break; + case PAGE_EXECUTE_READWRITE: + common = memory_permission::all; + break; + case PAGE_EXECUTE_WRITECOPY: + default: + throw std::runtime_error("Failed to map protection"); } + + return nt_memory_permission { common, ext }; } inline uint32_t map_emulator_to_nt_protection(const memory_permission permission) From 1276c7e2bc987cdab62027899d51f68098b3a874 Mon Sep 17 00:00:00 2001 From: 3fault Date: Tue, 1 Jul 2025 19:04:22 -0400 Subject: [PATCH 04/17] Update occurances of memory_permission to nt_memory_permission where needed --- .../icicle_x86_64_emulator.cpp | 8 ++--- .../unicorn_x86_64_emulator.cpp | 8 ++--- src/emulator/memory_interface.hpp | 4 +-- src/windows-emulator/memory_manager.cpp | 36 ++++++++++++------- src/windows-emulator/memory_manager.hpp | 18 +++++----- src/windows-emulator/syscalls/memory.cpp | 2 +- 6 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp index cb759e3a..6bb1d6fa 100644 --- a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp +++ b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp @@ -193,9 +193,9 @@ namespace icicle icicle_map_mmio(this->emu_, address, size, read_wrapper, ptr, write_wrapper, ptr); } - void map_memory(const uint64_t address, const size_t size, memory_permission permissions) override + void map_memory(const uint64_t address, const size_t size, nt_memory_permission permissions) override { - const auto res = icicle_map_memory(this->emu_, address, size, static_cast(permissions)); + const auto res = icicle_map_memory(this->emu_, address, size, static_cast(permissions.common)); ice(res, "Failed to map memory"); } @@ -222,9 +222,9 @@ namespace icicle ice(res, "Failed to write memory"); } - void apply_memory_protection(const uint64_t address, const size_t size, memory_permission permissions) override + void apply_memory_protection(const uint64_t address, const size_t size, nt_memory_permission permissions) override { - const auto res = icicle_protect_memory(this->emu_, address, size, static_cast(permissions)); + const auto res = icicle_protect_memory(this->emu_, address, size, static_cast(permissions.common)); ice(res, "Failed to apply permissions"); } diff --git a/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp index 52629f76..d1e48259 100644 --- a/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp @@ -335,9 +335,9 @@ namespace unicorn this->mmio_[address] = std::move(cb); } - void map_memory(const uint64_t address, const size_t size, memory_permission permissions) override + void map_memory(const uint64_t address, const size_t size, nt_memory_permission permissions) override { - uce(uc_mem_map(*this, address, size, static_cast(permissions))); + uce(uc_mem_map(*this, address, size, static_cast(permissions.common))); } void unmap_memory(const uint64_t address, const size_t size) override @@ -367,9 +367,9 @@ namespace unicorn } void apply_memory_protection(const uint64_t address, const size_t size, - memory_permission permissions) override + nt_memory_permission permissions) override { - uce(uc_mem_protect(*this, address, size, static_cast(permissions))); + uce(uc_mem_protect(*this, address, size, static_cast(permissions.common))); } emulator_hook* hook_instruction(const int instruction_type, instruction_hook_callback callback) override diff --git a/src/emulator/memory_interface.hpp b/src/emulator/memory_interface.hpp index 3f472040..b8e67096 100644 --- a/src/emulator/memory_interface.hpp +++ b/src/emulator/memory_interface.hpp @@ -22,10 +22,10 @@ class memory_interface private: virtual void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) = 0; - virtual void map_memory(uint64_t address, size_t size, memory_permission permissions) = 0; + virtual void map_memory(uint64_t address, size_t size, nt_memory_permission permissions) = 0; virtual void unmap_memory(uint64_t address, size_t size) = 0; - virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0; + virtual void apply_memory_protection(uint64_t address, size_t size, nt_memory_permission permissions) = 0; public: template diff --git a/src/windows-emulator/memory_manager.cpp b/src/windows-emulator/memory_manager.cpp index 98268bef..14c0ca55 100644 --- a/src/windows-emulator/memory_manager.cpp +++ b/src/windows-emulator/memory_manager.cpp @@ -1,8 +1,10 @@ +#include "memory_permission.hpp" #include "std_include.hpp" #include "memory_manager.hpp" #include "memory_region.hpp" #include "address_utils.hpp" +#include "windows-emulator/memory_permission_ext.hpp" #include #include @@ -73,7 +75,7 @@ namespace utils static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region) { region.length = static_cast(buffer.read()); - region.permissions = buffer.read(); + region.permissions = buffer.read(); } static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region) @@ -170,8 +172,8 @@ void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer } } -bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions, - memory_permission* old_permissions) +bool memory_manager::protect_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions, + nt_memory_permission* old_permissions) { const auto entry = this->find_reserved_region(address); if (entry == this->reserved_regions_.end()) @@ -249,7 +251,7 @@ bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mm return true; } -bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions, +bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions, const bool reserve_only) { if (this->overlaps_reserved_region(address, size)) @@ -268,7 +270,13 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size, if (!reserve_only) { this->map_memory(address, size, permissions); - entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write}; + + auto common = memory_permission::read_write; + auto extended = permissions.is_guarded() + ? memory_permission_ext::guard + : memory_permission_ext::none; + + entry->second.committed_regions[address] = committed_region{size, nt_memory_permission{common, extended}}; } this->update_layout_version(); @@ -276,7 +284,7 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size, return true; } -bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions) +bool memory_manager::commit_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions) { const auto entry = this->find_reserved_region(address); if (entry == this->reserved_regions_.end()) @@ -454,7 +462,7 @@ void memory_manager::unmap_all_memory() this->reserved_regions_.clear(); } -uint64_t memory_manager::allocate_memory(const size_t size, const memory_permission permissions, +uint64_t memory_manager::allocate_memory(const size_t size, const nt_memory_permission permissions, const bool reserve_only) { const auto allocation_base = this->find_free_allocation_base(size); @@ -500,8 +508,8 @@ region_info memory_manager::get_region_info(const uint64_t address) region_info result{}; result.start = MIN_ALLOCATION_ADDRESS; result.length = static_cast(MAX_ALLOCATION_ADDRESS - result.start); - result.permissions = memory_permission::none; - result.initial_permissions = memory_permission::none; + result.permissions = nt_memory_permission(); + result.initial_permissions = nt_memory_permission(); result.allocation_base = {}; result.allocation_length = result.length; result.is_committed = false; @@ -624,9 +632,13 @@ void memory_manager::map_mmio(const uint64_t address, const size_t size, mmio_re this->memory_->map_mmio(address, size, std::move(read_cb), std::move(write_cb)); } -void memory_manager::map_memory(const uint64_t address, const size_t size, const memory_permission permissions) +void memory_manager::map_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions) { - this->memory_->map_memory(address, size, permissions); + auto perms = permissions.is_guarded() + ? nt_memory_permission(memory_permission::none) + : permissions; + + this->memory_->map_memory(address, size, perms); } void memory_manager::unmap_memory(const uint64_t address, const size_t size) @@ -635,7 +647,7 @@ void memory_manager::unmap_memory(const uint64_t address, const size_t size) } void memory_manager::apply_memory_protection(const uint64_t address, const size_t size, - const memory_permission permissions) + const nt_memory_permission permissions) { this->memory_->apply_memory_protection(address, size, permissions); } diff --git a/src/windows-emulator/memory_manager.hpp b/src/windows-emulator/memory_manager.hpp index 4b5f087e..a67d383f 100644 --- a/src/windows-emulator/memory_manager.hpp +++ b/src/windows-emulator/memory_manager.hpp @@ -18,7 +18,7 @@ struct region_info : basic_memory_region size_t allocation_length{}; bool is_reserved{}; bool is_committed{}; - memory_permission initial_permissions{}; + nt_memory_permission initial_permissions{}; }; using mmio_read_callback = std::function; @@ -35,7 +35,7 @@ class memory_manager : public memory_interface struct committed_region { size_t length{}; - memory_permission permissions{}; + nt_memory_permission permissions{}; }; using committed_region_map = std::map; @@ -54,20 +54,20 @@ class memory_manager : public memory_interface bool try_read_memory(uint64_t address, void* data, size_t size) const final; void write_memory(uint64_t address, const void* data, size_t size) final; - bool protect_memory(uint64_t address, size_t size, memory_permission permissions, - memory_permission* old_permissions = nullptr); + bool protect_memory(uint64_t address, size_t size, nt_memory_permission permissions, + nt_memory_permission* old_permissions = nullptr); bool allocate_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb); - bool allocate_memory(uint64_t address, size_t size, memory_permission permissions, bool reserve_only = false); + bool allocate_memory(uint64_t address, size_t size, nt_memory_permission permissions, bool reserve_only = false); - bool commit_memory(uint64_t address, size_t size, memory_permission permissions); + bool commit_memory(uint64_t address, size_t size, nt_memory_permission permissions); bool decommit_memory(uint64_t address, size_t size); bool release_memory(uint64_t address, size_t size); void unmap_all_memory(); - uint64_t allocate_memory(size_t size, memory_permission permissions, bool reserve_only = false); + uint64_t allocate_memory(size_t size, nt_memory_permission permissions, bool reserve_only = false); uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const; @@ -96,9 +96,9 @@ class memory_manager : public memory_interface std::atomic layout_version_{0}; void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) final; - void map_memory(uint64_t address, size_t size, memory_permission permissions) final; + void map_memory(uint64_t address, size_t size, nt_memory_permission permissions) final; void unmap_memory(uint64_t address, size_t size) final; - void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) final; + void apply_memory_protection(uint64_t address, size_t size, nt_memory_permission permissions) final; void update_layout_version(); }; diff --git a/src/windows-emulator/syscalls/memory.cpp b/src/windows-emulator/syscalls/memory.cpp index efd5e2f2..0f08c057 100644 --- a/src/windows-emulator/syscalls/memory.cpp +++ b/src/windows-emulator/syscalls/memory.cpp @@ -148,7 +148,7 @@ namespace syscalls c.win_emu.callbacks.on_memory_protect(aligned_start, aligned_length, requested_protection); - memory_permission old_protection_value{}; + nt_memory_permission old_protection_value{}; try { From 2e17f37f78ef4d40939323f27ef2cd0b7d2e02f0 Mon Sep 17 00:00:00 2001 From: 3fault Date: Tue, 1 Jul 2025 19:17:05 -0400 Subject: [PATCH 05/17] Update hook_memory_violation to handle guard page violations --- src/analyzer/main.cpp | 2 +- src/emulator/memory_region.hpp | 4 ++-- src/windows-emulator/windows_emulator.cpp | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 5739b501..5f8814b8 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -317,7 +317,7 @@ namespace { for (const auto& section : exe.sections) { - if ((section.region.permissions & memory_permission::exec) != memory_permission::exec) + if ((section.region.permissions.common & memory_permission::exec) != memory_permission::exec) { continue; } diff --git a/src/emulator/memory_region.hpp b/src/emulator/memory_region.hpp index cdf25ce9..51324467 100644 --- a/src/emulator/memory_region.hpp +++ b/src/emulator/memory_region.hpp @@ -1,12 +1,12 @@ #pragma once -#include "memory_permission.hpp" +#include "windows-emulator/memory_permission_ext.hpp" #include struct basic_memory_region { uint64_t start{}; size_t length{}; // uint64_t? - memory_permission permissions{}; + nt_memory_permission permissions{}; }; struct memory_region : basic_memory_region diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 44175c43..8ee4b066 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -11,6 +11,7 @@ #include "apiset/apiset.hpp" #include "network/static_socket_factory.hpp" +#include "windows-emulator/memory_permission_ext.hpp" constexpr auto MAX_INSTRUCTIONS_PER_TIME_SLICE = 0x20000; @@ -499,8 +500,19 @@ void windows_emulator::setup_hooks() this->emu().hook_memory_violation([&](const uint64_t address, const size_t size, const memory_operation operation, const memory_violation_type type) { - this->callbacks.on_memory_violate(address, size, operation, type); - dispatch_access_violation(this->emu(), this->process, address, operation); + auto region = this->memory.get_region_info(address); + if (region.permissions.is_guarded()) + { + // Unset the GUARD_PAGE flag and dispatch a STATUS_GUARD_PAGE_VIOLATION + this->memory.protect_memory(region.allocation_base, region.length, region.permissions & ~memory_permission_ext::guard); + dispatch_guard_page_violation(this->emu(), this->process, address, operation); + } + else + { + this->callbacks.on_memory_violate(address, size, operation, type); + dispatch_access_violation(this->emu(), this->process, address, operation); + } + return memory_violation_continuation::resume; }); From 68a5800c856e8d44158aa5a67b9ddda447bbfd72 Mon Sep 17 00:00:00 2001 From: 3fault Date: Wed, 2 Jul 2025 12:09:21 -0400 Subject: [PATCH 06/17] Add test for guard page in test-sample, formatting fix --- src/samples/test-sample/test.cpp | 96 +++++++++++++++++++ .../memory_permission_ext.hpp | 3 +- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/samples/test-sample/test.cpp b/src/samples/test-sample/test.cpp index 6b5cdba6..a08be686 100644 --- a/src/samples/test-sample/test.cpp +++ b/src/samples/test-sample/test.cpp @@ -721,6 +721,101 @@ namespace SleepEx(1, TRUE); return executions == 2; } + + INT32 test_guard_page_seh_filter(LPVOID address, DWORD code, struct _EXCEPTION_POINTERS* ep) + { + // We are only looking for guard page exceptions. + if (code != STATUS_GUARD_PAGE_VIOLATION) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // The number of defined elements in the ExceptionInformation array for + // a guard page violation should be 2. + if (ep->ExceptionRecord->NumberParameters != 2) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // The ExceptionInformation array specifies additional arguments that + // describe the exception. + auto *exception_information = ep->ExceptionRecord->ExceptionInformation; + + // If this value is zero, the thread attempted to read the inaccessible + // data. If this value is 1, the thread attempted to write to an + // inaccessible address. + if (exception_information[0] != 1) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // The second array element specifies the virtual address of the + // inaccessible data. + if (exception_information[1] != (ULONG_PTR)address) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + return EXCEPTION_EXECUTE_HANDLER; + } + + bool test_guard_page() + { + SYSTEM_INFO sys_info; + GetSystemInfo(&sys_info); + + // Allocate a guarded memory region with the length of the system page + // size. + auto *addr = static_cast(VirtualAlloc( + nullptr, + sys_info.dwPageSize, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE | PAGE_GUARD + )); + if (addr == nullptr) + { + puts("Failed to allocate guard page"); + return false; + } + + bool success = false; + + // We want to access some arbitrary offset into the guarded page, to + // ensure that ExceptionInformation correctly contains the virtual + // address of the inaccessible data, not the base address of the region. + constexpr size_t offset = 10; + + // Trigger a guard page violation + __try + { + addr[offset] = 255; + } + // If the filter function returns EXCEPTION_CONTINUE_SEARCH, the + // exception contains all of the correct information. + __except(test_guard_page_seh_filter( + addr + offset, + GetExceptionCode(), + GetExceptionInformation())) + { + success = true; + } + + // The page guard should be lifted, so no exception should be raised. + __try { + // The previous write should not have went through, this is probably + // superflous. + if (addr[offset] == 255) { + success = false; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + puts("Failed to read from page after guard exception!"); + success = false; + } + + return success; + } } #define RUN_TEST(func, name) \ @@ -761,6 +856,7 @@ int main(const int argc, const char* argv[]) RUN_TEST(test_tls, "TLS") RUN_TEST(test_socket, "Socket") RUN_TEST(test_apc, "APC") + RUN_TEST(test_guard_page, "Guard Page") return valid ? 0 : 1; } diff --git a/src/windows-emulator/memory_permission_ext.hpp b/src/windows-emulator/memory_permission_ext.hpp index ca410475..257acfc6 100644 --- a/src/windows-emulator/memory_permission_ext.hpp +++ b/src/windows-emulator/memory_permission_ext.hpp @@ -75,7 +75,8 @@ struct nt_memory_permission return *this; } - constexpr bool is_guarded() const { + constexpr bool is_guarded() const + { return (this->extended & memory_permission_ext::guard) == memory_permission_ext::guard; } }; From 9a6da790cf5308c263d82ee37c0915c7faa60a87 Mon Sep 17 00:00:00 2001 From: 3fault Date: Wed, 2 Jul 2025 13:41:23 -0400 Subject: [PATCH 07/17] Remove #include in exception_dispatch --- src/windows-emulator/exception_dispatch.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/windows-emulator/exception_dispatch.cpp b/src/windows-emulator/exception_dispatch.cpp index df4164b1..2a15c02b 100644 --- a/src/windows-emulator/exception_dispatch.cpp +++ b/src/windows-emulator/exception_dispatch.cpp @@ -2,7 +2,6 @@ #include "exception_dispatch.hpp" #include "process_context.hpp" #include "cpu_context.hpp" -#include namespace { From 2ecd3e4440ef5f5a84cad9a42746f04e2e2a7d17 Mon Sep 17 00:00:00 2001 From: 3fault Date: Wed, 2 Jul 2025 14:06:37 -0400 Subject: [PATCH 08/17] add STATUS_GUARD_PAGE_VIOLATION constant to platform includes --- src/common/platform/status.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/platform/status.hpp b/src/common/platform/status.hpp index 7918fcf0..720f700c 100644 --- a/src/common/platform/status.hpp +++ b/src/common/platform/status.hpp @@ -10,6 +10,7 @@ using NTSTATUS = std::uint32_t; #define STATUS_TIMEOUT ((NTSTATUS)0x00000102L) #define STATUS_PENDING ((NTSTATUS)0x00000103L) +#define STATUS_GUARD_PAGE_VIOLATION ((NTSTATUS)0x80000001L) #define STATUS_BREAKPOINT ((NTSTATUS)0x80000003L) #define STATUS_SINGLE_STEP ((NTSTATUS)0x80000004L) From af21473cf1301842d64baa14e3dc119e5846ffa5 Mon Sep 17 00:00:00 2001 From: 3fault Date: Wed, 2 Jul 2025 14:27:05 -0400 Subject: [PATCH 09/17] merge test_guard_page with test_native_exceptions, fixes MinGW build --- src/samples/test-sample/test.cpp | 193 +++++++++++++++---------------- 1 file changed, 96 insertions(+), 97 deletions(-) diff --git a/src/samples/test-sample/test.cpp b/src/samples/test-sample/test.cpp index a08be686..e8b6fe12 100644 --- a/src/samples/test-sample/test.cpp +++ b/src/samples/test-sample/test.cpp @@ -646,10 +646,105 @@ namespace return res; } + + INT32 test_guard_page_seh_filter(LPVOID address, DWORD code, struct _EXCEPTION_POINTERS* ep) + { + // We are only looking for guard page exceptions. + if (code != STATUS_GUARD_PAGE_VIOLATION) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // The number of defined elements in the ExceptionInformation array for + // a guard page violation should be 2. + if (ep->ExceptionRecord->NumberParameters != 2) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // The ExceptionInformation array specifies additional arguments that + // describe the exception. + auto *exception_information = ep->ExceptionRecord->ExceptionInformation; + + // If this value is zero, the thread attempted to read the inaccessible + // data. If this value is 1, the thread attempted to write to an + // inaccessible address. + if (exception_information[0] != 1) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // The second array element specifies the virtual address of the + // inaccessible data. + if (exception_information[1] != (ULONG_PTR)address) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + return EXCEPTION_EXECUTE_HANDLER; + } + + bool test_guard_page_exception() + { + SYSTEM_INFO sys_info; + GetSystemInfo(&sys_info); + + // Allocate a guarded memory region with the length of the system page + // size. + auto *addr = static_cast(VirtualAlloc( + nullptr, + sys_info.dwPageSize, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE | PAGE_GUARD + )); + if (addr == nullptr) + { + puts("Failed to allocate guard page"); + return false; + } + + bool success = false; + + // We want to access some arbitrary offset into the guarded page, to + // ensure that ExceptionInformation correctly contains the virtual + // address of the inaccessible data, not the base address of the region. + constexpr size_t offset = 10; + + // Trigger a guard page violation + __try + { + addr[offset] = 255; + } + // If the filter function returns EXCEPTION_CONTINUE_SEARCH, the + // exception contains all of the correct information. + __except(test_guard_page_seh_filter( + addr + offset, + GetExceptionCode(), + GetExceptionInformation())) + { + success = true; + } + + // The page guard should be lifted, so no exception should be raised. + __try { + // The previous write should not have went through, this is probably + // superflous. + if (addr[offset] == 255) { + success = false; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + puts("Failed to read from page after guard exception!"); + success = false; + } + + return success; + } bool test_native_exceptions() { - return test_access_violation_exception() && test_illegal_instruction_exception(); + return test_access_violation_exception() && test_illegal_instruction_exception() && test_guard_page_exception(); } #endif @@ -721,101 +816,6 @@ namespace SleepEx(1, TRUE); return executions == 2; } - - INT32 test_guard_page_seh_filter(LPVOID address, DWORD code, struct _EXCEPTION_POINTERS* ep) - { - // We are only looking for guard page exceptions. - if (code != STATUS_GUARD_PAGE_VIOLATION) - { - return EXCEPTION_CONTINUE_SEARCH; - } - - // The number of defined elements in the ExceptionInformation array for - // a guard page violation should be 2. - if (ep->ExceptionRecord->NumberParameters != 2) - { - return EXCEPTION_CONTINUE_SEARCH; - } - - // The ExceptionInformation array specifies additional arguments that - // describe the exception. - auto *exception_information = ep->ExceptionRecord->ExceptionInformation; - - // If this value is zero, the thread attempted to read the inaccessible - // data. If this value is 1, the thread attempted to write to an - // inaccessible address. - if (exception_information[0] != 1) - { - return EXCEPTION_CONTINUE_SEARCH; - } - - // The second array element specifies the virtual address of the - // inaccessible data. - if (exception_information[1] != (ULONG_PTR)address) - { - return EXCEPTION_CONTINUE_SEARCH; - } - - return EXCEPTION_EXECUTE_HANDLER; - } - - bool test_guard_page() - { - SYSTEM_INFO sys_info; - GetSystemInfo(&sys_info); - - // Allocate a guarded memory region with the length of the system page - // size. - auto *addr = static_cast(VirtualAlloc( - nullptr, - sys_info.dwPageSize, - MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE | PAGE_GUARD - )); - if (addr == nullptr) - { - puts("Failed to allocate guard page"); - return false; - } - - bool success = false; - - // We want to access some arbitrary offset into the guarded page, to - // ensure that ExceptionInformation correctly contains the virtual - // address of the inaccessible data, not the base address of the region. - constexpr size_t offset = 10; - - // Trigger a guard page violation - __try - { - addr[offset] = 255; - } - // If the filter function returns EXCEPTION_CONTINUE_SEARCH, the - // exception contains all of the correct information. - __except(test_guard_page_seh_filter( - addr + offset, - GetExceptionCode(), - GetExceptionInformation())) - { - success = true; - } - - // The page guard should be lifted, so no exception should be raised. - __try { - // The previous write should not have went through, this is probably - // superflous. - if (addr[offset] == 255) { - success = false; - } - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - puts("Failed to read from page after guard exception!"); - success = false; - } - - return success; - } } #define RUN_TEST(func, name) \ @@ -856,7 +856,6 @@ int main(const int argc, const char* argv[]) RUN_TEST(test_tls, "TLS") RUN_TEST(test_socket, "Socket") RUN_TEST(test_apc, "APC") - RUN_TEST(test_guard_page, "Guard Page") return valid ? 0 : 1; } From 6e203d9590a3fe7544b1542dc51286e8306bbb87 Mon Sep 17 00:00:00 2001 From: 3fault Date: Wed, 2 Jul 2025 14:47:51 -0400 Subject: [PATCH 10/17] Fix formatting issues --- .../icicle_x86_64_emulator.cpp | 3 +- src/samples/test-sample/test.cpp | 43 ++++++++--------- src/windows-emulator/exception_dispatch.cpp | 2 +- src/windows-emulator/exception_dispatch.hpp | 2 +- src/windows-emulator/memory_manager.cpp | 12 ++--- .../memory_permission_ext.hpp | 48 +++++++++++++------ src/windows-emulator/memory_utils.hpp | 42 ++++++++-------- src/windows-emulator/windows_emulator.cpp | 7 +-- 8 files changed, 85 insertions(+), 74 deletions(-) diff --git a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp index 6bb1d6fa..2895fe1d 100644 --- a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp +++ b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp @@ -222,7 +222,8 @@ namespace icicle ice(res, "Failed to write memory"); } - void apply_memory_protection(const uint64_t address, const size_t size, nt_memory_permission permissions) override + void apply_memory_protection(const uint64_t address, const size_t size, + nt_memory_permission permissions) override { const auto res = icicle_protect_memory(this->emu_, address, size, static_cast(permissions.common)); ice(res, "Failed to apply permissions"); diff --git a/src/samples/test-sample/test.cpp b/src/samples/test-sample/test.cpp index e8b6fe12..feece83f 100644 --- a/src/samples/test-sample/test.cpp +++ b/src/samples/test-sample/test.cpp @@ -646,26 +646,26 @@ namespace return res; } - - INT32 test_guard_page_seh_filter(LPVOID address, DWORD code, struct _EXCEPTION_POINTERS* ep) + + INT32 test_guard_page_seh_filter(LPVOID address, DWORD code, struct _EXCEPTION_POINTERS* ep) { // We are only looking for guard page exceptions. - if (code != STATUS_GUARD_PAGE_VIOLATION) + if (code != STATUS_GUARD_PAGE_VIOLATION) { return EXCEPTION_CONTINUE_SEARCH; } // The number of defined elements in the ExceptionInformation array for // a guard page violation should be 2. - if (ep->ExceptionRecord->NumberParameters != 2) + if (ep->ExceptionRecord->NumberParameters != 2) { return EXCEPTION_CONTINUE_SEARCH; } // The ExceptionInformation array specifies additional arguments that // describe the exception. - auto *exception_information = ep->ExceptionRecord->ExceptionInformation; - + auto* exception_information = ep->ExceptionRecord->ExceptionInformation; + // If this value is zero, the thread attempted to read the inaccessible // data. If this value is 1, the thread attempted to write to an // inaccessible address. @@ -673,14 +673,14 @@ namespace { return EXCEPTION_CONTINUE_SEARCH; } - + // The second array element specifies the virtual address of the // inaccessible data. if (exception_information[1] != (ULONG_PTR)address) { return EXCEPTION_CONTINUE_SEARCH; } - + return EXCEPTION_EXECUTE_HANDLER; } @@ -691,12 +691,8 @@ namespace // Allocate a guarded memory region with the length of the system page // size. - auto *addr = static_cast(VirtualAlloc( - nullptr, - sys_info.dwPageSize, - MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE | PAGE_GUARD - )); + auto* addr = static_cast( + VirtualAlloc(nullptr, sys_info.dwPageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD)); if (addr == nullptr) { puts("Failed to allocate guard page"); @@ -704,12 +700,12 @@ namespace } bool success = false; - + // We want to access some arbitrary offset into the guarded page, to // ensure that ExceptionInformation correctly contains the virtual // address of the inaccessible data, not the base address of the region. constexpr size_t offset = 10; - + // Trigger a guard page violation __try { @@ -717,28 +713,27 @@ namespace } // If the filter function returns EXCEPTION_CONTINUE_SEARCH, the // exception contains all of the correct information. - __except(test_guard_page_seh_filter( - addr + offset, - GetExceptionCode(), - GetExceptionInformation())) + __except (test_guard_page_seh_filter(addr + offset, GetExceptionCode(), GetExceptionInformation())) { success = true; } // The page guard should be lifted, so no exception should be raised. - __try { + __try + { // The previous write should not have went through, this is probably // superflous. - if (addr[offset] == 255) { + if (addr[offset] == 255) + { success = false; } } - __except(EXCEPTION_EXECUTE_HANDLER) + __except (EXCEPTION_EXECUTE_HANDLER) { puts("Failed to read from page after guard exception!"); success = false; } - + return success; } diff --git a/src/windows-emulator/exception_dispatch.cpp b/src/windows-emulator/exception_dispatch.cpp index 2a15c02b..09e9d93c 100644 --- a/src/windows-emulator/exception_dispatch.cpp +++ b/src/windows-emulator/exception_dispatch.cpp @@ -183,7 +183,7 @@ void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc } void dispatch_guard_page_violation(x86_64_emulator& emu, const process_context& proc, const uint64_t address, - const memory_operation operation) + const memory_operation operation) { dispatch_exception(emu, proc, STATUS_GUARD_PAGE_VIOLATION, { diff --git a/src/windows-emulator/exception_dispatch.hpp b/src/windows-emulator/exception_dispatch.hpp index 428c55ef..46850435 100644 --- a/src/windows-emulator/exception_dispatch.hpp +++ b/src/windows-emulator/exception_dispatch.hpp @@ -20,7 +20,7 @@ void dispatch_exception(x86_64_emulator& emu, const process_context& proc, const void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, uint64_t address, memory_operation operation); void dispatch_guard_page_violation(x86_64_emulator& emu, const process_context& proc, uint64_t address, - memory_operation operation); + memory_operation operation); void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context& proc); void dispatch_integer_division_by_zero(x86_64_emulator& emu, const process_context& proc); void dispatch_single_step(x86_64_emulator& emu, const process_context& proc); diff --git a/src/windows-emulator/memory_manager.cpp b/src/windows-emulator/memory_manager.cpp index 14c0ca55..7e149e50 100644 --- a/src/windows-emulator/memory_manager.cpp +++ b/src/windows-emulator/memory_manager.cpp @@ -272,10 +272,8 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size, this->map_memory(address, size, permissions); auto common = memory_permission::read_write; - auto extended = permissions.is_guarded() - ? memory_permission_ext::guard - : memory_permission_ext::none; - + auto extended = permissions.is_guarded() ? memory_permission_ext::guard : memory_permission_ext::none; + entry->second.committed_regions[address] = committed_region{size, nt_memory_permission{common, extended}}; } @@ -634,10 +632,8 @@ void memory_manager::map_mmio(const uint64_t address, const size_t size, mmio_re void memory_manager::map_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions) { - auto perms = permissions.is_guarded() - ? nt_memory_permission(memory_permission::none) - : permissions; - + auto perms = permissions.is_guarded() ? nt_memory_permission(memory_permission::none) : permissions; + this->memory_->map_memory(address, size, perms); } diff --git a/src/windows-emulator/memory_permission_ext.hpp b/src/windows-emulator/memory_permission_ext.hpp index 257acfc6..63427929 100644 --- a/src/windows-emulator/memory_permission_ext.hpp +++ b/src/windows-emulator/memory_permission_ext.hpp @@ -53,29 +53,47 @@ inline memory_permission_ext& operator^=(memory_permission_ext& x, const memory_ * ****************************************************************************/ -struct nt_memory_permission +struct nt_memory_permission { - memory_permission common; + memory_permission common; memory_permission_ext extended; - constexpr nt_memory_permission() : common(memory_permission::none), extended(memory_permission_ext::none) {} - constexpr nt_memory_permission(memory_permission common) : common(common), extended(memory_permission_ext::none) {} - constexpr nt_memory_permission(memory_permission common, memory_permission_ext ext) : common(common), extended(ext) {} + constexpr nt_memory_permission() + : common(memory_permission::none), + extended(memory_permission_ext::none) + { + } + constexpr nt_memory_permission(memory_permission common) + : common(common), + extended(memory_permission_ext::none) + { + } + constexpr nt_memory_permission(memory_permission common, memory_permission_ext ext) + : common(common), + extended(ext) + { + } // Implicit coercions - operator memory_permission() const { return common; } - operator memory_permission_ext() const { return extended; } + operator memory_permission() const + { + return common; + } + operator memory_permission_ext() const + { + return extended; + } // This just does memberwise equality on each of the members in declaration order bool operator==(nt_memory_permission const&) const = default; - nt_memory_permission& operator=(memory_permission const& y) + nt_memory_permission& operator=(memory_permission const& y) { this->common = y; return *this; } - constexpr bool is_guarded() const + constexpr bool is_guarded() const { return (this->extended & memory_permission_ext::guard) == memory_permission_ext::guard; } @@ -87,32 +105,32 @@ struct nt_memory_permission constexpr nt_memory_permission operator&(const nt_memory_permission x, const memory_permission y) { - return nt_memory_permission { x.common & y, x.extended }; + return nt_memory_permission{x.common & y, x.extended}; } constexpr nt_memory_permission operator&(const nt_memory_permission x, const memory_permission_ext y) { - return nt_memory_permission { x.common, x.extended & y }; + return nt_memory_permission{x.common, x.extended & y}; } constexpr nt_memory_permission operator|(const nt_memory_permission x, const memory_permission y) { - return nt_memory_permission { x.common | y, x.extended }; + return nt_memory_permission{x.common | y, x.extended}; } constexpr nt_memory_permission operator|(const nt_memory_permission x, const memory_permission_ext y) { - return nt_memory_permission { x.common, x.extended | y }; + return nt_memory_permission{x.common, x.extended | y}; } constexpr nt_memory_permission operator^(const nt_memory_permission x, const memory_permission y) { - return nt_memory_permission { x.common ^ y, x.extended }; + return nt_memory_permission{x.common ^ y, x.extended}; } constexpr nt_memory_permission operator^(const nt_memory_permission x, const memory_permission_ext y) { - return nt_memory_permission { x.common, x.extended ^ y }; + return nt_memory_permission{x.common, x.extended ^ y}; } inline nt_memory_permission& operator&=(nt_memory_permission& x, const memory_permission y) diff --git a/src/windows-emulator/memory_utils.hpp b/src/windows-emulator/memory_utils.hpp index d524ffd3..bcb05747 100644 --- a/src/windows-emulator/memory_utils.hpp +++ b/src/windows-emulator/memory_utils.hpp @@ -33,29 +33,29 @@ inline nt_memory_permission map_nt_to_emulator_protection(uint32_t nt_protection memory_permission common = memory_permission::none; switch (nt_protection) { - case PAGE_NOACCESS: - common = memory_permission::none; - break; - case PAGE_READONLY: - common = memory_permission::read; - break; - case PAGE_READWRITE: - case PAGE_WRITECOPY: - common = memory_permission::read | memory_permission::write; - break; - case PAGE_EXECUTE: - case PAGE_EXECUTE_READ: - common = memory_permission::read | memory_permission::exec; - break; - case PAGE_EXECUTE_READWRITE: - common = memory_permission::all; - break; - case PAGE_EXECUTE_WRITECOPY: - default: - throw std::runtime_error("Failed to map protection"); + case PAGE_NOACCESS: + common = memory_permission::none; + break; + case PAGE_READONLY: + common = memory_permission::read; + break; + case PAGE_READWRITE: + case PAGE_WRITECOPY: + common = memory_permission::read | memory_permission::write; + break; + case PAGE_EXECUTE: + case PAGE_EXECUTE_READ: + common = memory_permission::read | memory_permission::exec; + break; + case PAGE_EXECUTE_READWRITE: + common = memory_permission::all; + break; + case PAGE_EXECUTE_WRITECOPY: + default: + throw std::runtime_error("Failed to map protection"); } - return nt_memory_permission { common, ext }; + return nt_memory_permission{common, ext}; } inline uint32_t map_emulator_to_nt_protection(const memory_permission permission) diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 8ee4b066..f89d58a8 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -503,8 +503,9 @@ void windows_emulator::setup_hooks() auto region = this->memory.get_region_info(address); if (region.permissions.is_guarded()) { - // Unset the GUARD_PAGE flag and dispatch a STATUS_GUARD_PAGE_VIOLATION - this->memory.protect_memory(region.allocation_base, region.length, region.permissions & ~memory_permission_ext::guard); + // Unset the GUARD_PAGE flag and dispatch a STATUS_GUARD_PAGE_VIOLATION + this->memory.protect_memory(region.allocation_base, region.length, + region.permissions & ~memory_permission_ext::guard); dispatch_guard_page_violation(this->emu(), this->process, address, operation); } else @@ -512,7 +513,7 @@ void windows_emulator::setup_hooks() this->callbacks.on_memory_violate(address, size, operation, type); dispatch_access_violation(this->emu(), this->process, address, operation); } - + return memory_violation_continuation::resume; }); From 8ec184d57f3203be28336ccd599ef3ea77d2bc03 Mon Sep 17 00:00:00 2001 From: 3fault Date: Wed, 9 Jul 2025 14:39:49 -0400 Subject: [PATCH 11/17] remove unneeded windows-emulator/memory_permission_ext.cpp --- src/windows-emulator/memory_permission_ext.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/windows-emulator/memory_permission_ext.cpp diff --git a/src/windows-emulator/memory_permission_ext.cpp b/src/windows-emulator/memory_permission_ext.cpp deleted file mode 100644 index e69de29b..00000000 From 19574fb512cf7ad9836d3fcdfee9e547319514ee Mon Sep 17 00:00:00 2001 From: 3fault Date: Wed, 9 Jul 2025 14:43:06 -0400 Subject: [PATCH 12/17] remove references to nt_memory_permission from backend --- src/analyzer/main.cpp | 2 +- .../icicle-emulator/icicle_x86_64_emulator.cpp | 9 ++++----- .../unicorn_x86_64_emulator.cpp | 8 ++++---- src/emulator/memory_interface.hpp | 6 +++--- src/emulator/memory_region.hpp | 4 ++-- src/windows-emulator/memory_manager.cpp | 18 ++++++------------ src/windows-emulator/memory_manager.hpp | 17 +++++++++++++---- src/windows-emulator/memory_utils.hpp | 1 + 8 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 5f8814b8..5739b501 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -317,7 +317,7 @@ namespace { for (const auto& section : exe.sections) { - if ((section.region.permissions.common & memory_permission::exec) != memory_permission::exec) + if ((section.region.permissions & memory_permission::exec) != memory_permission::exec) { continue; } diff --git a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp index 2895fe1d..cb759e3a 100644 --- a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp +++ b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp @@ -193,9 +193,9 @@ namespace icicle icicle_map_mmio(this->emu_, address, size, read_wrapper, ptr, write_wrapper, ptr); } - void map_memory(const uint64_t address, const size_t size, nt_memory_permission permissions) override + void map_memory(const uint64_t address, const size_t size, memory_permission permissions) override { - const auto res = icicle_map_memory(this->emu_, address, size, static_cast(permissions.common)); + const auto res = icicle_map_memory(this->emu_, address, size, static_cast(permissions)); ice(res, "Failed to map memory"); } @@ -222,10 +222,9 @@ namespace icicle ice(res, "Failed to write memory"); } - void apply_memory_protection(const uint64_t address, const size_t size, - nt_memory_permission permissions) override + void apply_memory_protection(const uint64_t address, const size_t size, memory_permission permissions) override { - const auto res = icicle_protect_memory(this->emu_, address, size, static_cast(permissions.common)); + const auto res = icicle_protect_memory(this->emu_, address, size, static_cast(permissions)); ice(res, "Failed to apply permissions"); } diff --git a/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp index d1e48259..52629f76 100644 --- a/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp @@ -335,9 +335,9 @@ namespace unicorn this->mmio_[address] = std::move(cb); } - void map_memory(const uint64_t address, const size_t size, nt_memory_permission permissions) override + void map_memory(const uint64_t address, const size_t size, memory_permission permissions) override { - uce(uc_mem_map(*this, address, size, static_cast(permissions.common))); + uce(uc_mem_map(*this, address, size, static_cast(permissions))); } void unmap_memory(const uint64_t address, const size_t size) override @@ -367,9 +367,9 @@ namespace unicorn } void apply_memory_protection(const uint64_t address, const size_t size, - nt_memory_permission permissions) override + memory_permission permissions) override { - uce(uc_mem_protect(*this, address, size, static_cast(permissions.common))); + uce(uc_mem_protect(*this, address, size, static_cast(permissions))); } emulator_hook* hook_instruction(const int instruction_type, instruction_hook_callback callback) override diff --git a/src/emulator/memory_interface.hpp b/src/emulator/memory_interface.hpp index b8e67096..2f735e9a 100644 --- a/src/emulator/memory_interface.hpp +++ b/src/emulator/memory_interface.hpp @@ -2,7 +2,7 @@ #include #include -#include "windows-emulator/memory_permission_ext.hpp" +#include "memory_permission.hpp" using mmio_read_callback = std::function; using mmio_write_callback = std::function; @@ -22,10 +22,10 @@ class memory_interface private: virtual void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) = 0; - virtual void map_memory(uint64_t address, size_t size, nt_memory_permission permissions) = 0; + virtual void map_memory(uint64_t address, size_t size, memory_permission permissions) = 0; virtual void unmap_memory(uint64_t address, size_t size) = 0; - virtual void apply_memory_protection(uint64_t address, size_t size, nt_memory_permission permissions) = 0; + virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0; public: template diff --git a/src/emulator/memory_region.hpp b/src/emulator/memory_region.hpp index 51324467..cdf25ce9 100644 --- a/src/emulator/memory_region.hpp +++ b/src/emulator/memory_region.hpp @@ -1,12 +1,12 @@ #pragma once -#include "windows-emulator/memory_permission_ext.hpp" +#include "memory_permission.hpp" #include struct basic_memory_region { uint64_t start{}; size_t length{}; // uint64_t? - nt_memory_permission permissions{}; + memory_permission permissions{}; }; struct memory_region : basic_memory_region diff --git a/src/windows-emulator/memory_manager.cpp b/src/windows-emulator/memory_manager.cpp index 7e149e50..9f45a8b4 100644 --- a/src/windows-emulator/memory_manager.cpp +++ b/src/windows-emulator/memory_manager.cpp @@ -1,4 +1,3 @@ -#include "memory_permission.hpp" #include "std_include.hpp" #include "memory_manager.hpp" @@ -269,12 +268,9 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size, if (!reserve_only) { - this->map_memory(address, size, permissions); - - auto common = memory_permission::read_write; - auto extended = permissions.is_guarded() ? memory_permission_ext::guard : memory_permission_ext::none; - - entry->second.committed_regions[address] = committed_region{size, nt_memory_permission{common, extended}}; + this->map_memory(address, size, permissions.is_guarded() ? memory_permission::none : permissions.common); + entry->second.committed_regions[address] = + committed_region{size, nt_memory_permission{memory_permission::read_write, permissions.extended}}; } this->update_layout_version(); @@ -630,11 +626,9 @@ void memory_manager::map_mmio(const uint64_t address, const size_t size, mmio_re this->memory_->map_mmio(address, size, std::move(read_cb), std::move(write_cb)); } -void memory_manager::map_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions) +void memory_manager::map_memory(const uint64_t address, const size_t size, const memory_permission permissions) { - auto perms = permissions.is_guarded() ? nt_memory_permission(memory_permission::none) : permissions; - - this->memory_->map_memory(address, size, perms); + this->memory_->map_memory(address, size, permissions); } void memory_manager::unmap_memory(const uint64_t address, const size_t size) @@ -643,7 +637,7 @@ void memory_manager::unmap_memory(const uint64_t address, const size_t size) } void memory_manager::apply_memory_protection(const uint64_t address, const size_t size, - const nt_memory_permission permissions) + const memory_permission permissions) { this->memory_->apply_memory_protection(address, size, permissions); } diff --git a/src/windows-emulator/memory_manager.hpp b/src/windows-emulator/memory_manager.hpp index a67d383f..8f533e3c 100644 --- a/src/windows-emulator/memory_manager.hpp +++ b/src/windows-emulator/memory_manager.hpp @@ -3,7 +3,7 @@ #include #include -#include "memory_region.hpp" +#include "memory_permission_ext.hpp" #include "serialization.hpp" #include @@ -12,7 +12,16 @@ constexpr auto ALLOCATION_GRANULARITY = 0x0000000000010000ULL; constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL; constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL; -struct region_info : basic_memory_region +// This maps to the `basic_memory_region` struct defined in +// emulator\memory_region.hpp +struct nt_basic_memory_region +{ + uint64_t start{}; + size_t length{}; + nt_memory_permission permissions{}; +}; + +struct region_info : nt_basic_memory_region { uint64_t allocation_base{}; size_t allocation_length{}; @@ -96,9 +105,9 @@ class memory_manager : public memory_interface std::atomic layout_version_{0}; void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) final; - void map_memory(uint64_t address, size_t size, nt_memory_permission permissions) final; + void map_memory(uint64_t address, size_t size, memory_permission permissions) final; void unmap_memory(uint64_t address, size_t size) final; - void apply_memory_protection(uint64_t address, size_t size, nt_memory_permission permissions) final; + void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) final; void update_layout_version(); }; diff --git a/src/windows-emulator/memory_utils.hpp b/src/windows-emulator/memory_utils.hpp index bcb05747..e50b0098 100644 --- a/src/windows-emulator/memory_utils.hpp +++ b/src/windows-emulator/memory_utils.hpp @@ -2,6 +2,7 @@ #include #include #include +#include "memory_permission_ext.hpp" inline std::string get_permission_string(const memory_permission permission) { From f4fc9c4a6c6c65c8dd2fb7110cf4f7fb2b7455da Mon Sep 17 00:00:00 2001 From: 3fault Date: Wed, 9 Jul 2025 14:49:26 -0400 Subject: [PATCH 13/17] fix memory_permissions_ext includes --- src/windows-emulator/memory_manager.cpp | 2 +- src/windows-emulator/windows_emulator.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/memory_manager.cpp b/src/windows-emulator/memory_manager.cpp index 9f45a8b4..9bdcf6d6 100644 --- a/src/windows-emulator/memory_manager.cpp +++ b/src/windows-emulator/memory_manager.cpp @@ -3,7 +3,7 @@ #include "memory_region.hpp" #include "address_utils.hpp" -#include "windows-emulator/memory_permission_ext.hpp" +#include "memory_permission_ext.hpp" #include #include diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index f89d58a8..a56f1a06 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -11,7 +11,7 @@ #include "apiset/apiset.hpp" #include "network/static_socket_factory.hpp" -#include "windows-emulator/memory_permission_ext.hpp" +#include "memory_permission_ext.hpp" constexpr auto MAX_INSTRUCTIONS_PER_TIME_SLICE = 0x20000; From 8cea9fc75a753f878423ab903a0d036830b7576a Mon Sep 17 00:00:00 2001 From: 3fault Date: Thu, 17 Jul 2025 10:55:45 -0400 Subject: [PATCH 14/17] Ensure memory is freed after guard page test --- src/samples/test-sample/test.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/samples/test-sample/test.cpp b/src/samples/test-sample/test.cpp index feece83f..fa2d844a 100644 --- a/src/samples/test-sample/test.cpp +++ b/src/samples/test-sample/test.cpp @@ -734,6 +734,13 @@ namespace success = false; } + // Free the allocated memory + if (!VirtualFree(addr, 0, MEM_RELEASE)) + { + puts("Failed to free allocated region"); + success = false; + } + return success; } From 9f637105fbeb7e915337396816e6fddf6060a27f Mon Sep 17 00:00:00 2001 From: 3fault Date: Thu, 17 Jul 2025 11:06:26 -0400 Subject: [PATCH 15/17] Unset extended flags in nt_memory_permission assignment operator --- src/windows-emulator/memory_permission_ext.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/windows-emulator/memory_permission_ext.hpp b/src/windows-emulator/memory_permission_ext.hpp index 63427929..1ea95426 100644 --- a/src/windows-emulator/memory_permission_ext.hpp +++ b/src/windows-emulator/memory_permission_ext.hpp @@ -90,6 +90,7 @@ struct nt_memory_permission nt_memory_permission& operator=(memory_permission const& y) { this->common = y; + this->extended = memory_permission_ext::none; return *this; } From f1d54f7417f48d2c3dc69739e25faff7eb87617f Mon Sep 17 00:00:00 2001 From: 3fault Date: Thu, 17 Jul 2025 11:19:56 -0400 Subject: [PATCH 16/17] Parameterize basic_memory_region to support extended memory flags --- src/emulator/memory_region.hpp | 5 +++-- src/windows-emulator/memory_manager.hpp | 10 ++-------- src/windows-emulator/module/mapped_module.hpp | 2 +- src/windows-emulator/module/module_manager.cpp | 4 ++-- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/emulator/memory_region.hpp b/src/emulator/memory_region.hpp index cdf25ce9..71482bb5 100644 --- a/src/emulator/memory_region.hpp +++ b/src/emulator/memory_region.hpp @@ -2,14 +2,15 @@ #include "memory_permission.hpp" #include +template struct basic_memory_region { uint64_t start{}; size_t length{}; // uint64_t? - memory_permission permissions{}; + PermissionType permissions{}; }; -struct memory_region : basic_memory_region +struct memory_region : basic_memory_region<> { bool committed{}; }; diff --git a/src/windows-emulator/memory_manager.hpp b/src/windows-emulator/memory_manager.hpp index 8f533e3c..15bf6dd4 100644 --- a/src/windows-emulator/memory_manager.hpp +++ b/src/windows-emulator/memory_manager.hpp @@ -4,6 +4,7 @@ #include #include "memory_permission_ext.hpp" +#include "memory_region.hpp" #include "serialization.hpp" #include @@ -14,14 +15,7 @@ constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL; // This maps to the `basic_memory_region` struct defined in // emulator\memory_region.hpp -struct nt_basic_memory_region -{ - uint64_t start{}; - size_t length{}; - nt_memory_permission permissions{}; -}; - -struct region_info : nt_basic_memory_region +struct region_info : basic_memory_region { uint64_t allocation_base{}; size_t allocation_length{}; diff --git a/src/windows-emulator/module/mapped_module.hpp b/src/windows-emulator/module/mapped_module.hpp index dfe2b268..2019243f 100644 --- a/src/windows-emulator/module/mapped_module.hpp +++ b/src/windows-emulator/module/mapped_module.hpp @@ -15,7 +15,7 @@ using address_name_mapping = std::map; struct mapped_section { std::string name{}; - basic_memory_region region{}; + basic_memory_region<> region{}; }; struct mapped_module diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index c8549218..bb57fa77 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -23,14 +23,14 @@ namespace utils buffer.read(sym.address); } - static void serialize(buffer_serializer& buffer, const basic_memory_region& region) + static void serialize(buffer_serializer& buffer, const basic_memory_region<>& region) { buffer.write(region.start); buffer.write(region.length); buffer.write(region.permissions); } - static void deserialize(buffer_deserializer& buffer, basic_memory_region& region) + static void deserialize(buffer_deserializer& buffer, basic_memory_region<>& region) { buffer.read(region.start); region.length = static_cast(buffer.read()); From 188b95af5deb30a46cc7934f03d9f7338a8f7c16 Mon Sep 17 00:00:00 2001 From: 3fault Date: Thu, 17 Jul 2025 11:27:23 -0400 Subject: [PATCH 17/17] Fix formatting --- src/emulator/memory_region.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emulator/memory_region.hpp b/src/emulator/memory_region.hpp index 71482bb5..6cec89c9 100644 --- a/src/emulator/memory_region.hpp +++ b/src/emulator/memory_region.hpp @@ -2,7 +2,7 @@ #include "memory_permission.hpp" #include -template +template struct basic_memory_region { uint64_t start{};