#include "std_include.hpp" #include "syscalls.hpp" #include #include "context_frame.hpp" #include "emulator_utils.hpp" #include struct syscall_context { x64_emulator& emu; process_context& proc; mutable bool write_status; }; namespace { 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); } } bool is_uppercase(const char character) { return toupper(character) == character; } bool is_syscall(const std::string_view name) { return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]); } std::vector find_syscalls(const exported_symbols& exports) { // Makes use of the fact that order of Nt* function addresses // is equal to the order of syscall IDs. // So first Nt* function is the first syscall with ID 0 std::map reference_count{}; std::map ordered_syscalls{}; for (const auto& symbol : exports) { if (is_syscall(symbol.name)) { ++reference_count[symbol.address]; ordered_syscalls[symbol.address] = symbol.name; } } std::vector syscalls{}; syscalls.reserve(ordered_syscalls.size()); for (auto& syscall : ordered_syscalls) { if (reference_count[syscall.first] == 1) { syscalls.push_back(std::move(syscall.second)); } else { printf("Skipping %s\n", syscall.second.c_str()); } } return syscalls; } void map_syscalls(std::unordered_map& handlers, const std::vector& syscalls, const uint64_t base_index) { for (size_t i = 0; i < syscalls.size(); ++i) { const auto& syscall = syscalls[i]; auto& entry = handlers[base_index + i]; entry.name = syscall; entry.handler = nullptr; } } uint64_t get_syscall_id(const std::vector& ntdll_syscalls, const std::vector& win32u_syscalls, const std::string_view name) { for (size_t i = 0; i < ntdll_syscalls.size(); ++i) { if (ntdll_syscalls[i] == name) { return i; } } for (size_t i = 0; i < win32u_syscalls.size(); ++i) { if (win32u_syscalls[i] == name) { return i + 0x1000; } } throw std::runtime_error("Unable to determine syscall id: " + std::string(name)); } 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>) 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++); } void write_status(const syscall_context& c, const NTSTATUS status, const uint64_t initial_ip) { if (c.write_status) { c.emu.reg(x64_register::rax, static_cast(status)); } const auto new_ip = c.emu.read_instruction_pointer(); if (initial_ip != new_ip) { c.emu.reg(x64_register::rip, new_ip - 2); } } 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)... }; 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); }; } NTSTATUS handle_NtQueryPerformanceCounter(const syscall_context&, const emulator_object performance_counter, const emulator_object performance_frequency) { try { if (performance_counter) { performance_counter.access([](LARGE_INTEGER& value) { QueryPerformanceCounter(&value); }); } if (performance_frequency) { performance_frequency.access([](LARGE_INTEGER& value) { QueryPerformanceFrequency(&value); }); } return STATUS_SUCCESS; } catch (...) { return STATUS_ACCESS_VIOLATION; } } NTSTATUS handle_NtManageHotPatch() { return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtCreateWorkerFactory() { return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtOpenKey() { return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtOpenKeyEx() { return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtSetInformationThread(const syscall_context& c, const uint64_t /*thread_handle*/, const THREADINFOCLASS info_class, const uint64_t /*thread_information*/, const uint32_t /*thread_information_length*/) { if (info_class == ThreadSchedulerSharedDataSlot) { return STATUS_SUCCESS; } printf("Unsupported thread info class: %X\n", info_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtSetEvent(const syscall_context& c, const uint64_t handle, const emulator_object previous_state) { const auto entry = c.proc.events.get(handle); if (!entry) { return STATUS_INVALID_HANDLE; } if (previous_state.value()) { previous_state.write(entry->signaled ? 1ULL : 0ULL); } entry->signaled = true; return STATUS_SUCCESS; } NTSTATUS handle_NtClose(const syscall_context& c, const uint64_t handle) { const auto value = get_handle_value(handle); if (value.is_pseudo) { return STATUS_SUCCESS; } if (value.type == handle_types::event && c.proc.events.erase(handle)) { return STATUS_SUCCESS; } if (value.type == handle_types::file && c.proc.files.erase(handle)) { return STATUS_SUCCESS; } if (value.type == handle_types::semaphore && c.proc.semaphores.erase(handle)) { return STATUS_SUCCESS; } return STATUS_INVALID_HANDLE; } NTSTATUS handle_NtTraceEvent() { return STATUS_SUCCESS; } NTSTATUS handle_NtOpenThreadToken() { return STATUS_NO_TOKEN; } NTSTATUS handle_NtCreateEvent(const syscall_context& c, const emulator_object event_handle, const ACCESS_MASK /*desired_access*/, const emulator_object object_attributes, const EVENT_TYPE event_type, const BOOLEAN initial_state) { if (object_attributes) { //const auto attributes = object_attributes.read(); puts("Unsupported object attributes"); //c.emu.stop(); //return STATUS_NOT_SUPPORTED; } event e{}; e.type = event_type; e.signaled = initial_state != FALSE; const auto handle = c.proc.events.store(std::move(e)); event_handle.write(handle.bits); static_assert(sizeof(EVENT_TYPE) == sizeof(uint32_t)); static_assert(sizeof(ACCESS_MASK) == sizeof(uint32_t)); return STATUS_SUCCESS; } NTSTATUS handle_NtQueryVolumeInformationFile(const syscall_context& c, uint64_t /*file_handle*/, uint64_t /*io_status_block*/, uint64_t fs_information, ULONG /*length*/, FS_INFORMATION_CLASS fs_information_class) { if (fs_information_class != FileFsDeviceInformation) { printf("Unsupported fs info class: %X\n", fs_information_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } const emulator_object info_obj{c.emu, fs_information}; info_obj.access([&](FILE_FS_DEVICE_INFORMATION& info) { info.DeviceType = FILE_DEVICE_DISK; info.Characteristics = 0x20020; }); return STATUS_SUCCESS; } NTSTATUS handle_NtOpenFile(const syscall_context& c, const emulator_object file_handle, const ACCESS_MASK desired_access, const emulator_object object_attributes, const emulator_object /*io_status_block*/, const ULONG share_access, const ULONG open_options) { file f{}; const auto attributes = object_attributes.read(); f.name = read_unicode_string(c.emu, attributes.ObjectName); UNICODE_STRING string{}; string.Buffer = f.name.data(); string.Length = static_cast(f.name.size() * 2); string.MaximumLength = string.Length; OBJECT_ATTRIBUTES new_attributes{}; new_attributes.ObjectName = &string; new_attributes.Length = sizeof(new_attributes); HANDLE h{}; IO_STATUS_BLOCK status_block{}; const auto res = NtOpenFile(&h, desired_access, &new_attributes, &status_block, share_access, open_options); if (res != STATUS_SUCCESS) { return res; } f.handle = h; const auto handle = c.proc.files.store(std::move(f)); file_handle.write(handle.bits); return STATUS_SUCCESS; } NTSTATUS handle_NtOpenSection(const syscall_context& c, const emulator_object section_handle, const ACCESS_MASK /*desired_access*/, const emulator_object object_attributes) { const auto attributes = object_attributes.read(); auto filename = read_unicode_string(c.emu, attributes.ObjectName); printf("Open section: %S\n", filename.c_str()); if (filename == L"\\Windows\\SharedSection") { section_handle.write(SHARED_SECTION.bits); return STATUS_SUCCESS; } if (reinterpret_cast(attributes.RootDirectory) != KNOWN_DLLS_DIRECTORY) { puts("Unsupported section"); c.emu.stop(); return STATUS_NOT_SUPPORTED; } if (filename.starts_with(L"api-ms-")) { filename = L"C:\\WINDOWS\\System32\\downlevel\\" + filename; } else { filename = L"C:\\WINDOWS\\System32\\" + filename; } if (!std::filesystem::exists(filename)) { return STATUS_FILE_INVALID; } file f{}; f.name = std::move(filename); const auto handle = c.proc.files.store(std::move(f)); section_handle.write(handle.bits); return STATUS_SUCCESS; } auto handle_NtMapViewOfSection(const syscall_context& c, uint64_t section_handle, uint64_t process_handle, emulator_object base_address, ULONG_PTR /*zero_bits*/, SIZE_T /*commit_size*/, const emulator_object /*section_offset*/, const emulator_object view_size, SECTION_INHERIT /*inherit_disposition*/, ULONG /*allocation_type*/, ULONG /*win32_protect*/) -> NTSTATUS { if (process_handle != ~0ULL) { return STATUS_INVALID_HANDLE; } if (section_handle == SHARED_SECTION) { const auto address = c.emu.find_free_allocation_base(c.proc.shared_section_size); 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; }); constexpr auto windows_dir_offset = 0x10; c.emu.write_memory(address + 8, windows_dir_offset); const auto obj_address = address + windows_dir_offset; 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); ucs.Buffer = reinterpret_cast(dir_address - obj_address); ucs.Length = static_cast(windows_dir_size); ucs.MaximumLength = ucs.Length; }); const emulator_object sysdir_obj{c.emu, obj_address + windir_obj.size()}; sysdir_obj.access([&](UNICODE_STRING& ucs) { c.proc.gs_segment.make_unicode_string(ucs, L"C:\\WINDOWS\\System32"); ucs.Buffer = reinterpret_cast(reinterpret_cast(ucs.Buffer) - obj_address); }); if (view_size.value()) { view_size.write(c.proc.shared_section_size); } base_address.write(address); return STATUS_SUCCESS; } const auto section_entry = c.proc.files.get(section_handle); if (!section_entry) { return STATUS_INVALID_HANDLE; } const auto binary = c.proc.module_manager.map_module(section_entry->name); if (!binary) { return STATUS_FILE_INVALID; } if (view_size.value()) { view_size.write(binary->size_of_image); } base_address.write(binary->image_base); return STATUS_SUCCESS; } NTSTATUS handle_NtCreateIoCompletion(const syscall_context& c, const emulator_object event_handle, const ACCESS_MASK desired_access, const emulator_object object_attributes, uint32_t /*number_of_concurrent_threads*/) { return handle_NtCreateEvent(c, event_handle, desired_access, object_attributes, NotificationEvent, FALSE); } NTSTATUS handle_NtCreateWaitCompletionPacket(const syscall_context& c, const emulator_object event_handle, const ACCESS_MASK desired_access, const emulator_object object_attributes) { return handle_NtCreateEvent(c, event_handle, desired_access, object_attributes, NotificationEvent, FALSE); } NTSTATUS handle_NtQueryVirtualMemory(const syscall_context& c, const uint64_t process_handle, const uint64_t base_address, const uint32_t info_class, const uint64_t memory_information, const uint32_t memory_information_length, const emulator_object return_length) { if (process_handle != ~0ULL) { return STATUS_NOT_SUPPORTED; } if (info_class == MemoryWorkingSetExInformation || info_class == MemoryImageExtensionInformation) { return STATUS_NOT_SUPPORTED; } if (info_class == MemoryBasicInformation) { if (return_length) { return_length.write(sizeof(MEMORY_BASIC_INFORMATION)); } if (memory_information_length != sizeof(MEMORY_BASIC_INFORMATION)) { return STATUS_BUFFER_OVERFLOW; } const emulator_object info{c.emu, memory_information}; info.access([&](MEMORY_BASIC_INFORMATION& image_info) { const auto region_info = c.emu.get_region_info(base_address); assert(!region_info.is_committed || region_info.is_reserved); image_info.BaseAddress = reinterpret_cast(region_info.start); image_info.AllocationBase = reinterpret_cast(region_info.allocation_base); image_info.AllocationProtect = 0; image_info.PartitionId = 0; image_info.RegionSize = region_info.length; image_info.State = region_info.is_committed ? MEM_COMMIT : (region_info.is_reserved ? MEM_RESERVE : MEM_FREE); image_info.Protect = map_emulator_to_nt_protection(region_info.pemissions); image_info.Type = MEM_PRIVATE; }); return STATUS_SUCCESS; } if (info_class == MemoryImageInformation) { if (return_length) { return_length.write(sizeof(MEMORY_IMAGE_INFORMATION)); } if (memory_information_length != sizeof(MEMORY_IMAGE_INFORMATION)) { return STATUS_BUFFER_OVERFLOW; } const auto mod = c.proc.module_manager.find_by_address(base_address); if (!mod) { printf("Bad address for memory image request: %llX\n", base_address); return STATUS_INVALID_ADDRESS; } const emulator_object info{c.emu, memory_information}; info.access([&](MEMORY_IMAGE_INFORMATION& image_info) { image_info.ImageBase = reinterpret_cast(mod->image_base); image_info.SizeOfImage = mod->size_of_image; }); return STATUS_SUCCESS; } printf("Unsupported memory info class: %X\n", info_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtQuerySystemInformation(const syscall_context& c, const uint32_t info_class, const uint64_t system_information, const uint32_t system_information_length, const emulator_object return_length) { if (info_class == SystemFlushInformation || info_class == SystemHypervisorSharedPageInformation ) { return STATUS_NOT_SUPPORTED; } if (info_class == SystemTimeOfDayInformation) { if (return_length) { return_length.write(sizeof(SYSTEM_TIMEOFDAY_INFORMATION)); } if (system_information_length != sizeof(SYSTEM_TIMEOFDAY_INFORMATION)) { return STATUS_BUFFER_TOO_SMALL; } const emulator_object info_obj{c.emu, system_information}; info_obj.access([&](SYSTEM_TIMEOFDAY_INFORMATION& info) { info.BootTime.QuadPart = 0; // TODO: Fill }); return STATUS_SUCCESS; } if (info_class == SystemRangeStartInformation) { if (return_length) { return_length.write(sizeof(SYSTEM_RANGE_START_INFORMATION)); } if (system_information_length != sizeof(SYSTEM_RANGE_START_INFORMATION)) { return STATUS_BUFFER_TOO_SMALL; } const emulator_object info_obj{c.emu, system_information}; info_obj.access([&](SYSTEM_RANGE_START_INFORMATION& info) { info.SystemRangeStart = 0xFFFF800000000000; }); return STATUS_SUCCESS; } if (info_class == SystemProcessorInformation) { if (return_length) { return_length.write(sizeof(SYSTEM_PROCESSOR_INFORMATION)); } if (system_information_length != sizeof(SYSTEM_PROCESSOR_INFORMATION)) { return STATUS_BUFFER_TOO_SMALL; } const emulator_object info_obj{c.emu, system_information}; info_obj.access([&](SYSTEM_PROCESSOR_INFORMATION& info) { memset(&info, 0, sizeof(info)); info.MaximumProcessors = 2; info.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64; }); return STATUS_SUCCESS; } if (info_class == SystemNumaProcessorMap) { if (return_length) { return_length.write(sizeof(SYSTEM_NUMA_INFORMATION)); } if (system_information_length != sizeof(SYSTEM_NUMA_INFORMATION)) { return STATUS_BUFFER_TOO_SMALL; } const emulator_object info_obj{c.emu, system_information}; info_obj.access([&](SYSTEM_NUMA_INFORMATION& info) { memset(&info, 0, sizeof(info)); info.ActiveProcessorsGroupAffinity->Mask = 0xFFF; info.AvailableMemory[0] = 0xFFF; info.Pad[0] = 0xFFF; }); return STATUS_SUCCESS; } if (info_class != SystemBasicInformation && info_class != SystemEmulationBasicInformation) { printf("Unsupported system info class: %X\n", info_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } if (return_length) { return_length.write(sizeof(SYSTEM_BASIC_INFORMATION)); } if (system_information_length != sizeof(SYSTEM_BASIC_INFORMATION)) { return STATUS_BUFFER_TOO_SMALL; } const emulator_object info{c.emu, system_information}; info.access([&](SYSTEM_BASIC_INFORMATION& basic_info) { basic_info.Reserved = 0; basic_info.TimerResolution = 0x0002625a; basic_info.PageSize = 0x1000; basic_info.LowestPhysicalPageNumber = 0x00000001; basic_info.HighestPhysicalPageNumber = 0x00c9c7ff; basic_info.AllocationGranularity = 0x10000; basic_info.MinimumUserModeAddress = 0x0000000000010000; basic_info.MaximumUserModeAddress = 0x00007ffffffeffff; basic_info.ActiveProcessorsAffinityMask = 0x0000000000000fff; basic_info.NumberOfProcessors = 1; }); return STATUS_SUCCESS; } NTSTATUS handle_NtDuplicateObject(const syscall_context& /*c*/, uint64_t source_process_handle, uint64_t source_handle, uint64_t target_process_handle, const emulator_object target_handle, const ACCESS_MASK /*desired_access*/, const ULONG /*handle_attributes*/, const ULONG /*options*/) { if (source_process_handle != ~0ULL || target_process_handle != ~0ULL) { return STATUS_NOT_SUPPORTED; } handle source{}; source.bits = source_handle; if (source.value.is_pseudo) { target_handle.write(source); return STATUS_SUCCESS; } puts("Duplicating non-pseudo object not supported yet!"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtQuerySystemInformationEx(const syscall_context& c, const uint32_t info_class, const uint64_t input_buffer, const uint32_t input_buffer_length, const uint64_t system_information, const uint32_t system_information_length, const emulator_object return_length) { if (info_class == SystemFlushInformation || info_class == SystemFeatureConfigurationInformation || info_class == SystemFeatureConfigurationSectionInformation) { printf("Unsupported, but allowed system info class: %X\n", info_class); return STATUS_NOT_SUPPORTED; } if (info_class == SystemLogicalProcessorAndGroupInformation) { void* buffer = calloc(1, input_buffer_length); void* res_buff = calloc(1, system_information_length); c.emu.read_memory(input_buffer, buffer, input_buffer_length); NTSTATUS code = STATUS_SUCCESS; return_length.access([&](uint32_t& len) { code = NtQuerySystemInformationEx(static_cast(info_class), buffer, input_buffer_length, res_buff, system_information_length, reinterpret_cast(&len)); }); if (code == 0) { c.emu.write_memory(system_information, res_buff, return_length.read()); } free(buffer); free(res_buff); return code; } if (info_class != SystemBasicInformation && info_class != SystemEmulationBasicInformation) { printf("Unsupported system info ex class: %X\n", info_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } if (return_length) { return_length.write(sizeof(SYSTEM_BASIC_INFORMATION)); } if (system_information_length != sizeof(SYSTEM_BASIC_INFORMATION)) { return STATUS_BUFFER_TOO_SMALL; } const emulator_object info{c.emu, system_information}; info.access([&](SYSTEM_BASIC_INFORMATION& basic_info) { basic_info.Reserved = 0; basic_info.TimerResolution = 0x0002625a; basic_info.PageSize = 0x1000; basic_info.LowestPhysicalPageNumber = 0x00000001; basic_info.HighestPhysicalPageNumber = 0x00c9c7ff; basic_info.AllocationGranularity = 0x10000; basic_info.MinimumUserModeAddress = 0x0000000000010000; basic_info.MaximumUserModeAddress = 0x00007ffffffeffff; basic_info.ActiveProcessorsAffinityMask = 0x0000000000000fff; basic_info.NumberOfProcessors = 1; }); return STATUS_SUCCESS; } NTSTATUS handle_NtQueryInformationProcess(const syscall_context& c, const uint64_t process_handle, const uint32_t info_class, const uint64_t process_information, const uint32_t process_information_length, const emulator_object return_length) { if (process_handle != ~0ULL) { return STATUS_NOT_SUPPORTED; } if (info_class == ProcessImageInformation) { if (return_length) { return_length.write(sizeof(SECTION_IMAGE_INFORMATION)); } if (process_information_length != sizeof(SECTION_IMAGE_INFORMATION)) { return STATUS_BUFFER_OVERFLOW; } const emulator_object info{c.emu, process_information}; info.access([&](SECTION_IMAGE_INFORMATION& i) { const auto& mod = *c.proc.executable; const emulator_object dos_header_obj{c.emu, mod.image_base}; const auto dos_header = dos_header_obj.read(); const emulator_object nt_headers_obj{c.emu, mod.image_base + dos_header.e_lfanew}; const auto nt_headers = nt_headers_obj.read(); const auto& file_header = nt_headers.FileHeader; const auto& optional_header = nt_headers.OptionalHeader; i.TransferAddress = nullptr; i.MaximumStackSize = optional_header.SizeOfStackReserve; i.CommittedStackSize = optional_header.SizeOfStackCommit; i.SubSystemType = optional_header.Subsystem; i.SubSystemMajorVersion = optional_header.MajorSubsystemVersion; i.SubSystemMinorVersion = optional_header.MinorSubsystemVersion; i.MajorOperatingSystemVersion = optional_header.MajorOperatingSystemVersion; i.MinorOperatingSystemVersion = optional_header.MinorOperatingSystemVersion; i.ImageCharacteristics = file_header.Characteristics; i.DllCharacteristics = optional_header.DllCharacteristics; i.Machine = file_header.Machine; i.ImageContainsCode = TRUE; i.ImageFlags = 0; // TODO i.ImageFileSize = optional_header.SizeOfImage; i.LoaderFlags = optional_header.LoaderFlags; i.CheckSum = optional_header.CheckSum; }); return STATUS_SUCCESS; } if (info_class == ProcessCookie) { if (return_length) { return_length.write(sizeof(uint32_t)); } if (process_information_length != sizeof(uint32_t)) { return STATUS_BUFFER_OVERFLOW; } const emulator_object info{c.emu, process_information}; info.write(0x01234567); return STATUS_SUCCESS; } if (info_class == ProcessDebugPort) { if (return_length) { return_length.write(sizeof(DWORD_PTR)); } if (process_information_length != sizeof(DWORD_PTR)) { return STATUS_BUFFER_OVERFLOW; } const emulator_object info{c.emu, process_information}; info.write(0); return STATUS_SUCCESS; } if (info_class == ProcessEnclaveInformation || info_class == ProcessMitigationPolicy) { return STATUS_NOT_SUPPORTED; } if (info_class == ProcessBasicInformation) { if (return_length) { return_length.write(sizeof(PROCESS_BASIC_INFORMATION)); } if (process_information_length != sizeof(PROCESS_BASIC_INFORMATION)) { return STATUS_BUFFER_OVERFLOW; } const emulator_object info{c.emu, process_information}; info.access([&](PROCESS_BASIC_INFORMATION& basic_info) { basic_info.PebBaseAddress = c.proc.peb.ptr(); basic_info.UniqueProcessId = reinterpret_cast(1); }); return STATUS_SUCCESS; } printf("Unsupported process info class: %X\n", info_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtQueryInformationThread(const syscall_context& c, const uint64_t thread_handle, const uint32_t info_class, const uint64_t thread_information, const uint32_t thread_information_length, const emulator_object return_length) { if (thread_handle != ~1ULL) { return STATUS_NOT_SUPPORTED; } if (info_class == ThreadBasicInformation) { if (return_length) { return_length.write(sizeof(THREAD_BASIC_INFORMATION)); } if (thread_information_length != sizeof(THREAD_BASIC_INFORMATION)) { return STATUS_BUFFER_OVERFLOW; } const emulator_object info{c.emu, thread_information}; info.access([&](THREAD_BASIC_INFORMATION& i) { i.TebBaseAddress = c.proc.teb.ptr(); i.ClientId = c.proc.teb.read().ClientId; }); return STATUS_SUCCESS; } printf("Unsupported thread info class: %X\n", info_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtSetInformationProcess(const syscall_context& c, const uint64_t process_handle, const uint32_t info_class, const uint64_t /*process_information*/, const uint32_t /*process_information_length*/) { if (process_handle != ~0ULL) { return STATUS_NOT_SUPPORTED; } if (info_class == ProcessSchedulerSharedData || info_class == ProcessTlsInformation || info_class == ProcessConsoleHostProcess || info_class == ProcessFaultInformation || info_class == ProcessRaiseUMExceptionOnInvalidHandleClose) { return STATUS_SUCCESS; } printf("Unsupported info process class: %X\n", info_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtApphelpCacheControl() { return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtProtectVirtualMemory(const syscall_context& c, const uint64_t process_handle, const emulator_object base_address, const emulator_object bytes_to_protect, const uint32_t protection, const emulator_object old_protection) { if (process_handle != ~0ULL) { return STATUS_NOT_SUPPORTED; } const auto orig_start = base_address.read(); const auto orig_length = bytes_to_protect.read(); const auto aligned_start = page_align_down(orig_start); const auto aligned_length = page_align_up(orig_start + orig_length) - aligned_start; base_address.write(aligned_start); bytes_to_protect.write(static_cast(aligned_length)); const auto requested_protection = map_nt_to_emulator_protection(protection); printf("Changing protection at %llX-%llX to %s\n", aligned_start, aligned_start + aligned_length, get_permission_string(requested_protection).c_str()); memory_permission old_protection_value{}; c.emu.protect_memory(aligned_start, aligned_length, requested_protection, &old_protection_value); const auto current_protection = map_emulator_to_nt_protection(old_protection_value); old_protection.write(current_protection); return STATUS_SUCCESS; } NTSTATUS handle_NtOpenDirectoryObject(const syscall_context& c, const emulator_object directory_handle, const ACCESS_MASK /*desired_access*/, const emulator_object object_attributes) { const auto attributes = object_attributes.read(); const auto object_name = read_unicode_string(c.emu, attributes.ObjectName); if (object_name == L"\\KnownDlls") { directory_handle.write(KNOWN_DLLS_DIRECTORY.bits); return STATUS_SUCCESS; } return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtOpenSymbolicLinkObject(const syscall_context& c, const emulator_object link_handle, ACCESS_MASK /*desired_access*/, const emulator_object object_attributes) { const auto attributes = object_attributes.read(); const auto object_name = read_unicode_string(c.emu, attributes.ObjectName); if (object_name == L"KnownDllPath") { link_handle.write(KNOWN_DLLS_SYMLINK.bits); return STATUS_SUCCESS; } return STATUS_NOT_SUPPORTED; } NTSTATUS WINAPI handle_NtQuerySymbolicLinkObject(const syscall_context& c, const uint64_t link_handle, const emulator_object link_target, const emulator_object returned_length) { if (link_handle == KNOWN_DLLS_SYMLINK) { constexpr std::wstring_view system32 = L"C:\\WINDOWS\\System32"; constexpr auto str_length = system32.size() * 2; constexpr auto max_length = str_length + 2; returned_length.write(max_length); bool too_small = false; link_target.access([&](UNICODE_STRING& str) { if (str.MaximumLength < max_length) { too_small = true; return; } str.Length = str_length; c.emu.write_memory(reinterpret_cast(str.Buffer), system32.data(), max_length); }); return too_small ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; } return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtAllocateVirtualMemoryEx(const syscall_context& c, const uint64_t process_handle, const emulator_object base_address, const emulator_object bytes_to_allocate, const uint32_t allocation_type, const uint32_t page_protection) { if (process_handle != ~0ULL) { return STATUS_NOT_SUPPORTED; } const auto allocation_bytes = bytes_to_allocate.read(); const auto protection = map_nt_to_emulator_protection(page_protection); auto potential_base = base_address.read(); if (!potential_base) { potential_base = c.emu.find_free_allocation_base(allocation_bytes); } if (!potential_base) { return STATUS_MEMORY_NOT_ALLOCATED; } base_address.write(potential_base); const bool reserve = allocation_type & MEM_RESERVE; const bool commit = allocation_type & MEM_COMMIT; if ((allocation_type & ~(MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN)) || (!commit && !reserve)) { throw std::runtime_error("Unsupported allocation type!"); } if (commit && !reserve) { return c.emu.commit_memory(potential_base, allocation_bytes, protection) ? STATUS_SUCCESS : STATUS_MEMORY_NOT_ALLOCATED; } return c.emu.allocate_memory(potential_base, allocation_bytes, protection, !commit) ? STATUS_SUCCESS : STATUS_MEMORY_NOT_ALLOCATED; } NTSTATUS handle_NtAllocateVirtualMemory(const syscall_context& c, const uint64_t process_handle, const emulator_object base_address, uint64_t /*zero_bits*/, const emulator_object bytes_to_allocate, const uint32_t allocation_type, const uint32_t page_protection) { return handle_NtAllocateVirtualMemoryEx(c, process_handle, base_address, bytes_to_allocate, allocation_type, page_protection); } NTSTATUS handle_NtFreeVirtualMemory(const syscall_context& c, const uint64_t process_handle, const emulator_object base_address, const emulator_object bytes_to_allocate, uint32_t free_type) { if (process_handle != ~0ULL) { return STATUS_NOT_SUPPORTED; } const auto allocation_base = base_address.read(); const auto allocation_size = bytes_to_allocate.read(); if (free_type & MEM_RELEASE) { return c.emu.release_memory(allocation_base, allocation_size) ? STATUS_SUCCESS : STATUS_MEMORY_NOT_ALLOCATED; } if (free_type & MEM_DECOMMIT) { return c.emu.decommit_memory(allocation_base, allocation_size) ? STATUS_SUCCESS : STATUS_MEMORY_NOT_ALLOCATED; } throw std::runtime_error("Bad free type"); } NTSTATUS handle_NtCreateSection(const syscall_context& c, const emulator_object section_handle, const ACCESS_MASK /*desired_access*/, const emulator_object /*object_attributes*/, const emulator_object maximum_size, const ULONG /*section_page_protection*/, const ULONG /*allocation_attributes*/, const uint64_t /*file_handle*/) { puts("NtCreateSection not supported"); section_handle.write(SHARED_SECTION.bits); maximum_size.access([&c](ULARGE_INTEGER& large_int) { large_int.QuadPart = page_align_up(large_int.QuadPart); c.proc.shared_section_size = large_int.QuadPart; }); return STATUS_SUCCESS; } NTSTATUS handle_NtConnectPort(const syscall_context& c, const emulator_object client_port_handle, const emulator_object server_port_name, const emulator_object /*security_qos*/, const emulator_object client_shared_memory, const emulator_object server_shared_memory, const emulator_object /*maximum_message_length*/, uint64_t /*connection_info*/, const emulator_object /*connection_info_length*/) { auto port_name = read_unicode_string(c.emu, server_port_name); printf("NtConnectPort: %S\n", port_name.c_str()); port p{}; p.name = std::move(port_name); const auto xx = server_shared_memory.read(); client_shared_memory.access([&](PORT_VIEW& view) { p.view_base = c.emu.allocate_memory(view.ViewSize, memory_permission::read_write); view.ViewBase = reinterpret_cast(p.view_base); view.ViewRemoteBase = view.ViewBase; }); const auto handle = c.proc.ports.store(std::move(p)); client_port_handle.write(handle.bits); return STATUS_SUCCESS; } NTSTATUS handle_NtReadVirtualMemory(const syscall_context& c, uint64_t process_handle, uint64_t base_address, uint64_t buffer, ULONG number_of_bytes_to_read, const emulator_object number_of_bytes_read) { number_of_bytes_read.write(0); if (process_handle != ~0ULL) { return STATUS_NOT_SUPPORTED; } std::vector memory{}; memory.resize(number_of_bytes_read); if (!c.emu.try_read_memory(base_address, memory.data(), memory.size())) { return STATUS_INVALID_ADDRESS; } c.emu.write_memory(buffer, memory.data(), memory.size()); number_of_bytes_read.write(number_of_bytes_to_read); return STATUS_SUCCESS; } NTSTATUS handle_NtDeviceIoControlFile() { puts("NtDeviceIoControlFile not supported"); return STATUS_SUCCESS; } NTSTATUS handle_NtQueryWnfStateData() { puts("NtQueryWnfStateData not supported"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtQueryWnfStateNameInformation() { puts("NtQueryWnfStateNameInformation not supported"); //return STATUS_NOT_SUPPORTED; return STATUS_SUCCESS; } NTSTATUS handle_NtOpenProcessToken() { puts("NtOpenProcessToken not supported"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtQuerySecurityAttributesToken() { puts("NtQuerySecurityAttributesToken not supported"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtQueryLicenseValue() { puts("NtQueryLicenseValue not supported"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtTestAlert() { puts("NtTestAlert not supported"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtQueryInformationToken() { puts("NtQueryInformationToken not supported"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtDxgkIsFeatureEnabled() { puts("NtDxgkIsFeatureEnabled not supported"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtUserDisplayConfigGetDeviceInfo() { puts("NtUserDisplayConfigGetDeviceInfo not supported"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtGdiInit2(const syscall_context& c) { c.proc.peb.access([&](PEB& peb) { if (!peb.GdiSharedHandleTable) { peb.GdiSharedHandleTable = c.proc.gs_segment.reserve().ptr(); } }); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtUserGetThreadState() { puts("NtUserGetThreadState not supported"); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& c, const uint64_t port_handle, const ULONG /*flags*/, const emulator_object /*send_message*/, const emulator_object /*send_message_attributes*/ , const emulator_object receive_message, const emulator_object /*buffer_length*/, const emulator_object /*receive_message_attributes*/, const emulator_object /*timeout*/) { const auto* port = c.proc.ports.get(port_handle); if (!port) { return STATUS_INVALID_HANDLE; } if (port->name != L"\\Windows\\ApiPort") { puts("!!! BAD PORT"); return STATUS_NOT_SUPPORTED; } const emulator_object data{c.emu, receive_message.value() + 0x48}; const auto dest = data.read(); const auto base = reinterpret_cast(dest.Base); const auto value = base + 0x10; c.emu.write_memory(base + 8, &value, sizeof(value)); return STATUS_SUCCESS; } NTSTATUS handle_NtInitializeNlsFiles(const syscall_context& c, const emulator_object base_address, const emulator_object default_locale_id, const emulator_object /*default_casing_table_size*/) { const auto locale_file = utils::io::read_file(R"(C:\Windows\System32\locale.nls)"); if (locale_file.empty()) { return STATUS_FILE_INVALID; } const auto size = page_align_up(locale_file.size()); const auto base = c.emu.allocate_memory(size, memory_permission::read); c.emu.write_memory(base, locale_file.data(), locale_file.size()); base_address.write(base); default_locale_id.write(0x407); return STATUS_SUCCESS; } NTSTATUS handle_NtContinue(const syscall_context& c, const emulator_object thread_context, const BOOLEAN /*raise_alert*/) { c.write_status = false; const auto context = thread_context.read(); context_frame::restore(c.emu, context); return STATUS_SUCCESS; } NTSTATUS handle_NtTerminateProcess(const syscall_context& c, const uint64_t process_handle, NTSTATUS /*exit_status*/) { if (process_handle == 0) { return STATUS_SUCCESS; } if (process_handle == ~0ULL) { c.emu.stop(); return STATUS_SUCCESS; } return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtWriteFile(const syscall_context& c, const uint64_t file_handle, const uint64_t /*event*/, const uint64_t /*apc_routine*/, const uint64_t /*apc_context*/, const emulator_object /*io_status_block*/, uint64_t buffer, const ULONG length, const emulator_object /*byte_offset*/, const emulator_object /*key*/) { if (file_handle == STDOUT_HANDLE) { std::string temp_buffer{}; temp_buffer.resize(length); c.emu.read_memory(buffer, temp_buffer.data(), temp_buffer.size()); (void)fwrite(temp_buffer.data(), 1, temp_buffer.size(), stdout); (void)fflush(stdout); return STATUS_SUCCESS; } puts("NtWriteFile not supported"); c.emu.stop(); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtCreateFile(const syscall_context& c, const emulator_object file_handle, ACCESS_MASK /*desired_access*/, const emulator_object object_attributes) { const auto attributes = object_attributes.read(); const auto filename = read_unicode_string(c.emu, attributes.ObjectName); if (filename == L"\\Device\\ConDrv\\Server") { file_handle.write(CONSOLE_SERVER.bits); return STATUS_SUCCESS; } if (filename == L"\\Device\\DeviceApi\\CMApi") { file_handle.write(CM_API.bits); return STATUS_SUCCESS; } handle root_handle{}; root_handle.bits = reinterpret_cast(attributes.RootDirectory); if (root_handle.value.is_pseudo && (filename == L"\\Reference" || filename == L"\\Connect")) { file_handle.write(root_handle.bits); return STATUS_SUCCESS; } printf("Unsupported file: %S\n", filename.c_str()); return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtRaiseHardError(const syscall_context& c, const NTSTATUS error_status, const ULONG /*number_of_parameters*/, const emulator_object /*unicode_string_parameter_mask*/, const emulator_object /*parameters*/, const HARDERROR_RESPONSE_OPTION /*valid_response_option*/, const emulator_object response) { if (response) { response.write(ResponseAbort); } printf("Hard error: %X\n", static_cast(error_status)); c.emu.stop(); return STATUS_SUCCESS; } NTSTATUS handle_NtCreateSemaphore(const syscall_context& c, const emulator_object semaphore_handle, const ACCESS_MASK /*desired_access*/, const emulator_object object_attributes, const ULONG initial_count, const ULONG maximum_count) { semaphore s{}; s.current_count = initial_count; s.max_count = maximum_count; if (object_attributes) { const auto attributes = object_attributes.read(); if (attributes.ObjectName) { s.name = read_unicode_string(c.emu, attributes.ObjectName); } } const auto handle = c.proc.semaphores.store(std::move(s)); semaphore_handle.write(handle.bits); return STATUS_SUCCESS; } NTSTATUS handle_NtAddAtomEx(const syscall_context& c, const uint64_t atom_name, const ULONG length, const emulator_object atom, const ULONG /*flags*/) { std::wstring name{}; name.resize(length / 2); c.emu.read_memory(atom_name, name.data(), length); uint16_t index = 0; if (!c.proc.atoms.empty()) { auto i = c.proc.atoms.end(); --i; index = i->first + 1; } std::optional last_entry{}; for (auto& entry : c.proc.atoms) { if (entry.second == name) { if (atom) { atom.write(entry.first); return STATUS_SUCCESS; } } if (entry.first > 0) { if (!last_entry) { index = 0; } else { const auto diff = entry.first - *last_entry; if (diff > 1) { index = *last_entry + 1; } } } last_entry = entry.first; } c.proc.atoms[index] = std::move(name); atom.write(index); return STATUS_SUCCESS; } NTSTATUS handle_NtUnmapViewOfSection(const syscall_context& c, uint64_t process_handle, uint64_t base_address ) { if (process_handle != ~0ULL) { return STATUS_NOT_SUPPORTED; } const auto* mod = c.proc.module_manager.find_by_address(base_address); if (!mod) { puts("Unmapping non-module section not supported!"); } else { printf("Unmapping section %s not supported!\n", mod->name.c_str()); } c.emu.stop(); return STATUS_NOT_SUPPORTED; } } void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports) { this->handlers_ = {}; const auto ntdll_syscalls = find_syscalls(ntdll_exports); const auto win32u_syscalls = find_syscalls(win32u_exports); map_syscalls(this->handlers_, ntdll_syscalls, 0); map_syscalls(this->handlers_, win32u_syscalls, 0x1000); this->add_handlers(); } syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports) { this->setup(ntdll_exports, win32u_exports); } void syscall_dispatcher::add_handlers() { std::unordered_map handler_mapping{}; handler_mapping.reserve(this->handlers_.size()); make_syscall_handler(); #define add_handler(syscall) \ do \ { \ handler_mapping[#syscall] = make_syscall_handler(); \ } while(0) add_handler(NtSetInformationThread) ; add_handler(NtSetEvent); add_handler(NtClose); add_handler(NtOpenKey); add_handler(NtAllocateVirtualMemory); add_handler(NtQueryInformationProcess); add_handler(NtSetInformationProcess); add_handler(NtFreeVirtualMemory); add_handler(NtQueryVirtualMemory); add_handler(NtOpenThreadToken); add_handler(NtQueryPerformanceCounter); add_handler(NtQuerySystemInformation); add_handler(NtCreateEvent); add_handler(NtProtectVirtualMemory); add_handler(NtOpenDirectoryObject); add_handler(NtTraceEvent); add_handler(NtAllocateVirtualMemoryEx); add_handler(NtCreateIoCompletion); add_handler(NtCreateWaitCompletionPacket); add_handler(NtCreateWorkerFactory); add_handler(NtManageHotPatch); add_handler(NtOpenSection); add_handler(NtMapViewOfSection); add_handler(NtOpenSymbolicLinkObject); add_handler(NtQuerySymbolicLinkObject); add_handler(NtQuerySystemInformationEx); add_handler(NtOpenFile); add_handler(NtQueryVolumeInformationFile); add_handler(NtApphelpCacheControl); add_handler(NtCreateSection); add_handler(NtConnectPort); add_handler(NtCreateFile); add_handler(NtDeviceIoControlFile); add_handler(NtQueryWnfStateData); add_handler(NtOpenProcessToken); add_handler(NtQuerySecurityAttributesToken); add_handler(NtQueryLicenseValue); add_handler(NtTestAlert); add_handler(NtContinue); add_handler(NtTerminateProcess); add_handler(NtWriteFile); add_handler(NtRaiseHardError); add_handler(NtCreateSemaphore); add_handler(NtReadVirtualMemory); add_handler(NtQueryInformationToken); add_handler(NtDxgkIsFeatureEnabled); add_handler(NtAddAtomEx); add_handler(NtInitializeNlsFiles); add_handler(NtUnmapViewOfSection); add_handler(NtDuplicateObject); add_handler(NtQueryInformationThread); add_handler(NtQueryWnfStateNameInformation); add_handler(NtAlpcSendWaitReceivePort); add_handler(NtGdiInit2); add_handler(NtUserGetThreadState); add_handler(NtOpenKeyEx); add_handler(NtUserDisplayConfigGetDeviceInfo); #undef add_handler for (auto& entry : this->handlers_) { const auto handler = handler_mapping.find(entry.second.name); if (handler == handler_mapping.end()) { continue; } entry.second.handler = handler->second; #ifndef NDEBUG handler_mapping.erase(handler); #endif } #ifndef NDEBUG if (!handler_mapping.empty()) { throw std::runtime_error("Unmapped handlers!"); } #endif } static void serialize(utils::buffer_serializer& buffer, const syscall_handler_entry& obj) { buffer.write(obj.name); } static void deserialize(utils::buffer_deserializer& buffer, syscall_handler_entry& obj) { buffer.read(obj.name); obj.handler = nullptr; } void syscall_dispatcher::serialize(utils::buffer_serializer& buffer) const { buffer.write_map(this->handlers_); } void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer) { buffer.read_map(this->handlers_); this->add_handlers(); } void syscall_dispatcher::dispatch(x64_emulator& emu, process_context& context) { const auto address = emu.read_instruction_pointer(); const auto syscall_id = emu.reg(x64_register::eax); const syscall_context c{emu, context, true}; try { const auto entry = this->handlers_.find(syscall_id); if (entry == this->handlers_.end()) { printf("Unknown syscall: %X\n", syscall_id); c.emu.reg(x64_register::rax, STATUS_NOT_SUPPORTED); c.emu.stop(); return; } if (!entry->second.handler) { printf("Unimplemented syscall: %s - %X\n", entry->second.name.c_str(), syscall_id); c.emu.reg(x64_register::rax, STATUS_NOT_SUPPORTED); c.emu.stop(); return; } printf("Handling syscall: %s with id %X at %llX \n", entry->second.name.c_str(), syscall_id, address); entry->second.handler(c); } catch (std::exception& e) { printf("Syscall threw an exception: %X (%llX) - %s\n", syscall_id, address, e.what()); emu.reg(x64_register::rax, STATUS_UNSUCCESSFUL); emu.stop(); } catch (...) { printf("Syscall threw an unknown exception: %X (%llX)\n", syscall_id, address); emu.reg(x64_register::rax, STATUS_UNSUCCESSFUL); emu.stop(); } }