#pragma once #include "windows_emulator.hpp" #include struct syscall_context { windows_emulator& win_emu; x64_emulator& emu; process_context& proc; mutable bool write_status{true}; mutable bool retrigger_syscall{false}; }; inline uint64_t get_syscall_argument(x64_emulator& emu, const size_t index) { switch (index) { case 0: return emu.reg(x64_register::r10); case 1: return emu.reg(x64_register::rdx); case 2: return emu.reg(x64_register::r8); case 3: return emu.reg(x64_register::r9); default: return emu.read_stack(index + 1); } } inline bool is_uppercase(const char character) { return toupper(character) == character; } inline bool is_syscall(const std::string_view name) { return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]); } inline std::optional extract_syscall_id(const exported_symbol& symbol, std::span data) { if (!is_syscall(symbol.name)) { return std::nullopt; } constexpr auto instruction_size = 5; constexpr auto instruction_offset = 3; constexpr auto instruction_operand_offset = 1; constexpr auto instruction_opcode = static_cast(0xB8); const auto instruction_rva = symbol.rva + instruction_offset; if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode) { return std::nullopt; } uint32_t syscall_id{0}; static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset)); memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id)); return syscall_id; } inline std::map find_syscalls(const exported_symbols& exports, std::span data) { std::map syscalls{}; for (const auto& symbol : exports) { const auto id = extract_syscall_id(symbol, data); if (id) { auto& entry = syscalls[*id]; if (!entry.empty()) { throw std::runtime_error("Syscall with id " + std::to_string(*id) + ", which is mapping to " + symbol.name + ", was already mapped to " + entry); } entry = symbol.name; } } return syscalls; } inline void map_syscalls(std::map& handlers, std::map syscalls) { for (auto& [id, name] : syscalls) { auto& entry = handlers[id]; if (!entry.name.empty()) { throw std::runtime_error("Syscall with id " + std::to_string(id) + ", which is mapping to " + name + ", was previously mapped to " + entry.name); } entry.name = std::move(name); entry.handler = nullptr; } } template requires(std::is_integral_v || std::is_enum_v) T resolve_argument(x64_emulator& emu, const size_t index) { const auto arg = get_syscall_argument(emu, index); return static_cast(arg); } template requires(std::is_same_v, handle>) handle resolve_argument(x64_emulator& emu, const size_t index) { handle h{}; h.bits = resolve_argument(emu, index); return h; } template requires(std::is_same_v>) T resolve_argument(x64_emulator& emu, const size_t index) { const auto arg = get_syscall_argument(emu, index); return T(emu, arg); } template T resolve_indexed_argument(x64_emulator& emu, size_t& index) { return resolve_argument(emu, index++); } inline void write_status(const syscall_context& c, const NTSTATUS status, const uint64_t initial_ip) { if (c.write_status && !c.retrigger_syscall) { c.emu.reg(x64_register::rax, static_cast(status)); } const auto new_ip = c.emu.read_instruction_pointer(); if (initial_ip != new_ip || c.retrigger_syscall) { c.emu.reg(x64_register::rip, new_ip - 2); } } inline void forward_syscall(const syscall_context& c, NTSTATUS (*handler)()) { const auto ip = c.emu.read_instruction_pointer(); const auto ret = handler(); write_status(c, ret, ip); } template void forward_syscall(const syscall_context& c, NTSTATUS (*handler)(const syscall_context&, Args...)) { const auto ip = c.emu.read_instruction_pointer(); size_t index = 0; std::tuple func_args{ c, resolve_indexed_argument>>(c.emu, index)...}; (void)index; const auto ret = std::apply(handler, std::move(func_args)); write_status(c, ret, ip); } template syscall_handler make_syscall_handler() { return +[](const syscall_context& c) { forward_syscall(c, Handler); }; } template void write_attribute(emulator& emu, const PS_ATTRIBUTE& attribute, const T& value) { if (attribute.ReturnLength) { emulator_object{emu, attribute.ReturnLength}.write(sizeof(T)); } if (attribute.Size >= sizeof(T)) { emulator_object{emu, attribute.Value}.write(value); } } 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) { if (delay_interval.QuadPart <= 0) { const auto relative_time = -delay_interval.QuadPart; const auto relative_ticks_in_ms = relative_time / 10; const auto relative_fraction_ns = (relative_time % 10) * 100; 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; } const auto delay_seconds_since_1601 = delay_interval.QuadPart / HUNDRED_NANOSECONDS_IN_ONE_SECOND; const auto delay_fraction_ns = (delay_interval.QuadPart % HUNDRED_NANOSECONDS_IN_ONE_SECOND) * 100; const auto delay_seconds_since_1970 = delay_seconds_since_1601 - EPOCH_DIFFERENCE_1601_TO_1970_SECONDS; 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 duration_until_target = std::chrono::duration_cast(target_time - now_system); 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)); } #ifndef OS_WINDOWS using __time64_t = int64_t; #endif inline LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time) { LARGE_INTEGER windows_time{}; windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND; return windows_time; }