From d782c80f3fa91d758ee9f7fa0f749a52b3ca0fb6 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 25 Oct 2024 18:56:16 +0200 Subject: [PATCH] Prepare support for serialization of non-default-constructible objects --- src/emulator/serialization.hpp | 41 +++++++++++-- src/windows-emulator-test/emulation_test.cpp | 2 +- .../emulation_test_utils.hpp | 19 +++--- .../serialization_test.cpp | 12 ++-- src/windows-emulator/emulator_utils.hpp | 9 ++- src/windows-emulator/handles.hpp | 2 +- src/windows-emulator/process_context.hpp | 58 ++++++++++++++----- src/windows-emulator/syscalls.cpp | 2 +- src/windows-emulator/windows_emulator.cpp | 13 +++-- 9 files changed, 112 insertions(+), 46 deletions(-) diff --git a/src/emulator/serialization.hpp b/src/emulator/serialization.hpp index cbc03229..c4e38e8c 100644 --- a/src/emulator/serialization.hpp +++ b/src/emulator/serialization.hpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace utils { @@ -145,7 +146,7 @@ namespace utils template T read() { - T object{}; + auto object = this->construct_object(); this->read(object); return object; } @@ -165,11 +166,11 @@ namespace utils template requires(std::is_invocable_r_v) - void read_optional(std::optional& val, const F& constructor) + void read_optional(std::optional& val, const F& factory) { if (this->read()) { - val.emplace(constructor()); + val.emplace(factory()); this->read(*val); } else @@ -214,7 +215,7 @@ namespace utils auto key = this->read(); auto value = this->read(); - map[std::move(key)] = std::move(value); + map.emplace(std::move(key), std::move(value)); } } @@ -263,9 +264,41 @@ namespace utils return this->offset_; } + template + requires(std::is_invocable_r_v) + void register_factory(F factory) + { + this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* { + return new T(f()); + }; + } + private: size_t offset_{0}; std::span buffer_{}; + std::unordered_map> factories_{}; + + template + T construct_object() + { + if constexpr (std::is_default_constructible_v) + { + return {}; + } + + const auto factory = this->factories_.find(std::type_index(typeid(T))); + if (factory == this->factories_.end()) + { + throw std::runtime_error( + "Object construction failed. Missing factory for type: " + std::string(typeid(T).name())); + } + + auto* object = static_cast(factory->second()); + auto obj = std::move(*object); + delete object; + + return obj; + } }; class buffer_serializer diff --git a/src/windows-emulator-test/emulation_test.cpp b/src/windows-emulator-test/emulation_test.cpp index 1c28b11a..2088359d 100644 --- a/src/windows-emulator-test/emulation_test.cpp +++ b/src/windows-emulator-test/emulation_test.cpp @@ -8,7 +8,7 @@ namespace test emu.logger.disable_output(true); emu.start(); - assert_terminated_successfully(emu); + ASSER_TERMINATED_SUCCESSFULLY(emu); } TEST(EmulationTest, CountedEmulationWorks) diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp index 1445ead8..3a6b5c62 100644 --- a/src/windows-emulator-test/emulation_test_utils.hpp +++ b/src/windows-emulator-test/emulation_test_utils.hpp @@ -3,16 +3,11 @@ #include #include -namespace test -{ - inline void assert_terminated_with_status(const windows_emulator& win_emu, const NTSTATUS status) - { - ASSERT_TRUE(win_emu.process().exit_status.has_value()); - ASSERT_EQ(*win_emu.process().exit_status, status); - } +#define ASSER_TERMINATED_WITH_STATUS(win_emu, status) \ + do { \ + ASSERT_TRUE(win_emu.process().exit_status.has_value()); \ + ASSERT_EQ(*win_emu.process().exit_status, status); \ + } while(false) - inline void assert_terminated_successfully(const windows_emulator& win_emu) - { - assert_terminated_with_status(win_emu, STATUS_SUCCESS); - } -} +#define ASSER_TERMINATED_SUCCESSFULLY(win_emu) \ + ASSER_TERMINATED_WITH_STATUS(win_emu, STATUS_SUCCESS) diff --git a/src/windows-emulator-test/serialization_test.cpp b/src/windows-emulator-test/serialization_test.cpp index be27f7a7..0dcc85f6 100644 --- a/src/windows-emulator-test/serialization_test.cpp +++ b/src/windows-emulator-test/serialization_test.cpp @@ -2,13 +2,13 @@ namespace test { - TEST(SerializationTest, DISABLED_SerializedDataIsReproducible) + TEST(SerializationTest, SerializedDataIsReproducible) { windows_emulator emu1{ "./test-sample.exe" }; emu1.logger.disable_output(true); emu1.start(); - assert_terminated_successfully(emu1); + ASSER_TERMINATED_SUCCESSFULLY(emu1); utils::buffer_serializer serializer1{}; emu1.serialize(serializer1); @@ -30,7 +30,7 @@ namespace test emu1.logger.disable_output(true); emu1.start(); - assert_terminated_successfully(emu1); + ASSER_TERMINATED_SUCCESSFULLY(emu1); utils::buffer_serializer serializer1{}; emu1.serialize(serializer1); @@ -39,7 +39,7 @@ namespace test emu2.logger.disable_output(true); emu2.start(); - assert_terminated_successfully(emu2); + ASSER_TERMINATED_SUCCESSFULLY(emu2); utils::buffer_serializer serializer2{}; emu2.serialize(serializer2); @@ -63,10 +63,10 @@ namespace test new_emu.deserialize(deserializer); new_emu.start(); - assert_terminated_successfully(new_emu); + ASSER_TERMINATED_SUCCESSFULLY(new_emu); emu.start(); - assert_terminated_successfully(emu); + ASSER_TERMINATED_SUCCESSFULLY(emu); utils::buffer_serializer serializer1{}; utils::buffer_serializer serializer2{}; diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index 5c54742d..c41966b5 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -7,7 +7,7 @@ class emulator_object public: using value_type = T; - emulator_object() = default; + //emulator_object() = default; emulator_object(emulator& emu, const uint64_t address = 0) : emu_(&emu) @@ -25,7 +25,7 @@ public: return this->address_; } - uint64_t size() const + constexpr uint64_t size() const { return sizeof(T); } @@ -174,6 +174,11 @@ public: return this->active_address_; } + emulator& get_emulator() const + { + return *this->emu_; + } + void serialize(utils::buffer_serializer& buffer) const { buffer.write(this->address_); diff --git a/src/windows-emulator/handles.hpp b/src/windows-emulator/handles.hpp index a6f2ff2a..49fd1d55 100644 --- a/src/windows-emulator/handles.hpp +++ b/src/windows-emulator/handles.hpp @@ -105,7 +105,7 @@ public: handle store(T value) { auto index = this->find_free_index(); - this->store_[index] = std::move(value); + this->store_.emplace(index, std::move(value)); return make_handle(index); } diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 130f24c8..b2b104e0 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -192,7 +192,10 @@ private: class emulator_thread : ref_counted_object { public: - emulator_thread() = default; + emulator_thread(x64_emulator& emu) + : emu_ptr(&emu) + { + } emulator_thread(x64_emulator& emu, const process_context& context, uint64_t start_address, uint64_t argument, uint64_t stack_size, uint32_t id); @@ -205,20 +208,7 @@ public: ~emulator_thread() { - if (marker.was_moved()) - { - return; - } - - if (this->stack_base) - { - this->emu_ptr->release_memory(this->stack_base, this->stack_size); - } - - if (this->gs_segment) - { - this->gs_segment->release(); - } + this->release(); } moved_marker marker{}; @@ -285,6 +275,11 @@ public: void serialize(utils::buffer_serializer& buffer) const { + if (this->marker.was_moved()) + { + throw std::runtime_error("Object was moved!"); + } + buffer.write(this->stack_base); buffer.write(this->stack_size); buffer.write(this->start_address); @@ -310,6 +305,13 @@ public: void deserialize(utils::buffer_deserializer& buffer) { + if (this->marker.was_moved()) + { + throw std::runtime_error("Object was moved!"); + } + + this->release(); + buffer.read(this->stack_base); buffer.read(this->stack_size); buffer.read(this->start_address); @@ -335,6 +337,31 @@ public: private: void setup_registers(x64_emulator& emu, const process_context& context) const; + + void release() + { + if (this->marker.was_moved()) + { + return; + } + + if (this->stack_base) + { + if (!this->emu_ptr) + { + throw std::runtime_error("Emulator was never assigned!"); + } + + this->emu_ptr->release_memory(this->stack_base, this->stack_size); + this->stack_base = 0; + } + + if (this->gs_segment) + { + this->gs_segment->release(); + this->gs_segment = {}; + } + } }; struct process_context @@ -454,6 +481,7 @@ struct process_context buffer.read_vector(this->default_register_set); buffer.read(this->current_thread_id); + buffer.read(this->threads); this->active_thread = this->threads.get(buffer.read()); diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index eee3288d..2ab2b3af 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -1848,7 +1848,7 @@ namespace const ULONG share_access, const ULONG open_options) { - return handle_NtCreateFile(c, file_handle, desired_access, object_attributes, io_status_block, {}, 0, + return handle_NtCreateFile(c, file_handle, desired_access, object_attributes, io_status_block, {c.emu}, 0, share_access, FILE_OPEN, open_options, 0, 0); } diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 30866e60..684537fe 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -279,9 +279,9 @@ namespace if (record.ExceptionRecord) { - record_mapping[&record] = record_obj; + record_mapping.emplace(&record, record_obj); - emulator_object nested_record_obj{}; + emulator_object nested_record_obj{allocator.get_emulator()}; const auto nested_record = record_mapping.find(record.ExceptionRecord); if (nested_record != record_mapping.end()) @@ -584,7 +584,7 @@ void emulator_thread::mark_as_ready(const NTSTATUS status) bool emulator_thread::is_thread_ready(process_context& context) { - if(this->exit_status.has_value()) + if (this->exit_status.has_value()) { return false; } @@ -903,7 +903,7 @@ void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count) const auto current_instructions = this->process().executed_instructions; const auto diff = current_instructions - start_instructions; - if(diff >= count) + if (diff >= count) { break; } @@ -923,6 +923,11 @@ void windows_emulator::serialize(utils::buffer_serializer& buffer) const void windows_emulator::deserialize(utils::buffer_deserializer& buffer) { + buffer.register_factory([this] + { + return emulator_thread(this->emu()); + }); + this->emu().deserialize(buffer); this->process_.deserialize(buffer); this->dispatcher_.deserialize(buffer);