diff --git a/src/emulator/memory_manager.cpp b/src/emulator/memory_manager.cpp index 46f8d09f..d741993e 100644 --- a/src/emulator/memory_manager.cpp +++ b/src/emulator/memory_manager.cpp @@ -84,17 +84,52 @@ static void serialize(utils::buffer_serializer& buffer, const memory_manager::re static void deserialize(utils::buffer_deserializer& buffer, memory_manager::reserved_region& region) { region.length = static_cast(buffer.read()); - region.committed_regions = buffer.read_map(); + buffer.read_map(region.committed_regions); } void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer) const { buffer.write_map(this->reserved_regions_); + + if (!this->use_in_place_serialization()) + { + std::vector data{}; + + for (const auto& reserved_region : this->reserved_regions_) + { + for (const auto& region : reserved_region.second.committed_regions) + { + data.resize(region.second.length); + + this->read_memory(region.first, data.data(), region.second.length); + + buffer.write(data.data(), region.second.length); + } + } + } } void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer) { - this->reserved_regions_ = buffer.read_map(); + buffer.read_map(this->reserved_regions_); + + if (!this->use_in_place_serialization()) + { + std::vector data{}; + + for (const auto& reserved_region : this->reserved_regions_) + { + for (const auto& region : reserved_region.second.committed_regions) + { + data.resize(region.second.length); + + buffer.read(data.data(), region.second.length); + + this->map_memory(region.first, region.second.length, region.second.pemissions); + this->write_memory(region.first, data.data(), region.second.length); + } + } + } } bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions, diff --git a/src/emulator/memory_manager.hpp b/src/emulator/memory_manager.hpp index 0205644e..116ea7af 100644 --- a/src/emulator/memory_manager.hpp +++ b/src/emulator/memory_manager.hpp @@ -45,8 +45,8 @@ public: this->write_memory(address, &value, sizeof(value)); } - virtual void read_memory(uint64_t address, void* data, size_t size) = 0; - virtual bool try_read_memory(uint64_t address, void* data, size_t size) = 0; + 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, @@ -75,10 +75,22 @@ public: return allocation_base; } + bool use_in_place_serialization() const + { + return this->in_place_serialization_; + } + + void set_in_place_serialization(const bool value) + { + this->in_place_serialization_ = value; + } + private: using reserved_region_map = std::map; reserved_region_map reserved_regions_{}; + bool in_place_serialization_{false}; + reserved_region_map::iterator find_reserved_region(uint64_t address); bool overlaps_reserved_region(uint64_t address, size_t size) const; diff --git a/src/emulator/serialization.hpp b/src/emulator/serialization.hpp index 5235de2a..dfa0546f 100644 --- a/src/emulator/serialization.hpp +++ b/src/emulator/serialization.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -20,32 +21,52 @@ namespace utils namespace detail { template - struct has_serialize_function : std::false_type {}; + struct has_serialize_function : std::false_type + { + }; template - struct has_serialize_function(), std::declval()))>> - : std::true_type {}; + struct has_serialize_function(), + std::declval()))>> + : std::true_type + { + }; template - struct has_deserialize_function : std::false_type {}; + struct has_deserialize_function : std::false_type + { + }; template - struct has_deserialize_function(), std::declval()))>> - : std::true_type {}; + struct has_deserialize_function(), std::declval()))>> + : std::true_type + { + }; } class buffer_deserializer { public: template - buffer_deserializer(const std::span& buffer) + buffer_deserializer(const std::span buffer) : buffer_(reinterpret_cast(buffer.data()), buffer.size() * sizeof(T)) { static_assert(std::is_trivially_copyable_v, "Type must be trivially copyable"); } + template + buffer_deserializer(const std::vector& buffer) + : buffer_deserializer(std::span(buffer)) + { + } + std::span read_data(const size_t length) { +#ifndef NDEBUG + const uint64_t real_old_size = this->offset_; +#endif + if (this->offset_ + length > this->buffer_.size()) { throw std::runtime_error("Out of bounds read from byte buffer"); @@ -54,6 +75,23 @@ namespace utils const std::span result(this->buffer_.data() + this->offset_, length); this->offset_ += length; + +#ifndef NDEBUG + uint64_t old_size{}; + if (this->offset_ + sizeof(old_size) > this->buffer_.size()) + { + throw std::runtime_error("Out of bounds read from byte buffer"); + } + + memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size)); + if (old_size != real_old_size) + { + throw std::runtime_error("Reading from serialized buffer mismatches written data!"); + } + + this->offset_ += sizeof(old_size); +#endif + return result; } @@ -64,10 +102,8 @@ namespace utils } template - T read() + void read(T& object) { - T object{}; - if constexpr (std::is_base_of_v) { object.deserialize(*this); @@ -78,63 +114,98 @@ namespace utils } else if constexpr (std::is_trivially_copyable_v) { - this->read(&object, sizeof(object)); + union + { + T* type_{}; + void* void_; + } pointers; + + pointers.type_ = &object; + + this->read(pointers.void_, sizeof(object)); } else { static_assert(std::false_type::value, "Key must be trivially copyable or implement serializable!"); std::abort(); } + } + template + T read() + { + T object{}; + this->read(object); return object; } + template + void read_vector(std::vector& result) + { + const auto size = this->read(); + result.clear(); + result.reserve(size); + + for (uint64_t i = 0; i < size; ++i) + { + result.emplace_back(this->read()); + } + } + template std::vector read_vector() { - static_assert(std::is_trivially_copyable_v, "Type must be trivially copyable"); - std::vector result{}; - const auto size = this->read(); - 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); - + this->read_vector(result); return result; } - template - std::map read_map() + template + void read_map(Map& map) { + using key_type = typename Map::key_type; + using value_type = typename Map::mapped_type; + + map.clear(); + const auto size = this->read(); - std::map map{}; for (uint64_t i = 0; i < size; ++i) { - auto key = this->read(); - auto value = this->read(); + auto key = this->read(); + auto value = this->read(); map[std::move(key)] = std::move(value); } + } + template + Map read_map() + { + Map map{}; + this->read_map(map); return map; } - std::string read_string() + template + void read_string(std::basic_string& result) { - std::string result{}; const auto size = this->read(); - const auto span = this->read_data(size); - result.resize(size); - memcpy(result.data(), span.data(), size); + result.clear(); + result.reserve(size); + for (uint64_t i = 0; i < size; ++i) + { + result.push_back(this->read()); + } + } + + template + std::basic_string read_string() + { + std::basic_string result{}; + this->read_string(result); return result; } @@ -165,7 +236,17 @@ namespace utils void write(const void* buffer, const size_t length) { - this->buffer_.append(static_cast(buffer), length); +#ifndef NDEBUG + const uint64_t old_size = this->buffer_.size(); +#endif + + const auto* byte_buffer = static_cast(buffer); + this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length); + +#ifndef NDEBUG + const auto* security_buffer = reinterpret_cast(&old_size); + this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size)); +#endif } void write(const buffer_serializer& object) @@ -187,7 +268,15 @@ namespace utils } else if constexpr (std::is_trivially_copyable_v) { - this->write(&object, sizeof(object)); + union + { + const T* type_{}; + const void* void_; + } pointers; + + pointers.type_ = &object; + + this->write(pointers.void_, sizeof(object)); } else { @@ -207,8 +296,26 @@ namespace utils } } - template - void write_map(const std::map& map) + template + void write_vector(const std::vector vec) + { + this->write_span(std::span(vec)); + } + + template + void write_string(const std::basic_string_view str) + { + this->write_span(str); + } + + template + void write_string(const std::basic_string& str) + { + this->write_string(std::basic_string_view(str)); + } + + template + void write_map(const Map& map) { this->write(map.size()); @@ -219,17 +326,41 @@ namespace utils } } - const std::string& get_buffer() const + const std::vector& get_buffer() const { return this->buffer_; } - std::string move_buffer() + std::vector move_buffer() { return std::move(this->buffer_); } private: - std::string buffer_{}; + std::vector buffer_{}; }; + + template <> + inline void buffer_deserializer::read(std::string& object) + { + object = this->read_string(); + } + + template <> + inline void buffer_deserializer::read(std::wstring& object) + { + object = this->read_string(); + } + + template <> + inline void buffer_serializer::write(const std::string& object) + { + this->write_string(object); + } + + template <> + inline void buffer_serializer::write(const std::wstring& object) + { + this->write_string(object); + } } diff --git a/src/unicorn_emulator/unicorn_x64_emulator.cpp b/src/unicorn_emulator/unicorn_x64_emulator.cpp index 55a91f51..8e2677c0 100644 --- a/src/unicorn_emulator/unicorn_x64_emulator.cpp +++ b/src/unicorn_emulator/unicorn_x64_emulator.cpp @@ -109,10 +109,12 @@ namespace unicorn class uc_context_serializer { public: - uc_context_serializer(uc_engine* uc) + uc_context_serializer(uc_engine* uc, const bool in_place) : uc_(uc) - , size_(uc_context_size(uc)) { + uc_ctl_context_mode(uc, UC_CTL_CONTEXT_CPU | (in_place ? UC_CTL_CONTEXT_MEMORY : 0)); + + this->size_ = uc_context_size(uc); uce(uc_context_alloc(uc, &this->context_)); } @@ -296,12 +298,12 @@ namespace unicorn uce(uc_mem_unmap(*this, address, size)); } - bool try_read_memory(const uint64_t address, void* data, const size_t size) override + bool try_read_memory(const uint64_t address, void* data, const size_t size) const override { return uc_mem_read(*this, address, data, size) == UC_ERR_OK; } - void read_memory(const uint64_t address, void* data, const size_t size) override + void read_memory(const uint64_t address, void* data, const size_t size) const override { uce(uc_mem_read(*this, address, data, size)); } @@ -498,13 +500,13 @@ namespace unicorn void serialize_state(utils::buffer_serializer& buffer) const override { - const uc_context_serializer serializer(this->uc_); + const uc_context_serializer serializer(this->uc_, this->use_in_place_serialization()); serializer.serialize(buffer); } void deserialize_state(utils::buffer_deserializer& buffer) override { - const uc_context_serializer serializer(this->uc_); + const uc_context_serializer serializer(this->uc_, this->use_in_place_serialization()); serializer.deserialize(buffer); } diff --git a/src/windows_emulator/emulator_utils.hpp b/src/windows_emulator/emulator_utils.hpp index 33a60872..7598cb64 100644 --- a/src/windows_emulator/emulator_utils.hpp +++ b/src/windows_emulator/emulator_utils.hpp @@ -2,21 +2,21 @@ #include "memory_utils.hpp" template -class emulator_object +class emulator_object : utils::serializable { public: using value_type = T; emulator_object() = default; - emulator_object(emulator& emu, const void* address) - : emulator_object(emu, reinterpret_cast(address)) + emulator_object(emulator& emu, const uint64_t address = 0) + : emu_(&emu) + , address_(address) { } - emulator_object(emulator& emu, const uint64_t address) - : emu_(&emu) - , address_(address) + emulator_object(emulator& emu, const void* address) + : emulator_object(emu, reinterpret_cast(address)) { } @@ -68,15 +68,28 @@ public: this->write(obj, index); } + void serialize(utils::buffer_serializer& buffer) const override + { + buffer.write(this->address_); + } + + void deserialize(utils::buffer_deserializer& buffer) override + { + buffer.read(this->address_); + } + private: emulator* emu_{}; uint64_t address_{}; }; -class emulator_allocator +class emulator_allocator : utils::serializable { public: - emulator_allocator() = default; + emulator_allocator(emulator& emu) + : emu_(&emu) + { + } emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size) : emu_(&emu) @@ -146,6 +159,20 @@ public: return this->size_; } + void serialize(utils::buffer_serializer& buffer) const override + { + buffer.write(this->address_); + buffer.write(this->size_); + buffer.write(this->active_address_); + } + + void deserialize(utils::buffer_deserializer& buffer) override + { + buffer.read(this->address_); + buffer.read(this->size_); + buffer.read(this->active_address_); + } + private: emulator* emu_{}; uint64_t address_{}; diff --git a/src/windows_emulator/handles.hpp b/src/windows_emulator/handles.hpp index 72a74a1e..01769eac 100644 --- a/src/windows_emulator/handles.hpp +++ b/src/windows_emulator/handles.hpp @@ -68,7 +68,8 @@ constexpr handle make_pseudo_handle(const uint32_t id, const handle_types::type } template -class handle_store + requires(std::is_base_of_v) +class handle_store : utils::serializable { public: handle store(T value) @@ -134,6 +135,16 @@ public: return this->erase(hh); } + void serialize(utils::buffer_serializer& buffer) const override + { + buffer.write_map(this->store_); + } + + void deserialize(utils::buffer_deserializer& buffer) override + { + buffer.read_map(this->store_); + } + private: using value_map = std::map; diff --git a/src/windows_emulator/main.cpp b/src/windows_emulator/main.cpp index 3c983883..3ae3b7a8 100644 --- a/src/windows_emulator/main.cpp +++ b/src/windows_emulator/main.cpp @@ -289,7 +289,7 @@ namespace process_context setup_context(x64_emulator& emu, const std::filesystem::path& file) { - process_context context{}; + process_context context{emu}; setup_stack(emu, STACK_ADDRESS, STACK_SIZE); setup_gdt(emu); diff --git a/src/windows_emulator/module/module_manager.cpp b/src/windows_emulator/module/module_manager.cpp index 4dde2fa3..f708f5ae 100644 --- a/src/windows_emulator/module/module_manager.cpp +++ b/src/windows_emulator/module/module_manager.cpp @@ -2,6 +2,48 @@ #include "module_manager.hpp" #include "module_mapping.hpp" +static void serialize(utils::buffer_serializer& buffer, const exported_symbol& sym) +{ + buffer.write(sym.name); + buffer.write(sym.ordinal); + buffer.write(sym.rva); + buffer.write(sym.address); +} + +static void deserialize(utils::buffer_deserializer& buffer, exported_symbol& sym) +{ + buffer.read(sym.name); + buffer.read(sym.ordinal); + buffer.read(sym.rva); + buffer.read(sym.address); +} + +static void serialize(utils::buffer_serializer& buffer, const mapped_module& mod) +{ + buffer.write_string(mod.name); + buffer.write_string(mod.path.wstring()); + + buffer.write(mod.image_base); + buffer.write(mod.size_of_image); + buffer.write(mod.entry_point); + + buffer.write_vector(mod.exports); + buffer.write_map(mod.address_names); +} + +static void deserialize(utils::buffer_deserializer& buffer, mapped_module& mod) +{ + mod.name = buffer.read_string(); + mod.path = buffer.read_string(); + + buffer.read(mod.image_base); + buffer.read(mod.size_of_image); + buffer.read(mod.entry_point); + + buffer.read_vector(mod.exports); + buffer.read_map(mod.address_names); +} + module_manager::module_manager(emulator& emu) : emu_(&emu) { @@ -30,3 +72,13 @@ mapped_module* module_manager::map_module(const std::filesystem::path& file) const auto entry = this->modules_.try_emplace(image_base, std::move(*mod)); return &entry.first->second; } + +void module_manager::serialize(utils::buffer_serializer& buffer) const +{ + buffer.write_map(this->modules_); +} + +void module_manager::deserialize(utils::buffer_deserializer& buffer) +{ + buffer.read_map(this->modules_); +} diff --git a/src/windows_emulator/module/module_manager.hpp b/src/windows_emulator/module/module_manager.hpp index 39af5e69..7b753a7c 100644 --- a/src/windows_emulator/module/module_manager.hpp +++ b/src/windows_emulator/module/module_manager.hpp @@ -2,10 +2,9 @@ #include "mapped_module.hpp" #include -class module_manager +class module_manager : public utils::serializable { public: - module_manager() = default; // TODO: Get rid of that module_manager(emulator& emu); mapped_module* map_module(const std::filesystem::path& file); @@ -21,6 +20,9 @@ public: return nullptr; } + void serialize(utils::buffer_serializer& buffer) const override; + void deserialize(utils::buffer_deserializer& buffer) override; + private: emulator* emu_{}; diff --git a/src/windows_emulator/process_context.hpp b/src/windows_emulator/process_context.hpp index a9ee313e..dfeebbe1 100644 --- a/src/windows_emulator/process_context.hpp +++ b/src/windows_emulator/process_context.hpp @@ -5,7 +5,9 @@ #include "module/module_manager.hpp" #include -struct event +#include + +struct event : utils::serializable { bool signaled{}; EVENT_TYPE type{}; @@ -21,30 +23,80 @@ struct event return res; } + + void serialize(utils::buffer_serializer& buffer) const override + { + buffer.write(this->signaled); + buffer.write(this->type); + } + + void deserialize(utils::buffer_deserializer& buffer) override + { + buffer.read(this->signaled); + buffer.read(this->type); + } }; -struct file +struct file : utils::serializable { utils::nt::handle handle{}; std::wstring name{}; + + void serialize(utils::buffer_serializer& buffer) const override + { + buffer.write(this->name); + // TODO: Serialize handle + } + + void deserialize(utils::buffer_deserializer& buffer) override + { + buffer.read(this->name); + this->handle = INVALID_HANDLE_VALUE; + } }; -struct semaphore +struct semaphore : utils::serializable { std::wstring name{}; volatile uint32_t current_count{}; uint32_t max_count{}; + + void serialize(utils::buffer_serializer& buffer) const override + { + buffer.write(this->name); + buffer.write(this->current_count); + buffer.write(this->max_count); + } + + void deserialize(utils::buffer_deserializer& buffer) override + { + buffer.read(this->name); + buffer.read(this->current_count); + buffer.read(this->max_count); + } }; -struct process_context +struct process_context : utils::serializable { - uint64_t executed_instructions{0}; - emulator_object teb{}; - emulator_object peb{}; - emulator_object process_params{}; - emulator_object kusd{}; + process_context(x64_emulator& emu) + : emu(&emu) + , teb(emu) + , peb(emu) + , process_params(emu) + , kusd(emu) + , module_manager(emu) + , gs_segment(emu) + { + } - module_manager module_manager{}; + x64_emulator* emu{}; + uint64_t executed_instructions{0}; + emulator_object teb; + emulator_object peb; + emulator_object process_params; + emulator_object kusd; + + module_manager module_manager; mapped_module* executable{}; mapped_module* ntdll{}; @@ -56,7 +108,53 @@ struct process_context handle_store files{}; handle_store semaphores{}; std::map atoms{}; - emulator_allocator gs_segment{}; + emulator_allocator gs_segment; bool verbose{false}; + + void serialize(utils::buffer_serializer& buffer) const override + { + buffer.write(this->executed_instructions); + buffer.write(this->teb); + buffer.write(this->peb); + buffer.write(this->process_params); + buffer.write(this->kusd); + buffer.write(this->module_manager); + + buffer.write(this->executable->image_base); + buffer.write(this->ntdll->image_base); + buffer.write(this->win32u->image_base); + + buffer.write(this->shared_section_size); + buffer.write(this->events); + buffer.write(this->files); + buffer.write(this->semaphores); + buffer.write_map(this->atoms); + buffer.write(this->gs_segment); + } + + void deserialize(utils::buffer_deserializer& buffer) override + { + buffer.read(this->executed_instructions); + buffer.read(this->teb); + buffer.read(this->peb); + buffer.read(this->process_params); + buffer.read(this->kusd); + buffer.read(this->module_manager); + + const auto executable_base = buffer.read(); + const auto ntdll_base = buffer.read(); + const auto win32u_base = buffer.read(); + + this->executable = this->module_manager.find_by_address(executable_base); + this->ntdll = this->module_manager.find_by_address(ntdll_base); + this->win32u = this->module_manager.find_by_address(win32u_base); + + buffer.read(this->shared_section_size); + buffer.read(this->events); + buffer.read(this->files); + buffer.read(this->semaphores); + buffer.read_map(this->atoms); + buffer.read(this->gs_segment); + } }; diff --git a/src/windows_emulator/syscalls.cpp b/src/windows_emulator/syscalls.cpp index 09863317..efec9c0e 100644 --- a/src/windows_emulator/syscalls.cpp +++ b/src/windows_emulator/syscalls.cpp @@ -308,7 +308,10 @@ namespace //return STATUS_NOT_SUPPORTED; } - event e{initial_state != FALSE, event_type}; + event e{}; + e.type = event_type; + e.signaled = initial_state != FALSE; + const auto handle = c.proc.events.store(std::move(e)); event_handle.write(handle.bits); @@ -414,7 +417,10 @@ namespace return STATUS_FILE_INVALID; } - const auto handle = c.proc.files.store(file{{}, std::move(filename)}); + file f{}; + f.name = std::move(filename); + + const auto handle = c.proc.files.store(std::move(f)); section_handle.write(handle.bits); return STATUS_SUCCESS; @@ -1316,12 +1322,15 @@ namespace return STATUS_NOT_SUPPORTED; } - NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& /*c*/, const uint64_t /*port_handle*/, const ULONG /*flags*/, + NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& /*c*/, const uint64_t /*port_handle*/, + const ULONG /*flags*/, const emulator_object /*send_message*/, - const emulator_object /*send_message_attributes*/, + const emulator_object /*send_message_attributes*/ + , const emulator_object receive_message, const emulator_object /*buffer_length*/, - const emulator_object /*receive_message_attributes*/, + const emulator_object + /*receive_message_attributes*/, const emulator_object /*timeout*/) { receive_message.access([](PORT_MESSAGE& msg)