mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-28 15:31:02 +00:00
Cleanup windows-emulator and extract classes
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user