diff --git a/src/common/utils/time.cpp b/src/common/utils/time.cpp index 18957e0d..4f594b0a 100644 --- a/src/common/utils/time.cpp +++ b/src/common/utils/time.cpp @@ -3,7 +3,8 @@ namespace utils { - std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(const LARGE_INTEGER delay_interval) + std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(clock& c, + const LARGE_INTEGER delay_interval) { if (delay_interval.QuadPart <= 0) { @@ -13,7 +14,7 @@ namespace utils const auto relative_duration = std::chrono::microseconds(relative_ticks_in_ms) + std::chrono::nanoseconds(relative_fraction_ns); - return std::chrono::steady_clock::now() + relative_duration; + return c.steady_now() + relative_duration; } const auto delay_seconds_since_1601 = delay_interval.QuadPart / HUNDRED_NANOSECONDS_IN_ONE_SECOND; @@ -24,12 +25,12 @@ namespace utils const auto target_time = std::chrono::system_clock::from_time_t(delay_seconds_since_1970) + std::chrono::nanoseconds(delay_fraction_ns); - const auto now_system = std::chrono::system_clock::now(); + const auto now_system = c.system_now(); const auto duration_until_target = std::chrono::duration_cast(target_time - now_system); - return std::chrono::steady_clock::now() + duration_until_target; + return c.steady_now() + duration_until_target; } KSYSTEM_TIME convert_to_ksystem_time(const std::chrono::system_clock::time_point& tp) diff --git a/src/common/utils/time.hpp b/src/common/utils/time.hpp index bf592f18..b887e789 100644 --- a/src/common/utils/time.hpp +++ b/src/common/utils/time.hpp @@ -10,7 +10,78 @@ constexpr auto WINDOWS_EPOCH_DIFFERENCE = EPOCH_DIFFERENCE_1601_TO_1970_SECONDS namespace utils { - std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(const LARGE_INTEGER delay_interval); + struct clock + { + using system_time_point = std::chrono::system_clock::time_point; + using steady_time_point = std::chrono::steady_clock::time_point; + + using system_duration = system_time_point::duration; + using steady_duration = steady_time_point::duration; + + virtual ~clock() = default; + + virtual system_time_point system_now() + { + return std::chrono::system_clock::now(); + } + + virtual steady_time_point steady_now() + { + return std::chrono::steady_clock::now(); + } + }; + + class tick_clock : public clock + { + public: + tick_clock(const uint64_t frequency = 1, const system_time_point system_start = {}, + const steady_time_point steady_start = {}) + : frequency_(frequency), + system_start_(system_start), + steady_start_(steady_start) + { + if (this->frequency_ == 0) + { + throw std::invalid_argument("Frequency can not be 0"); + } + } + + system_time_point system_now() override + { + return this->now(this->system_start_); + } + + steady_time_point steady_now() override + { + return this->now(this->steady_start_); + } + + virtual uint64_t ticks() = 0; + + uint64_t get_frequency() const + { + return this->frequency_; + } + + private: + uint64_t frequency_{1}; + system_time_point system_start_{}; + steady_time_point steady_start_{}; + + template + TimePoint now(const TimePoint start) + { + using duration = typename TimePoint::duration; + + const auto passed_ticks = this->ticks(); + const auto passed_time = duration(passed_ticks * duration::period::den / this->frequency_); + + return start + passed_time; + } + }; + + std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(clock& c, LARGE_INTEGER delay_interval); + KSYSTEM_TIME convert_to_ksystem_time(const std::chrono::system_clock::time_point& tp); void convert_to_ksystem_time(volatile KSYSTEM_TIME* dest, const std::chrono::system_clock::time_point& tp); std::chrono::system_clock::time_point convert_from_ksystem_time(const KSYSTEM_TIME& time); @@ -18,5 +89,5 @@ namespace utils #ifndef OS_WINDOWS using __time64_t = int64_t; #endif - LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time); + LARGE_INTEGER convert_unix_to_windows_time(__time64_t unix_time); } diff --git a/src/samples/test-sample/test.cpp b/src/samples/test-sample/test.cpp index 7c1d5b60..d4f27cb4 100644 --- a/src/samples/test-sample/test.cpp +++ b/src/samples/test-sample/test.cpp @@ -423,6 +423,8 @@ int main(const int argc, const char* argv[]) RUN_TEST(test_native_exceptions, "Native Exceptions") RUN_TEST(test_tls, "TLS") + Sleep(1); + if (!reproducible) { RUN_TEST(test_socket, "Socket") diff --git a/src/windows-emulator-test/emulation_test.cpp b/src/windows-emulator-test/emulation_test.cpp index 3a8eec32..f5833843 100644 --- a/src/windows-emulator-test/emulation_test.cpp +++ b/src/windows-emulator-test/emulation_test.cpp @@ -17,7 +17,7 @@ namespace test auto emu = create_sample_emulator(); emu.start({}, count); - ASSERT_EQ(emu.process.executed_instructions, count); + ASSERT_EQ(emu.get_executed_instructions(), count); } TEST(EmulationTest, CountedEmulationIsAccurate) @@ -27,7 +27,7 @@ namespace test ASSERT_TERMINATED_SUCCESSFULLY(emu); - const auto executedInstructions = emu.process.executed_instructions; + const auto executedInstructions = emu.get_executed_instructions(); auto new_emu = create_sample_emulator(); @@ -36,12 +36,12 @@ namespace test new_emu.start({}, instructionsToExecute); - ASSERT_EQ(new_emu.process.executed_instructions, instructionsToExecute); + ASSERT_EQ(new_emu.get_executed_instructions(), instructionsToExecute); ASSERT_NOT_TERMINATED(new_emu); new_emu.start({}, offset); ASSERT_TERMINATED_SUCCESSFULLY(new_emu); - ASSERT_EQ(new_emu.process.executed_instructions, executedInstructions); + ASSERT_EQ(new_emu.get_executed_instructions(), executedInstructions); } } diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp index be6e10fb..a6e8a7ec 100644 --- a/src/windows-emulator-test/emulation_test_utils.hpp +++ b/src/windows-emulator-test/emulation_test_utils.hpp @@ -101,7 +101,7 @@ namespace test emu.serialize(start_state); emu.start(); - const auto limit = emu.process.executed_instructions; + const auto limit = emu.get_executed_instructions(); const auto reset_emulator = [&] { utils::buffer_deserializer deserializer{start_state.get_buffer()}; diff --git a/src/windows-emulator-test/serialization_test.cpp b/src/windows-emulator-test/serialization_test.cpp index 97060080..a14b7e89 100644 --- a/src/windows-emulator-test/serialization_test.cpp +++ b/src/windows-emulator-test/serialization_test.cpp @@ -49,7 +49,7 @@ namespace test utils::buffer_deserializer deserializer{serializer1.get_buffer()}; - windows_emulator new_emu{{.emulation_root = get_emulator_root()}}; + windows_emulator new_emu{{.emulation_root = get_emulator_root(), .use_relative_time = true}}; new_emu.deserialize(deserializer); utils::buffer_serializer serializer2{}; @@ -92,7 +92,7 @@ namespace test utils::buffer_deserializer deserializer{serializer.get_buffer()}; - windows_emulator new_emu{{.emulation_root = get_emulator_root()}}; + windows_emulator new_emu{{.emulation_root = get_emulator_root(), .use_relative_time = true}}; new_emu.log.disable_output(true); new_emu.deserialize(deserializer); diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index e3d38399..33af053e 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -457,7 +457,7 @@ namespace const auto status = this->execute_ioctl(win_emu, *this->delayed_ioctl_); if (status == STATUS_PENDING) { - if (!this->timeout_ || this->timeout_ > std::chrono::steady_clock::now()) + if (!this->timeout_ || this->timeout_ > win_emu.clock().steady_now()) { return; } @@ -593,7 +593,7 @@ namespace std::optional timeout{}; if (info.Timeout.QuadPart != std::numeric_limits::max()) { - timeout = utils::convert_delay_interval_to_time_point(info.Timeout); + timeout = utils::convert_delay_interval_to_time_point(win_emu.clock(), info.Timeout); } this->delay_ioctrl(c, timeout); diff --git a/src/windows-emulator/emulator_thread.cpp b/src/windows-emulator/emulator_thread.cpp index 8798520e..2b3ca72c 100644 --- a/src/windows-emulator/emulator_thread.cpp +++ b/src/windows-emulator/emulator_thread.cpp @@ -148,7 +148,7 @@ bool emulator_thread::is_terminated() const return this->exit_status.has_value(); } -bool emulator_thread::is_thread_ready(process_context& process) +bool emulator_thread::is_thread_ready(process_context& process, utils::clock& clock) { if (this->is_terminated()) { @@ -162,7 +162,7 @@ bool emulator_thread::is_thread_ready(process_context& process) this->mark_as_ready(STATUS_ALERTED); return true; } - if (this->is_await_time_over()) + if (this->is_await_time_over(clock)) { this->mark_as_ready(STATUS_TIMEOUT); return true; @@ -194,7 +194,7 @@ bool emulator_thread::is_thread_ready(process_context& process) return true; } - if (this->is_await_time_over()) + if (this->is_await_time_over(clock)) { this->mark_as_ready(STATUS_TIMEOUT); return true; @@ -205,7 +205,7 @@ bool emulator_thread::is_thread_ready(process_context& process) if (this->await_time.has_value()) { - if (this->is_await_time_over()) + if (this->is_await_time_over(clock)) { this->mark_as_ready(STATUS_SUCCESS); return true; diff --git a/src/windows-emulator/emulator_thread.hpp b/src/windows-emulator/emulator_thread.hpp index 2908b9a9..dc64f98c 100644 --- a/src/windows-emulator/emulator_thread.hpp +++ b/src/windows-emulator/emulator_thread.hpp @@ -65,14 +65,14 @@ class emulator_thread : public ref_counted_object void mark_as_ready(NTSTATUS status); - bool is_await_time_over() const + bool is_await_time_over(utils::clock& clock) const { - return this->await_time.has_value() && this->await_time.value() < std::chrono::steady_clock::now(); + return this->await_time.has_value() && this->await_time.value() < clock.steady_now(); } bool is_terminated() const; - bool is_thread_ready(process_context& process); + bool is_thread_ready(process_context& process, utils::clock& clock); void save(x64_emulator& emu) { diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index 11d87d01..05adfcae 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -6,6 +6,8 @@ #include "memory_utils.hpp" #include "address_utils.hpp" +#include + // TODO: Replace with pointer handling structure for future 32 bit support using emulator_pointer = uint64_t; @@ -43,6 +45,7 @@ class windows_emulator; class module_manager; struct process_context; +using clock_wrapper = object_wrapper; using x64_emulator_wrapper = object_wrapper; using memory_manager_wrapper = object_wrapper; using module_manager_wrapper = object_wrapper; diff --git a/src/windows-emulator/kusd_mmio.cpp b/src/windows-emulator/kusd_mmio.cpp index 256f02e4..03fcd950 100644 --- a/src/windows-emulator/kusd_mmio.cpp +++ b/src/windows-emulator/kusd_mmio.cpp @@ -10,7 +10,7 @@ constexpr auto KUSD_BUFFER_SIZE = page_align_up(KUSD_SIZE); namespace { - void setup_kusd(KUSER_SHARED_DATA64& kusd, const bool use_relative_time) + void setup_kusd(KUSER_SHARED_DATA64& kusd) { memset(reinterpret_cast(&kusd), 0, sizeof(kusd)); @@ -60,25 +60,17 @@ namespace 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; - } + kusd.QpcData.QpcData = 0x0083; + kusd.QpcData.QpcBypassEnabled = 0x83; + kusd.QpcBias = 0x000000159530c4af; + kusd.QpcFrequency = utils::clock::steady_duration::period::den; constexpr std::u16string_view root_dir{u"C:\\WINDOWS"}; memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2); @@ -102,9 +94,9 @@ namespace utils } } -kusd_mmio::kusd_mmio(memory_manager& memory, process_context& process) +kusd_mmio::kusd_mmio(memory_manager& memory, utils::clock& clock) : memory_(&memory), - process_(&process) + clock_(&clock) { } @@ -114,32 +106,24 @@ kusd_mmio::~kusd_mmio() } kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer) - : kusd_mmio(buffer.read(), buffer.read()) + : kusd_mmio(buffer.read(), buffer.read()) { } -void kusd_mmio::setup(const bool use_relative_time) +void kusd_mmio::setup() { - this->use_relative_time_ = use_relative_time; - - setup_kusd(this->kusd_, use_relative_time); - this->start_time_ = utils::convert_from_ksystem_time(this->kusd_.SystemTime); - + setup_kusd(this->kusd_); 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(); @@ -178,21 +162,7 @@ uint64_t kusd_mmio::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 = static_cast(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(); - } - + const auto time = this->clock_->system_now(); utils::convert_to_ksystem_time(&this->kusd_.SystemTime, time); } diff --git a/src/windows-emulator/kusd_mmio.hpp b/src/windows-emulator/kusd_mmio.hpp index b9d408a3..24c4df53 100644 --- a/src/windows-emulator/kusd_mmio.hpp +++ b/src/windows-emulator/kusd_mmio.hpp @@ -5,13 +5,15 @@ #include "x64_emulator.hpp" +#include + struct process_context; class windows_emulator; class kusd_mmio { public: - kusd_mmio(memory_manager& memory, process_context& process); + kusd_mmio(memory_manager& memory, utils::clock& clock); ~kusd_mmio(); kusd_mmio(utils::buffer_deserializer& buffer); @@ -36,17 +38,15 @@ class kusd_mmio static uint64_t address(); - void setup(bool use_relative_time); + void setup(); private: memory_manager* memory_{}; - process_context* process_{}; + utils::clock* clock_{}; bool registered_{}; - bool use_relative_time_{}; KUSER_SHARED_DATA64 kusd_{}; - std::chrono::system_clock::time_point start_time_{}; uint64_t read(uint64_t addr, size_t size); diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index 67bdec3c..1ce0a315 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -28,12 +28,12 @@ namespace } void process_context::setup(x64_emulator& emu, memory_manager& memory, const application_settings& app_settings, - const emulator_settings& emu_settings, const mapped_module& executable, - const mapped_module& ntdll, const apiset::container& apiset_container) + const mapped_module& executable, const mapped_module& ntdll, + const apiset::container& apiset_container) { setup_gdt(emu, memory); - this->kusd.setup(emu_settings.use_relative_time); + this->kusd.setup(); this->base_allocator = create_allocator(memory, PEB_SEGMENT_SIZE); auto& allocator = this->base_allocator; @@ -125,7 +125,6 @@ void process_context::setup(x64_emulator& emu, memory_manager& memory, const app void process_context::serialize(utils::buffer_serializer& buffer) const { - buffer.write(this->executed_instructions); buffer.write(this->current_ip); buffer.write(this->previous_ip); buffer.write_optional(this->exception_rip); @@ -159,7 +158,6 @@ void process_context::serialize(utils::buffer_serializer& buffer) const void process_context::deserialize(utils::buffer_deserializer& buffer) { - buffer.read(this->executed_instructions); buffer.read(this->current_ip); buffer.read(this->previous_ip); buffer.read_optional(this->exception_rip); diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index ba7c2f47..e0ae016b 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -38,18 +38,17 @@ struct process_context utils::optional_function on_thread_terminated{}; }; - process_context(x64_emulator& emu, memory_manager& memory, callbacks& cb) + process_context(x64_emulator& emu, memory_manager& memory, utils::clock& clock, callbacks& cb) : callbacks_(&cb), base_allocator(emu), peb(emu), process_params(emu), - kusd(memory, *this) + kusd(memory, clock) { } void setup(x64_emulator& emu, memory_manager& memory, const application_settings& app_settings, - const emulator_settings& emu_settings, const mapped_module& executable, const mapped_module& ntdll, - const apiset::container& apiset_container); + const mapped_module& executable, const mapped_module& ntdll, const apiset::container& apiset_container); handle create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument, const uint64_t stack_size); @@ -59,7 +58,6 @@ struct process_context callbacks* callbacks_{}; - uint64_t executed_instructions{0}; uint64_t current_ip{0}; uint64_t previous_ip{0}; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 8999236d..b2d0b71a 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -27,21 +27,15 @@ namespace if (performance_counter) { performance_counter.access([&](LARGE_INTEGER& value) { - if (c.win_emu.time_is_relative()) - { - value.QuadPart = static_cast(c.proc.executed_instructions); - } - else - { - value.QuadPart = std::chrono::steady_clock::now().time_since_epoch().count(); - } + value.QuadPart = c.win_emu.clock().steady_now().time_since_epoch().count(); }); } if (performance_frequency) { - performance_frequency.access( - [&](LARGE_INTEGER& value) { value.QuadPart = c.proc.kusd.get().QpcFrequency; }); + performance_frequency.access([&](LARGE_INTEGER& value) { + value.QuadPart = c.proc.kusd.get().QpcFrequency; // + }); } return STATUS_SUCCESS; @@ -3639,7 +3633,7 @@ namespace if (timeout.value() && !t.await_time.has_value()) { - t.await_time = utils::convert_delay_interval_to_time_point(timeout.read()); + t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), timeout.read()); } c.win_emu.yield_thread(); @@ -3666,7 +3660,7 @@ namespace if (timeout.value() && !t.await_time.has_value()) { - t.await_time = utils::convert_delay_interval_to_time_point(timeout.read()); + t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), timeout.read()); } c.win_emu.yield_thread(); @@ -3703,7 +3697,7 @@ namespace } auto& t = c.win_emu.current_thread(); - t.await_time = utils::convert_delay_interval_to_time_point(delay_interval.read()); + t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), delay_interval.read()); c.win_emu.yield_thread(); @@ -3745,7 +3739,7 @@ namespace if (timeout.value() && !t.await_time.has_value()) { - t.await_time = utils::convert_delay_interval_to_time_point(timeout.read()); + t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), timeout.read()); } c.win_emu.yield_thread(); diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 708cc8bf..9d75ef33 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -86,7 +86,7 @@ namespace auto& emu = win_emu.emu(); auto& context = win_emu.process; - const auto is_ready = thread.is_thread_ready(context); + const auto is_ready = thread.is_thread_ready(context, win_emu.clock()); if (!is_ready && !force) { @@ -161,6 +161,33 @@ namespace return false; } + + struct instruction_tick_clock : utils::tick_clock + { + const uint64_t* instructions_{}; + + instruction_tick_clock(const uint64_t& instructions, const system_time_point system_start = {}, + const steady_time_point steady_start = {}) + : tick_clock(1000, system_start, steady_start), + instructions_(&instructions) + { + } + + uint64_t ticks() override + { + return *this->instructions_; + } + }; + + std::unique_ptr get_clock(const uint64_t& instructions, const bool use_relative_time) + { + if (use_relative_time) + { + return std::make_unique(instructions); + } + + return std::make_unique(); + } } std::unique_ptr create_default_x64_emulator() @@ -175,17 +202,18 @@ windows_emulator::windows_emulator(application_settings app_settings, const emul this->callbacks = std::move(callbacks); fixup_application_settings(app_settings); - this->setup_process(app_settings, settings); + this->setup_process(app_settings); } windows_emulator::windows_emulator(const emulator_settings& settings, std::unique_ptr emu) : emu_(std::move(emu)), + clock_(get_clock(this->executed_instructions_, settings.use_relative_time)), emulation_root{settings.emulation_root.empty() ? settings.emulation_root : absolute(settings.emulation_root)}, file_sys(emulation_root.empty() ? emulation_root : emulation_root / "filesys"), memory(*this->emu_), registry(emulation_root.empty() ? settings.registry_directory : emulation_root / "registry"), mod_manager(memory, file_sys, callbacks), - process(*this->emu_, memory, callbacks) + process(*this->emu_, memory, *this->clock_, callbacks) { #ifndef OS_WINDOWS if (this->emulation_root.empty()) @@ -215,7 +243,7 @@ windows_emulator::windows_emulator(const emulator_settings& settings, std::uniqu windows_emulator::~windows_emulator() = default; -void windows_emulator::setup_process(const application_settings& app_settings, const emulator_settings& emu_settings) +void windows_emulator::setup_process(const application_settings& app_settings) { const auto& emu = this->emu(); auto& context = this->process; @@ -229,7 +257,7 @@ void windows_emulator::setup_process(const application_settings& app_settings, c const auto apiset_data = apiset::obtain(this->emulation_root); - this->process.setup(this->emu(), this->memory, app_settings, emu_settings, *executable, *ntdll, apiset_data); + this->process.setup(this->emu(), this->memory, app_settings, *executable, *ntdll, apiset_data); const auto ntdll_data = emu.read_memory(ntdll->image_base, ntdll->size_of_image); const auto win32u_data = emu.read_memory(win32u->image_base, win32u->size_of_image); @@ -251,8 +279,14 @@ void windows_emulator::perform_thread_switch() this->switch_thread_ = false; while (!switch_to_next_thread(*this)) { - // TODO: Optimize that - std::this_thread::sleep_for(1ms); + if (this->use_relative_time_) + { + this->executed_instructions_ += MAX_INSTRUCTIONS_PER_TIME_SLICE; + } + else + { + std::this_thread::sleep_for(1ms); + } } } @@ -271,7 +305,7 @@ void windows_emulator::on_instruction_execution(const uint64_t address) { auto& thread = this->current_thread(); - ++this->process.executed_instructions; + ++this->executed_instructions_; const auto thread_insts = ++thread.executed_instructions; if (thread_insts % MAX_INSTRUCTIONS_PER_TIME_SLICE == 0) { @@ -362,7 +396,7 @@ void windows_emulator::setup_hooks() }); this->emu().hook_instruction(x64_hookable_instructions::rdtsc, [&] { - const auto instructions = this->process.executed_instructions; + const auto instructions = this->executed_instructions_; this->emu().reg(x64_register::rax, instructions & 0xFFFFFFFF); this->emu().reg(x64_register::rdx, (instructions >> 32) & 0xFFFFFFFF); return instruction_hook_continuation::skip_instruction; @@ -443,14 +477,14 @@ void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count) const auto use_timeout = timeout != std::chrono::nanoseconds{}; const auto start_time = std::chrono::high_resolution_clock::now(); - const auto start_instructions = this->process.executed_instructions; + const auto start_instructions = this->executed_instructions_; const auto target_time = start_time + timeout; const auto target_instructions = start_instructions + count; while (true) { - if (this->switch_thread_ || !this->current_thread().is_thread_ready(this->process)) + if (this->switch_thread_ || !this->current_thread().is_thread_ready(this->process, this->clock())) { this->perform_thread_switch(); } @@ -476,7 +510,7 @@ void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count) if (use_count) { - const auto current_instructions = this->process.executed_instructions; + const auto current_instructions = this->executed_instructions_; if (current_instructions >= target_instructions) { @@ -490,6 +524,7 @@ void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count) void windows_emulator::serialize(utils::buffer_serializer& buffer) const { + buffer.write(this->executed_instructions_); buffer.write(this->switch_thread_); buffer.write(this->use_relative_time_); this->emu().serialize_state(buffer, false); @@ -517,9 +552,21 @@ void windows_emulator::deserialize(utils::buffer_deserializer& buffer) return windows_emulator_wrapper{*this}; // }); + buffer.register_factory([this] { + return clock_wrapper{this->clock()}; // + }); + + buffer.read(this->executed_instructions_); buffer.read(this->switch_thread_); + + const auto old_relative_time = this->use_relative_time_; buffer.read(this->use_relative_time_); + if (old_relative_time != this->use_relative_time_) + { + throw std::runtime_error("Can not deserialize emulator with different time dimensions"); + } + this->memory.unmap_all_memory(); this->emu().deserialize_state(buffer, false); diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index b8b7b700..8d5eed04 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -47,7 +47,10 @@ struct emulator_settings class windows_emulator { + uint64_t executed_instructions_{0}; + std::unique_ptr emu_{}; + std::unique_ptr clock_{}; public: std::filesystem::path emulation_root{}; @@ -83,6 +86,16 @@ class windows_emulator return *this->emu_; } + utils::clock& clock() + { + return *this->clock_; + } + + const utils::clock& clock() const + { + return *this->clock_; + } + emulator_thread& current_thread() const { if (!this->process.active_thread) @@ -93,6 +106,11 @@ class windows_emulator return *this->process.active_thread; } + uint64_t get_executed_instructions() const + { + return this->executed_instructions_; + } + void start(std::chrono::nanoseconds timeout = {}, size_t count = 0); void serialize(utils::buffer_serializer& buffer) const; @@ -149,11 +167,6 @@ class windows_emulator void perform_thread_switch(); bool activate_thread(uint32_t id); - bool time_is_relative() const - { - return this->use_relative_time_; - } - private: bool switch_thread_{false}; bool use_relative_time_{false}; @@ -166,6 +179,6 @@ class windows_emulator // std::optional process_snapshot_{}; void setup_hooks(); - void setup_process(const application_settings& app_settings, const emulator_settings& emu_settings); + void setup_process(const application_settings& app_settings); void on_instruction_execution(uint64_t address); };