diff --git a/src/emulator/cpu_interface.hpp b/src/emulator/cpu_interface.hpp index 06e35593..83dce7e8 100644 --- a/src/emulator/cpu_interface.hpp +++ b/src/emulator/cpu_interface.hpp @@ -18,5 +18,6 @@ struct cpu_interface virtual std::vector save_registers() = 0; virtual void restore_registers(const std::vector& register_data) = 0; + // TODO: Remove this virtual bool has_violation() const = 0; }; diff --git a/src/emulator/empty.cpp b/src/emulator/empty.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/emulator/emulator.hpp b/src/emulator/emulator.hpp index 91fc0d80..05ee17cf 100644 --- a/src/emulator/emulator.hpp +++ b/src/emulator/emulator.hpp @@ -2,9 +2,11 @@ #include "cpu_interface.hpp" #include "hook_interface.hpp" -#include "memory_manager.hpp" +#include "memory_interface.hpp" -class emulator : public cpu_interface, public memory_manager, public hook_interface +#include "serialization.hpp" + +class emulator : public cpu_interface, public memory_interface, public hook_interface { public: emulator() = default; @@ -16,54 +18,6 @@ class emulator : public cpu_interface, public memory_manager, public hook_interf emulator(emulator&&) = delete; emulator& operator=(emulator&&) = delete; - void serialize(utils::buffer_serializer& buffer) const - { - this->perform_serialization(buffer, false); - } - - void deserialize(utils::buffer_deserializer& buffer) - { - this->perform_deserialization(buffer, false); - } - - void save_snapshot() - { - utils::buffer_serializer serializer{}; - this->perform_serialization(serializer, true); - this->last_snapshot_data_ = serializer.move_buffer(); - } - - void restore_snapshot() - { - if (this->last_snapshot_data_.empty()) - { - return; - } - - utils::buffer_deserializer deserializer{this->last_snapshot_data_}; - this->perform_deserialization(deserializer, true); - } - - private: - std::vector last_snapshot_data_{}; - - void perform_serialization(utils::buffer_serializer& buffer, const bool is_snapshot) const - { - this->serialize_state(buffer, is_snapshot); - this->serialize_memory_state(buffer, is_snapshot); - } - - void perform_deserialization(utils::buffer_deserializer& buffer, const bool is_snapshot) - { - if (!is_snapshot) - { - this->unmap_all_memory(); - } - - this->deserialize_state(buffer, is_snapshot); - this->deserialize_memory_state(buffer, is_snapshot); - } - virtual void serialize_state(utils::buffer_serializer& buffer, bool is_snapshot) const = 0; virtual void deserialize_state(utils::buffer_deserializer& buffer, bool is_snapshot) = 0; }; diff --git a/src/emulator/memory_interface.hpp b/src/emulator/memory_interface.hpp new file mode 100644 index 00000000..3cdcc895 --- /dev/null +++ b/src/emulator/memory_interface.hpp @@ -0,0 +1,76 @@ +#pragma once +#include +#include + +#include "memory_permission.hpp" + +using mmio_read_callback = std::function; +using mmio_write_callback = std::function; + +class memory_manager; + +class memory_interface +{ + public: + friend memory_manager; + + virtual ~memory_interface() = default; + + virtual void read_memory(uint64_t address, void* data, size_t size) const = 0; + virtual bool try_read_memory(uint64_t address, void* data, size_t size) const = 0; + virtual void write_memory(uint64_t address, const void* data, size_t size) = 0; + + 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 unmap_memory(uint64_t address, size_t size) = 0; + + virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0; + + public: + template + T read_memory(const uint64_t address) const + { + T value{}; + this->read_memory(address, &value, sizeof(value)); + return value; + } + + template + T read_memory(const void* address) const + { + return this->read_memory(reinterpret_cast(address)); + } + + std::vector read_memory(const uint64_t address, const size_t size) const + { + std::vector data{}; + data.resize(size); + + this->read_memory(address, data.data(), data.size()); + + return data; + } + + std::vector read_memory(const void* address, const size_t size) const + { + return this->read_memory(reinterpret_cast(address), size); + } + + template + void write_memory(const uint64_t address, const T& value) + { + this->write_memory(address, &value, sizeof(value)); + } + + template + void write_memory(void* address, const T& value) + { + this->write_memory(reinterpret_cast(address), &value, sizeof(value)); + } + + void write_memory(void* address, const void* data, const size_t size) + { + this->write_memory(reinterpret_cast(address), data, size); + } +}; diff --git a/src/emulator/memory_manager.hpp b/src/emulator/memory_manager.hpp deleted file mode 100644 index 9b6969c6..00000000 --- a/src/emulator/memory_manager.hpp +++ /dev/null @@ -1,156 +0,0 @@ -#pragma once -#include -#include -#include - -#include "memory_region.hpp" -#include "address_utils.hpp" -#include "serialization.hpp" - -struct region_info : basic_memory_region -{ - uint64_t allocation_base{}; - size_t allocation_length{}; - bool is_reserved{}; - bool is_committed{}; -}; - -using mmio_read_callback = std::function; -using mmio_write_callback = std::function; - -class memory_manager -{ - public: - struct committed_region - { - size_t length{}; - memory_permission permissions{}; - }; - - using committed_region_map = std::map; - - struct reserved_region - { - size_t length{}; - committed_region_map committed_regions{}; - bool is_mmio{false}; - }; - - using reserved_region_map = std::map; - - virtual ~memory_manager() = default; - - template - T read_memory(const uint64_t address) const - { - T value{}; - this->read_memory(address, &value, sizeof(value)); - return value; - } - - template - T read_memory(const void* address) const - { - return this->read_memory(reinterpret_cast(address)); - } - - std::vector read_memory(const uint64_t address, const size_t size) const - { - std::vector data{}; - data.resize(size); - - this->read_memory(address, data.data(), data.size()); - - return data; - } - - std::vector read_memory(const void* address, const size_t size) const - { - return this->read_memory(reinterpret_cast(address), size); - } - - template - void write_memory(const uint64_t address, const T& value) - { - this->write_memory(address, &value, sizeof(value)); - } - - template - void write_memory(void* address, const T& value) - { - this->write_memory(reinterpret_cast(address), &value, sizeof(value)); - } - - void write_memory(void* address, const void* data, const size_t size) - { - this->write_memory(reinterpret_cast(address), data, size); - } - - virtual void read_memory(uint64_t address, void* data, size_t size) const = 0; - virtual bool try_read_memory(uint64_t address, void* data, size_t size) const = 0; - virtual void write_memory(uint64_t address, const void* data, size_t size) = 0; - - bool protect_memory(uint64_t address, size_t size, memory_permission permissions, - 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 commit_memory(uint64_t address, size_t size, 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(const size_t size, const memory_permission permissions, const bool reserve_only = false) - { - const auto allocation_base = this->find_free_allocation_base(size); - if (!allocate_memory(allocation_base, size, permissions, reserve_only)) - { - return 0; - } - - return allocation_base; - } - - uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const; - - region_info get_region_info(uint64_t address); - - reserved_region_map::iterator find_reserved_region(uint64_t address); - - bool overlaps_reserved_region(uint64_t address, size_t size) const; - - const reserved_region_map& get_reserved_regions() const - { - return reserved_regions_; - } - - std::uint64_t get_memory_layout_state_ver() const - { - return memory_layout_state_version_.load(std::memory_order_relaxed); - } - - private: - reserved_region_map reserved_regions_{}; - - 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 unmap_memory(uint64_t address, size_t size) = 0; - - virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0; - - protected: - std::atomic memory_layout_state_version_{0}; - - void invalidate_memory_layout_state_version() - { -#if MOMO_REFLECTION_LEVEL > 0 - memory_layout_state_version_.fetch_add(1, std::memory_order_relaxed); -#endif - } - - void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const; - void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot); -}; diff --git a/src/fuzzer/main.cpp b/src/fuzzer/main.cpp index 244b75b9..439d815d 100644 --- a/src/fuzzer/main.cpp +++ b/src/fuzzer/main.cpp @@ -83,8 +83,8 @@ namespace restore_emulator(); - const auto memory = emu.emu().allocate_memory(page_align_up(std::max(data.size(), size_t(1))), - memory_permission::read_write); + const auto memory = emu.memory().allocate_memory( + page_align_up(std::max(data.size(), static_cast(1))), memory_permission::read_write); emu.emu().write_memory(memory, data.data(), data.size()); emu.emu().reg(x64_register::rcx, memory); diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index 61114451..b9b0b21f 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -1,7 +1,11 @@ #pragma once -#include "memory_utils.hpp" + #include +#include "memory_manager.hpp" +#include "memory_utils.hpp" +#include "address_utils.hpp" + // TODO: Replace with pointer handling structure for future 32 bit support using emulator_pointer = uint64_t; @@ -39,6 +43,7 @@ class windows_emulator; struct process_context; using x64_emulator_wrapper = object_wrapper; +using memory_manager_wrapper = object_wrapper; using process_context_wrapper = object_wrapper; using windows_emulator_wrapper = object_wrapper; @@ -53,8 +58,8 @@ class emulator_object { } - emulator_object(emulator& emu, const uint64_t address = 0) - : emu_(&emu), + emulator_object(memory_interface& memory, const uint64_t address = 0) + : memory_(&memory), address_(address) { } @@ -92,13 +97,13 @@ class emulator_object T read(const size_t index = 0) const { T obj{}; - this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj)); + this->memory_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj)); return obj; } void write(const T& value, const size_t index = 0) const { - this->emu_->write_memory(this->address_ + index * this->size(), &value, sizeof(value)); + this->memory_->write_memory(this->address_ + index * this->size(), &value, sizeof(value)); } void write_if_valid(const T& value, const size_t index = 0) const @@ -113,7 +118,7 @@ class emulator_object void access(const F& accessor, const size_t index = 0) const { T obj{}; - this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj)); + this->memory_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj)); accessor(obj); @@ -136,7 +141,7 @@ class emulator_object } private: - emulator* emu_{}; + memory_interface* memory_{}; uint64_t address_{}; }; @@ -144,13 +149,13 @@ class emulator_object class emulator_allocator { public: - emulator_allocator(emulator& emu) - : emu_(&emu) + emulator_allocator(memory_interface& memory) + : memory_(&memory) { } - emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size) - : emu_(&emu), + emulator_allocator(memory_interface& memory, const uint64_t address, const uint64_t size) + : memory_(&memory), address_(address), size_(size), active_address_(address) @@ -177,7 +182,7 @@ class emulator_allocator emulator_object reserve(const size_t count = 1) { const auto potential_start = this->reserve(sizeof(T) * count, alignof(T)); - return emulator_object(*this->emu_, potential_start); + return emulator_object(*this->memory_, potential_start); } char16_t* copy_string(const std::u16string_view str) @@ -199,10 +204,10 @@ class emulator_allocator const auto string_buffer = this->reserve(max_length, required_alignment); - this->emu_->write_memory(string_buffer, str.data(), total_length); + this->memory_->write_memory(string_buffer, str.data(), total_length); constexpr std::array nullbyte{}; - this->emu_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size()); + this->memory_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size()); result.Buffer = string_buffer; result.Length = static_cast(total_length); @@ -236,9 +241,9 @@ class emulator_allocator return this->active_address_; } - emulator& get_emulator() const + memory_interface& get_memory() const { - return *this->emu_; + return *this->memory_; } void serialize(utils::buffer_serializer& buffer) const @@ -255,18 +260,18 @@ class emulator_allocator buffer.read(this->active_address_); } - void release() + void release(memory_manager& manager) { - if (this->emu_ && this->address_ && this->size_) + if (this->address_ && this->size_) { - this->emu_->release_memory(this->address_, this->size_); + manager.release_memory(this->address_, this->size_); this->address_ = 0; this->size_ = 0; } } private: - emulator* emu_{}; + memory_interface* memory_{}; uint64_t address_{}; uint64_t size_{}; uint64_t active_address_{0}; diff --git a/src/windows-emulator/kusd_mmio.cpp b/src/windows-emulator/kusd_mmio.cpp index 5494f247..256f02e4 100644 --- a/src/windows-emulator/kusd_mmio.cpp +++ b/src/windows-emulator/kusd_mmio.cpp @@ -102,8 +102,8 @@ namespace utils } } -kusd_mmio::kusd_mmio(x64_emulator& emu, process_context& process) - : emu_(&emu), +kusd_mmio::kusd_mmio(memory_manager& memory, process_context& process) + : memory_(&memory), process_(&process) { } @@ -114,7 +114,7 @@ kusd_mmio::~kusd_mmio() } kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer) - : kusd_mmio(buffer.read(), buffer.read()) + : kusd_mmio(buffer.read(), buffer.read()) { } @@ -205,7 +205,7 @@ void kusd_mmio::register_mmio() this->registered_ = true; - this->emu_->allocate_mmio( + this->memory_->allocate_mmio( KUSD_ADDRESS, KUSD_BUFFER_SIZE, [this](const uint64_t addr, const size_t size) { return this->read(addr, size); }, [](const uint64_t, const size_t, const uint64_t) { @@ -218,6 +218,6 @@ void kusd_mmio::deregister_mmio() if (this->registered_) { this->registered_ = false; - this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE); + this->memory_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE); } } diff --git a/src/windows-emulator/kusd_mmio.hpp b/src/windows-emulator/kusd_mmio.hpp index 1399799f..b9d408a3 100644 --- a/src/windows-emulator/kusd_mmio.hpp +++ b/src/windows-emulator/kusd_mmio.hpp @@ -11,7 +11,7 @@ class windows_emulator; class kusd_mmio { public: - kusd_mmio(x64_emulator& emu, process_context& process); + kusd_mmio(memory_manager& memory, process_context& process); ~kusd_mmio(); kusd_mmio(utils::buffer_deserializer& buffer); @@ -39,7 +39,7 @@ class kusd_mmio void setup(bool use_relative_time); private: - x64_emulator* emu_{}; + memory_manager* memory_{}; process_context* process_{}; bool registered_{}; diff --git a/src/emulator/memory_manager.cpp b/src/windows-emulator/memory_manager.cpp similarity index 88% rename from src/emulator/memory_manager.cpp rename to src/windows-emulator/memory_manager.cpp index 660dad75..8af1222a 100644 --- a/src/emulator/memory_manager.cpp +++ b/src/windows-emulator/memory_manager.cpp @@ -1,580 +1,636 @@ -#include "memory_manager.hpp" - -#include "memory_region.hpp" -#include "address_utils.hpp" - -#include -#include -#include -#include - -namespace -{ - constexpr auto ALLOCATION_GRANULARITY = 0x0000000000010000ULL; - constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL; - constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL; - - void split_regions(memory_manager::committed_region_map& regions, const std::vector& split_points) - { - for (auto i = regions.begin(); i != regions.end(); ++i) - { - for (const auto split_point : split_points) - { - if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point) - { - const auto first_length = split_point - i->first; - const auto second_length = i->second.length - first_length; - - i->second.length = first_length; - - regions[split_point] = memory_manager::committed_region{second_length, i->second.permissions}; - } - } - } - } - - void merge_regions(memory_manager::committed_region_map& regions) - { - for (auto i = regions.begin(); i != regions.end();) - { - assert(i->second.length > 0); - - auto next = i; - std::advance(next, 1); - - if (next == regions.end()) - { - break; - } - - assert(next->second.length > 0); - - const auto end = i->first + i->second.length; - assert(end <= next->first); - - if (end != next->first || i->second.permissions != next->second.permissions) - { - ++i; - continue; - } - - i->second.length += next->second.length; - regions.erase(next); - } - } -} - -namespace utils -{ - static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region) - { - buffer.write(region.length); - buffer.write(region.permissions); - } - - static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region) - { - region.length = static_cast(buffer.read()); - region.permissions = buffer.read(); - } - - static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region) - { - buffer.write(region.is_mmio); - buffer.write(region.length); - buffer.write_map(region.committed_regions); - } - - static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region) - { - buffer.read(region.is_mmio); - region.length = static_cast(buffer.read()); - buffer.read_map(region.committed_regions); - } -} - -void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const -{ - buffer.write_atomic(this->memory_layout_state_version_); - buffer.write_map(this->reserved_regions_); - - if (is_snapshot) - { - return; - } - - std::vector data{}; - - for (const auto& reserved_region : this->reserved_regions_) - { - if (reserved_region.second.is_mmio) - { - continue; - } - - for (const auto& region : reserved_region.second.committed_regions) - { - data.resize(region.second.length); - - this->read_memory(region.first, data.data(), region.second.length); - - buffer.write(data.data(), region.second.length); - } - } -} - -void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer, const bool is_snapshot) -{ - if (!is_snapshot) - { - assert(this->reserved_regions_.empty()); - } - - buffer.read_atomic(this->memory_layout_state_version_); - buffer.read_map(this->reserved_regions_); - - if (is_snapshot) - { - return; - } - - std::vector data{}; - - for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();) - { - auto& reserved_region = i->second; - if (reserved_region.is_mmio) - { - i = this->reserved_regions_.erase(i); - continue; - } - - ++i; - - for (const auto& region : reserved_region.committed_regions) - { - data.resize(region.second.length); - - buffer.read(data.data(), region.second.length); - - this->map_memory(region.first, region.second.length, region.second.permissions); - this->write_memory(region.first, data.data(), region.second.length); - } - } -} - -bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions, - memory_permission* old_permissions) -{ - const auto entry = this->find_reserved_region(address); - if (entry == this->reserved_regions_.end()) - { - return false; - } - - const auto end = address + size; - const auto region_end = entry->first + entry->second.length; - - if (region_end < end) - { - throw std::runtime_error("Cross region protect not supported yet!"); - } - - std::optional old_first_permissions{}; - - auto& committed_regions = entry->second.committed_regions; - split_regions(committed_regions, {address, end}); - - for (auto& sub_region : committed_regions) - { - if (sub_region.first >= end) - { - break; - } - - const auto sub_region_end = sub_region.first + sub_region.second.length; - if (sub_region.first >= address && sub_region_end <= end) - { - if (!old_first_permissions.has_value()) - { - old_first_permissions = sub_region.second.permissions; - } - - this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions); - sub_region.second.permissions = permissions; - } - } - - if (old_permissions) - { - *old_permissions = old_first_permissions.value_or(memory_permission::none); - } - - merge_regions(committed_regions); - - invalidate_memory_layout_state_version(); - - return true; -} - -bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, - mmio_write_callback write_cb) -{ - if (this->overlaps_reserved_region(address, size)) - { - return false; - } - - this->map_mmio(address, size, std::move(read_cb), std::move(write_cb)); - - const auto entry = this->reserved_regions_ - .try_emplace(address, - reserved_region{ - .length = size, - .is_mmio = true, - }) - .first; - - entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write}; - - invalidate_memory_layout_state_version(); - - return true; -} - -bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions, - const bool reserve_only) -{ - if (this->overlaps_reserved_region(address, size)) - { - return false; - } - - const auto entry = this->reserved_regions_ - .try_emplace(address, - reserved_region{ - .length = size, - }) - .first; - - if (!reserve_only) - { - this->map_memory(address, size, permissions); - entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write}; - } - - invalidate_memory_layout_state_version(); - - return true; -} - -bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions) -{ - const auto entry = this->find_reserved_region(address); - if (entry == this->reserved_regions_.end()) - { - return false; - } - - const auto end = address + size; - const auto region_end = entry->first + entry->second.length; - - if (region_end < end) - { - throw std::runtime_error("Cross region commit not supported yet!"); - } - - auto& committed_regions = entry->second.committed_regions; - split_regions(committed_regions, {address, end}); - - uint64_t last_region_start{}; - const committed_region* last_region{nullptr}; - - for (auto& sub_region : committed_regions) - { - if (sub_region.first >= end) - { - break; - } - - const auto sub_region_end = sub_region.first + sub_region.second.length; - if (sub_region.first >= address && sub_region_end <= end) - { - const auto map_start = last_region ? (last_region_start + last_region->length) : address; - const auto map_length = sub_region.first - map_start; - - if (map_length > 0) - { - this->map_memory(map_start, map_length, permissions); - committed_regions[map_start] = committed_region{map_length, permissions}; - } - - last_region_start = sub_region.first; - last_region = &sub_region.second; - } - } - - if (!last_region || (last_region_start + last_region->length) < end) - { - const auto map_start = last_region ? (last_region_start + last_region->length) : address; - const auto map_length = end - map_start; - - this->map_memory(map_start, map_length, permissions); - committed_regions[map_start] = committed_region{map_length, permissions}; - } - - merge_regions(committed_regions); - - invalidate_memory_layout_state_version(); - - return true; -} - -bool memory_manager::decommit_memory(const uint64_t address, const size_t size) -{ - const auto entry = this->find_reserved_region(address); - if (entry == this->reserved_regions_.end()) - { - return false; - } - - if (entry->second.is_mmio) - { - throw std::runtime_error("Not allowed to decommit MMIO!"); - } - - const auto end = address + size; - const auto region_end = entry->first + entry->second.length; - - if (region_end < end) - { - throw std::runtime_error("Cross region decommit not supported yet!"); - } - - auto& committed_regions = entry->second.committed_regions; - - split_regions(committed_regions, {address, end}); - - for (auto i = committed_regions.begin(); i != committed_regions.end();) - { - if (i->first >= end) - { - break; - } - - const auto sub_region_end = i->first + i->second.length; - if (i->first >= address && sub_region_end <= end) - { - this->unmap_memory(i->first, i->second.length); - i = committed_regions.erase(i); - continue; - } - - ++i; - } - - invalidate_memory_layout_state_version(); - - return true; -} - -bool memory_manager::release_memory(const uint64_t address, size_t size) -{ - const auto entry = this->reserved_regions_.find(address); - if (entry == this->reserved_regions_.end()) - { - return false; - } - - if (!size) - { - size = entry->second.length; - } - - if (size > entry->second.length) - { - throw std::runtime_error("Cross region release not supported yet!"); - } - - const auto end = address + size; - auto& committed_regions = entry->second.committed_regions; - - split_regions(committed_regions, {end}); - - for (auto i = committed_regions.begin(); i != committed_regions.end();) - { - if (i->first >= end) - { - break; - } - - const auto sub_region_end = i->first + i->second.length; - if (i->first >= address && sub_region_end <= end) - { - this->unmap_memory(i->first, i->second.length); - i = committed_regions.erase(i); - } - else - { - ++i; - } - } - - entry->second.length -= size; - if (entry->second.length > 0) - { - this->reserved_regions_[address + size] = std::move(entry->second); - } - - this->reserved_regions_.erase(entry); - invalidate_memory_layout_state_version(); - return true; -} - -void memory_manager::unmap_all_memory() -{ - for (const auto& reserved_region : this->reserved_regions_) - { - for (const auto& region : reserved_region.second.committed_regions) - { - this->unmap_memory(region.first, region.second.length); - } - } - - this->reserved_regions_.clear(); -} - -uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const -{ - uint64_t start_address = std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL); - start_address = align_up(start_address, ALLOCATION_GRANULARITY); - - for (const auto& region : this->reserved_regions_) - { - const auto region_end = region.first + region.second.length; - if (region_end < start_address) - { - continue; - } - - if (!regions_with_length_intersect(start_address, size, region.first, region.second.length)) - { - return start_address; - } - - start_address = align_up(region_end, ALLOCATION_GRANULARITY); - } - - if (start_address + size <= MAX_ALLOCATION_ADDRESS) - { - return start_address; - } - - return 0; -} - -region_info memory_manager::get_region_info(const uint64_t address) -{ - region_info result{}; - result.start = MIN_ALLOCATION_ADDRESS; - result.length = MAX_ALLOCATION_ADDRESS - result.start; - result.permissions = memory_permission::none; - result.allocation_base = {}; - result.allocation_length = result.length; - result.is_committed = false; - result.is_reserved = false; - - if (this->reserved_regions_.empty()) - { - return result; - } - - auto upper_bound = this->reserved_regions_.upper_bound(address); - if (upper_bound == this->reserved_regions_.begin()) - { - result.length = upper_bound->first - result.start; - return result; - } - - const auto entry = --upper_bound; - const auto lower_end = entry->first + entry->second.length; - if (lower_end <= address) - { - result.start = lower_end; - result.length = MAX_ALLOCATION_ADDRESS - result.start; - return result; - } - - // We have a reserved region - const auto& reserved_region = entry->second; - const auto& committed_regions = reserved_region.committed_regions; - - result.is_reserved = true; - result.allocation_base = entry->first; - result.allocation_length = reserved_region.length; - result.start = result.allocation_base; - result.length = result.allocation_length; - - if (committed_regions.empty()) - { - return result; - } - - auto committed_bound = committed_regions.upper_bound(address); - if (committed_bound == committed_regions.begin()) - { - result.length = committed_bound->first - result.start; - return result; - } - - const auto committed_entry = --committed_bound; - const auto committed_lower_end = committed_entry->first + committed_entry->second.length; - if (committed_lower_end <= address) - { - result.start = committed_lower_end; - result.length = lower_end - result.start; - return result; - } - - result.is_committed = true; - result.start = committed_entry->first; - result.length = committed_entry->second.length; - result.permissions = committed_entry->second.permissions; - - return result; -} - -memory_manager::reserved_region_map::iterator memory_manager::find_reserved_region(const uint64_t address) -{ - if (this->reserved_regions_.empty()) - { - return this->reserved_regions_.end(); - } - - auto upper_bound = this->reserved_regions_.upper_bound(address); - if (upper_bound == this->reserved_regions_.begin()) - { - return this->reserved_regions_.end(); - } - - const auto entry = --upper_bound; - if (entry->first + entry->second.length <= address) - { - return this->reserved_regions_.end(); - } - - return entry; -} - -bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const -{ - for (const auto& region : this->reserved_regions_) - { - if (regions_with_length_intersect(address, size, region.first, region.second.length)) - { - return true; - } - } - - return false; -} +#include "memory_manager.hpp" + +#include "memory_region.hpp" +#include "address_utils.hpp" + +#include +#include +#include +#include + +namespace +{ + constexpr auto ALLOCATION_GRANULARITY = 0x0000000000010000ULL; + constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL; + constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL; + + void split_regions(memory_manager::committed_region_map& regions, const std::vector& split_points) + { + for (auto i = regions.begin(); i != regions.end(); ++i) + { + for (const auto split_point : split_points) + { + if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point) + { + const auto first_length = split_point - i->first; + const auto second_length = i->second.length - first_length; + + i->second.length = first_length; + + regions[split_point] = memory_manager::committed_region{second_length, i->second.permissions}; + } + } + } + } + + void merge_regions(memory_manager::committed_region_map& regions) + { + for (auto i = regions.begin(); i != regions.end();) + { + assert(i->second.length > 0); + + auto next = i; + std::advance(next, 1); + + if (next == regions.end()) + { + break; + } + + assert(next->second.length > 0); + + const auto end = i->first + i->second.length; + assert(end <= next->first); + + if (end != next->first || i->second.permissions != next->second.permissions) + { + ++i; + continue; + } + + i->second.length += next->second.length; + regions.erase(next); + } + } +} + +namespace utils +{ + static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region) + { + buffer.write(region.length); + buffer.write(region.permissions); + } + + static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region) + { + region.length = static_cast(buffer.read()); + region.permissions = buffer.read(); + } + + static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region) + { + buffer.write(region.is_mmio); + buffer.write(region.length); + buffer.write_map(region.committed_regions); + } + + static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region) + { + buffer.read(region.is_mmio); + region.length = static_cast(buffer.read()); + buffer.read_map(region.committed_regions); + } +} + +void memory_manager::update_layout_version() +{ +#if MOMO_REFLECTION_LEVEL > 0 + this->layout_version_.fetch_add(1, std::memory_order_relaxed); +#endif +} + +void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const +{ + buffer.write_atomic(this->layout_version_); + buffer.write_map(this->reserved_regions_); + + if (is_snapshot) + { + return; + } + + std::vector data{}; + + for (const auto& reserved_region : this->reserved_regions_) + { + if (reserved_region.second.is_mmio) + { + continue; + } + + for (const auto& region : reserved_region.second.committed_regions) + { + data.resize(region.second.length); + + this->read_memory(region.first, data.data(), region.second.length); + + buffer.write(data.data(), region.second.length); + } + } +} + +void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer, const bool is_snapshot) +{ + if (!is_snapshot) + { + assert(this->reserved_regions_.empty()); + } + + buffer.read_atomic(this->layout_version_); + buffer.read_map(this->reserved_regions_); + + if (is_snapshot) + { + return; + } + + std::vector data{}; + + for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();) + { + auto& reserved_region = i->second; + if (reserved_region.is_mmio) + { + i = this->reserved_regions_.erase(i); + continue; + } + + ++i; + + for (const auto& region : reserved_region.committed_regions) + { + data.resize(region.second.length); + + buffer.read(data.data(), region.second.length); + + this->map_memory(region.first, region.second.length, region.second.permissions); + this->write_memory(region.first, data.data(), region.second.length); + } + } +} + +bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions, + memory_permission* old_permissions) +{ + const auto entry = this->find_reserved_region(address); + if (entry == this->reserved_regions_.end()) + { + return false; + } + + const auto end = address + size; + const auto region_end = entry->first + entry->second.length; + + if (region_end < end) + { + throw std::runtime_error("Cross region protect not supported yet!"); + } + + std::optional old_first_permissions{}; + + auto& committed_regions = entry->second.committed_regions; + split_regions(committed_regions, {address, end}); + + for (auto& sub_region : committed_regions) + { + if (sub_region.first >= end) + { + break; + } + + const auto sub_region_end = sub_region.first + sub_region.second.length; + if (sub_region.first >= address && sub_region_end <= end) + { + if (!old_first_permissions.has_value()) + { + old_first_permissions = sub_region.second.permissions; + } + + this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions); + sub_region.second.permissions = permissions; + } + } + + if (old_permissions) + { + *old_permissions = old_first_permissions.value_or(memory_permission::none); + } + + merge_regions(committed_regions); + + this->update_layout_version(); + + return true; +} + +bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, + mmio_write_callback write_cb) +{ + if (this->overlaps_reserved_region(address, size)) + { + return false; + } + + this->map_mmio(address, size, std::move(read_cb), std::move(write_cb)); + + const auto entry = this->reserved_regions_ + .try_emplace(address, + reserved_region{ + .length = size, + .is_mmio = true, + }) + .first; + + entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write}; + + this->update_layout_version(); + + return true; +} + +bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions, + const bool reserve_only) +{ + if (this->overlaps_reserved_region(address, size)) + { + return false; + } + + const auto entry = this->reserved_regions_ + .try_emplace(address, + reserved_region{ + .length = size, + }) + .first; + + if (!reserve_only) + { + this->map_memory(address, size, permissions); + entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write}; + } + + this->update_layout_version(); + + return true; +} + +bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions) +{ + const auto entry = this->find_reserved_region(address); + if (entry == this->reserved_regions_.end()) + { + return false; + } + + const auto end = address + size; + const auto region_end = entry->first + entry->second.length; + + if (region_end < end) + { + throw std::runtime_error("Cross region commit not supported yet!"); + } + + auto& committed_regions = entry->second.committed_regions; + split_regions(committed_regions, {address, end}); + + uint64_t last_region_start{}; + const committed_region* last_region{nullptr}; + + for (auto& sub_region : committed_regions) + { + if (sub_region.first >= end) + { + break; + } + + const auto sub_region_end = sub_region.first + sub_region.second.length; + if (sub_region.first >= address && sub_region_end <= end) + { + const auto map_start = last_region ? (last_region_start + last_region->length) : address; + const auto map_length = sub_region.first - map_start; + + if (map_length > 0) + { + this->map_memory(map_start, map_length, permissions); + committed_regions[map_start] = committed_region{map_length, permissions}; + } + + last_region_start = sub_region.first; + last_region = &sub_region.second; + } + } + + if (!last_region || (last_region_start + last_region->length) < end) + { + const auto map_start = last_region ? (last_region_start + last_region->length) : address; + const auto map_length = end - map_start; + + this->map_memory(map_start, map_length, permissions); + committed_regions[map_start] = committed_region{map_length, permissions}; + } + + merge_regions(committed_regions); + + this->update_layout_version(); + + return true; +} + +bool memory_manager::decommit_memory(const uint64_t address, const size_t size) +{ + const auto entry = this->find_reserved_region(address); + if (entry == this->reserved_regions_.end()) + { + return false; + } + + if (entry->second.is_mmio) + { + throw std::runtime_error("Not allowed to decommit MMIO!"); + } + + const auto end = address + size; + const auto region_end = entry->first + entry->second.length; + + if (region_end < end) + { + throw std::runtime_error("Cross region decommit not supported yet!"); + } + + auto& committed_regions = entry->second.committed_regions; + + split_regions(committed_regions, {address, end}); + + for (auto i = committed_regions.begin(); i != committed_regions.end();) + { + if (i->first >= end) + { + break; + } + + const auto sub_region_end = i->first + i->second.length; + if (i->first >= address && sub_region_end <= end) + { + this->unmap_memory(i->first, i->second.length); + i = committed_regions.erase(i); + continue; + } + + ++i; + } + + this->update_layout_version(); + + return true; +} + +bool memory_manager::release_memory(const uint64_t address, size_t size) +{ + const auto entry = this->reserved_regions_.find(address); + if (entry == this->reserved_regions_.end()) + { + return false; + } + + if (!size) + { + size = entry->second.length; + } + + if (size > entry->second.length) + { + throw std::runtime_error("Cross region release not supported yet!"); + } + + const auto end = address + size; + auto& committed_regions = entry->second.committed_regions; + + split_regions(committed_regions, {end}); + + for (auto i = committed_regions.begin(); i != committed_regions.end();) + { + if (i->first >= end) + { + break; + } + + const auto sub_region_end = i->first + i->second.length; + if (i->first >= address && sub_region_end <= end) + { + this->unmap_memory(i->first, i->second.length); + i = committed_regions.erase(i); + } + else + { + ++i; + } + } + + entry->second.length -= size; + if (entry->second.length > 0) + { + this->reserved_regions_[address + size] = std::move(entry->second); + } + + this->reserved_regions_.erase(entry); + this->update_layout_version(); + return true; +} + +void memory_manager::unmap_all_memory() +{ + for (const auto& reserved_region : this->reserved_regions_) + { + for (const auto& region : reserved_region.second.committed_regions) + { + this->unmap_memory(region.first, region.second.length); + } + } + + this->reserved_regions_.clear(); +} + +uint64_t memory_manager::allocate_memory(const size_t size, const memory_permission permissions, + const bool reserve_only) +{ + const auto allocation_base = this->find_free_allocation_base(size); + if (!allocate_memory(allocation_base, size, permissions, reserve_only)) + { + return 0; + } + + return allocation_base; +} + +uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const +{ + uint64_t start_address = std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL); + start_address = align_up(start_address, ALLOCATION_GRANULARITY); + + for (const auto& region : this->reserved_regions_) + { + const auto region_end = region.first + region.second.length; + if (region_end < start_address) + { + continue; + } + + if (!regions_with_length_intersect(start_address, size, region.first, region.second.length)) + { + return start_address; + } + + start_address = align_up(region_end, ALLOCATION_GRANULARITY); + } + + if (start_address + size <= MAX_ALLOCATION_ADDRESS) + { + return start_address; + } + + return 0; +} + +region_info memory_manager::get_region_info(const uint64_t address) +{ + region_info result{}; + result.start = MIN_ALLOCATION_ADDRESS; + result.length = MAX_ALLOCATION_ADDRESS - result.start; + result.permissions = memory_permission::none; + result.allocation_base = {}; + result.allocation_length = result.length; + result.is_committed = false; + result.is_reserved = false; + + if (this->reserved_regions_.empty()) + { + return result; + } + + auto upper_bound = this->reserved_regions_.upper_bound(address); + if (upper_bound == this->reserved_regions_.begin()) + { + result.length = upper_bound->first - result.start; + return result; + } + + const auto entry = --upper_bound; + const auto lower_end = entry->first + entry->second.length; + if (lower_end <= address) + { + result.start = lower_end; + result.length = MAX_ALLOCATION_ADDRESS - result.start; + return result; + } + + // We have a reserved region + const auto& reserved_region = entry->second; + const auto& committed_regions = reserved_region.committed_regions; + + result.is_reserved = true; + result.allocation_base = entry->first; + result.allocation_length = reserved_region.length; + result.start = result.allocation_base; + result.length = result.allocation_length; + + if (committed_regions.empty()) + { + return result; + } + + auto committed_bound = committed_regions.upper_bound(address); + if (committed_bound == committed_regions.begin()) + { + result.length = committed_bound->first - result.start; + return result; + } + + const auto committed_entry = --committed_bound; + const auto committed_lower_end = committed_entry->first + committed_entry->second.length; + if (committed_lower_end <= address) + { + result.start = committed_lower_end; + result.length = lower_end - result.start; + return result; + } + + result.is_committed = true; + result.start = committed_entry->first; + result.length = committed_entry->second.length; + result.permissions = committed_entry->second.permissions; + + return result; +} + +memory_manager::reserved_region_map::iterator memory_manager::find_reserved_region(const uint64_t address) +{ + if (this->reserved_regions_.empty()) + { + return this->reserved_regions_.end(); + } + + auto upper_bound = this->reserved_regions_.upper_bound(address); + if (upper_bound == this->reserved_regions_.begin()) + { + return this->reserved_regions_.end(); + } + + const auto entry = --upper_bound; + if (entry->first + entry->second.length <= address) + { + return this->reserved_regions_.end(); + } + + return entry; +} + +bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const +{ + for (const auto& region : this->reserved_regions_) + { + if (regions_with_length_intersect(address, size, region.first, region.second.length)) + { + return true; + } + } + + return false; +} + +void memory_manager::read_memory(const uint64_t address, void* data, const size_t size) const +{ + this->memory_->read_memory(address, data, size); +} + +bool memory_manager::try_read_memory(const uint64_t address, void* data, const size_t size) const +{ + return this->memory_->try_read_memory(address, data, size); +} + +void memory_manager::write_memory(const uint64_t address, const void* data, const size_t size) +{ + this->memory_->write_memory(address, data, size); +} + +void memory_manager::map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, + mmio_write_callback write_cb) +{ + 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) +{ + this->memory_->map_memory(address, size, permissions); +} + +void memory_manager::unmap_memory(const uint64_t address, const size_t size) +{ + this->memory_->unmap_memory(address, size); +} + +void memory_manager::apply_memory_protection(const uint64_t address, const size_t size, + 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 new file mode 100644 index 00000000..1ab6aa23 --- /dev/null +++ b/src/windows-emulator/memory_manager.hpp @@ -0,0 +1,98 @@ +#pragma once +#include +#include +#include + +#include "memory_region.hpp" +#include "serialization.hpp" + +#include + +struct region_info : basic_memory_region +{ + uint64_t allocation_base{}; + size_t allocation_length{}; + bool is_reserved{}; + bool is_committed{}; +}; + +using mmio_read_callback = std::function; +using mmio_write_callback = std::function; + +class memory_manager : public memory_interface +{ + public: + memory_manager(memory_interface& memory) + : memory_(&memory) + { + } + + struct committed_region + { + size_t length{}; + memory_permission permissions{}; + }; + + using committed_region_map = std::map; + + struct reserved_region + { + size_t length{}; + committed_region_map committed_regions{}; + bool is_mmio{false}; + }; + + using reserved_region_map = std::map; + + void read_memory(uint64_t address, void* data, size_t size) const final; + 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 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 commit_memory(uint64_t address, size_t size, 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 find_free_allocation_base(size_t size, uint64_t start = 0) const; + + region_info get_region_info(uint64_t address); + + reserved_region_map::iterator find_reserved_region(uint64_t address); + + bool overlaps_reserved_region(uint64_t address, size_t size) const; + + const reserved_region_map& get_reserved_regions() const + { + return this->reserved_regions_; + } + + std::uint64_t get_layout_version() const + { + return this->layout_version_.load(std::memory_order_relaxed); + } + + void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const; + void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot); + + private: + memory_interface* memory_{}; + reserved_region_map reserved_regions_{}; + 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 unmap_memory(uint64_t address, size_t size) 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/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index dd36834d..41f285a1 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -54,8 +54,8 @@ namespace utils } } -module_manager::module_manager(emulator& emu, file_system& file_sys) - : emu_(&emu), +module_manager::module_manager(memory_manager& memory, file_system& file_sys) + : memory_(&memory), file_sys_(&file_sys) { } @@ -80,7 +80,7 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil try { - auto mod = map_module_from_file(*this->emu_, std::move(local_file)); + auto mod = map_module_from_file(*this->memory_, std::move(local_file)); mod.is_static = is_static; logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base); @@ -126,7 +126,7 @@ bool module_manager::unmap(const uint64_t address, const logger& logger) logger.log("Unmapping %s (0x%" PRIx64 ")\n", mod->second.path.generic_string().c_str(), mod->second.image_base); - unmap_module(*this->emu_, mod->second); + unmap_module(*this->memory_, mod->second); this->modules_.erase(mod); return true; diff --git a/src/windows-emulator/module/module_manager.hpp b/src/windows-emulator/module/module_manager.hpp index 918b18a9..a44d52da 100644 --- a/src/windows-emulator/module/module_manager.hpp +++ b/src/windows-emulator/module/module_manager.hpp @@ -10,7 +10,7 @@ class module_manager { public: using module_map = std::map; - module_manager(emulator& emu, file_system& file_sys); + module_manager(memory_manager& memory, file_system& file_sys); 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); @@ -47,7 +47,7 @@ class module_manager } private: - emulator* emu_{}; + memory_manager* memory_{}; file_system* file_sys_{}; module_map modules_{}; diff --git a/src/windows-emulator/module/module_mapping.cpp b/src/windows-emulator/module/module_mapping.cpp index 5e6af650..a85f5ea0 100644 --- a/src/windows-emulator/module/module_mapping.cpp +++ b/src/windows-emulator/module/module_mapping.cpp @@ -20,13 +20,13 @@ namespace return nt_headers_offset + (first_section_absolute - absolute_base); } - std::vector read_mapped_memory(const emulator& emu, const mapped_module& binary) + std::vector read_mapped_memory(const memory_manager& memory, const mapped_module& binary) { - std::vector memory{}; - memory.resize(binary.size_of_image); - emu.read_memory(binary.image_base, memory.data(), memory.size()); + std::vector mem{}; + mem.resize(binary.size_of_image); + memory.read_memory(binary.image_base, mem.data(), mem.size()); - return memory; + return mem; } void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor buffer, @@ -141,7 +141,8 @@ namespace } } - void map_sections(emulator& emu, mapped_module& binary, const utils::safe_buffer_accessor buffer, + void map_sections(memory_manager& memory, mapped_module& binary, + const utils::safe_buffer_accessor buffer, const PENTHeaders_t& nt_headers, const uint64_t nt_headers_offset) { const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset); @@ -156,7 +157,7 @@ namespace { const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize); const auto* source_ptr = buffer.get_pointer_for_range(section.PointerToRawData, size_of_data); - emu.write_memory(target_ptr, source_ptr, size_of_data); + memory.write_memory(target_ptr, source_ptr, size_of_data); } auto permissions = memory_permission::none; @@ -178,7 +179,7 @@ namespace const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize)); - emu.protect_memory(target_ptr, size_of_section, permissions, nullptr); + memory.protect_memory(target_ptr, size_of_section, permissions, nullptr); mapped_section section_info{}; section_info.region.start = target_ptr; @@ -195,7 +196,8 @@ namespace } } -mapped_module map_module_from_data(emulator& emu, const std::span data, std::filesystem::path file) +mapped_module map_module_from_data(memory_manager& memory, const std::span data, + std::filesystem::path file) { mapped_module binary{}; binary.path = std::move(file); @@ -217,14 +219,15 @@ mapped_module map_module_from_data(emulator& emu, const std::span binary.image_base = optional_header.ImageBase; binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize - if (!emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read)) + if (!memory.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read)) { - binary.image_base = emu.find_free_allocation_base(binary.size_of_image); + binary.image_base = memory.find_free_allocation_base(binary.size_of_image); const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL; const auto has_dynamic_base = optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; const auto is_relocatable = is_dll || has_dynamic_base; - if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read)) + if (!is_relocatable || + !memory.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read)) { throw std::runtime_error("Memory range not allocatable"); } @@ -233,22 +236,22 @@ mapped_module map_module_from_data(emulator& emu, const std::span binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint; const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders); - emu.write_memory(binary.image_base, header_buffer, optional_header.SizeOfHeaders); + memory.write_memory(binary.image_base, header_buffer, optional_header.SizeOfHeaders); - map_sections(emu, binary, buffer, nt_headers, nt_headers_offset); + map_sections(memory, binary, buffer, nt_headers, nt_headers_offset); - auto mapped_memory = read_mapped_memory(emu, binary); + auto mapped_memory = read_mapped_memory(memory, binary); utils::safe_buffer_accessor mapped_buffer{mapped_memory}; apply_relocations(binary, mapped_buffer, optional_header); collect_exports(binary, mapped_buffer, optional_header); - emu.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size()); + memory.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size()); return binary; } -mapped_module map_module_from_file(emulator& emu, std::filesystem::path file) +mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path file) { const auto data = utils::io::read_file(file); if (data.empty()) @@ -256,10 +259,10 @@ mapped_module map_module_from_file(emulator& emu, std::filesystem::path file) throw std::runtime_error("Bad file data"); } - return map_module_from_data(emu, data, std::move(file)); + return map_module_from_data(memory, data, std::move(file)); } -bool unmap_module(emulator& emu, const mapped_module& mod) +bool unmap_module(memory_manager& memory, const mapped_module& mod) { - return emu.release_memory(mod.image_base, mod.size_of_image); + return memory.release_memory(mod.image_base, mod.size_of_image); } diff --git a/src/windows-emulator/module/module_mapping.hpp b/src/windows-emulator/module/module_mapping.hpp index de93d6c7..60f0a553 100644 --- a/src/windows-emulator/module/module_mapping.hpp +++ b/src/windows-emulator/module/module_mapping.hpp @@ -1,9 +1,9 @@ #pragma once -#include #include "mapped_module.hpp" +#include "../memory_manager.hpp" -mapped_module map_module_from_data(emulator& emu, std::span data, std::filesystem::path file); -mapped_module map_module_from_file(emulator& emu, std::filesystem::path file); +mapped_module map_module_from_data(memory_manager& memory, std::span data, std::filesystem::path file); +mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path file); -bool unmap_module(emulator& emu, const mapped_module& mod); +bool unmap_module(memory_manager& memory, const mapped_module& mod); diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index be6b6688..9144ef26 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -318,17 +318,17 @@ class moved_marker class emulator_thread : public ref_counted_object { public: - emulator_thread(x64_emulator& emu) - : emu_ptr(&emu) + emulator_thread(memory_manager& memory) + : memory_ptr(&memory) { } emulator_thread(utils::buffer_deserializer& buffer) - : emulator_thread(buffer.read().get()) + : emulator_thread(buffer.read().get()) { } - emulator_thread(x64_emulator& emu, const process_context& context, uint64_t start_address, uint64_t argument, + emulator_thread(memory_manager& memory, const process_context& context, uint64_t start_address, uint64_t argument, uint64_t stack_size, uint32_t id); emulator_thread(const emulator_thread&) = delete; @@ -337,14 +337,14 @@ class emulator_thread : public ref_counted_object emulator_thread(emulator_thread&& obj) noexcept = default; emulator_thread& operator=(emulator_thread&& obj) noexcept = default; - ~emulator_thread() + ~emulator_thread() override { this->release(); } moved_marker marker{}; - x64_emulator* emu_ptr{}; + memory_manager* memory_ptr{}; uint64_t stack_base{}; uint64_t stack_size{}; @@ -407,7 +407,7 @@ class emulator_thread : public ref_counted_object } } - void serialize_object(utils::buffer_serializer& buffer) const + void serialize_object(utils::buffer_serializer& buffer) const override { if (this->marker.was_moved()) { @@ -438,7 +438,7 @@ class emulator_thread : public ref_counted_object buffer.write_vector(this->last_registers); } - void deserialize_object(utils::buffer_deserializer& buffer) + void deserialize_object(utils::buffer_deserializer& buffer) override { if (this->marker.was_moved()) { @@ -465,8 +465,8 @@ class emulator_thread : public ref_counted_object buffer.read_optional(this->await_time); buffer.read_optional(this->pending_status); - buffer.read_optional(this->gs_segment, [this] { return emulator_allocator(*this->emu_ptr); }); - buffer.read_optional(this->teb, [this] { return emulator_object(*this->emu_ptr); }); + buffer.read_optional(this->gs_segment, [this] { return emulator_allocator(*this->memory_ptr); }); + buffer.read_optional(this->teb, [this] { return emulator_object(*this->memory_ptr); }); buffer.read_vector(this->last_registers); } @@ -488,18 +488,18 @@ class emulator_thread : public ref_counted_object if (this->stack_base) { - if (!this->emu_ptr) + if (!this->memory_ptr) { throw std::runtime_error("Emulator was never assigned!"); } - this->emu_ptr->release_memory(this->stack_base, this->stack_size); + this->memory_ptr->release_memory(this->stack_base, this->stack_size); this->stack_base = 0; } if (this->gs_segment) { - this->gs_segment->release(); + this->gs_segment->release(*this->memory_ptr); this->gs_segment = {}; } } @@ -507,12 +507,12 @@ class emulator_thread : public ref_counted_object struct process_context { - process_context(x64_emulator& emu, file_system& file_sys) + process_context(x64_emulator& emu, memory_manager& memory, file_system& file_sys) : base_allocator(emu), peb(emu), process_params(emu), - kusd(emu, *this), - mod_manager(emu, file_sys) + kusd(memory, *this), + mod_manager(memory, file_sys) { } @@ -645,10 +645,10 @@ struct process_context this->active_thread = this->threads.get(buffer.read()); } - handle create_thread(x64_emulator& emu, const uint64_t start_address, const uint64_t argument, + handle create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument, const uint64_t stack_size) { - emulator_thread t{emu, *this, start_address, argument, stack_size, ++this->spawned_thread_count}; + emulator_thread t{memory, *this, start_address, argument, stack_size, ++this->spawned_thread_count}; return this->threads.store(std::move(t)); } }; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 0862713b..9c9a4552 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -704,8 +704,8 @@ namespace { constexpr auto shared_section_size = 0x10000; - const auto address = c.emu.find_free_allocation_base(shared_section_size); - c.emu.allocate_memory(address, shared_section_size, memory_permission::read_write); + const auto address = c.win_emu.memory().find_free_allocation_base(shared_section_size); + c.win_emu.memory().allocate_memory(address, shared_section_size, memory_permission::read_write); const std::u16string_view windows_dir = c.proc.kusd.get().NtSystemRoot.arr; const auto windows_dir_size = windows_dir.size() * 2; @@ -789,7 +789,7 @@ namespace } const auto protection = map_nt_to_emulator_protection(section_entry->section_page_protection); - const auto address = c.emu.allocate_memory(size, protection); + const auto address = c.win_emu.memory().allocate_memory(size, protection); if (!file_data.empty()) { @@ -850,7 +850,7 @@ namespace const emulator_object info{c.emu, memory_information}; info.access([&](EMU_MEMORY_BASIC_INFORMATION64& image_info) { - const auto region_info = c.emu.get_region_info(base_address); + const auto region_info = c.win_emu.memory().get_region_info(base_address); assert(!region_info.is_committed || region_info.is_reserved); @@ -910,7 +910,7 @@ namespace return STATUS_BUFFER_OVERFLOW; } - const auto region_info = c.emu.get_region_info(base_address); + const auto region_info = c.win_emu.memory().get_region_info(base_address); if (!region_info.is_reserved) { return STATUS_INVALID_ADDRESS; @@ -2015,7 +2015,8 @@ namespace try { - c.emu.protect_memory(aligned_start, aligned_length, requested_protection, &old_protection_value); + c.win_emu.memory().protect_memory(aligned_start, aligned_length, requested_protection, + &old_protection_value); } catch (...) { @@ -2117,7 +2118,7 @@ namespace auto potential_base = base_address.read(); if (!potential_base) { - potential_base = c.emu.find_free_allocation_base(allocation_bytes); + potential_base = c.win_emu.memory().find_free_allocation_base(allocation_bytes); } if (!potential_base) @@ -2137,7 +2138,7 @@ namespace throw std::runtime_error("Unsupported allocation type!"); } - if (commit && !reserve && c.emu.commit_memory(potential_base, allocation_bytes, protection)) + if (commit && !reserve && c.win_emu.memory().commit_memory(potential_base, allocation_bytes, protection)) { c.win_emu.log.print(color::dark_gray, "--> Committed 0x%" PRIx64 " - 0x%" PRIx64 "\n", potential_base, potential_base + allocation_bytes); @@ -2148,7 +2149,7 @@ namespace c.win_emu.log.print(color::dark_gray, "--> Allocated 0x%" PRIx64 " - 0x%" PRIx64 "\n", potential_base, potential_base + allocation_bytes); - return c.emu.allocate_memory(potential_base, allocation_bytes, protection, !commit) + return c.win_emu.memory().allocate_memory(potential_base, allocation_bytes, protection, !commit) ? STATUS_SUCCESS : STATUS_MEMORY_NOT_ALLOCATED; } @@ -2176,14 +2177,14 @@ namespace if (free_type & MEM_RELEASE) { - return c.emu.release_memory(allocation_base, allocation_size) ? STATUS_SUCCESS - : STATUS_MEMORY_NOT_ALLOCATED; + return c.win_emu.memory().release_memory(allocation_base, allocation_size) ? STATUS_SUCCESS + : STATUS_MEMORY_NOT_ALLOCATED; } if (free_type & MEM_DECOMMIT) { - return c.emu.decommit_memory(allocation_base, allocation_size) ? STATUS_SUCCESS - : STATUS_MEMORY_NOT_ALLOCATED; + return c.win_emu.memory().decommit_memory(allocation_base, allocation_size) ? STATUS_SUCCESS + : STATUS_MEMORY_NOT_ALLOCATED; } throw std::runtime_error("Bad free type"); @@ -2260,7 +2261,7 @@ namespace } client_shared_memory.access([&](PORT_VIEW64& view) { - p.view_base = c.emu.allocate_memory(view.ViewSize, memory_permission::read_write); + p.view_base = c.win_emu.memory().allocate_memory(view.ViewSize, memory_permission::read_write); view.ViewBase = p.view_base; view.ViewRemoteBase = view.ViewBase; }); @@ -2752,7 +2753,7 @@ namespace } const auto size = page_align_up(locale_file.size()); - const auto base = c.emu.allocate_memory(size, memory_permission::read); + const auto base = c.win_emu.memory().allocate_memory(size, memory_permission::read); c.emu.write_memory(base, locale_file.data(), locale_file.size()); base_address.write(base); @@ -3445,7 +3446,7 @@ namespace return STATUS_NOT_SUPPORTED; } - const auto h = c.proc.create_thread(c.emu, start_routine, argument, stack_size); + const auto h = c.proc.create_thread(c.win_emu.memory(), start_routine, argument, stack_size); thread_handle.write(h); if (!attribute_list) diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 277e6f0a..4652d847 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -190,19 +190,19 @@ namespace reinterpret_cast(*obtain_api_set(location, root).data())); } - emulator_allocator create_allocator(emulator& emu, const size_t size) + emulator_allocator create_allocator(memory_manager& memory, const size_t size) { - const auto base = emu.find_free_allocation_base(size); - emu.allocate_memory(base, size, memory_permission::read_write); + const auto base = memory.find_free_allocation_base(size); + memory.allocate_memory(base, size, memory_permission::read_write); - return emulator_allocator{emu, base, size}; + return emulator_allocator{memory, base, size}; } - void setup_gdt(x64_emulator& emu) + void setup_gdt(x64_emulator& emu, memory_manager& memory) { constexpr uint64_t gdtr[4] = {0, GDT_ADDR, GDT_LIMIT, 0}; emu.write_register(x64_register::gdtr, &gdtr, sizeof(gdtr)); - emu.allocate_memory(GDT_ADDR, GDT_LIMIT, memory_permission::read); + memory.allocate_memory(GDT_ADDR, GDT_LIMIT, memory_permission::read); emu.write_memory(GDT_ADDR + 6 * (sizeof(uint64_t)), 0xEFFE000000FFFF); emu.reg(x64_register::cs, 0x33); @@ -216,8 +216,9 @@ namespace { auto& emu = win_emu.emu(); auto& context = win_emu.process(); + auto& memory = win_emu.memory(); - setup_gdt(emu); + setup_gdt(emu, memory); context.registry = registry_manager(win_emu.get_emulation_root().empty() ? settings.registry_directory @@ -225,7 +226,7 @@ namespace context.kusd.setup(settings.use_relative_time); - context.base_allocator = create_allocator(emu, PEB_SEGMENT_SIZE); + context.base_allocator = create_allocator(memory, PEB_SEGMENT_SIZE); auto& allocator = context.base_allocator; context.peb = allocator.reserve(); @@ -317,23 +318,22 @@ namespace }); } - using exception_record_map = std::unordered_map>*, - emulator_object>>>; + using exception_record = EMU_EXCEPTION_RECORD>; + using exception_record_map = std::unordered_map>; - emulator_object>> save_exception_record( - emulator_allocator& allocator, const EMU_EXCEPTION_RECORD>& record, - exception_record_map& record_mapping) + emulator_object save_exception_record(emulator_allocator& allocator, + const exception_record& record, + exception_record_map& record_mapping) { - const auto record_obj = allocator.reserve>>(); + const auto record_obj = allocator.reserve(); record_obj.write(record); if (record.ExceptionRecord) { record_mapping.emplace(&record, record_obj); - emulator_object>> nested_record_obj{allocator.get_emulator()}; - const auto nested_record = record_mapping.find( - reinterpret_cast>*>(record.ExceptionRecord)); + emulator_object nested_record_obj{allocator.get_memory()}; + const auto nested_record = record_mapping.find(reinterpret_cast(record.ExceptionRecord)); if (nested_record != record_mapping.end()) { @@ -342,11 +342,10 @@ namespace else { nested_record_obj = save_exception_record( - allocator, *reinterpret_cast>*>(record.ExceptionRecord), - record_mapping); + allocator, *reinterpret_cast(record.ExceptionRecord), record_mapping); } - record_obj.access([&](EMU_EXCEPTION_RECORD>& r) { + record_obj.access([&](exception_record& r) { r.ExceptionRecord = reinterpret_cast::PVOID>(nested_record_obj.ptr()); }); } @@ -354,8 +353,8 @@ namespace return record_obj; } - emulator_object>> save_exception_record( - emulator_allocator& allocator, const EMU_EXCEPTION_RECORD>& record) + emulator_object save_exception_record(emulator_allocator& allocator, + const exception_record& record) { exception_record_map record_mapping{}; return save_exception_record(allocator, record, record_mapping); @@ -374,12 +373,12 @@ namespace } } - size_t calculate_exception_record_size(const EMU_EXCEPTION_RECORD>& record) + size_t calculate_exception_record_size(const exception_record& record) { - std::unordered_set>*> records{}; + std::unordered_set records{}; size_t total_size = 0; - const EMU_EXCEPTION_RECORD>* current_record = &record; + const exception_record* current_record = &record; while (current_record) { if (!records.insert(current_record).second) @@ -388,7 +387,7 @@ namespace } total_size += sizeof(*current_record); - current_record = reinterpret_cast>*>(record.ExceptionRecord); + current_record = reinterpret_cast(record.ExceptionRecord); } return total_size; @@ -408,8 +407,8 @@ namespace { constexpr auto mach_frame_size = 0x40; constexpr auto context_record_size = 0x4F0; - const auto exception_record_size = calculate_exception_record_size( - *reinterpret_cast>*>(pointers.ExceptionRecord)); + const auto exception_record_size = + calculate_exception_record_size(*reinterpret_cast(pointers.ExceptionRecord)); const auto combined_size = align_up(exception_record_size + context_record_size, 0x10); assert(combined_size == 0x590); @@ -434,8 +433,8 @@ namespace context_record_obj.write(*reinterpret_cast(pointers.ContextRecord)); emulator_allocator allocator{emu, new_sp + context_record_size, exception_record_size}; - const auto exception_record_obj = save_exception_record( - allocator, *reinterpret_cast>*>(pointers.ExceptionRecord)); + const auto exception_record_obj = + save_exception_record(allocator, *reinterpret_cast(pointers.ExceptionRecord)); if (exception_record_obj.value() != allocator.get_base()) { @@ -462,7 +461,7 @@ namespace ctx.ContextFlags = CONTEXT64_ALL; context_frame::save(emu, ctx); - EMU_EXCEPTION_RECORD> record{}; + exception_record record{}; memset(&record, 0, sizeof(record)); record.ExceptionCode = static_cast(status); record.ExceptionFlags = 0; @@ -679,20 +678,20 @@ namespace } } -emulator_thread::emulator_thread(x64_emulator& emu, const process_context& context, const uint64_t start_address, +emulator_thread::emulator_thread(memory_manager& memory, const process_context& context, const uint64_t start_address, const uint64_t argument, const uint64_t stack_size, const uint32_t id) - : emu_ptr(&emu), + : memory_ptr(&memory), stack_size(page_align_up(std::max(stack_size, static_cast(STACK_SIZE)))), start_address(start_address), argument(argument), id(id), last_registers(context.default_register_set) { - this->stack_base = emu.allocate_memory(this->stack_size, memory_permission::read_write); + this->stack_base = memory.allocate_memory(this->stack_size, memory_permission::read_write); this->gs_segment = emulator_allocator{ - emu, - emu.allocate_memory(GS_SEGMENT_SIZE, memory_permission::read_write), + memory, + memory.allocate_memory(GS_SEGMENT_SIZE, memory_permission::read_write), GS_SEGMENT_SIZE, }; @@ -877,7 +876,8 @@ windows_emulator::windows_emulator(const std::filesystem::path& emulation_root, : emulation_root_{emulation_root.empty() ? emulation_root : absolute(emulation_root)}, file_sys_(emulation_root_.empty() ? emulation_root_ : emulation_root_ / "filesys"), emu_(std::move(emu)), - process_(*emu_, file_sys_) + memory_manager_(*this->emu_), + process_(*emu_, memory_manager_, file_sys_) { #ifndef OS_WINDOWS if (this->get_emulation_root().empty()) @@ -896,7 +896,7 @@ void windows_emulator::setup_process(const emulator_settings& settings, const wi auto& emu = this->emu(); auto& context = this->process(); - context.mod_manager = module_manager(emu, this->file_sys()); // TODO: Cleanup module manager + context.mod_manager = module_manager(this->memory(), this->file_sys()); // TODO: Cleanup module manager const auto application = settings.application.is_absolute() // ? settings.application @@ -924,7 +924,7 @@ void windows_emulator::setup_process(const emulator_settings& settings, const wi context.default_register_set = emu.save_registers(); - const auto main_thread_id = context.create_thread(emu, context.executable->entry_point, 0, 0); + const auto main_thread_id = context.create_thread(this->memory(), context.executable->entry_point, 0, 0); switch_to_thread(*this, main_thread_id); } @@ -1188,13 +1188,18 @@ void windows_emulator::serialize(utils::buffer_serializer& buffer) const { buffer.write(this->switch_thread_); buffer.write(this->use_relative_time_); - this->emu().serialize(buffer); + this->emu().serialize_state(buffer, false); + this->memory().serialize_memory_state(buffer, false); this->process_.serialize(buffer); this->dispatcher_.serialize(buffer); } void windows_emulator::deserialize(utils::buffer_deserializer& buffer) { + buffer.register_factory([this] { + return memory_manager_wrapper{this->memory()}; // + }); + buffer.register_factory([this] { return x64_emulator_wrapper{this->emu()}; // }); @@ -1206,16 +1211,19 @@ void windows_emulator::deserialize(utils::buffer_deserializer& buffer) buffer.read(this->switch_thread_); buffer.read(this->use_relative_time_); - this->emu().deserialize(buffer); + this->memory().unmap_all_memory(); + + this->emu().deserialize_state(buffer, false); + this->memory().deserialize_memory_state(buffer, false); this->process_.deserialize(buffer); this->dispatcher_.deserialize(buffer); } void windows_emulator::save_snapshot() { - this->emu().save_snapshot(); - utils::buffer_serializer serializer{}; + this->emu().serialize_state(serializer, true); + this->memory().serialize_memory_state(serializer, true); this->process_.serialize(serializer); this->process_snapshot_ = serializer.move_buffer(); @@ -1232,9 +1240,9 @@ void windows_emulator::restore_snapshot() return; } - this->emu().restore_snapshot(); - utils::buffer_deserializer deserializer{this->process_snapshot_}; + this->emu().deserialize_state(deserializer, true); + this->memory().deserialize_memory_state(deserializer, true); this->process_.deserialize(deserializer); // this->process_ = *this->process_snapshot_; } diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index e586c3cc..e54f8f3b 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -9,6 +9,7 @@ #include "process_context.hpp" #include "logger.hpp" #include "file_system.hpp" +#include "memory_manager.hpp" std::unique_ptr create_default_x64_emulator(); @@ -186,6 +187,16 @@ class windows_emulator return this->file_sys_; } + memory_manager& memory() + { + return this->memory_manager_; + } + + const memory_manager& memory() const + { + return this->memory_manager_; + } + const std::filesystem::path& get_emulation_root() { return this->emulation_root_; @@ -205,6 +216,8 @@ class windows_emulator std::vector syscall_hooks_{}; std::unordered_map port_mappings_{}; + memory_manager memory_manager_; + std::set> modules_{}; process_context process_;