mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-19 03:33:56 +00:00
Prepare serialization support
This commit is contained in:
@@ -1,191 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class buffer_deserializer
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::basic_string_view<T>& buffer)
|
||||
: buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T))
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::basic_string<T>& buffer)
|
||||
: buffer_deserializer(std::basic_string_view<T>(buffer.data(), buffer.size()))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::vector<T>& buffer)
|
||||
: buffer_deserializer(std::basic_string_view<T>(buffer.data(), buffer.size()))
|
||||
{
|
||||
}
|
||||
|
||||
void read(void* data, const size_t length)
|
||||
{
|
||||
if (this->offset_ + length > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
memcpy(data, this->buffer_.data() + this->offset_, length);
|
||||
this->offset_ += length;
|
||||
}
|
||||
|
||||
std::string read_data(const size_t length)
|
||||
{
|
||||
std::string result{};
|
||||
result.resize(length);
|
||||
|
||||
this->read(result.data(), result.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T read()
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
|
||||
T object{};
|
||||
this->read(&object, sizeof(object));
|
||||
return object;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> read_vector()
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
|
||||
std::vector<T> result{};
|
||||
const auto size = this->read<uint32_t>();
|
||||
const auto totalSize = size * sizeof(T);
|
||||
|
||||
if (this->offset_ + totalSize > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
result.resize(size);
|
||||
this->read(result.data(), totalSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string read_string()
|
||||
{
|
||||
std::string result{};
|
||||
const auto size = this->read<uint32_t>();
|
||||
|
||||
if (this->offset_ + size > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
result.resize(size);
|
||||
this->read(result.data(), size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t get_remaining_size() const
|
||||
{
|
||||
return this->buffer_.size() - offset_;
|
||||
}
|
||||
|
||||
std::string get_remaining_data()
|
||||
{
|
||||
return this->read_data(this->get_remaining_size());
|
||||
}
|
||||
|
||||
size_t get_offset() const
|
||||
{
|
||||
return this->offset_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t offset_{0};
|
||||
std::basic_string_view<std::byte> buffer_{};
|
||||
};
|
||||
|
||||
class buffer_serializer
|
||||
{
|
||||
public:
|
||||
buffer_serializer() = default;
|
||||
|
||||
void write(const void* buffer, const size_t length)
|
||||
{
|
||||
this->buffer_.append(static_cast<const char*>(buffer), length);
|
||||
}
|
||||
|
||||
void write(const char* text)
|
||||
{
|
||||
this->write(text, strlen(text));
|
||||
}
|
||||
|
||||
void write_string(const char* str, const size_t length)
|
||||
{
|
||||
this->write<uint32_t>(static_cast<uint32_t>(length));
|
||||
this->write(str, length);
|
||||
}
|
||||
|
||||
void write_string(const std::string& str)
|
||||
{
|
||||
this->write_string(str.data(), str.size());
|
||||
}
|
||||
|
||||
void write_string(const char* str)
|
||||
{
|
||||
this->write_string(str, strlen(str));
|
||||
}
|
||||
|
||||
void write(const buffer_serializer& object)
|
||||
{
|
||||
const auto& buffer = object.get_buffer();
|
||||
this->write(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& object)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
this->write(&object, sizeof(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const std::vector<T>& vec)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
this->write(vec.data(), vec.size() * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_vector(const std::vector<T>& vec)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
this->write(static_cast<uint32_t>(vec.size()));
|
||||
this->write(vec);
|
||||
}
|
||||
|
||||
const std::string& get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
std::string move_buffer()
|
||||
{
|
||||
return std::move(this->buffer_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string buffer_{};
|
||||
};
|
||||
}
|
||||
@@ -63,6 +63,40 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
static void serialize(utils::buffer_serializer& buffer, const memory_manager::committed_region& region)
|
||||
{
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write(region.pemissions);
|
||||
}
|
||||
|
||||
static void deserialize(utils::buffer_deserializer& buffer, memory_manager::committed_region& region)
|
||||
{
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
region.pemissions = buffer.read<memory_permission>();
|
||||
}
|
||||
|
||||
static void serialize(utils::buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write_map(region.committed_regions);
|
||||
}
|
||||
|
||||
static void deserialize(utils::buffer_deserializer& buffer, memory_manager::reserved_region& region)
|
||||
{
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
region.committed_regions = buffer.read_map<uint64_t, memory_manager::committed_region>();
|
||||
}
|
||||
|
||||
void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write_map(this->reserved_regions_);
|
||||
}
|
||||
|
||||
void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
this->reserved_regions_ = buffer.read_map<uint64_t, reserved_region>();
|
||||
}
|
||||
|
||||
bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions,
|
||||
memory_permission* old_permissions)
|
||||
{
|
||||
@@ -293,7 +327,7 @@ uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint
|
||||
for (const auto& region : this->reserved_regions_)
|
||||
{
|
||||
const auto region_end = region.first + region.second.length;
|
||||
if(region_end < start_address)
|
||||
if (region_end < start_address)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "memory_region.hpp"
|
||||
#include "address_utils.hpp"
|
||||
#include "serialization.hpp"
|
||||
|
||||
struct region_info : basic_memory_region
|
||||
{
|
||||
@@ -85,4 +86,8 @@ private:
|
||||
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:
|
||||
void serialize_memory_state(utils::buffer_serializer& buffer) const;
|
||||
void deserialize_memory_state(utils::buffer_deserializer& buffer);
|
||||
};
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
#include "byte_buffer.hpp"
|
||||
|
||||
struct serializable
|
||||
{
|
||||
virtual ~serializable() = default;
|
||||
virtual void serialize(utils::buffer_serializer& buffer) = 0;
|
||||
virtual void deserialize(utils::buffer_deserializer& buffer) = 0;
|
||||
};
|
||||
235
src/emulator/serialization.hpp
Normal file
235
src/emulator/serialization.hpp
Normal file
@@ -0,0 +1,235 @@
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class buffer_serializer;
|
||||
class buffer_deserializer;
|
||||
|
||||
struct serializable
|
||||
{
|
||||
virtual ~serializable() = default;
|
||||
virtual void serialize(buffer_serializer& buffer) const = 0;
|
||||
virtual void deserialize(buffer_deserializer& buffer) = 0;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename, typename = void>
|
||||
struct has_serialize_function : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_serialize_function<T, std::void_t<decltype(serialize(std::declval<buffer_serializer&>(), std::declval<const T&>()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename, typename = void>
|
||||
struct has_deserialize_function : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_deserialize_function<T, std::void_t<decltype(deserialize(std::declval<buffer_deserializer&>(), std::declval<T&>()))>>
|
||||
: std::true_type {};
|
||||
}
|
||||
|
||||
class buffer_deserializer
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::span<T>& buffer)
|
||||
: buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T))
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
}
|
||||
|
||||
std::span<const std::byte> read_data(const size_t length)
|
||||
{
|
||||
if (this->offset_ + length > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
const std::span result(this->buffer_.data() + this->offset_, length);
|
||||
this->offset_ += length;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void read(void* data, const size_t length)
|
||||
{
|
||||
const auto span = this->read_data(length);
|
||||
memcpy(data, span.data(), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T read()
|
||||
{
|
||||
T object{};
|
||||
|
||||
if constexpr (std::is_base_of_v<serializable, T>)
|
||||
{
|
||||
object.deserialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_deserialize_function<T>::value)
|
||||
{
|
||||
deserialize(*this, object);
|
||||
}
|
||||
else if constexpr (std::is_trivially_copyable_v<T>)
|
||||
{
|
||||
this->read(&object, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(std::false_type::value, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> read_vector()
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
|
||||
std::vector<T> result{};
|
||||
const auto size = this->read<uint64_t>();
|
||||
const auto totalSize = size * sizeof(T);
|
||||
|
||||
if (this->offset_ + totalSize > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
result.resize(size);
|
||||
this->read(result.data(), totalSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
std::map<Key, Value> read_map()
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
std::map<Key, Value> map{};
|
||||
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto key = this->read<Key>();
|
||||
auto value = this->read<Value>();
|
||||
|
||||
map[std::move(key)] = std::move(value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
std::string read_string()
|
||||
{
|
||||
std::string result{};
|
||||
const auto size = this->read<uint64_t>();
|
||||
const auto span = this->read_data(size);
|
||||
|
||||
result.resize(size);
|
||||
memcpy(result.data(), span.data(), size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t get_remaining_size() const
|
||||
{
|
||||
return this->buffer_.size() - offset_;
|
||||
}
|
||||
|
||||
std::span<const std::byte> get_remaining_data()
|
||||
{
|
||||
return this->read_data(this->get_remaining_size());
|
||||
}
|
||||
|
||||
size_t get_offset() const
|
||||
{
|
||||
return this->offset_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t offset_{0};
|
||||
std::span<const std::byte> buffer_{};
|
||||
};
|
||||
|
||||
class buffer_serializer
|
||||
{
|
||||
public:
|
||||
buffer_serializer() = default;
|
||||
|
||||
void write(const void* buffer, const size_t length)
|
||||
{
|
||||
this->buffer_.append(static_cast<const char*>(buffer), length);
|
||||
}
|
||||
|
||||
void write(const buffer_serializer& object)
|
||||
{
|
||||
const auto& buffer = object.get_buffer();
|
||||
this->write(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& object)
|
||||
{
|
||||
if constexpr (std::is_base_of_v<serializable, T>)
|
||||
{
|
||||
object.serialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_serialize_function<T>::value)
|
||||
{
|
||||
serialize(*this, object);
|
||||
}
|
||||
else if constexpr (std::is_trivially_copyable_v<T>)
|
||||
{
|
||||
this->write(&object, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(std::false_type::value, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_span(const std::span<T> vec)
|
||||
{
|
||||
this->write(static_cast<uint64_t>(vec.size()));
|
||||
|
||||
for (const auto& v : vec)
|
||||
{
|
||||
this->write(v);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void write_map(const std::map<Key, Value>& map)
|
||||
{
|
||||
this->write<uint64_t>(map.size());
|
||||
|
||||
for (const auto& entry : map)
|
||||
{
|
||||
this->write(entry.first);
|
||||
this->write(entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
std::string move_buffer()
|
||||
{
|
||||
return std::move(this->buffer_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string buffer_{};
|
||||
};
|
||||
}
|
||||
@@ -318,7 +318,7 @@ namespace
|
||||
context.process_params.access([&](RTL_USER_PROCESS_PARAMETERS& proc_params)
|
||||
{
|
||||
proc_params.Length = sizeof(proc_params);
|
||||
proc_params.Flags = 0x6001; //| 0x80000000; // Prevent CsrClientConnectToServer
|
||||
proc_params.Flags = 0x6001 | 0x80000000; // Prevent CsrClientConnectToServer
|
||||
|
||||
proc_params.ConsoleHandle = CONSOLE_HANDLE.h;
|
||||
proc_params.StandardOutput = STDOUT_HANDLE.h;
|
||||
@@ -572,6 +572,7 @@ namespace
|
||||
const auto permission = get_permission_string(operation);
|
||||
const auto ip = emu->read_instruction_pointer();
|
||||
|
||||
|
||||
if (type == memory_violation_type::protection)
|
||||
{
|
||||
printf("Protection violation: %llX (%zX) - %s at %llX\n", address, size, permission.c_str(), ip);
|
||||
@@ -585,18 +586,23 @@ namespace
|
||||
return memory_violation_continuation::resume;
|
||||
});
|
||||
|
||||
/*
|
||||
watch_object(*emu, context.teb);
|
||||
watch_object(*emu, context.peb);
|
||||
watch_object(*emu, context.process_params);
|
||||
watch_object(*emu, context.kusd);
|
||||
*/
|
||||
|
||||
context.verbose = false;
|
||||
|
||||
emu->hook_memory_execution(0, std::numeric_limits<size_t>::max(), [&](const uint64_t address, const size_t)
|
||||
{
|
||||
++context.executed_instructions;
|
||||
|
||||
if(address == 0x1800629BA)
|
||||
{
|
||||
puts("Bad dll");
|
||||
emu->stop();
|
||||
}
|
||||
|
||||
const auto* binary = context.module_manager.find_by_address(address);
|
||||
|
||||
if (binary)
|
||||
|
||||
@@ -1316,6 +1316,22 @@ namespace
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& /*c*/, const uint64_t /*port_handle*/, const ULONG /*flags*/,
|
||||
const emulator_object<PORT_MESSAGE> /*send_message*/,
|
||||
const emulator_object<ALPC_MESSAGE_ATTRIBUTES> /*send_message_attributes*/,
|
||||
const emulator_object<PORT_MESSAGE> receive_message,
|
||||
const emulator_object<SIZE_T> /*buffer_length*/,
|
||||
const emulator_object<ALPC_MESSAGE_ATTRIBUTES> /*receive_message_attributes*/,
|
||||
const emulator_object<LARGE_INTEGER> /*timeout*/)
|
||||
{
|
||||
receive_message.access([](PORT_MESSAGE& msg)
|
||||
{
|
||||
msg.u1.Length = 0;
|
||||
});
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtInitializeNlsFiles(const syscall_context& c, const emulator_object<uint64_t> base_address,
|
||||
const emulator_object<LCID> default_locale_id,
|
||||
const emulator_object<LARGE_INTEGER> /*default_casing_table_size*/)
|
||||
@@ -1602,6 +1618,7 @@ syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, co
|
||||
add_handler(NtDuplicateObject);
|
||||
add_handler(NtQueryInformationThread);
|
||||
add_handler(NtQueryWnfStateNameInformation);
|
||||
add_handler(NtAlpcSendWaitReceivePort);
|
||||
|
||||
#undef add_handler
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user