From 2e2b4ffb2fd28be314ca173b860a7a5401ebde2c Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 23 Nov 2024 19:32:14 +0100 Subject: [PATCH] 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 00000000..af1d668a --- /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 00000000..74e15b15 --- /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 ea3fce9a..e6fb1edb 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 d834a288..1c4b4026 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 920dea56..c800b935 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);