diff --git a/src/emulator/serialization_helper.hpp b/src/emulator/serialization_helper.hpp index fbf769cd..6af6e0f0 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/test-sample/test.cpp b/src/test-sample/test.cpp index 2e7b9dcc..e8a8460f 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") diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp index f874329a..c45db025 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 00000000..6e114205 --- /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/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index d64179c3..fdd6b47b 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -36,8 +36,10 @@ public: }; 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 new file mode 100644 index 00000000..83fdb0e4 --- /dev/null +++ b/src/windows-emulator/kusd_mmio.cpp @@ -0,0 +1,222 @@ +#include "kusd_mmio.hpp" +#include "syscall_utils.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(x64_emulator& emu, process_context& process) + : emu_(&emu) + , process_(&process) +{ +} + +kusd_mmio::~kusd_mmio() +{ + this->deregister_mmio(); +} + +kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer) + : kusd_mmio(buffer.read(), buffer.read()) +{ +} + +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 +{ + 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->deregister_mmio(); + 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::update() +{ + auto time = this->start_time_; + + if (this->use_relative_time_) + { + const auto passed_time = this->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() +{ + if (this->registered_) + { + return; + } + + this->registered_ = true; + + 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() +{ + if (this->registered_) + { + this->registered_ = false; + 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 new file mode 100644 index 00000000..97548ceb --- /dev/null +++ b/src/windows-emulator/kusd_mmio.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "std_include.hpp" +#include + +#include "x64_emulator.hpp" + +struct process_context; +class windows_emulator; + +class kusd_mmio +{ +public: + kusd_mmio(x64_emulator& emu, process_context& process); + ~kusd_mmio(); + + 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; + + 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(); + + void setup(bool use_relative_time); + +private: + x64_emulator* emu_{}; + process_context* process_{}; + + bool registered_{}; + bool use_relative_time_{}; + + KUSER_SHARED_DATA kusd_{}; + std::chrono::system_clock::time_point start_time_{}; + + uint64_t read(uint64_t addr, size_t size); + + 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..cb8c8ab8 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,7 @@ struct process_context : base_allocator(emu) , peb(emu) , process_params(emu) - , kusd(emu) + , kusd(emu, *this) , module_manager(emu) { } @@ -384,7 +383,7 @@ struct process_context emulator_object peb; emulator_object process_params; - emulator_object kusd; + kusd_mmio kusd; module_manager module_manager; diff --git a/src/windows-emulator/syscall_utils.hpp b/src/windows-emulator/syscall_utils.hpp index afcd7ba9..cb053c0a 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)); +} diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index d834a288..dd977b99 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); @@ -1995,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 920dea56..fa8ef0c4 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.setup(settings.use_relative_time); context.base_allocator = create_allocator(emu, PEB_SEGMENT_SIZE); auto& allocator = context.base_allocator; @@ -776,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); @@ -795,7 +710,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); diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 23bce872..a31f39f5 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 @@ public: 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 @@ private: bool use_relative_time_{false}; std::unique_ptr emu_{}; std::vector syscall_hooks_{}; - + std::function stdout_callback_{}; process_context process_; syscall_dispatcher dispatcher_;