From 2e2b4ffb2fd28be314ca173b860a7a5401ebde2c Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 23 Nov 2024 19:32:14 +0100 Subject: [PATCH 1/5] KUSD MMIO --- src/windows-emulator/kusd_mmio.cpp | 218 ++++++++++++++++++++++ src/windows-emulator/kusd_mmio.hpp | 50 +++++ src/windows-emulator/process_context.hpp | 10 +- src/windows-emulator/syscalls.cpp | 18 +- src/windows-emulator/windows_emulator.cpp | 98 +--------- 5 files changed, 282 insertions(+), 112 deletions(-) create mode 100644 src/windows-emulator/kusd_mmio.cpp create mode 100644 src/windows-emulator/kusd_mmio.hpp diff --git a/src/windows-emulator/kusd_mmio.cpp b/src/windows-emulator/kusd_mmio.cpp new file mode 100644 index 0000000..af1d668 --- /dev/null +++ b/src/windows-emulator/kusd_mmio.cpp @@ -0,0 +1,218 @@ +#include "kusd_mmio.hpp" +#include "windows_emulator.hpp" + +#include + +constexpr auto KUSD_ADDRESS = 0x7ffe0000ULL; +constexpr auto KUSD_SIZE = sizeof(KUSER_SHARED_DATA); +constexpr auto KUSD_BUFFER_SIZE = page_align_up(KUSD_SIZE); + +namespace +{ + void setup_kusd(KUSER_SHARED_DATA& kusd, const bool use_relative_time) + { + memset(&kusd, 0, sizeof(kusd)); + + kusd.TickCountMultiplier = 0x0fa00000; + kusd.InterruptTime.LowPart = 0x17bd9547; + kusd.InterruptTime.High1Time = 0x0000004b; + kusd.InterruptTime.High2Time = 0x0000004b; + kusd.SystemTime.LowPart = 0x7af9da99; + kusd.SystemTime.High1Time = 0x01db27b9; + kusd.SystemTime.High2Time = 0x01db27b9; + kusd.TimeZoneBias.LowPart = 0x3c773000; + kusd.TimeZoneBias.High1Time = -17; + kusd.TimeZoneBias.High2Time = -17; + kusd.TimeZoneId = 0x00000002; + kusd.LargePageMinimum = 0x00200000; + kusd.RNGSeedVersion = 0x0000000000000013; + kusd.TimeZoneBiasStamp = 0x00000004; + kusd.NtBuildNumber = 0x00006c51; + kusd.NtProductType = NtProductWinNt; + kusd.ProductTypeIsValid = 0x01; + kusd.NativeProcessorArchitecture = 0x0009; + kusd.NtMajorVersion = 0x0000000a; + kusd.BootId = 0x0000000b; + kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300; + kusd.SuiteMask = 0x00000110; + kusd.MitigationPolicies.MitigationPolicies = 0x0a; + kusd.MitigationPolicies.NXSupportPolicy = 0x02; + kusd.MitigationPolicies.SEHValidationPolicy = 0x02; + kusd.CyclesPerYield = 0x0064; + kusd.DismountCount = 0x00000006; + kusd.ComPlusPackage = 0x00000001; + kusd.LastSystemRITEventTickCount = 0x01ec1fd3; + kusd.NumberOfPhysicalPages = 0x00bf0958; + kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958; + kusd.TickCount.TickCount.LowPart = 0x001f7f05; + kusd.TickCount.TickCountQuad = 0x00000000001f7f05; + kusd.Cookie = 0x1c3471da; + kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4; + kusd.TimeUpdateLock = 0x0000000002b28586; + kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c; + kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c; + kusd.QpcSystemTimeIncrement = 0x8000000000000000; + kusd.QpcInterruptTimeIncrement = 0x8000000000000000; + kusd.QpcSystemTimeIncrementShift = 0x01; + kusd.QpcInterruptTimeIncrementShift = 0x01; + kusd.UnparkedProcessorCount = 0x000c; + kusd.TelemetryCoverageRound = 0x00000001; + kusd.LangGenerationCount = 0x00000003; + kusd.InterruptTimeBias = 0x00000015a5d56406; + kusd.QpcBias = 0x000000159530c4af; + kusd.ActiveProcessorCount = 0x0000000c; + kusd.ActiveGroupCount = 0x01; + kusd.QpcData.QpcData = 0x0083; + kusd.QpcData.QpcBypassEnabled = 0x83; + kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff; + kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800; + kusd.XState.EnabledFeatures = 0x000000000000001f; + kusd.XState.EnabledVolatileFeatures = 0x000000000000000f; + kusd.XState.Size = 0x000003c0; + + if (use_relative_time) + { + kusd.QpcFrequency = 1000; + } + else + { + kusd.QpcFrequency = std::chrono::steady_clock::period::den; + } + + constexpr std::wstring_view root_dir{L"C:\\WINDOWS"}; + memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2); + + kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386; + kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64; + } +} + +inline void serialize(utils::buffer_serializer& buffer, const KUSER_SHARED_DATA& kusd) +{ + static_assert(KUSD_SIZE == sizeof(kusd)); + buffer.write(&kusd, KUSD_SIZE); +} + +inline void deserialize(utils::buffer_deserializer& buffer, KUSER_SHARED_DATA& kusd) +{ + buffer.read(&kusd, KUSD_SIZE); +} + +kusd_mmio::kusd_mmio(windows_emulator& win_emu, const bool use_relative_time, const bool perform_registration) + : use_relative_time_(use_relative_time) + , win_emu_(&win_emu) +{ + setup_kusd(this->kusd_, use_relative_time); + + if (perform_registration) + { + this->register_mmio(); + } +} + +kusd_mmio::~kusd_mmio() +{ + this->deregister_mmio(); +} + +kusd_mmio::kusd_mmio(kusd_mmio&& obj) // throws! + : use_relative_time_(obj.use_relative_time_) + , win_emu_(obj.win_emu_) +{ + memcpy(&this->kusd_, &obj.kusd_, sizeof(this->kusd_)); + + if (obj.registered_) + { + obj.deregister_mmio(); + this->register_mmio(); + } +} + +kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer) + : kusd_mmio(buffer.read().get(), true, false) +{ +} + +void kusd_mmio::serialize(utils::buffer_serializer& buffer) const +{ + buffer.write(this->use_relative_time_); + buffer.write(this->kusd_); +} + +void kusd_mmio::deserialize(utils::buffer_deserializer& buffer) +{ + buffer.read(this->use_relative_time_); + buffer.read(this->kusd_); + + this->register_mmio(); +} + +uint64_t kusd_mmio::read(const uint64_t addr, const size_t size) +{ + uint64_t result{}; + + this->update(); + + if (addr >= KUSD_SIZE) + { + return result; + } + + const auto end = addr + size; + const auto valid_end = std::min(end, KUSD_SIZE); + const auto real_size = valid_end - addr; + + if (real_size > sizeof(result)) + { + return result; + } + + const auto* kusd_buffer = reinterpret_cast(&this->kusd_); + memcpy(&result, kusd_buffer + addr, real_size); + + return result; +} + +uint64_t kusd_mmio::address() +{ + return KUSD_ADDRESS; +} + +void kusd_mmio::write(const uint64_t /*addr*/, const size_t /*size*/, const uint64_t /*data*/) +{ + // Unsupported! +} + +void kusd_mmio::update() +{ + // TODO +} + +void kusd_mmio::register_mmio() +{ + if (this->registered_) + { + return; + } + + this->registered_ = true; + + this->win_emu_->emu().allocate_mmio( // + KUSD_ADDRESS, KUSD_BUFFER_SIZE, + [this](const uint64_t addr, const size_t size) + { + return this->read(addr, size); + }, [this](const uint64_t addr, const size_t size, const uint64_t data) + { + this->write(addr, size, data); + }); +} + +void kusd_mmio::deregister_mmio() +{ + if (this->registered_) + { + this->registered_ = false; + this->win_emu_->emu().release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE); + } +} diff --git a/src/windows-emulator/kusd_mmio.hpp b/src/windows-emulator/kusd_mmio.hpp new file mode 100644 index 0000000..74e15b1 --- /dev/null +++ b/src/windows-emulator/kusd_mmio.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "std_include.hpp" +#include + +class windows_emulator; + +class kusd_mmio +{ +public: + kusd_mmio(windows_emulator& win_emu, bool use_relative_time, bool perform_registration = true); + ~kusd_mmio(); + + kusd_mmio(kusd_mmio&& obj); + + kusd_mmio(utils::buffer_deserializer& buffer); + + kusd_mmio(const kusd_mmio&) = delete; + kusd_mmio& operator=(kusd_mmio&& obj) = delete; + kusd_mmio& operator=(const kusd_mmio&) = delete; + + void serialize(utils::buffer_serializer& buffer) const; + void deserialize(utils::buffer_deserializer& buffer); + + KUSER_SHARED_DATA& get() + { + return this->kusd_; + } + + const KUSER_SHARED_DATA& get() const + { + return this->kusd_; + } + + static uint64_t address(); + +private: + bool registered_{}; + bool use_relative_time_{}; + windows_emulator* win_emu_{}; + KUSER_SHARED_DATA kusd_{}; + + uint64_t read(uint64_t addr, size_t size); + void write(uint64_t addr, size_t size, uint64_t data); + + void update(); + + void register_mmio(); + void deregister_mmio(); +}; diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index ea3fce9..e6fb1ed 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -12,14 +12,13 @@ #include #include "io_device.hpp" +#include "kusd_mmio.hpp" #define PEB_SEGMENT_SIZE (20 << 20) // 20 MB #define GS_SEGMENT_SIZE (1 << 20) // 1 MB #define IA32_GS_BASE_MSR 0xC0000101 -#define KUSD_ADDRESS 0x7ffe0000 - #define STACK_SIZE 0x40000ULL #define GDT_ADDR 0x30000 @@ -366,7 +365,6 @@ struct process_context : base_allocator(emu) , peb(emu) , process_params(emu) - , kusd(emu) , module_manager(emu) { } @@ -384,7 +382,7 @@ struct process_context emulator_object peb; emulator_object process_params; - emulator_object kusd; + std::optional kusd{}; module_manager module_manager; @@ -423,7 +421,7 @@ struct process_context buffer.write(this->base_allocator); buffer.write(this->peb); buffer.write(this->process_params); - buffer.write(this->kusd); + buffer.write_optional(this->kusd); buffer.write(this->module_manager); buffer.write(this->executable->image_base); @@ -461,7 +459,7 @@ struct process_context buffer.read(this->base_allocator); buffer.read(this->peb); buffer.read(this->process_params); - buffer.read(this->kusd); + buffer.read_optional(this->kusd); buffer.read(this->module_manager); const auto executable_base = buffer.read(); diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index d834a28..1c4b402 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -35,15 +35,9 @@ namespace if (performance_frequency) { - int64_t frequency{}; - c.proc.kusd.access([&](const KUSER_SHARED_DATA& kusd) - { - frequency = kusd.QpcFrequency; - }); - performance_frequency.access([&](LARGE_INTEGER& value) { - value.QuadPart = frequency; + value.QuadPart = c.proc.kusd->get().QpcFrequency; }); } @@ -540,12 +534,8 @@ namespace c.emu.allocate_memory(address, c.proc.shared_section_size, memory_permission::read_write); - size_t windows_dir_size{}; - c.proc.kusd.access([&](const KUSER_SHARED_DATA& kusd) - { - const std::wstring_view windows_dir = kusd.NtSystemRoot.arr; - windows_dir_size = windows_dir.size() * 2; - }); + const std::wstring_view windows_dir = c.proc.kusd->get().NtSystemRoot.arr; + const auto windows_dir_size = windows_dir.size() * 2; constexpr auto windows_dir_offset = 0x10; c.emu.write_memory(address + 8, windows_dir_offset); @@ -555,7 +545,7 @@ namespace const emulator_object windir_obj{c.emu, obj_address}; windir_obj.access([&](UNICODE_STRING& ucs) { - const auto dir_address = c.proc.kusd.value() + offsetof(KUSER_SHARED_DATA, NtSystemRoot); + const auto dir_address = kusd_mmio::address() + offsetof(KUSER_SHARED_DATA, NtSystemRoot); ucs.Buffer = reinterpret_cast(dir_address - obj_address); ucs.Length = static_cast(windows_dir_size); diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 920dea5..c800b93 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -50,95 +50,6 @@ namespace emu.write_register(x64_register::msr, &value, sizeof(value)); } - emulator_object setup_kusd(x64_emulator& emu, bool use_relative_time) - { - // TODO: Fix that. Use hooks to feed dynamic data, e.g. time values - - emu.allocate_memory(KUSD_ADDRESS, page_align_up(sizeof(KUSER_SHARED_DATA)), memory_permission::read); - - const emulator_object kusd_object{emu, KUSD_ADDRESS}; - kusd_object.access([&](KUSER_SHARED_DATA& kusd) - { - kusd.TickCountMultiplier = 0x0fa00000; - kusd.InterruptTime.LowPart = 0x17bd9547; - kusd.InterruptTime.High1Time = 0x0000004b; - kusd.InterruptTime.High2Time = 0x0000004b; - kusd.SystemTime.LowPart = 0x7af9da99; - kusd.SystemTime.High1Time = 0x01db27b9; - kusd.SystemTime.High2Time = 0x01db27b9; - kusd.TimeZoneBias.LowPart = 0x3c773000; - kusd.TimeZoneBias.High1Time = -17; - kusd.TimeZoneBias.High2Time = -17; - kusd.TimeZoneId = 0x00000002; - kusd.LargePageMinimum = 0x00200000; - kusd.RNGSeedVersion = 0x0000000000000013; - kusd.TimeZoneBiasStamp = 0x00000004; - kusd.NtBuildNumber = 0x00006c51; - kusd.NtProductType = NtProductWinNt; - kusd.ProductTypeIsValid = 0x01; - kusd.NativeProcessorArchitecture = 0x0009; - kusd.NtMajorVersion = 0x0000000a; - kusd.BootId = 0x0000000b; - kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300; - kusd.SuiteMask = 0x00000110; - kusd.MitigationPolicies.MitigationPolicies = 0x0a; - kusd.MitigationPolicies.NXSupportPolicy = 0x02; - kusd.MitigationPolicies.SEHValidationPolicy = 0x02; - kusd.CyclesPerYield = 0x0064; - kusd.DismountCount = 0x00000006; - kusd.ComPlusPackage = 0x00000001; - kusd.LastSystemRITEventTickCount = 0x01ec1fd3; - kusd.NumberOfPhysicalPages = 0x00bf0958; - kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958; - kusd.TickCount.TickCount.LowPart = 0x001f7f05; - kusd.TickCount.TickCountQuad = 0x00000000001f7f05; - kusd.Cookie = 0x1c3471da; - kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4; - kusd.TimeUpdateLock = 0x0000000002b28586; - kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c; - kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c; - kusd.QpcSystemTimeIncrement = 0x8000000000000000; - kusd.QpcInterruptTimeIncrement = 0x8000000000000000; - kusd.QpcSystemTimeIncrementShift = 0x01; - kusd.QpcInterruptTimeIncrementShift = 0x01; - kusd.UnparkedProcessorCount = 0x000c; - kusd.TelemetryCoverageRound = 0x00000001; - kusd.LangGenerationCount = 0x00000003; - kusd.InterruptTimeBias = 0x00000015a5d56406; - kusd.QpcBias = 0x000000159530c4af; - kusd.ActiveProcessorCount = 0x0000000c; - kusd.ActiveGroupCount = 0x01; - kusd.QpcData.QpcData = 0x0083; - kusd.QpcData.QpcBypassEnabled = 0x83; - kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff; - kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800; - kusd.XState.EnabledFeatures = 0x000000000000001f; - kusd.XState.EnabledVolatileFeatures = 0x000000000000000f; - kusd.XState.Size = 0x000003c0; - - if (use_relative_time) - { - kusd.QpcFrequency = 1000; - } - else - { - kusd.QpcFrequency = std::chrono::steady_clock::period::den; - } - - constexpr std::wstring_view root_dir{L"C:\\WINDOWS"}; - memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2); - - kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386; - kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64; - - memset(&kusd.ProcessorFeatures, 0, sizeof(kusd.ProcessorFeatures)); - - // ... - }); - - return kusd_object; - } - uint64_t copy_string(x64_emulator& emu, emulator_allocator& allocator, const void* base_ptr, const uint64_t offset, const size_t length) { @@ -262,13 +173,16 @@ namespace return canonical(absolute(path).parent_path()).make_preferred(); } - void setup_context(process_context& context, x64_emulator& emu, const emulator_settings& settings) + void setup_context(windows_emulator& win_emu, const emulator_settings& settings) { + auto& emu = win_emu.emu(); + auto& context = win_emu.process(); + setup_gdt(emu); context.registry = registry_manager(settings.registry_directory); - context.kusd = setup_kusd(emu, settings.use_relative_time); + context.kusd.emplace(win_emu, settings.use_relative_time); context.base_allocator = create_allocator(emu, PEB_SEGMENT_SIZE); auto& allocator = context.base_allocator; @@ -795,7 +709,7 @@ void windows_emulator::setup_process(const emulator_settings& settings) auto& context = this->process(); context.module_manager = module_manager(emu); // TODO: Cleanup module manager - setup_context(context, emu, settings); + setup_context(*this, settings); context.executable = context.module_manager.map_module(settings.application, this->logger); From d3cf88a0c48a37ab34125b611be7576981f35513 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 23 Nov 2024 21:46:34 +0100 Subject: [PATCH 2/5] Dynamically update KUSD system time --- src/emulator/serialization_helper.hpp | 14 +++++++++ src/windows-emulator/kusd_mmio.cpp | 22 +++++++++++++- src/windows-emulator/kusd_mmio.hpp | 1 + src/windows-emulator/syscall_utils.hpp | 42 ++++++++++++++++++++++++-- 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/emulator/serialization_helper.hpp b/src/emulator/serialization_helper.hpp index fbf769c..6af6e0f 100644 --- a/src/emulator/serialization_helper.hpp +++ b/src/emulator/serialization_helper.hpp @@ -19,6 +19,20 @@ inline void deserialize(utils::buffer_deserializer& buffer, std::chrono::steady_ tp = time_point{duration{count}}; } +inline void serialize(utils::buffer_serializer& buffer, const std::chrono::system_clock::time_point& tp) +{ + buffer.write(tp.time_since_epoch().count()); +} + +inline void deserialize(utils::buffer_deserializer& buffer, std::chrono::system_clock::time_point& tp) +{ + using time_point = std::chrono::system_clock::time_point; + using duration = time_point::duration; + + const auto count = buffer.read(); + tp = time_point{duration{count}}; +} + inline void serialize(utils::buffer_serializer& buffer, const std::filesystem::path& path) { buffer.write_string(path.wstring()); diff --git a/src/windows-emulator/kusd_mmio.cpp b/src/windows-emulator/kusd_mmio.cpp index af1d668..a44dd61 100644 --- a/src/windows-emulator/kusd_mmio.cpp +++ b/src/windows-emulator/kusd_mmio.cpp @@ -1,4 +1,5 @@ #include "kusd_mmio.hpp" +#include "syscall_utils.hpp" #include "windows_emulator.hpp" #include @@ -103,6 +104,7 @@ kusd_mmio::kusd_mmio(windows_emulator& win_emu, const bool use_relative_time, co , win_emu_(&win_emu) { setup_kusd(this->kusd_, use_relative_time); + this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime); if (perform_registration) { @@ -118,6 +120,7 @@ kusd_mmio::~kusd_mmio() kusd_mmio::kusd_mmio(kusd_mmio&& obj) // throws! : use_relative_time_(obj.use_relative_time_) , win_emu_(obj.win_emu_) + , start_time_(obj.start_time_) { memcpy(&this->kusd_, &obj.kusd_, sizeof(this->kusd_)); @@ -137,12 +140,14 @@ void kusd_mmio::serialize(utils::buffer_serializer& buffer) const { buffer.write(this->use_relative_time_); buffer.write(this->kusd_); + buffer.write(this->start_time_); } void kusd_mmio::deserialize(utils::buffer_deserializer& buffer) { buffer.read(this->use_relative_time_); buffer.read(this->kusd_); + buffer.read(this->start_time_); this->register_mmio(); } @@ -185,7 +190,22 @@ void kusd_mmio::write(const uint64_t /*addr*/, const size_t /*size*/, const uint void kusd_mmio::update() { - // TODO + auto time = this->start_time_; + + if (this->use_relative_time_) + { + const auto passed_time = this->win_emu_->process().executed_instructions; + const auto clock_frequency = this->kusd_.QpcFrequency; + + using duration = std::chrono::system_clock::duration; + time += duration(passed_time * duration::period::den / clock_frequency); + } + else + { + time = std::chrono::system_clock::now(); + } + + convert_to_ksystem_time(&this->kusd_.SystemTime, time); } void kusd_mmio::register_mmio() diff --git a/src/windows-emulator/kusd_mmio.hpp b/src/windows-emulator/kusd_mmio.hpp index 74e15b1..d122109 100644 --- a/src/windows-emulator/kusd_mmio.hpp +++ b/src/windows-emulator/kusd_mmio.hpp @@ -39,6 +39,7 @@ class kusd_mmio bool use_relative_time_{}; windows_emulator* win_emu_{}; KUSER_SHARED_DATA kusd_{}; + std::chrono::system_clock::time_point start_time_{}; uint64_t read(uint64_t addr, size_t size); void write(uint64_t addr, size_t size, uint64_t data); diff --git a/src/windows-emulator/syscall_utils.hpp b/src/windows-emulator/syscall_utils.hpp index afcd7ba..cb053c0 100644 --- a/src/windows-emulator/syscall_utils.hpp +++ b/src/windows-emulator/syscall_utils.hpp @@ -175,11 +175,12 @@ void write_attribute(emulator& emu, const PS_ATTRIBUTE& attribute, const T& valu } } +constexpr auto HUNDRED_NANOSECONDS_IN_ONE_SECOND = 10000000LL; +constexpr auto EPOCH_DIFFERENCE_1601_TO_1970_SECONDS = 11644473600LL; +constexpr auto WINDOWS_EPOCH_DIFFERENCE = EPOCH_DIFFERENCE_1601_TO_1970_SECONDS * HUNDRED_NANOSECONDS_IN_ONE_SECOND; + inline std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(const LARGE_INTEGER delay_interval) { - constexpr auto HUNDRED_NANOSECONDS_IN_ONE_SECOND = 10000000LL; - constexpr auto EPOCH_DIFFERENCE_1601_TO_1970_SECONDS = 11644473600LL; - if (delay_interval.QuadPart <= 0) { const auto relative_time = -delay_interval.QuadPart; @@ -207,3 +208,38 @@ inline std::chrono::steady_clock::time_point convert_delay_interval_to_time_poin return std::chrono::steady_clock::now() + duration_until_target; } + +inline KSYSTEM_TIME convert_to_ksystem_time(const std::chrono::system_clock::time_point& tp) +{ + const auto duration = tp.time_since_epoch(); + const auto ns_duration = std::chrono::duration_cast(duration); + + const auto total_ticks = ns_duration.count() / 100 + WINDOWS_EPOCH_DIFFERENCE; + + KSYSTEM_TIME time{}; + time.LowPart = static_cast(total_ticks); + time.High1Time = static_cast(total_ticks >> 32); + time.High2Time = time.High1Time; + + return time; +} + +inline void convert_to_ksystem_time(volatile KSYSTEM_TIME* dest, const std::chrono::system_clock::time_point& tp) +{ + const auto time = convert_to_ksystem_time(tp); + memcpy(const_cast(dest), &time, sizeof(*dest)); +} + +inline std::chrono::system_clock::time_point convert_from_ksystem_time(const KSYSTEM_TIME& time) +{ + auto totalTicks = (static_cast(time.High1Time) << 32) | time.LowPart; + totalTicks -= WINDOWS_EPOCH_DIFFERENCE; + + const auto duration = std::chrono::system_clock::duration(totalTicks * 100); + return std::chrono::system_clock::time_point(duration); +} + +inline std::chrono::system_clock::time_point convert_from_ksystem_time(const volatile KSYSTEM_TIME& time) +{ + return convert_from_ksystem_time(*const_cast(&time)); +} From 5f49b30fe0c678ea30153c2b4fb3c171063240fc Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 24 Nov 2024 10:37:26 +0100 Subject: [PATCH 3/5] Cleanup KUSD MMIO --- src/windows-emulator/emulator_utils.hpp | 2 + src/windows-emulator/kusd_mmio.cpp | 62 +++++++++-------------- src/windows-emulator/kusd_mmio.hpp | 16 ++++-- src/windows-emulator/process_context.hpp | 7 +-- src/windows-emulator/syscalls.cpp | 4 +- src/windows-emulator/windows_emulator.cpp | 2 +- 6 files changed, 43 insertions(+), 50 deletions(-) diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index d64179c..fdd6b47 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -36,8 +36,10 @@ class object_wrapper }; class windows_emulator; +struct process_context; using x64_emulator_wrapper = object_wrapper; +using process_context_wrapper = object_wrapper; using windows_emulator_wrapper = object_wrapper; template diff --git a/src/windows-emulator/kusd_mmio.cpp b/src/windows-emulator/kusd_mmio.cpp index a44dd61..83fdb0e 100644 --- a/src/windows-emulator/kusd_mmio.cpp +++ b/src/windows-emulator/kusd_mmio.cpp @@ -99,17 +99,10 @@ inline void deserialize(utils::buffer_deserializer& buffer, KUSER_SHARED_DATA& k buffer.read(&kusd, KUSD_SIZE); } -kusd_mmio::kusd_mmio(windows_emulator& win_emu, const bool use_relative_time, const bool perform_registration) - : use_relative_time_(use_relative_time) - , win_emu_(&win_emu) +kusd_mmio::kusd_mmio(x64_emulator& emu, process_context& process) + : emu_(&emu) + , process_(&process) { - setup_kusd(this->kusd_, use_relative_time); - this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime); - - if (perform_registration) - { - this->register_mmio(); - } } kusd_mmio::~kusd_mmio() @@ -117,23 +110,19 @@ kusd_mmio::~kusd_mmio() this->deregister_mmio(); } -kusd_mmio::kusd_mmio(kusd_mmio&& obj) // throws! - : use_relative_time_(obj.use_relative_time_) - , win_emu_(obj.win_emu_) - , start_time_(obj.start_time_) +kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer) + : kusd_mmio(buffer.read(), buffer.read()) { - memcpy(&this->kusd_, &obj.kusd_, sizeof(this->kusd_)); - - if (obj.registered_) - { - obj.deregister_mmio(); - this->register_mmio(); - } } -kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer) - : kusd_mmio(buffer.read().get(), true, false) +void kusd_mmio::setup(const bool use_relative_time) { + this->use_relative_time_ = use_relative_time; + + setup_kusd(this->kusd_, use_relative_time); + this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime); + + this->register_mmio(); } void kusd_mmio::serialize(utils::buffer_serializer& buffer) const @@ -149,6 +138,7 @@ void kusd_mmio::deserialize(utils::buffer_deserializer& buffer) buffer.read(this->kusd_); buffer.read(this->start_time_); + this->deregister_mmio(); this->register_mmio(); } @@ -183,18 +173,13 @@ uint64_t kusd_mmio::address() return KUSD_ADDRESS; } -void kusd_mmio::write(const uint64_t /*addr*/, const size_t /*size*/, const uint64_t /*data*/) -{ - // Unsupported! -} - void kusd_mmio::update() { auto time = this->start_time_; if (this->use_relative_time_) { - const auto passed_time = this->win_emu_->process().executed_instructions; + const auto passed_time = this->process_->executed_instructions; const auto clock_frequency = this->kusd_.QpcFrequency; using duration = std::chrono::system_clock::duration; @@ -217,15 +202,14 @@ void kusd_mmio::register_mmio() this->registered_ = true; - this->win_emu_->emu().allocate_mmio( // - KUSD_ADDRESS, KUSD_BUFFER_SIZE, - [this](const uint64_t addr, const size_t size) - { - return this->read(addr, size); - }, [this](const uint64_t addr, const size_t size, const uint64_t data) - { - this->write(addr, size, data); - }); + this->emu_->allocate_mmio(KUSD_ADDRESS, KUSD_BUFFER_SIZE, + [this](const uint64_t addr, const size_t size) + { + return this->read(addr, size); + }, [this](const uint64_t, const size_t, const uint64_t) + { + // Writing not supported! + }); } void kusd_mmio::deregister_mmio() @@ -233,6 +217,6 @@ void kusd_mmio::deregister_mmio() if (this->registered_) { this->registered_ = false; - this->win_emu_->emu().release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE); + this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE); } } diff --git a/src/windows-emulator/kusd_mmio.hpp b/src/windows-emulator/kusd_mmio.hpp index d122109..97548ce 100644 --- a/src/windows-emulator/kusd_mmio.hpp +++ b/src/windows-emulator/kusd_mmio.hpp @@ -3,18 +3,20 @@ #include "std_include.hpp" #include +#include "x64_emulator.hpp" + +struct process_context; class windows_emulator; class kusd_mmio { public: - kusd_mmio(windows_emulator& win_emu, bool use_relative_time, bool perform_registration = true); + kusd_mmio(x64_emulator& emu, process_context& process); ~kusd_mmio(); - kusd_mmio(kusd_mmio&& obj); - kusd_mmio(utils::buffer_deserializer& buffer); + kusd_mmio(kusd_mmio&&) = delete; kusd_mmio(const kusd_mmio&) = delete; kusd_mmio& operator=(kusd_mmio&& obj) = delete; kusd_mmio& operator=(const kusd_mmio&) = delete; @@ -34,15 +36,19 @@ class kusd_mmio static uint64_t address(); + void setup(bool use_relative_time); + private: + x64_emulator* emu_{}; + process_context* process_{}; + bool registered_{}; bool use_relative_time_{}; - windows_emulator* win_emu_{}; + KUSER_SHARED_DATA kusd_{}; std::chrono::system_clock::time_point start_time_{}; uint64_t read(uint64_t addr, size_t size); - void write(uint64_t addr, size_t size, uint64_t data); void update(); diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index e6fb1ed..cb8c8ab 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -365,6 +365,7 @@ struct process_context : base_allocator(emu) , peb(emu) , process_params(emu) + , kusd(emu, *this) , module_manager(emu) { } @@ -382,7 +383,7 @@ struct process_context emulator_object peb; emulator_object process_params; - std::optional kusd{}; + kusd_mmio kusd; module_manager module_manager; @@ -421,7 +422,7 @@ struct process_context buffer.write(this->base_allocator); buffer.write(this->peb); buffer.write(this->process_params); - buffer.write_optional(this->kusd); + buffer.write(this->kusd); buffer.write(this->module_manager); buffer.write(this->executable->image_base); @@ -459,7 +460,7 @@ struct process_context buffer.read(this->base_allocator); buffer.read(this->peb); buffer.read(this->process_params); - buffer.read_optional(this->kusd); + buffer.read(this->kusd); buffer.read(this->module_manager); const auto executable_base = buffer.read(); diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 1c4b402..bac61fe 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -37,7 +37,7 @@ namespace { performance_frequency.access([&](LARGE_INTEGER& value) { - value.QuadPart = c.proc.kusd->get().QpcFrequency; + value.QuadPart = c.proc.kusd.get().QpcFrequency; }); } @@ -534,7 +534,7 @@ namespace c.emu.allocate_memory(address, c.proc.shared_section_size, memory_permission::read_write); - const std::wstring_view windows_dir = c.proc.kusd->get().NtSystemRoot.arr; + const std::wstring_view windows_dir = c.proc.kusd.get().NtSystemRoot.arr; const auto windows_dir_size = windows_dir.size() * 2; constexpr auto windows_dir_offset = 0x10; diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index c800b93..2e0ff39 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -182,7 +182,7 @@ namespace context.registry = registry_manager(settings.registry_directory); - context.kusd.emplace(win_emu, settings.use_relative_time); + context.kusd.setup(settings.use_relative_time); context.base_allocator = create_allocator(emu, PEB_SEGMENT_SIZE); auto& allocator = context.base_allocator; From a687dc13f7b60b9affb0d197803903d5b3878cf4 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 24 Nov 2024 10:49:13 +0100 Subject: [PATCH 4/5] Add time print mode to test application --- src/test-sample/test.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/test-sample/test.cpp b/src/test-sample/test.cpp index 2e7b9dc..e8a8460 100644 --- a/src/test-sample/test.cpp +++ b/src/test-sample/test.cpp @@ -4,10 +4,14 @@ #include #include #include -#include #include +#include +#include + #include +using namespace std::literals; + // Externally visible and potentially modifiable state // to trick compiler optimizations __declspec(dllexport) bool do_the_task = true; @@ -178,6 +182,12 @@ bool test_native_exceptions() } } +void print_time() +{ + const auto epoch_time = std::chrono::system_clock::now().time_since_epoch(); + printf("Time: %lld\n", epoch_time.count()); +} + #define RUN_TEST(func, name) \ { \ printf("Running test '" name "': "); \ @@ -186,8 +196,14 @@ bool test_native_exceptions() puts(res ? "Success" : "Fail"); \ } -int main(int /*argc*/, const char* /*argv*/[]) +int main(int argc, const char* argv[]) { + if(argc == 2 && argv[1] == "-time"sv) + { + print_time(); + return 0; + } + bool valid = true; RUN_TEST(test_io, "I/O") From ee0e2676f15043ba845b00c9ec0696a39e2f9672 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 24 Nov 2024 11:27:10 +0100 Subject: [PATCH 5/5] Add system time test --- .../emulation_test_utils.hpp | 17 ++++--- src/windows-emulator-test/time_test.cpp | 45 +++++++++++++++++++ src/windows-emulator/syscalls.cpp | 1 + src/windows-emulator/windows_emulator.cpp | 1 + src/windows-emulator/windows_emulator.hpp | 12 ++++- 5 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 src/windows-emulator-test/time_test.cpp diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp index f874329..c45db02 100644 --- a/src/windows-emulator-test/emulation_test_utils.hpp +++ b/src/windows-emulator-test/emulation_test_utils.hpp @@ -5,14 +5,14 @@ #define ASSERT_NOT_TERMINATED(win_emu) \ do { \ - ASSERT_FALSE(win_emu.process().exit_status.has_value()); \ + ASSERT_FALSE((win_emu).process().exit_status.has_value()); \ } while(false) #define ASSERT_TERMINATED_WITH_STATUS(win_emu, status) \ do { \ - ASSERT_TRUE(win_emu.process().exit_status.has_value()); \ - ASSERT_EQ(*win_emu.process().exit_status, status); \ + ASSERT_TRUE((win_emu).process().exit_status.has_value()); \ + ASSERT_EQ(*(win_emu).process().exit_status, status); \ } while(false) #define ASSERT_TERMINATED_SUCCESSFULLY(win_emu) \ @@ -20,15 +20,20 @@ namespace test { + inline windows_emulator create_sample_emulator(emulator_settings& settings) + { + settings.application = "./test-sample.exe"; + return windows_emulator{settings}; + } + inline windows_emulator create_sample_emulator() { - const emulator_settings settings + emulator_settings settings { - .application = "./test-sample.exe", .disable_logging = true, .use_relative_time = true, }; - return windows_emulator{settings}; + return create_sample_emulator(settings); } } diff --git a/src/windows-emulator-test/time_test.cpp b/src/windows-emulator-test/time_test.cpp new file mode 100644 index 0000000..6e11420 --- /dev/null +++ b/src/windows-emulator-test/time_test.cpp @@ -0,0 +1,45 @@ +#include "emulation_test_utils.hpp" + +namespace test +{ + TEST(TimeTest, SystemTimeIsAccurate) + { + std::string output_buffer{}; + + emulator_settings settings{ + .arguments = {L"-time"}, + .stdout_callback = [&output_buffer](const std::string_view data) + { + output_buffer.append(data); + }, + .disable_logging = true, + .use_relative_time = false, + }; + + auto emu = create_sample_emulator(settings); + emu.start(); + + constexpr auto prefix = "Time: "sv; + + ASSERT_TERMINATED_SUCCESSFULLY(emu); + ASSERT_TRUE(output_buffer.starts_with(prefix)); + + output_buffer = output_buffer.substr(prefix.size()); + while (!output_buffer.empty() && (output_buffer.back() == '\n' || output_buffer.back() == '\r')) + { + output_buffer.pop_back(); + } + + const auto time = strtoll(output_buffer.c_str(), nullptr, 10); + + using time_point = std::chrono::system_clock::time_point; + + const time_point::duration time_duration(time); + const time_point tp(time_duration); + + const auto now = std::chrono::system_clock::now(); + const auto diff = now - tp; + + ASSERT_LE(diff, std::chrono::hours(1)); + } +} diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index bac61fe..dd977b9 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -1985,6 +1985,7 @@ namespace temp_buffer.push_back('\n'); } + c.win_emu.on_stdout(temp_buffer); c.win_emu.logger.info("%.*s", static_cast(temp_buffer.size()), temp_buffer.data()); return STATUS_SUCCESS; diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 2e0ff39..fa8ef0c 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -690,6 +690,7 @@ windows_emulator::windows_emulator(const emulator_settings& settings, std::unique_ptr emu) : windows_emulator(std::move(emu)) { + this->stdout_callback_ = std::move(settings.stdout_callback); this->use_relative_time_ = settings.use_relative_time; this->logger.disable_output(settings.disable_logging); this->setup_process(settings); diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 23bce87..a31f39f 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -9,12 +9,14 @@ std::unique_ptr create_default_x64_emulator(); +// TODO: Split up into application and emulator settings struct emulator_settings { std::filesystem::path application{}; std::filesystem::path working_directory{}; std::filesystem::path registry_directory{"./registry"}; std::vector arguments{}; + std::function stdout_callback{}; bool disable_logging{false}; bool use_relative_time{false}; }; @@ -86,6 +88,14 @@ class windows_emulator this->syscall_hooks_.push_back(std::move(callback)); } + void on_stdout(const std::string_view data) const + { + if (this->stdout_callback_) + { + this->stdout_callback_(data); + } + } + logger logger{}; bool verbose{false}; bool verbose_calls{false}; @@ -105,7 +115,7 @@ class windows_emulator bool use_relative_time_{false}; std::unique_ptr emu_{}; std::vector syscall_hooks_{}; - + std::function stdout_callback_{}; process_context process_; syscall_dispatcher dispatcher_;