mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
@@ -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<std::chrono::microseconds>(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)
|
||||
|
||||
@@ -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 <typename TimePoint>
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<std::chrono::steady_clock::time_point> timeout{};
|
||||
if (info.Timeout.QuadPart != std::numeric_limits<int64_t>::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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "memory_utils.hpp"
|
||||
#include "address_utils.hpp"
|
||||
|
||||
#include <utils/time.hpp>
|
||||
|
||||
// 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<utils::clock>;
|
||||
using x64_emulator_wrapper = object_wrapper<x64_emulator>;
|
||||
using memory_manager_wrapper = object_wrapper<memory_manager>;
|
||||
using module_manager_wrapper = object_wrapper<module_manager>;
|
||||
|
||||
@@ -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<void*>(&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<memory_manager_wrapper>(), buffer.read<process_context_wrapper>())
|
||||
: kusd_mmio(buffer.read<memory_manager_wrapper>(), buffer.read<clock_wrapper>())
|
||||
{
|
||||
}
|
||||
|
||||
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<uint64_t>(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);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,15 @@
|
||||
|
||||
#include "x64_emulator.hpp"
|
||||
|
||||
#include <utils/time.hpp>
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -38,18 +38,17 @@ struct process_context
|
||||
utils::optional_function<void(handle h, emulator_thread& thr)> 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};
|
||||
|
||||
|
||||
@@ -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<LONGLONG>(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();
|
||||
|
||||
@@ -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<utils::clock> get_clock(const uint64_t& instructions, const bool use_relative_time)
|
||||
{
|
||||
if (use_relative_time)
|
||||
{
|
||||
return std::make_unique<instruction_tick_clock>(instructions);
|
||||
}
|
||||
|
||||
return std::make_unique<utils::clock>();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<x64_emulator> 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<x64_emulator> 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<clock_wrapper>([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);
|
||||
|
||||
@@ -47,7 +47,10 @@ struct emulator_settings
|
||||
|
||||
class windows_emulator
|
||||
{
|
||||
uint64_t executed_instructions_{0};
|
||||
|
||||
std::unique_ptr<x64_emulator> emu_{};
|
||||
std::unique_ptr<utils::clock> 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_context> 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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user