mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-22 05:03:56 +00:00
Isolate memory manager (#137)
This commit is contained in:
@@ -18,5 +18,6 @@ struct cpu_interface
|
||||
virtual std::vector<std::byte> save_registers() = 0;
|
||||
virtual void restore_registers(const std::vector<std::byte>& register_data) = 0;
|
||||
|
||||
// TODO: Remove this
|
||||
virtual bool has_violation() const = 0;
|
||||
};
|
||||
|
||||
0
src/emulator/empty.cpp
Normal file
0
src/emulator/empty.cpp
Normal file
@@ -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<std::byte> 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;
|
||||
};
|
||||
|
||||
76
src/emulator/memory_interface.hpp
Normal file
76
src/emulator/memory_interface.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "memory_permission.hpp"
|
||||
|
||||
using mmio_read_callback = std::function<uint64_t(uint64_t addr, size_t size)>;
|
||||
using mmio_write_callback = std::function<void(uint64_t addr, size_t size, uint64_t data)>;
|
||||
|
||||
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 <typename T>
|
||||
T read_memory(const uint64_t address) const
|
||||
{
|
||||
T value{};
|
||||
this->read_memory(address, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T read_memory(const void* address) const
|
||||
{
|
||||
return this->read_memory<T>(reinterpret_cast<uint64_t>(address));
|
||||
}
|
||||
|
||||
std::vector<std::byte> read_memory(const uint64_t address, const size_t size) const
|
||||
{
|
||||
std::vector<std::byte> data{};
|
||||
data.resize(size);
|
||||
|
||||
this->read_memory(address, data.data(), data.size());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<std::byte> read_memory(const void* address, const size_t size) const
|
||||
{
|
||||
return this->read_memory(reinterpret_cast<uint64_t>(address), size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_memory(const uint64_t address, const T& value)
|
||||
{
|
||||
this->write_memory(address, &value, sizeof(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_memory(void* address, const T& value)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value));
|
||||
}
|
||||
|
||||
void write_memory(void* address, const void* data, const size_t size)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), data, size);
|
||||
}
|
||||
};
|
||||
@@ -1,156 +0,0 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#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<uint64_t(uint64_t addr, size_t size)>;
|
||||
using mmio_write_callback = std::function<void(uint64_t addr, size_t size, uint64_t data)>;
|
||||
|
||||
class memory_manager
|
||||
{
|
||||
public:
|
||||
struct committed_region
|
||||
{
|
||||
size_t length{};
|
||||
memory_permission permissions{};
|
||||
};
|
||||
|
||||
using committed_region_map = std::map<uint64_t, committed_region>;
|
||||
|
||||
struct reserved_region
|
||||
{
|
||||
size_t length{};
|
||||
committed_region_map committed_regions{};
|
||||
bool is_mmio{false};
|
||||
};
|
||||
|
||||
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
||||
|
||||
virtual ~memory_manager() = default;
|
||||
|
||||
template <typename T>
|
||||
T read_memory(const uint64_t address) const
|
||||
{
|
||||
T value{};
|
||||
this->read_memory(address, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T read_memory(const void* address) const
|
||||
{
|
||||
return this->read_memory<T>(reinterpret_cast<uint64_t>(address));
|
||||
}
|
||||
|
||||
std::vector<std::byte> read_memory(const uint64_t address, const size_t size) const
|
||||
{
|
||||
std::vector<std::byte> data{};
|
||||
data.resize(size);
|
||||
|
||||
this->read_memory(address, data.data(), data.size());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<std::byte> read_memory(const void* address, const size_t size) const
|
||||
{
|
||||
return this->read_memory(reinterpret_cast<uint64_t>(address), size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_memory(const uint64_t address, const T& value)
|
||||
{
|
||||
this->write_memory(address, &value, sizeof(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_memory(void* address, const T& value)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value));
|
||||
}
|
||||
|
||||
void write_memory(void* address, const void* data, const size_t size)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(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<std::uint64_t> 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);
|
||||
};
|
||||
@@ -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<size_t>(1))), memory_permission::read_write);
|
||||
emu.emu().write_memory(memory, data.data(), data.size());
|
||||
|
||||
emu.emu().reg(x64_register::rcx, memory);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#pragma once
|
||||
#include "memory_utils.hpp"
|
||||
|
||||
#include <x64_emulator.hpp>
|
||||
|
||||
#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<x64_emulator>;
|
||||
using memory_manager_wrapper = object_wrapper<memory_manager>;
|
||||
using process_context_wrapper = object_wrapper<process_context>;
|
||||
using windows_emulator_wrapper = object_wrapper<windows_emulator>;
|
||||
|
||||
@@ -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<T> reserve(const size_t count = 1)
|
||||
{
|
||||
const auto potential_start = this->reserve(sizeof(T) * count, alignof(T));
|
||||
return emulator_object<T>(*this->emu_, potential_start);
|
||||
return emulator_object<T>(*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<char, element_size> 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<USHORT>(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};
|
||||
|
||||
@@ -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<x64_emulator_wrapper>(), buffer.read<process_context_wrapper>())
|
||||
: kusd_mmio(buffer.read<memory_manager_wrapper>(), buffer.read<process_context_wrapper>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_{};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
98
src/windows-emulator/memory_manager.hpp
Normal file
98
src/windows-emulator/memory_manager.hpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include "memory_region.hpp"
|
||||
#include "serialization.hpp"
|
||||
|
||||
#include <memory_interface.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<uint64_t(uint64_t addr, size_t size)>;
|
||||
using mmio_write_callback = std::function<void(uint64_t addr, size_t size, uint64_t data)>;
|
||||
|
||||
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<uint64_t, committed_region>;
|
||||
|
||||
struct reserved_region
|
||||
{
|
||||
size_t length{};
|
||||
committed_region_map committed_regions{};
|
||||
bool is_mmio{false};
|
||||
};
|
||||
|
||||
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
||||
|
||||
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<std::uint64_t> 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();
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -10,7 +10,7 @@ class module_manager
|
||||
{
|
||||
public:
|
||||
using module_map = std::map<uint64_t, mapped_module>;
|
||||
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_{};
|
||||
|
||||
@@ -20,13 +20,13 @@ namespace
|
||||
return nt_headers_offset + (first_section_absolute - absolute_base);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> read_mapped_memory(const emulator& emu, const mapped_module& binary)
|
||||
std::vector<uint8_t> read_mapped_memory(const memory_manager& memory, const mapped_module& binary)
|
||||
{
|
||||
std::vector<uint8_t> memory{};
|
||||
memory.resize(binary.size_of_image);
|
||||
emu.read_memory(binary.image_base, memory.data(), memory.size());
|
||||
std::vector<uint8_t> 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<const uint8_t> buffer,
|
||||
@@ -141,7 +141,8 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void map_sections(emulator& emu, mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
|
||||
void map_sections(memory_manager& memory, mapped_module& binary,
|
||||
const utils::safe_buffer_accessor<const uint8_t> buffer,
|
||||
const PENTHeaders_t<std::uint64_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<const uint8_t> data, std::filesystem::path file)
|
||||
mapped_module map_module_from_data(memory_manager& memory, const std::span<const uint8_t> 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<const uint8_t>
|
||||
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<const uint8_t>
|
||||
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<uint8_t> 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);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <x64_emulator.hpp>
|
||||
#include "mapped_module.hpp"
|
||||
#include "../memory_manager.hpp"
|
||||
|
||||
mapped_module map_module_from_data(emulator& emu, std::span<const uint8_t> 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<const uint8_t> 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);
|
||||
|
||||
@@ -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<x64_emulator_wrapper>().get())
|
||||
: emulator_thread(buffer.read<memory_manager_wrapper>().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<TEB64>(*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<TEB64>(*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<uint64_t>());
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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<EMU_MEMORY_BASIC_INFORMATION64> 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)
|
||||
|
||||
@@ -190,19 +190,19 @@ namespace
|
||||
reinterpret_cast<const API_SET_NAMESPACE&>(*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<uint64_t>(GDT_ADDR + 6 * (sizeof(uint64_t)), 0xEFFE000000FFFF);
|
||||
emu.reg<uint16_t>(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<PEB64>();
|
||||
@@ -317,23 +318,22 @@ namespace
|
||||
});
|
||||
}
|
||||
|
||||
using exception_record_map = std::unordered_map<const EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>*,
|
||||
emulator_object<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>>>;
|
||||
using exception_record = EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>;
|
||||
using exception_record_map = std::unordered_map<const exception_record*, emulator_object<exception_record>>;
|
||||
|
||||
emulator_object<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>> save_exception_record(
|
||||
emulator_allocator& allocator, const EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>& record,
|
||||
exception_record_map& record_mapping)
|
||||
emulator_object<exception_record> save_exception_record(emulator_allocator& allocator,
|
||||
const exception_record& record,
|
||||
exception_record_map& record_mapping)
|
||||
{
|
||||
const auto record_obj = allocator.reserve<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>>();
|
||||
const auto record_obj = allocator.reserve<exception_record>();
|
||||
record_obj.write(record);
|
||||
|
||||
if (record.ExceptionRecord)
|
||||
{
|
||||
record_mapping.emplace(&record, record_obj);
|
||||
|
||||
emulator_object<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>> nested_record_obj{allocator.get_emulator()};
|
||||
const auto nested_record = record_mapping.find(
|
||||
reinterpret_cast<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>*>(record.ExceptionRecord));
|
||||
emulator_object<exception_record> nested_record_obj{allocator.get_memory()};
|
||||
const auto nested_record = record_mapping.find(reinterpret_cast<exception_record*>(record.ExceptionRecord));
|
||||
|
||||
if (nested_record != record_mapping.end())
|
||||
{
|
||||
@@ -342,11 +342,10 @@ namespace
|
||||
else
|
||||
{
|
||||
nested_record_obj = save_exception_record(
|
||||
allocator, *reinterpret_cast<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>*>(record.ExceptionRecord),
|
||||
record_mapping);
|
||||
allocator, *reinterpret_cast<exception_record*>(record.ExceptionRecord), record_mapping);
|
||||
}
|
||||
|
||||
record_obj.access([&](EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>& r) {
|
||||
record_obj.access([&](exception_record& r) {
|
||||
r.ExceptionRecord = reinterpret_cast<EmulatorTraits<Emu64>::PVOID>(nested_record_obj.ptr());
|
||||
});
|
||||
}
|
||||
@@ -354,8 +353,8 @@ namespace
|
||||
return record_obj;
|
||||
}
|
||||
|
||||
emulator_object<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>> save_exception_record(
|
||||
emulator_allocator& allocator, const EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>& record)
|
||||
emulator_object<exception_record> 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<EmulatorTraits<Emu64>>& record)
|
||||
size_t calculate_exception_record_size(const exception_record& record)
|
||||
{
|
||||
std::unordered_set<const EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>*> records{};
|
||||
std::unordered_set<const exception_record*> records{};
|
||||
size_t total_size = 0;
|
||||
|
||||
const EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>* 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<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>*>(record.ExceptionRecord);
|
||||
current_record = reinterpret_cast<exception_record*>(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<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>*>(pointers.ExceptionRecord));
|
||||
const auto exception_record_size =
|
||||
calculate_exception_record_size(*reinterpret_cast<exception_record*>(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<CONTEXT64*>(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<EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>*>(pointers.ExceptionRecord));
|
||||
const auto exception_record_obj =
|
||||
save_exception_record(allocator, *reinterpret_cast<exception_record*>(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<EmulatorTraits<Emu64>> record{};
|
||||
exception_record record{};
|
||||
memset(&record, 0, sizeof(record));
|
||||
record.ExceptionCode = static_cast<DWORD>(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<uint64_t>(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<memory_manager_wrapper>([this] {
|
||||
return memory_manager_wrapper{this->memory()}; //
|
||||
});
|
||||
|
||||
buffer.register_factory<x64_emulator_wrapper>([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_;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "process_context.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "file_system.hpp"
|
||||
#include "memory_manager.hpp"
|
||||
|
||||
std::unique_ptr<x64_emulator> 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<instruction_hook_callback> syscall_hooks_{};
|
||||
std::unordered_map<uint16_t, uint16_t> port_mappings_{};
|
||||
|
||||
memory_manager memory_manager_;
|
||||
|
||||
std::set<std::string, std::less<>> modules_{};
|
||||
|
||||
process_context process_;
|
||||
|
||||
Reference in New Issue
Block a user