Cleanup windows-emulator and extract classes

This commit is contained in:
Maurice Heumann
2025-02-10 13:42:49 +01:00
parent 1b544376ce
commit 2686251fde
12 changed files with 980 additions and 923 deletions

View File

@@ -6,13 +6,13 @@
#include "module/module_manager.hpp"
#include <utils/nt_handle.hpp>
#include <utils/file_handle.hpp>
#include <x64_emulator.hpp>
#include <serialization_helper.hpp>
#include "io_device.hpp"
#include "kusd_mmio.hpp"
#include "windows_objects.hpp"
#include "emulator_thread.hpp"
#define PEB_SEGMENT_SIZE (20 << 20) // 20 MB
#define GS_SEGMENT_SIZE (1 << 20) // 1 MB
@@ -25,486 +25,6 @@
#define GDT_LIMIT 0x1000
#define GDT_ENTRY_SIZE 0x8
class windows_emulator;
struct event : ref_counted_object
{
bool signaled{};
EVENT_TYPE type{};
std::u16string name{};
bool is_signaled()
{
const auto res = this->signaled;
if (this->type == SynchronizationEvent)
{
this->signaled = false;
}
return res;
}
void serialize_object(utils::buffer_serializer& buffer) const override
{
buffer.write(this->signaled);
buffer.write(this->type);
buffer.write(this->name);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
{
buffer.read(this->signaled);
buffer.read(this->type);
buffer.read(this->name);
}
};
struct mutant : ref_counted_object
{
uint32_t locked_count{0};
uint32_t owning_thread_id{};
std::u16string name{};
bool try_lock(const uint32_t thread_id)
{
if (this->locked_count == 0)
{
++this->locked_count;
this->owning_thread_id = thread_id;
return true;
}
if (this->owning_thread_id != thread_id)
{
return false;
}
++this->locked_count;
return true;
}
std::pair<uint32_t, bool> release(const uint32_t thread_id)
{
const auto old_count = this->locked_count;
if (this->locked_count <= 0 || this->owning_thread_id != thread_id)
{
return {old_count, false};
}
--this->locked_count;
return {old_count, true};
}
void serialize_object(utils::buffer_serializer& buffer) const override
{
buffer.write(this->locked_count);
buffer.write(this->owning_thread_id);
buffer.write(this->name);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
{
buffer.read(this->locked_count);
buffer.read(this->owning_thread_id);
buffer.read(this->name);
}
};
struct file_entry
{
std::filesystem::path file_path{};
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->file_path);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->file_path);
}
};
struct file_enumeration_state
{
size_t current_index{0};
std::vector<file_entry> files{};
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->current_index);
buffer.write_vector(this->files);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->current_index);
buffer.read_vector(this->files);
}
};
struct file : ref_counted_object
{
utils::file_handle handle{};
std::u16string name{};
std::optional<file_enumeration_state> enumeration_state{};
bool is_file() const
{
return this->handle;
}
bool is_directory() const
{
return !this->is_file();
}
void serialize_object(utils::buffer_serializer& buffer) const override
{
// TODO: Serialize handle
buffer.write(this->name);
buffer.write_optional(this->enumeration_state);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
{
buffer.read(this->name);
buffer.read_optional(this->enumeration_state);
this->handle = {};
}
};
struct section : ref_counted_object
{
std::u16string name{};
std::u16string file_name{};
uint64_t maximum_size{};
uint32_t section_page_protection{};
uint32_t allocation_attributes{};
bool is_image() const
{
return this->allocation_attributes & SEC_IMAGE;
}
void serialize_object(utils::buffer_serializer& buffer) const override
{
buffer.write(this->name);
buffer.write(this->file_name);
buffer.write(this->maximum_size);
buffer.write(this->section_page_protection);
buffer.write(this->allocation_attributes);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
{
buffer.read(this->name);
buffer.read(this->file_name);
buffer.read(this->maximum_size);
buffer.read(this->section_page_protection);
buffer.read(this->allocation_attributes);
}
};
struct semaphore : ref_counted_object
{
std::u16string name{};
uint32_t current_count{};
uint32_t max_count{};
bool try_lock()
{
if (this->current_count > 0)
{
--this->current_count;
return true;
}
return false;
}
std::pair<uint32_t, bool> release(const uint32_t release_count)
{
const auto old_count = this->current_count;
if (this->current_count + release_count > this->max_count)
{
return {old_count, false};
}
this->current_count += release_count;
return {old_count, true};
}
void serialize_object(utils::buffer_serializer& buffer) const override
{
buffer.write(this->name);
buffer.write(this->current_count);
buffer.write(this->max_count);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
{
buffer.read(this->name);
buffer.read(this->current_count);
buffer.read(this->max_count);
}
};
struct port : ref_counted_object
{
std::u16string name{};
uint64_t view_base{};
void serialize_object(utils::buffer_serializer& buffer) const override
{
buffer.write(this->name);
buffer.write(this->view_base);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
{
buffer.read(this->name);
buffer.read(this->view_base);
}
};
struct process_context;
class moved_marker
{
public:
moved_marker() = default;
moved_marker(const moved_marker& copy) = default;
moved_marker& operator=(const moved_marker&) = default;
moved_marker(moved_marker&& obj) noexcept
: moved_marker()
{
this->operator=(std::move(obj));
}
moved_marker& operator=(moved_marker&& obj) noexcept
{
if (this != &obj)
{
this->was_moved_ = obj.was_moved_;
obj.was_moved_ = true;
}
return *this;
}
~moved_marker() = default;
bool was_moved() const
{
return this->was_moved_;
}
void mark_as_moved()
{
this->was_moved_ = true;
}
private:
bool was_moved_{false};
};
class emulator_thread : public ref_counted_object
{
public:
emulator_thread(memory_manager& memory)
: memory_ptr(&memory)
{
}
emulator_thread(utils::buffer_deserializer& buffer)
: emulator_thread(buffer.read<memory_manager_wrapper>().get())
{
}
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;
emulator_thread& operator=(const emulator_thread&) = delete;
emulator_thread(emulator_thread&& obj) noexcept = default;
emulator_thread& operator=(emulator_thread&& obj) noexcept = default;
~emulator_thread() override
{
this->release();
}
moved_marker marker{};
memory_manager* memory_ptr{};
uint64_t stack_base{};
uint64_t stack_size{};
uint64_t start_address{};
uint64_t argument{};
uint64_t executed_instructions{0};
uint32_t id{};
std::u16string name{};
std::optional<NTSTATUS> exit_status{};
std::vector<handle> await_objects{};
bool await_any{false};
bool waiting_for_alert{false};
bool alerted{false};
std::optional<std::chrono::steady_clock::time_point> await_time{};
std::optional<NTSTATUS> pending_status{};
std::optional<emulator_allocator> gs_segment;
std::optional<emulator_object<TEB64>> teb;
std::vector<std::byte> last_registers{};
void mark_as_ready(NTSTATUS status);
bool is_await_time_over() const
{
return this->await_time.has_value() && this->await_time.value() < std::chrono::steady_clock::now();
}
bool is_terminated() const;
bool is_thread_ready(windows_emulator& win_emu);
void save(x64_emulator& emu)
{
this->last_registers = emu.save_registers();
}
void restore(x64_emulator& emu) const
{
emu.restore_registers(this->last_registers);
}
void setup_if_necessary(x64_emulator& emu, const process_context& context)
{
if (!this->executed_instructions)
{
this->setup_registers(emu, context);
}
if (this->pending_status.has_value())
{
const auto status = *this->pending_status;
this->pending_status = {};
emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
}
}
void serialize_object(utils::buffer_serializer& buffer) const override
{
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);
buffer.write(this->argument);
buffer.write(this->executed_instructions);
buffer.write(this->id);
buffer.write_string(this->name);
buffer.write_optional(this->exit_status);
buffer.write_vector(this->await_objects);
buffer.write(this->await_any);
buffer.write(this->waiting_for_alert);
buffer.write(this->alerted);
buffer.write_optional(this->await_time);
buffer.write_optional(this->pending_status);
buffer.write_optional(this->gs_segment);
buffer.write_optional(this->teb);
buffer.write_vector(this->last_registers);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
{
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);
buffer.read(this->argument);
buffer.read(this->executed_instructions);
buffer.read(this->id);
buffer.read_string(this->name);
buffer.read_optional(this->exit_status);
buffer.read_vector(this->await_objects);
buffer.read(this->await_any);
buffer.read(this->waiting_for_alert);
buffer.read(this->alerted);
buffer.read_optional(this->await_time);
buffer.read_optional(this->pending_status);
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);
}
void leak_memory()
{
this->marker.mark_as_moved();
}
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->memory_ptr)
{
throw std::runtime_error("Emulator was never assigned!");
}
this->memory_ptr->release_memory(this->stack_base, this->stack_size);
this->stack_base = 0;
}
if (this->gs_segment)
{
this->gs_segment->release(*this->memory_ptr);
this->gs_segment = {};
}
}
};
struct process_context
{
process_context(x64_emulator& emu, memory_manager& memory, file_system& file_sys)