mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-10 16:16:16 +00:00
Fix virtual memory semantics and refactor region policy
This commit is contained in:
@@ -23,16 +23,22 @@ using NTSTATUS = std::uint32_t;
|
||||
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
|
||||
|
||||
#define STATUS_ALERTED ((NTSTATUS)0x00000101L)
|
||||
#define STATUS_PIPE_LISTENING ((NTSTATUS)0x00000105L)
|
||||
#define STATUS_PIPE_CONNECTED ((NTSTATUS)0x00000106L)
|
||||
|
||||
#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L)
|
||||
#define STATUS_SERVICE_NOTIFICATION ((NTSTATUS)0x40000018L)
|
||||
|
||||
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
|
||||
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
|
||||
#define STATUS_NO_MORE_ENTRIES ((NTSTATUS)0x8000001AL)
|
||||
|
||||
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
|
||||
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
|
||||
#define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018L)
|
||||
#define STATUS_NOT_MAPPED_VIEW ((NTSTATUS)0xC0000019L)
|
||||
#define STATUS_UNABLE_TO_DELETE_SECTION ((NTSTATUS)0xC000001BL)
|
||||
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
|
||||
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
|
||||
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
|
||||
@@ -40,14 +46,35 @@ using NTSTATUS = std::uint32_t;
|
||||
#define STATUS_MUTANT_NOT_OWNED ((NTSTATUS)0xC0000046L)
|
||||
#define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS)0xC0000047L)
|
||||
#define STATUS_SECTION_NOT_IMAGE ((NTSTATUS)0xC0000049L)
|
||||
#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
|
||||
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
|
||||
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
|
||||
#define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS)0xC000009FL)
|
||||
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
|
||||
#define STATUS_PIPE_BUSY ((NTSTATUS)0xC00000AAL)
|
||||
#define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS)0xC00000ACL)
|
||||
#define STATUS_INVALID_PIPE_STATE ((NTSTATUS)0xC00000ADL)
|
||||
#define STATUS_PIPE_DISCONNECTED ((NTSTATUS)0xC00000B0L)
|
||||
#define STATUS_PIPE_CLOSING ((NTSTATUS)0xC00000B1L)
|
||||
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
|
||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
|
||||
#define STATUS_PIPE_NOT_CONNECTED ((NTSTATUS)0xC00000BEL)
|
||||
#define STATUS_PIPE_EMPTY ((NTSTATUS)0xC00000D9L)
|
||||
#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L)
|
||||
#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
|
||||
#define STATUS_INVALID_PARAMETER_1 ((NTSTATUS)0xC00000EFL)
|
||||
#define STATUS_INVALID_PARAMETER_2 ((NTSTATUS)0xC00000F0L)
|
||||
#define STATUS_INVALID_PARAMETER_3 ((NTSTATUS)0xC00000F1L)
|
||||
#define STATUS_INVALID_PARAMETER_4 ((NTSTATUS)0xC00000F2L)
|
||||
#define STATUS_INVALID_PARAMETER_5 ((NTSTATUS)0xC00000F3L)
|
||||
#define STATUS_INVALID_PARAMETER_6 ((NTSTATUS)0xC00000F4L)
|
||||
#define STATUS_INVALID_PARAMETER_7 ((NTSTATUS)0xC00000F5L)
|
||||
#define STATUS_INVALID_PARAMETER_8 ((NTSTATUS)0xC00000F6L)
|
||||
#define STATUS_INVALID_PARAMETER_9 ((NTSTATUS)0xC00000F7L)
|
||||
#define STATUS_INVALID_PARAMETER_10 ((NTSTATUS)0xC00000F8L)
|
||||
#define STATUS_INVALID_PARAMETER_11 ((NTSTATUS)0xC00000F9L)
|
||||
#define STATUS_INVALID_PARAMETER_12 ((NTSTATUS)0xC00000FAL)
|
||||
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
|
||||
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
|
||||
#define STATUS_CONNECTION_RESET ((NTSTATUS)0xC000020DL)
|
||||
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
|
||||
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
|
||||
@@ -55,20 +82,6 @@ using NTSTATUS = std::uint32_t;
|
||||
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
|
||||
#define STATUS_PORT_NOT_SET ((NTSTATUS)0xC0000353L)
|
||||
#define STATUS_DEBUGGER_INACTIVE ((NTSTATUS)0xC0000354L)
|
||||
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
|
||||
#define STATUS_PIPE_EMPTY ((NTSTATUS)0xC00000D9L)
|
||||
#define STATUS_PIPE_BUSY ((NTSTATUS)0xC00000AAL)
|
||||
#define STATUS_PIPE_DISCONNECTED ((NTSTATUS)0xC00000B0L)
|
||||
#define STATUS_PIPE_LISTENING ((NTSTATUS)0x00000105L)
|
||||
#define STATUS_PIPE_CONNECTED ((NTSTATUS)0x00000106L)
|
||||
#define STATUS_PIPE_CLOSING ((NTSTATUS)0xC00000B1L)
|
||||
#define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS)0xC00000ACL)
|
||||
#define STATUS_INVALID_PIPE_STATE ((NTSTATUS)0xC00000ADL)
|
||||
#define STATUS_PIPE_NOT_CONNECTED ((NTSTATUS)0xC00000BEL)
|
||||
|
||||
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
|
||||
|
||||
#define STATUS_SERVICE_NOTIFICATION ((NTSTATUS)0x40000018L)
|
||||
|
||||
#define FILE_DEVICE_NETWORK 0x00000012
|
||||
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace utils
|
||||
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.write(region.is_mmio);
|
||||
buffer.write(region.kind);
|
||||
buffer.write(region.initial_permission);
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write_map(region.committed_regions);
|
||||
@@ -86,7 +86,7 @@ namespace utils
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.read(region.is_mmio);
|
||||
buffer.read(region.kind);
|
||||
buffer.read(region.initial_permission);
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
buffer.read_map(region.committed_regions);
|
||||
@@ -133,7 +133,7 @@ void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, co
|
||||
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
if (reserved_region.second.is_mmio)
|
||||
if (reserved_region.second.kind == memory_region_kind::mmio)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -169,7 +169,7 @@ void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer
|
||||
for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
|
||||
{
|
||||
auto& reserved_region = i->second;
|
||||
if (reserved_region.is_mmio)
|
||||
if (reserved_region.kind == memory_region_kind::mmio)
|
||||
{
|
||||
i = this->reserved_regions_.erase(i);
|
||||
continue;
|
||||
@@ -183,7 +183,9 @@ void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer
|
||||
|
||||
buffer.read(data.data(), region.second.length);
|
||||
|
||||
this->map_memory(region.first, region.second.length, region.second.permissions);
|
||||
const auto effective_permission =
|
||||
region.second.permissions.is_guarded() ? memory_permission::none : region.second.permissions.common;
|
||||
this->map_memory(region.first, region.second.length, effective_permission);
|
||||
this->write_memory(region.first, data.data(), region.second.length);
|
||||
}
|
||||
}
|
||||
@@ -211,6 +213,8 @@ bool memory_manager::protect_memory(const uint64_t address, const size_t size, c
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
split_regions(committed_regions, {address, end});
|
||||
|
||||
const auto effective_permission = permissions.is_guarded() ? memory_permission::none : permissions.common;
|
||||
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
@@ -226,7 +230,7 @@ bool memory_manager::protect_memory(const uint64_t address, const size_t size, c
|
||||
old_first_permissions = sub_region.second.permissions;
|
||||
}
|
||||
|
||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
|
||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, effective_permission);
|
||||
sub_region.second.permissions = permissions;
|
||||
}
|
||||
}
|
||||
@@ -256,7 +260,7 @@ bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mm
|
||||
.try_emplace(address,
|
||||
reserved_region{
|
||||
.length = size,
|
||||
.is_mmio = true,
|
||||
.kind = memory_region_kind::mmio,
|
||||
})
|
||||
.first;
|
||||
|
||||
@@ -268,7 +272,7 @@ bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mm
|
||||
}
|
||||
|
||||
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions,
|
||||
const bool reserve_only)
|
||||
const bool reserve_only, const memory_region_kind kind)
|
||||
{
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
@@ -280,6 +284,7 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size,
|
||||
reserved_region{
|
||||
.length = size,
|
||||
.initial_permission = permissions,
|
||||
.kind = kind,
|
||||
})
|
||||
.first;
|
||||
|
||||
@@ -316,6 +321,8 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co
|
||||
uint64_t last_region_start{};
|
||||
const committed_region* last_region{nullptr};
|
||||
|
||||
const auto effective_permission = permissions.is_guarded() ? memory_permission::none : permissions.common;
|
||||
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
@@ -331,7 +338,7 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co
|
||||
|
||||
if (map_length > 0)
|
||||
{
|
||||
this->map_memory(map_start, static_cast<size_t>(map_length), permissions);
|
||||
this->map_memory(map_start, static_cast<size_t>(map_length), effective_permission);
|
||||
committed_regions[map_start] = committed_region{static_cast<size_t>(map_length), permissions};
|
||||
}
|
||||
|
||||
@@ -345,7 +352,7 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co
|
||||
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, static_cast<size_t>(map_length), permissions);
|
||||
this->map_memory(map_start, static_cast<size_t>(map_length), effective_permission);
|
||||
committed_regions[map_start] = committed_region{static_cast<size_t>(map_length), permissions};
|
||||
}
|
||||
|
||||
@@ -364,7 +371,7 @@ bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry->second.is_mmio)
|
||||
if (entry->second.kind == memory_region_kind::mmio)
|
||||
{
|
||||
throw std::runtime_error("Not allowed to decommit MMIO!");
|
||||
}
|
||||
@@ -405,6 +412,8 @@ bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
|
||||
}
|
||||
|
||||
bool memory_manager::release_memory(const uint64_t address, size_t size)
|
||||
{
|
||||
if (!size)
|
||||
{
|
||||
const auto entry = this->reserved_regions_.find(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
@@ -412,32 +421,51 @@ bool memory_manager::release_memory(const uint64_t address, size_t size)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!size)
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
size = entry->second.length;
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
}
|
||||
|
||||
size = static_cast<size_t>(page_align_up(size));
|
||||
this->reserved_regions_.erase(entry);
|
||||
this->update_layout_version();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (size > entry->second.length)
|
||||
const auto aligned_start = page_align_down(address);
|
||||
const auto aligned_end = page_align_up(address + size);
|
||||
size = static_cast<size_t>(aligned_end - aligned_start);
|
||||
|
||||
const auto entry = this->find_reserved_region(aligned_start);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto reserved_start = entry->first;
|
||||
const auto reserved_end = entry->first + entry->second.length;
|
||||
|
||||
if (reserved_end < aligned_end)
|
||||
{
|
||||
throw std::runtime_error("Cross region release not supported yet!");
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
reserved_region region = std::move(entry->second);
|
||||
this->reserved_regions_.erase(entry);
|
||||
|
||||
split_regions(committed_regions, {end});
|
||||
auto& committed_regions = region.committed_regions;
|
||||
split_regions(committed_regions, {aligned_start, aligned_end});
|
||||
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
if (i->first >= aligned_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
if (i->first >= aligned_start && sub_region_end <= aligned_end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
@@ -448,13 +476,41 @@ bool memory_manager::release_memory(const uint64_t address, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
entry->second.length -= size;
|
||||
if (entry->second.length > 0)
|
||||
committed_region_map left_committed{};
|
||||
committed_region_map right_committed{};
|
||||
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
this->reserved_regions_[address + size] = std::move(entry->second);
|
||||
if (sub_region.first < aligned_start)
|
||||
{
|
||||
left_committed.emplace(sub_region.first, std::move(sub_region.second));
|
||||
}
|
||||
else if (sub_region.first >= aligned_end)
|
||||
{
|
||||
right_committed.emplace(sub_region.first, std::move(sub_region.second));
|
||||
}
|
||||
}
|
||||
|
||||
if (reserved_start < aligned_start)
|
||||
{
|
||||
reserved_region left_region{};
|
||||
left_region.length = static_cast<size_t>(aligned_start - reserved_start);
|
||||
left_region.initial_permission = region.initial_permission;
|
||||
left_region.kind = region.kind;
|
||||
left_region.committed_regions = std::move(left_committed);
|
||||
this->reserved_regions_.try_emplace(reserved_start, std::move(left_region));
|
||||
}
|
||||
|
||||
if (aligned_end < reserved_end)
|
||||
{
|
||||
reserved_region right_region{};
|
||||
right_region.length = static_cast<size_t>(reserved_end - aligned_end);
|
||||
right_region.initial_permission = region.initial_permission;
|
||||
right_region.kind = region.kind;
|
||||
right_region.committed_regions = std::move(right_committed);
|
||||
this->reserved_regions_.try_emplace(aligned_end, std::move(right_region));
|
||||
}
|
||||
|
||||
this->reserved_regions_.erase(entry);
|
||||
this->update_layout_version();
|
||||
return true;
|
||||
}
|
||||
@@ -472,10 +528,11 @@ void memory_manager::unmap_all_memory()
|
||||
this->reserved_regions_.clear();
|
||||
}
|
||||
|
||||
uint64_t memory_manager::allocate_memory(const size_t size, const nt_memory_permission permissions, const bool reserve_only, uint64_t start)
|
||||
uint64_t memory_manager::allocate_memory(const size_t size, const nt_memory_permission permissions, const bool reserve_only, uint64_t start,
|
||||
const memory_region_kind kind)
|
||||
{
|
||||
const auto allocation_base = this->find_free_allocation_base(size, start);
|
||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only))
|
||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only, kind))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -569,6 +626,7 @@ region_info memory_manager::get_region_info(const uint64_t address)
|
||||
result.start = result.allocation_base;
|
||||
result.length = result.allocation_length;
|
||||
result.initial_permissions = entry->second.initial_permission;
|
||||
result.kind = reserved_region.kind;
|
||||
|
||||
if (committed_regions.empty())
|
||||
{
|
||||
@@ -634,6 +692,28 @@ bool memory_manager::overlaps_reserved_region(const uint64_t address, const size
|
||||
return false;
|
||||
}
|
||||
|
||||
memory_region_kind memory_manager::get_region_kind(const uint64_t address) const
|
||||
{
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return memory_region_kind::free;
|
||||
}
|
||||
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
return memory_region_kind::free;
|
||||
}
|
||||
|
||||
const auto entry = --upper_bound;
|
||||
if (entry->first + entry->second.length <= address)
|
||||
{
|
||||
return memory_region_kind::free;
|
||||
}
|
||||
|
||||
return entry->second.kind;
|
||||
}
|
||||
|
||||
void memory_manager::read_memory(const uint64_t address, void* data, const size_t size) const
|
||||
{
|
||||
this->memory_->read_memory(address, data, size);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "std_include.hpp"
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
@@ -16,6 +17,15 @@ constexpr auto MAX_ALLOCATION_END_EXCL = MAX_ALLOCATION_ADDRESS + 1ULL;
|
||||
constexpr auto DEFAULT_ALLOCATION_ADDRESS_64BIT = 0x100000000ULL;
|
||||
constexpr auto DEFAULT_ALLOCATION_ADDRESS_32BIT = 0x10000ULL;
|
||||
|
||||
enum class memory_region_kind : uint8_t
|
||||
{
|
||||
free = 0,
|
||||
private_allocation,
|
||||
section_view,
|
||||
section_image,
|
||||
mmio,
|
||||
};
|
||||
|
||||
// This maps to the `basic_memory_region` struct defined in
|
||||
// emulator\memory_region.hpp
|
||||
struct region_info : basic_memory_region<nt_memory_permission>
|
||||
@@ -25,6 +35,7 @@ struct region_info : basic_memory_region<nt_memory_permission>
|
||||
bool is_reserved{};
|
||||
bool is_committed{};
|
||||
nt_memory_permission initial_permissions{};
|
||||
memory_region_kind kind{memory_region_kind::free};
|
||||
};
|
||||
|
||||
using mmio_read_callback = std::function<void(uint64_t addr, void* data, size_t size)>;
|
||||
@@ -57,7 +68,7 @@ class memory_manager : public memory_interface
|
||||
size_t length{};
|
||||
memory_permission initial_permission{};
|
||||
committed_region_map committed_regions{};
|
||||
bool is_mmio{false};
|
||||
memory_region_kind kind{memory_region_kind::private_allocation};
|
||||
};
|
||||
|
||||
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
||||
@@ -70,7 +81,8 @@ class memory_manager : public memory_interface
|
||||
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, nt_memory_permission permissions, bool reserve_only = false);
|
||||
bool allocate_memory(uint64_t address, size_t size, nt_memory_permission permissions, bool reserve_only = false,
|
||||
memory_region_kind kind = memory_region_kind::private_allocation);
|
||||
|
||||
bool commit_memory(uint64_t address, size_t size, nt_memory_permission permissions);
|
||||
bool decommit_memory(uint64_t address, size_t size);
|
||||
@@ -79,7 +91,8 @@ class memory_manager : public memory_interface
|
||||
|
||||
void unmap_all_memory();
|
||||
|
||||
uint64_t allocate_memory(size_t size, nt_memory_permission permissions, bool reserve_only = false, uint64_t start = 0);
|
||||
uint64_t allocate_memory(size_t size, nt_memory_permission permissions, bool reserve_only = false, uint64_t start = 0,
|
||||
memory_region_kind kind = memory_region_kind::private_allocation);
|
||||
|
||||
uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const;
|
||||
|
||||
@@ -89,6 +102,8 @@ class memory_manager : public memory_interface
|
||||
|
||||
bool overlaps_reserved_region(uint64_t address, size_t size) const;
|
||||
|
||||
memory_region_kind get_region_kind(uint64_t address) const;
|
||||
|
||||
const reserved_region_map& get_reserved_regions() const
|
||||
{
|
||||
return this->reserved_regions_;
|
||||
@@ -127,3 +142,46 @@ class memory_manager : public memory_interface
|
||||
|
||||
void update_layout_version();
|
||||
};
|
||||
|
||||
namespace memory_region_policy
|
||||
{
|
||||
constexpr bool is_section_kind(const memory_region_kind kind)
|
||||
{
|
||||
return kind == memory_region_kind::section_view || kind == memory_region_kind::section_image;
|
||||
}
|
||||
|
||||
constexpr bool is_mapped_memory_kind(const memory_region_kind kind)
|
||||
{
|
||||
return is_section_kind(kind) || kind == memory_region_kind::mmio;
|
||||
}
|
||||
|
||||
constexpr uint32_t to_memory_basic_information_type(const memory_region_kind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case memory_region_kind::section_image:
|
||||
return MEM_IMAGE;
|
||||
case memory_region_kind::section_view:
|
||||
return MEM_MAPPED;
|
||||
case memory_region_kind::mmio:
|
||||
return MEM_MAPPED;
|
||||
default:
|
||||
return MEM_PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr NTSTATUS nt_free_virtual_memory_denied_status(const memory_region_kind kind)
|
||||
{
|
||||
if (is_section_kind(kind))
|
||||
{
|
||||
return STATUS_UNABLE_TO_DELETE_SECTION;
|
||||
}
|
||||
|
||||
if (kind == memory_region_kind::mmio)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../cpu_context.hpp"
|
||||
#include "../emulator_utils.hpp"
|
||||
#include "../syscall_utils.hpp"
|
||||
#include "../memory_manager.hpp"
|
||||
|
||||
namespace syscalls
|
||||
{
|
||||
@@ -58,7 +59,15 @@ namespace syscalls
|
||||
|
||||
image_info.Protect = map_emulator_to_nt_protection(region_info.permissions);
|
||||
image_info.AllocationProtect = map_emulator_to_nt_protection(region_info.initial_permissions);
|
||||
image_info.Type = MEM_PRIVATE;
|
||||
|
||||
if (!region_info.is_reserved)
|
||||
{
|
||||
image_info.Type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
image_info.Type = memory_region_policy::to_memory_basic_information_type(region_info.kind);
|
||||
}
|
||||
});
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
@@ -199,6 +208,12 @@ namespace syscalls
|
||||
allocation_bytes = page_align_up(allocation_bytes);
|
||||
bytes_to_allocate.write(allocation_bytes);
|
||||
|
||||
const auto base_protection = page_protection & ~static_cast<uint32_t>(PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE);
|
||||
if (base_protection == PAGE_WRITECOPY || base_protection == PAGE_EXECUTE_WRITECOPY)
|
||||
{
|
||||
return STATUS_INVALID_PAGE_PROTECTION;
|
||||
}
|
||||
|
||||
const auto protection = try_map_nt_to_emulator_protection(page_protection);
|
||||
if (!protection.has_value())
|
||||
{
|
||||
@@ -218,7 +233,7 @@ namespace syscalls
|
||||
// initial value of BaseAddress is non-NULL, the region is allocated starting at the specified virtual
|
||||
// address rounded down to the next host page size address boundary. If the initial value of BaseAddress
|
||||
// is NULL, the operating system will determine where to allocate the region.
|
||||
potential_base = page_align_up(potential_base);
|
||||
potential_base = page_align_down(potential_base);
|
||||
}
|
||||
|
||||
if (!potential_base)
|
||||
@@ -267,7 +282,7 @@ namespace syscalls
|
||||
|
||||
if (free_type == 0)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
return STATUS_INVALID_PARAMETER_4;
|
||||
}
|
||||
|
||||
const auto allocation_base = base_address.read();
|
||||
@@ -275,14 +290,70 @@ namespace syscalls
|
||||
|
||||
if (free_type & MEM_RELEASE)
|
||||
{
|
||||
return c.win_emu.memory.release_memory(allocation_base, static_cast<size_t>(allocation_size)) ? STATUS_SUCCESS
|
||||
: STATUS_MEMORY_NOT_ALLOCATED;
|
||||
if (!allocation_size)
|
||||
{
|
||||
const auto region_info = c.win_emu.memory.get_region_info(allocation_base);
|
||||
if (!region_info.is_reserved)
|
||||
{
|
||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
if (region_info.allocation_base != allocation_base)
|
||||
{
|
||||
return STATUS_FREE_VM_NOT_AT_BASE;
|
||||
}
|
||||
}
|
||||
|
||||
const auto region_kind = c.win_emu.memory.get_region_kind(allocation_base);
|
||||
const auto denied_status = memory_region_policy::nt_free_virtual_memory_denied_status(region_kind);
|
||||
if (denied_status != STATUS_SUCCESS)
|
||||
{
|
||||
return denied_status;
|
||||
}
|
||||
|
||||
const bool success = c.win_emu.memory.release_memory(allocation_base, static_cast<size_t>(allocation_size));
|
||||
if (success)
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
const auto region_info = c.win_emu.memory.get_region_info(allocation_base);
|
||||
if (!region_info.is_reserved)
|
||||
{
|
||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
return STATUS_FREE_VM_NOT_AT_BASE;
|
||||
}
|
||||
|
||||
if (free_type & MEM_DECOMMIT)
|
||||
{
|
||||
return c.win_emu.memory.decommit_memory(allocation_base, static_cast<size_t>(allocation_size)) ? STATUS_SUCCESS
|
||||
: STATUS_MEMORY_NOT_ALLOCATED;
|
||||
const auto region_kind = c.win_emu.memory.get_region_kind(allocation_base);
|
||||
const auto denied_status = memory_region_policy::nt_free_virtual_memory_denied_status(region_kind);
|
||||
if (denied_status != STATUS_SUCCESS)
|
||||
{
|
||||
return denied_status;
|
||||
}
|
||||
|
||||
auto decommit_size = static_cast<size_t>(allocation_size);
|
||||
if (!decommit_size)
|
||||
{
|
||||
const auto region_info = c.win_emu.memory.get_region_info(allocation_base);
|
||||
if (!region_info.is_reserved)
|
||||
{
|
||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
if (region_info.allocation_base != allocation_base)
|
||||
{
|
||||
return STATUS_FREE_VM_NOT_AT_BASE;
|
||||
}
|
||||
|
||||
decommit_size = region_info.allocation_length;
|
||||
}
|
||||
|
||||
const bool success = c.win_emu.memory.decommit_memory(allocation_base, decommit_size);
|
||||
return success ? STATUS_SUCCESS : STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Bad free type");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "../emulator_utils.hpp"
|
||||
#include "../syscall_utils.hpp"
|
||||
#include "../memory_manager.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
|
||||
@@ -193,7 +194,8 @@ namespace syscalls
|
||||
constexpr auto shared_section_size = 0x10000;
|
||||
|
||||
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);
|
||||
c.win_emu.memory.allocate_memory(address, shared_section_size, memory_permission::read_write, false,
|
||||
memory_region_kind::section_view);
|
||||
c.proc.shared_section_address = address;
|
||||
c.proc.shared_section_size = shared_section_size;
|
||||
|
||||
@@ -206,7 +208,8 @@ namespace syscalls
|
||||
constexpr auto dbwin_buffer_section_size = 0x1000;
|
||||
|
||||
const auto address = c.win_emu.memory.find_free_allocation_base(dbwin_buffer_section_size);
|
||||
c.win_emu.memory.allocate_memory(address, dbwin_buffer_section_size, memory_permission::read_write);
|
||||
c.win_emu.memory.allocate_memory(address, dbwin_buffer_section_size, memory_permission::read_write, false,
|
||||
memory_region_kind::section_view);
|
||||
c.proc.dbwin_buffer = address;
|
||||
c.proc.dbwin_buffer_size = dbwin_buffer_section_size;
|
||||
|
||||
@@ -373,7 +376,7 @@ namespace syscalls
|
||||
const auto aligned_size = static_cast<size_t>(page_align_up(size));
|
||||
const auto reserve_only = section_entry->allocation_attributes == SEC_RESERVE;
|
||||
const auto protection = map_nt_to_emulator_protection(section_entry->section_page_protection);
|
||||
const auto address = c.win_emu.memory.allocate_memory(aligned_size, protection, reserve_only);
|
||||
const auto address = c.win_emu.memory.allocate_memory(aligned_size, protection, reserve_only, 0, memory_region_kind::section_view);
|
||||
|
||||
if (!reserve_only && !file_data.empty())
|
||||
{
|
||||
@@ -577,24 +580,27 @@ namespace syscalls
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (base_address == c.proc.shared_section_address)
|
||||
if (c.proc.shared_section_address && base_address >= c.proc.shared_section_address &&
|
||||
base_address < c.proc.shared_section_address + c.proc.shared_section_size)
|
||||
{
|
||||
const auto address = c.proc.shared_section_address;
|
||||
c.proc.shared_section_address = 0;
|
||||
c.win_emu.memory.release_memory(base_address, static_cast<size_t>(c.proc.shared_section_size));
|
||||
c.win_emu.memory.release_memory(address, static_cast<size_t>(c.proc.shared_section_size));
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (base_address == c.proc.dbwin_buffer)
|
||||
if (c.proc.dbwin_buffer && base_address >= c.proc.dbwin_buffer && base_address < c.proc.dbwin_buffer + c.proc.dbwin_buffer_size)
|
||||
{
|
||||
const auto address = c.proc.dbwin_buffer;
|
||||
c.proc.dbwin_buffer = 0;
|
||||
c.win_emu.memory.release_memory(base_address, static_cast<size_t>(c.proc.dbwin_buffer_size));
|
||||
c.win_emu.memory.release_memory(address, static_cast<size_t>(c.proc.dbwin_buffer_size));
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
const auto* mod = c.win_emu.mod_manager.find_by_address(base_address);
|
||||
if (mod != nullptr)
|
||||
{
|
||||
if (c.win_emu.mod_manager.unmap(base_address))
|
||||
if (c.win_emu.mod_manager.unmap(mod->image_base))
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -602,14 +608,14 @@ namespace syscalls
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (c.win_emu.memory.release_memory(base_address, 0))
|
||||
const auto region_info = c.win_emu.memory.get_region_info(base_address);
|
||||
if (region_info.is_reserved && memory_region_policy::is_section_kind(region_info.kind) &&
|
||||
c.win_emu.memory.release_memory(region_info.allocation_base, 0))
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
c.win_emu.log.error("Unmapping non-module/non-memory section not supported!\n");
|
||||
c.emu.stop();
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
return STATUS_NOT_MAPPED_VIEW;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtUnmapViewOfSectionEx(const syscall_context& c, const handle process_handle, const uint64_t base_address,
|
||||
|
||||
Reference in New Issue
Block a user