Files
windows-user-space-emulator/src/windows-emulator/syscalls.cpp
2024-12-22 10:28:44 +01:00

3005 lines
86 KiB
C++

#include "std_include.hpp"
#include "syscall_dispatcher.hpp"
#include "context_frame.hpp"
#include "emulator_utils.hpp"
#include "syscall_utils.hpp"
#include <numeric>
#include <cwctype>
#include <utils/io.hpp>
#include <utils/string.hpp>
#include <utils/finally.hpp>
namespace
{
NTSTATUS handle_NtQueryPerformanceCounter(const syscall_context& c,
const emulator_object<LARGE_INTEGER> performance_counter,
const emulator_object<LARGE_INTEGER> performance_frequency)
{
try
{
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();
}
});
}
if (performance_frequency)
{
performance_frequency.access([&](LARGE_INTEGER& value)
{
value.QuadPart = c.proc.kusd.get().QpcFrequency;
});
}
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(const syscall_context& c, const emulator_object<handle> key_handle,
const ACCESS_MASK /*desired_access*/,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes)
{
const auto attributes = object_attributes.read();
auto key = read_unicode_string(c.emu, attributes.ObjectName);
if (attributes.RootDirectory)
{
const auto* parent_handle = c.proc.registry_keys.get(reinterpret_cast<uint64_t>(attributes.RootDirectory));
if (!parent_handle)
{
return STATUS_INVALID_HANDLE;
}
key = parent_handle->hive / parent_handle->path / key;
}
c.win_emu.logger.print(color::dark_gray, "--> Registry key: %S\n", key.c_str());
auto entry = c.proc.registry.get_key(key);
if (!entry.has_value())
{
return STATUS_OBJECT_NAME_NOT_FOUND;
}
const auto handle = c.proc.registry_keys.store(std::move(entry.value()));
key_handle.write(handle);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtOpenKeyEx(const syscall_context& c, const emulator_object<handle> key_handle,
const ACCESS_MASK desired_access,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes,
ULONG /*open_options*/)
{
return handle_NtOpenKey(c, key_handle, desired_access, object_attributes);
}
NTSTATUS handle_NtQueryKey(const syscall_context& c, const handle key_handle,
const KEY_INFORMATION_CLASS key_information_class,
const uint64_t key_information, const ULONG length,
const emulator_object<ULONG> result_length)
{
const auto* key = c.proc.registry_keys.get(key_handle);
if (!key)
{
return STATUS_INVALID_HANDLE;
}
if (key_information_class == KeyNameInformation)
{
auto key_name = (key->hive / key->path).wstring();
while (key_name.ends_with('/') || key_name.ends_with('\\'))
{
key_name.pop_back();
}
std::ranges::transform(key_name, key_name.begin(), std::towupper);
const auto required_size = sizeof(KEY_NAME_INFORMATION) + (key_name.size() * 2) - 1;
result_length.write(static_cast<ULONG>(required_size));
if (required_size > length)
{
return STATUS_BUFFER_TOO_SMALL;
}
KEY_NAME_INFORMATION info{};
info.NameLength = static_cast<ULONG>(key_name.size() * 2);
const emulator_object<KEY_NAME_INFORMATION> info_obj{c.emu, key_information};
info_obj.write(info);
c.emu.write_memory(key_information + offsetof(KEY_NAME_INFORMATION, Name),
key_name.data(),
info.NameLength);
return STATUS_SUCCESS;
}
if (key_information_class == KeyHandleTagsInformation)
{
constexpr auto required_size = sizeof(KEY_HANDLE_TAGS_INFORMATION);
result_length.write(required_size);
if (required_size > length)
{
return STATUS_BUFFER_TOO_SMALL;
}
KEY_HANDLE_TAGS_INFORMATION info{};
info.HandleTags = 0; // ?
const emulator_object<KEY_HANDLE_TAGS_INFORMATION> info_obj{c.emu, key_information};
info_obj.write(info);
return STATUS_SUCCESS;
}
c.win_emu.logger.print(color::gray, "Unsupported registry class: %X\n", key_information_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtQueryValueKey(const syscall_context& c, const handle key_handle,
const emulator_object<UNICODE_STRING> value_name,
const KEY_VALUE_INFORMATION_CLASS key_value_information_class,
const uint64_t key_value_information,
const ULONG length, const emulator_object<ULONG> result_length)
{
const auto* key = c.proc.registry_keys.get(key_handle);
if (!key)
{
return STATUS_INVALID_HANDLE;
}
const auto query_name = read_unicode_string(c.emu, value_name);
const std::string name(query_name.begin(), query_name.end());
const auto value = c.proc.registry.get_value(*key, name);
if (!value)
{
return STATUS_OBJECT_NAME_NOT_FOUND;
}
const std::wstring original_name(value->name.begin(), value->name.end());
if (key_value_information_class == KeyValueBasicInformation)
{
constexpr auto base_size = offsetof(KEY_VALUE_BASIC_INFORMATION, Name);
const auto required_size = base_size + (original_name.size() * 2) - 1;
result_length.write(static_cast<ULONG>(required_size));
KEY_VALUE_BASIC_INFORMATION info{};
info.TitleIndex = 0;
info.Type = value->type;
info.NameLength = static_cast<ULONG>(original_name.size() * 2);
if (base_size <= length)
{
c.emu.write_memory(key_value_information, &info, base_size);
}
if (required_size > length)
{
return STATUS_BUFFER_OVERFLOW;
}
c.emu.write_memory(key_value_information + base_size, original_name.data(), info.NameLength);
return STATUS_SUCCESS;
}
if (key_value_information_class == KeyValuePartialInformation)
{
constexpr auto base_size = offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data);
const auto required_size = base_size + value->data.size();
result_length.write(static_cast<ULONG>(required_size));
KEY_VALUE_PARTIAL_INFORMATION info{};
info.TitleIndex = 0;
info.Type = value->type;
info.DataLength = static_cast<ULONG>(value->data.size());
if (base_size <= length)
{
c.emu.write_memory(key_value_information, &info, base_size);
}
if (required_size > length)
{
return STATUS_BUFFER_OVERFLOW;
}
c.emu.write_memory(key_value_information + base_size, value->data.data(), value->data.size());
return STATUS_SUCCESS;
}
if (key_value_information_class == KeyValueFullInformation)
{
constexpr auto base_size = offsetof(KEY_VALUE_FULL_INFORMATION, Name);
const auto name_size = original_name.size() * 2;
const auto value_size = value->data.size();
const auto required_size = base_size + name_size + value_size + -1;
result_length.write(static_cast<ULONG>(required_size));
KEY_VALUE_FULL_INFORMATION info{};
info.TitleIndex = 0;
info.Type = value->type;
info.DataLength = static_cast<ULONG>(value->data.size());
info.NameLength = static_cast<ULONG>(original_name.size() * 2);
if (base_size <= length)
{
c.emu.write_memory(key_value_information, &info, base_size);
}
if (required_size > length)
{
return STATUS_BUFFER_OVERFLOW;
}
c.emu.write_memory(key_value_information + base_size,
original_name.data(),
info.NameLength);
c.emu.write_memory(key_value_information + base_size + info.NameLength,
value->data.data(),
value->data.size());
return STATUS_SUCCESS;
}
c.win_emu.logger.print(color::gray, "Unsupported registry value class: %X\n", key_value_information_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtCreateKey()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtNotifyChangeKey()
{
return STATUS_SUCCESS;
}
NTSTATUS handle_NtSetInformationThread(const syscall_context& c, const handle thread_handle,
const THREADINFOCLASS info_class,
const uint64_t thread_information,
const uint32_t thread_information_length)
{
auto* thread = thread_handle == CURRENT_THREAD
? c.proc.active_thread
: c.proc.threads.get(thread_handle);
if (!thread)
{
return STATUS_INVALID_HANDLE;
}
if (info_class == ThreadSchedulerSharedDataSlot)
{
return STATUS_SUCCESS;
}
if (info_class == ThreadNameInformation)
{
if (thread_information_length != sizeof(THREAD_NAME_INFORMATION))
{
return STATUS_BUFFER_OVERFLOW;
}
const emulator_object<THREAD_NAME_INFORMATION> info{c.emu, thread_information};
const auto i = info.read();
thread->name = read_unicode_string(c.emu, i.ThreadName);
c.win_emu.logger.print(color::blue, "Setting thread (%d) name: %S\n", thread->id, thread->name.c_str());
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<LONG> 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 handle h)
{
const auto value = h.value;
if (value.is_pseudo)
{
return STATUS_SUCCESS;
}
if (value.type == handle_types::thread && c.proc.threads.erase(h))
{
return STATUS_SUCCESS;
}
if (value.type == handle_types::event && c.proc.events.erase(h))
{
return STATUS_SUCCESS;
}
if (value.type == handle_types::file && c.proc.files.erase(h))
{
return STATUS_SUCCESS;
}
if (value.type == handle_types::device && c.proc.devices.erase(h))
{
return STATUS_SUCCESS;
}
if (value.type == handle_types::semaphore && c.proc.semaphores.erase(h))
{
return STATUS_SUCCESS;
}
if (value.type == handle_types::registry && c.proc.registry_keys.erase(h))
{
return STATUS_SUCCESS;
}
return STATUS_INVALID_HANDLE;
}
NTSTATUS handle_NtTraceEvent()
{
return STATUS_SUCCESS;
}
NTSTATUS handle_NtCreateEvent(const syscall_context& c, const emulator_object<handle> event_handle,
const ACCESS_MASK /*desired_access*/,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes,
const EVENT_TYPE event_type, const BOOLEAN initial_state)
{
std::wstring name{};
if (object_attributes)
{
const auto attributes = object_attributes.read();
if (attributes.ObjectName)
{
name = read_unicode_string(c.emu, attributes.ObjectName);
}
}
event e{};
e.type = event_type;
e.signaled = initial_state != FALSE;
e.name = std::move(name);
const auto handle = c.proc.events.store(std::move(e));
event_handle.write(handle);
static_assert(sizeof(EVENT_TYPE) == sizeof(uint32_t));
static_assert(sizeof(ACCESS_MASK) == sizeof(uint32_t));
return STATUS_SUCCESS;
}
NTSTATUS handle_NtOpenEvent(const syscall_context& c, const emulator_object<uint64_t> event_handle,
const ACCESS_MASK /*desired_access*/,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes)
{
const auto attributes = object_attributes.read();
const auto name = read_unicode_string(c.emu, attributes.ObjectName);
for (auto& entry : c.proc.events)
{
if (entry.second.name == name)
{
++entry.second.ref_count;
event_handle.write(c.proc.events.make_handle(entry.first).bits);
return STATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
}
NTSTATUS handle_NtQueryVolumeInformationFile(const syscall_context& c, const handle file_handle,
const uint64_t /*io_status_block*/, const uint64_t fs_information,
const ULONG /*length*/,
const 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<FILE_FS_DEVICE_INFORMATION> info_obj{c.emu, fs_information};
info_obj.access([&](FILE_FS_DEVICE_INFORMATION& info)
{
if (file_handle == STDOUT_HANDLE.bits && !c.win_emu.buffer_stdout)
{
info.DeviceType = FILE_DEVICE_CONSOLE;
info.Characteristics = 0x20000;
}
else
{
info.DeviceType = FILE_DEVICE_DISK;
info.Characteristics = 0x20020;
}
});
return STATUS_SUCCESS;
}
NTSTATUS handle_NtOpenSection(const syscall_context& c, const emulator_object<handle> section_handle,
const ACCESS_MASK /*desired_access*/,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes)
{
const auto attributes = object_attributes.read();
auto filename = read_unicode_string(c.emu, attributes.ObjectName);
c.win_emu.logger.print(color::dark_gray, "--> Opening section: %S\n", filename.c_str());
if (filename == L"\\Windows\\SharedSection")
{
section_handle.write(SHARED_SECTION);
return STATUS_SUCCESS;
}
if (reinterpret_cast<uint64_t>(attributes.RootDirectory) != KNOWN_DLLS_DIRECTORY)
{
puts("Unsupported section");
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
utils::string::to_lower_inplace(filename);
for (auto& section_entry : c.proc.sections)
{
if (section_entry.second.is_image() && section_entry.second.name == filename)
{
section_handle.write(c.proc.sections.make_handle(section_entry.first));
return STATUS_SUCCESS;
}
}
return STATUS_OBJECT_NAME_NOT_FOUND;
}
NTSTATUS handle_NtMapViewOfSection(const syscall_context& c, const handle section_handle,
const handle process_handle, const emulator_object<uint64_t> base_address,
const ULONG_PTR /*zero_bits*/, const SIZE_T /*commit_size*/,
const emulator_object<LARGE_INTEGER> /*section_offset*/,
const emulator_object<SIZE_T> view_size,
const SECTION_INHERIT /*inherit_disposition*/, const ULONG /*allocation_type*/,
const ULONG /*win32_protect*/)
{
if (process_handle != CURRENT_PROCESS)
{
return STATUS_INVALID_HANDLE;
}
if (section_handle == SHARED_SECTION)
{
constexpr auto shared_section_size = 0x10000;
const auto address = c.emu.find_free_allocation_base(shared_section_size);
c.emu.allocate_memory(address, shared_section_size, memory_permission::read_write);
const std::wstring_view windows_dir = c.proc.kusd.get().NtSystemRoot.arr;
const auto windows_dir_size = windows_dir.size() * 2;
constexpr auto windows_dir_offset = 0x10;
c.emu.write_memory(address + 8, windows_dir_offset);
const auto obj_address = address + windows_dir_offset;
const emulator_object<UNICODE_STRING> windir_obj{c.emu, obj_address};
windir_obj.access([&](UNICODE_STRING& ucs)
{
const auto dir_address = kusd_mmio::address() + offsetof(KUSER_SHARED_DATA, NtSystemRoot);
ucs.Buffer = reinterpret_cast<wchar_t*>(dir_address - obj_address);
ucs.Length = static_cast<uint16_t>(windows_dir_size);
ucs.MaximumLength = ucs.Length;
});
const emulator_object<UNICODE_STRING> sysdir_obj{c.emu, windir_obj.value() + windir_obj.size()};
sysdir_obj.access([&](UNICODE_STRING& ucs)
{
c.proc.base_allocator.make_unicode_string(ucs, L"C:\\WINDOWS\\System32");
ucs.Buffer = reinterpret_cast<wchar_t*>(reinterpret_cast<uint64_t>(ucs.Buffer) - obj_address);
});
const emulator_object<UNICODE_STRING> base_dir_obj{c.emu, sysdir_obj.value() + sysdir_obj.size()};
base_dir_obj.access([&](UNICODE_STRING& ucs)
{
c.proc.base_allocator.make_unicode_string(ucs, L"\\Sessions\\1\\BaseNamedObjects");
ucs.Buffer = reinterpret_cast<wchar_t*>(reinterpret_cast<uint64_t>(ucs.Buffer) - obj_address);
});
if (view_size)
{
view_size.write(shared_section_size);
}
base_address.write(address);
return STATUS_SUCCESS;
}
const auto section_entry = c.proc.sections.get(section_handle);
if (!section_entry)
{
return STATUS_INVALID_HANDLE;
}
if (section_entry->is_image())
{
const auto binary = c.proc.module_manager.map_module(section_entry->file_name, c.win_emu.logger);
if (!binary)
{
return STATUS_FILE_INVALID;
}
std::wstring wide_name(binary->name.begin(), binary->name.end());
section_entry->name = utils::string::to_lower_consume(wide_name);
if (view_size.value())
{
view_size.write(binary->size_of_image);
}
base_address.write(binary->image_base);
return STATUS_SUCCESS;
}
uint64_t size = section_entry->maximum_size;
std::vector<uint8_t> file_data{};
if (!section_entry->file_name.empty())
{
if (!utils::io::read_file(section_entry->file_name, &file_data))
{
return STATUS_INVALID_PARAMETER;
}
size = page_align_up(file_data.size());
}
const auto protection = map_nt_to_emulator_protection(section_entry->section_page_protection);
const auto address = c.emu.allocate_memory(size, protection);
if (!file_data.empty())
{
c.emu.write_memory(address, file_data.data(), file_data.size());
}
if (view_size)
{
view_size.write(size);
}
base_address.write(address);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtCreateIoCompletion(const syscall_context& c, const emulator_object<handle> event_handle,
const ACCESS_MASK desired_access,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes,
const 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<handle> event_handle,
const ACCESS_MASK desired_access,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes)
{
return handle_NtCreateEvent(c, event_handle, desired_access, object_attributes, NotificationEvent, FALSE);
}
NTSTATUS handle_NtQueryVirtualMemory(const syscall_context& c, const handle 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<uint32_t> return_length)
{
if (process_handle != CURRENT_PROCESS)
{
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<MEMORY_BASIC_INFORMATION> 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<void*>(region_info.start);
image_info.AllocationBase = reinterpret_cast<void*>(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: 0x%llX\n", base_address);
return STATUS_INVALID_ADDRESS;
}
const emulator_object<MEMORY_IMAGE_INFORMATION> info{c.emu, memory_information};
info.access([&](MEMORY_IMAGE_INFORMATION& image_info)
{
image_info.ImageBase = reinterpret_cast<void*>(mod->image_base);
image_info.SizeOfImage = mod->size_of_image;
});
return STATUS_SUCCESS;
}
if (info_class == MemoryRegionInformation)
{
if (return_length)
{
return_length.write(sizeof(MEMORY_REGION_INFORMATION));
}
if (memory_information_length != sizeof(MEMORY_REGION_INFORMATION))
{
return STATUS_BUFFER_OVERFLOW;
}
const auto region_info = c.emu.get_region_info(base_address);
if (!region_info.is_reserved)
{
return STATUS_INVALID_ADDRESS;
}
const emulator_object<MEMORY_REGION_INFORMATION> info{c.emu, memory_information};
info.access([&](MEMORY_REGION_INFORMATION& image_info)
{
memset(&image_info, 0, sizeof(image_info));
image_info.AllocationBase = reinterpret_cast<void*>(region_info.allocation_base);
image_info.AllocationProtect = 0;
image_info.PartitionId = 0;
image_info.RegionSize = region_info.allocation_length;
image_info.Reserved = 0x10;
});
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<uint32_t> return_length)
{
if (info_class == SystemFlushInformation
|| info_class == SystemHypervisorSharedPageInformation
|| info_class == 250 // Build 27744
)
{
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<SYSTEM_TIMEOFDAY_INFORMATION> 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<SYSTEM_RANGE_START_INFORMATION> 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<SYSTEM_PROCESSOR_INFORMATION> 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<SYSTEM_NUMA_INFORMATION> 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 == SystemErrorPortTimeouts)
{
if (return_length)
{
return_length.write(sizeof(SYSTEM_ERROR_PORT_TIMEOUTS));
}
if (system_information_length != sizeof(SYSTEM_ERROR_PORT_TIMEOUTS))
{
return STATUS_BUFFER_TOO_SMALL;
}
const emulator_object<SYSTEM_ERROR_PORT_TIMEOUTS> info_obj{c.emu, system_information};
info_obj.access([&](SYSTEM_ERROR_PORT_TIMEOUTS& info)
{
info.StartTimeout = 0;
info.CommTimeout = 0;
});
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<SYSTEM_BASIC_INFORMATION> 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*/, const handle source_process_handle,
const handle source_handle, const handle target_process_handle,
const emulator_object<handle> target_handle, const ACCESS_MASK /*desired_access*/,
const ULONG /*handle_attributes*/, const ULONG /*options*/)
{
if (source_process_handle != CURRENT_PROCESS || target_process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
if (source_handle.value.is_pseudo)
{
target_handle.write(source_handle);
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<uint32_t> return_length)
{
if (info_class == SystemFlushInformation
|| info_class == SystemFeatureConfigurationInformation
|| info_class == SystemSupportedProcessorArchitectures2
|| 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<SYSTEM_INFORMATION_CLASS>(info_class), buffer,
input_buffer_length,
res_buff,
system_information_length, reinterpret_cast<ULONG*>(&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<SYSTEM_BASIC_INFORMATION> 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 handle process_handle,
const uint32_t info_class, const uint64_t process_information,
const uint32_t process_information_length,
const emulator_object<uint32_t> return_length)
{
if (process_handle != CURRENT_PROCESS)
{
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<SECTION_IMAGE_INFORMATION> info{c.emu, process_information};
info.access([&](SECTION_IMAGE_INFORMATION& i)
{
const auto& mod = *c.proc.executable;
const emulator_object<IMAGE_DOS_HEADER> dos_header_obj{c.emu, mod.image_base};
const auto dos_header = dos_header_obj.read();
const emulator_object<IMAGE_NT_HEADERS> 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<uint32_t> 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<DWORD_PTR> info{c.emu, process_information};
info.write(0);
return STATUS_SUCCESS;
}
if (info_class == ProcessDefaultHardErrorMode)
{
if (return_length)
{
return_length.write(sizeof(ULONG));
}
if (process_information_length != sizeof(ULONG))
{
return STATUS_BUFFER_OVERFLOW;
}
const emulator_object<ULONG> 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<PROCESS_BASIC_INFORMATION> info{c.emu, process_information};
info.access([&](PROCESS_BASIC_INFORMATION& basic_info)
{
basic_info.PebBaseAddress = c.proc.peb.ptr();
basic_info.UniqueProcessId = reinterpret_cast<HANDLE>(1);
});
return STATUS_SUCCESS;
}
if (info_class == ProcessImageFileNameWin32)
{
const auto peb = c.proc.peb.read();
emulator_object<RTL_USER_PROCESS_PARAMETERS> proc_params{c.emu, peb.ProcessParameters};
const auto params = proc_params.read();
const auto length = params.ImagePathName.Length + sizeof(UNICODE_STRING) + 2;
if (return_length)
{
return_length.write(static_cast<uint32_t>(length));
}
if (process_information_length < length)
{
return STATUS_BUFFER_OVERFLOW;
}
const emulator_object<UNICODE_STRING> info{c.emu, process_information};
info.access([&](UNICODE_STRING& str)
{
const auto buffer_start = static_cast<uint64_t>(process_information) + sizeof(UNICODE_STRING);
const auto string = read_unicode_string(c.emu, params.ImagePathName);
c.emu.write_memory(buffer_start, string.c_str(), (string.size() + 1) * 2);
str.Length = params.ImagePathName.Length;
str.MaximumLength = str.Length;
str.Buffer = reinterpret_cast<wchar_t*>(buffer_start);
});
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 handle thread_handle,
const uint32_t info_class, const uint64_t thread_information,
const uint32_t thread_information_length,
const emulator_object<uint32_t> return_length)
{
if (thread_handle != CURRENT_THREAD)
{
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<THREAD_BASIC_INFORMATION> info{c.emu, thread_information};
info.access([&](THREAD_BASIC_INFORMATION& i)
{
i.TebBaseAddress = c.win_emu.current_thread().teb->ptr();
i.ClientId = c.win_emu.current_thread().teb->read().ClientId;
});
return STATUS_SUCCESS;
}
if (info_class == ThreadAmILastThread)
{
if (return_length)
{
return_length.write(sizeof(ULONG));
}
if (thread_information_length != sizeof(ULONG))
{
return STATUS_BUFFER_OVERFLOW;
}
const emulator_object<ULONG> info{c.emu, thread_information};
info.write(c.proc.threads.size() <= 1);
return STATUS_SUCCESS;
}
printf("Unsupported thread info class: %X\n", info_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtSetInformationFile(const syscall_context& c, const handle file_handle,
const emulator_object<IO_STATUS_BLOCK> io_status_block,
const uint64_t file_information,
const ULONG length, const FILE_INFORMATION_CLASS info_class)
{
const auto* f = c.proc.files.get(file_handle);
if (!f)
{
return STATUS_INVALID_HANDLE;
}
if (info_class == FilePositionInformation)
{
if (!f->handle)
{
return STATUS_NOT_SUPPORTED;
}
if (io_status_block)
{
IO_STATUS_BLOCK block{};
block.Information = sizeof(FILE_POSITION_INFORMATION);
io_status_block.write(block);
}
if (length != sizeof(FILE_POSITION_INFORMATION))
{
return STATUS_BUFFER_OVERFLOW;
}
const emulator_object<FILE_POSITION_INFORMATION> info{c.emu, file_information};
const auto i = info.read();
if (!f->handle.seek_to(i.CurrentByteOffset.QuadPart))
{
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
printf("Unsupported set file info class: %X\n", info_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtQueryInformationFile(const syscall_context& c, const handle file_handle,
const emulator_object<IO_STATUS_BLOCK> io_status_block,
const uint64_t file_information, const uint32_t length,
const uint32_t info_class)
{
const auto* f = c.proc.files.get(file_handle);
if (!f)
{
return STATUS_INVALID_HANDLE;
}
if (info_class == FileStandardInformation)
{
if (io_status_block)
{
IO_STATUS_BLOCK block{};
block.Information = sizeof(FILE_STANDARD_INFORMATION);
io_status_block.write(block);
}
if (length != sizeof(FILE_STANDARD_INFORMATION))
{
return STATUS_BUFFER_OVERFLOW;
}
const emulator_object<FILE_STANDARD_INFORMATION> info{c.emu, file_information};
FILE_STANDARD_INFORMATION i{};
i.Directory = f->handle ? FALSE : TRUE;
if (f->handle)
{
i.EndOfFile.QuadPart = f->handle.size();
}
info.write(i);
return STATUS_SUCCESS;
}
if (info_class == FilePositionInformation)
{
if (!f->handle)
{
return STATUS_NOT_SUPPORTED;
}
if (io_status_block)
{
IO_STATUS_BLOCK block{};
block.Information = sizeof(FILE_POSITION_INFORMATION);
io_status_block.write(block);
}
if (length != sizeof(FILE_POSITION_INFORMATION))
{
return STATUS_BUFFER_OVERFLOW;
}
const emulator_object<FILE_POSITION_INFORMATION> info{c.emu, file_information};
FILE_POSITION_INFORMATION i{};
i.CurrentByteOffset.QuadPart = f->handle.tell();
info.write(i);
return STATUS_SUCCESS;
}
printf("Unsupported query file info class: %X\n", info_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
struct THREAD_TLS_INFO
{
ULONG Flags;
union
{
PVOID* TlsVector;
PVOID TlsModulePointer;
};
ULONG_PTR ThreadId;
};
static_assert(sizeof(THREAD_TLS_INFO) == 0x18);
struct PROCESS_TLS_INFO
{
ULONG Unknown;
PROCESS_TLS_INFORMATION_TYPE TlsRequest;
ULONG ThreadDataCount;
union
{
ULONG TlsIndex;
ULONG TlsVectorLength;
};
THREAD_TLS_INFO ThreadData[1];
};
static_assert(sizeof(PROCESS_TLS_INFO) - sizeof(THREAD_TLS_INFO) == 0x10);
NTSTATUS handle_NtSetInformationProcess(const syscall_context& c, const handle process_handle,
const uint32_t info_class, const uint64_t process_information,
const uint32_t process_information_length)
{
if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
if (info_class == ProcessSchedulerSharedData
|| info_class == ProcessConsoleHostProcess
|| info_class == ProcessFaultInformation
|| info_class == ProcessDefaultHardErrorMode
|| info_class == ProcessRaiseUMExceptionOnInvalidHandleClose)
{
return STATUS_SUCCESS;
}
if (info_class == ProcessTlsInformation)
{
constexpr auto thread_data_offset = offsetof(PROCESS_TLS_INFO, ThreadData);
if (process_information_length < thread_data_offset)
{
return STATUS_BUFFER_OVERFLOW;
}
const emulator_object<THREAD_TLS_INFO> data{c.emu, process_information + thread_data_offset};
PROCESS_TLS_INFO tls_info{};
c.emu.read_memory(process_information, &tls_info, thread_data_offset);
for (uint32_t i = 0; i < tls_info.ThreadDataCount; ++i)
{
auto entry = data.read(i);
const auto _ = utils::finally([&]
{
data.write(entry, i);
});
if (i >= c.proc.threads.size())
{
entry.Flags = 0;
continue;
}
auto thread_iterator = c.proc.threads.begin();
std::advance(thread_iterator, i);
entry.Flags = 2;
thread_iterator->second.teb->access([&](TEB& teb)
{
const auto tls_vector = static_cast<PVOID*>(teb.ThreadLocalStoragePointer);
if (tls_info.TlsRequest == ProcessTlsReplaceIndex)
{
const auto tls_entry_ptr = tls_vector + tls_info.TlsIndex;
const auto old_entry = c.emu.read_memory<void*>(tls_entry_ptr);
c.emu.write_memory<void*>(tls_entry_ptr, entry.TlsModulePointer);
entry.TlsModulePointer = old_entry;
}
else if (tls_info.TlsRequest == ProcessTlsReplaceVector)
{
const auto new_tls_vector = entry.TlsVector;
for (uint32_t j = 1; j < tls_info.TlsVectorLength; ++j)
{
const auto index = j - 1;
const auto old_entry = c.emu.read_memory<void*>(tls_vector + index);
c.emu.write_memory<void*>(new_tls_vector + index, old_entry);
}
teb.ThreadLocalStoragePointer = new_tls_vector;
entry.TlsVector = tls_vector;
}
});
}
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 handle process_handle,
const emulator_object<uint64_t> base_address,
const emulator_object<uint32_t> bytes_to_protect,
const uint32_t protection,
const emulator_object<uint32_t> old_protection)
{
if (process_handle != CURRENT_PROCESS)
{
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<uint32_t>(aligned_length));
const auto requested_protection = map_nt_to_emulator_protection(protection);
c.win_emu.logger.print(color::dark_gray, "--> Changing protection at 0x%llX-0x%llX to %s\n", aligned_start,
aligned_start + aligned_length, get_permission_string(requested_protection).c_str());
memory_permission old_protection_value{};
try
{
c.emu.protect_memory(aligned_start, aligned_length, requested_protection, &old_protection_value);
}
catch (...)
{
return STATUS_INVALID_ADDRESS;
}
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<handle> directory_handle,
const ACCESS_MASK /*desired_access*/,
const emulator_object<OBJECT_ATTRIBUTES> 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);
return STATUS_SUCCESS;
}
if (object_name == L"\\Sessions\\1\\BaseNamedObjects")
{
directory_handle.write(BASE_NAMED_OBJECTS_DIRECTORY);
return STATUS_SUCCESS;
}
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtOpenSymbolicLinkObject(const syscall_context& c, const emulator_object<handle> link_handle,
ACCESS_MASK /*desired_access*/,
const emulator_object<OBJECT_ATTRIBUTES> 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);
return STATUS_SUCCESS;
}
return STATUS_NOT_SUPPORTED;
}
NTSTATUS WINAPI handle_NtQuerySymbolicLinkObject(const syscall_context& c, const handle link_handle,
const emulator_object<UNICODE_STRING> link_target,
const emulator_object<ULONG> 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<uint64_t>(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 handle process_handle,
const emulator_object<uint64_t> base_address,
const emulator_object<uint64_t> bytes_to_allocate,
const uint32_t allocation_type,
const uint32_t page_protection)
{
if (process_handle != CURRENT_PROCESS)
{
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 handle process_handle,
const emulator_object<uint64_t> base_address, const uint64_t /*zero_bits*/,
const emulator_object<uint64_t> 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 handle process_handle,
const emulator_object<uint64_t> base_address,
const emulator_object<uint64_t> bytes_to_allocate, const uint32_t free_type)
{
if (process_handle != CURRENT_PROCESS)
{
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<handle> section_handle,
const ACCESS_MASK /*desired_access*/,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes,
const emulator_object<ULARGE_INTEGER> maximum_size,
const ULONG section_page_protection, const ULONG allocation_attributes,
const handle file_handle)
{
section s{};
s.section_page_protection = section_page_protection;
s.allocation_attributes = allocation_attributes;
const auto* file = c.proc.files.get(file_handle);
if (file)
{
c.win_emu.logger.print(color::dark_gray, "--> Section for file %S\n", file->name.c_str());
s.file_name = file->name;
}
if (object_attributes)
{
const auto attributes = object_attributes.read();
if (attributes.ObjectName)
{
const auto name = read_unicode_string(c.emu, attributes.ObjectName);
c.win_emu.logger.print(color::dark_gray, "--> Section with name %S\n", name.c_str());
s.name = std::move(name);
}
}
if (maximum_size)
{
maximum_size.access([&](ULARGE_INTEGER& large_int)
{
large_int.QuadPart = page_align_up(large_int.QuadPart);
s.maximum_size = large_int.QuadPart;
});
}
else if (!file)
{
return STATUS_INVALID_PARAMETER;
}
const auto h = c.proc.sections.store(std::move(s));
section_handle.write(h);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtConnectPort(const syscall_context& c, const emulator_object<handle> client_port_handle,
const emulator_object<UNICODE_STRING> server_port_name,
const emulator_object<SECURITY_QUALITY_OF_SERVICE> /*security_qos*/,
const emulator_object<PORT_VIEW> client_shared_memory,
const emulator_object<REMOTE_PORT_VIEW> /*server_shared_memory*/,
const emulator_object<ULONG> /*maximum_message_length*/,
const emulator_pointer connection_info,
const emulator_object<ULONG> connection_info_length)
{
auto port_name = read_unicode_string(c.emu, server_port_name);
c.win_emu.logger.print(color::dark_gray, "NtConnectPort: %S\n", port_name.c_str());
port p{};
p.name = std::move(port_name);
if (connection_info)
{
std::vector<uint8_t> zero_mem{};
zero_mem.resize(connection_info_length.read(), 0);
c.emu.write_memory(connection_info, zero_mem.data(), zero_mem.size());
}
client_shared_memory.access([&](PORT_VIEW& view)
{
p.view_base = c.emu.allocate_memory(view.ViewSize, memory_permission::read_write);
view.ViewBase = reinterpret_cast<void*>(p.view_base);
view.ViewRemoteBase = view.ViewBase;
});
const auto handle = c.proc.ports.store(std::move(p));
client_port_handle.write(handle);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtReadVirtualMemory(const syscall_context& c, const handle process_handle,
const emulator_pointer base_address,
const emulator_pointer buffer, const ULONG number_of_bytes_to_read,
const emulator_object<ULONG> number_of_bytes_read)
{
number_of_bytes_read.write(0);
if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
std::vector<uint8_t> 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(const syscall_context& c, const handle file_handle,
const handle event,
const emulator_pointer /*PIO_APC_ROUTINE*/ apc_routine,
const emulator_pointer apc_context,
const emulator_object<IO_STATUS_BLOCK> io_status_block,
const ULONG io_control_code,
const emulator_pointer input_buffer,
const ULONG input_buffer_length, const emulator_pointer output_buffer,
const ULONG output_buffer_length)
{
auto* device = c.proc.devices.get(file_handle);
if (!device)
{
return STATUS_INVALID_HANDLE;
}
io_device_context context{c.emu};
context.event = event;
context.apc_routine = apc_routine;
context.apc_context = apc_context;
context.io_status_block = io_status_block;
context.io_control_code = io_control_code;
context.input_buffer = input_buffer;
context.input_buffer_length = input_buffer_length;
context.output_buffer = output_buffer;
context.output_buffer_length = output_buffer_length;
return device->execute_ioctl(c.win_emu, context);
}
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_NtOpenThreadToken(const syscall_context&, const handle thread_handle,
const ACCESS_MASK /*desired_access*/, const BOOLEAN /*open_as_self*/,
const emulator_object<handle> token_handle)
{
if (thread_handle != CURRENT_THREAD)
{
return STATUS_NOT_SUPPORTED;
}
token_handle.write(CURRENT_THREAD_TOKEN);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtOpenThreadTokenEx(const syscall_context& c, const handle thread_handle,
const ACCESS_MASK desired_access, const BOOLEAN open_as_self,
const ULONG /*handle_attributes*/,
const emulator_object<handle> token_handle)
{
return handle_NtOpenThreadToken(c, thread_handle, desired_access, open_as_self, token_handle);
}
NTSTATUS handle_NtOpenProcessToken(const syscall_context&, const handle process_handle,
const ACCESS_MASK /*desired_access*/, const emulator_object<handle> token_handle)
{
if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
token_handle.write(CURRENT_PROCESS_TOKEN);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtOpenProcessTokenEx(const syscall_context& c, const handle process_handle,
const ACCESS_MASK desired_access, const ULONG /*handle_attributes*/,
const emulator_object<handle> token_handle)
{
return handle_NtOpenProcessToken(c, process_handle, desired_access, token_handle);
}
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(const syscall_context& c, const handle token_handle,
const TOKEN_INFORMATION_CLASS token_information_class,
const uint64_t token_information, const ULONG token_information_length,
const emulator_object<ULONG> return_length)
{
if (token_handle != CURRENT_PROCESS_TOKEN
&& token_handle != CURRENT_THREAD_TOKEN
&& token_handle != CURRENT_THREAD_EFFECTIVE_TOKEN
)
{
return STATUS_NOT_SUPPORTED;
}
if (token_information_class == TokenUser)
{
const uint8_t sid[] =
{
0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x15, 0x00, 0x00, 0x00, 0x84, 0x94,
0xD4, 0x04, 0x4B, 0x68, 0x42, 0x34, 0x23,
0xBE, 0x69, 0x4E, 0xE9, 0x03, 0x00, 0x00,
};
constexpr auto required_size = sizeof(sid) + 0x10;
return_length.write(required_size);
if (required_size > token_information_length)
{
return STATUS_BUFFER_TOO_SMALL;
}
TOKEN_USER user{};
user.User.Attributes = 0;
user.User.Sid = reinterpret_cast<void*>(token_information + 0x10);
emulator_object<TOKEN_USER>{c.emu, token_information}.write(user);
c.emu.write_memory(token_information + 0x10, sid, sizeof(sid));
return STATUS_SUCCESS;
}
if (token_information_class == TokenSessionId)
{
constexpr auto required_size = sizeof(ULONG);
return_length.write(required_size);
if (required_size > token_information_length)
{
return STATUS_BUFFER_TOO_SMALL;
}
emulator_object<ULONG>{c.emu, token_information}.write(1);
return STATUS_SUCCESS;
}
if (token_information_class == TokenPrivateNameSpace)
{
constexpr auto required_size = sizeof(ULONG);
return_length.write(required_size);
if (required_size > token_information_length)
{
return STATUS_BUFFER_TOO_SMALL;
}
emulator_object<ULONG>{c.emu, token_information}.write(0);
return STATUS_SUCCESS;
}
if (token_information_class == TokenUIAccess)
{
constexpr auto required_size = sizeof(ULONG);
return_length.write(required_size);
if (required_size > token_information_length)
{
return STATUS_BUFFER_TOO_SMALL;
}
emulator_object<ULONG>{c.emu, token_information}.write(1);
return STATUS_SUCCESS;
}
if (token_information_class == TokenElevation)
{
constexpr auto required_size = sizeof(TOKEN_ELEVATION);
return_length.write(required_size);
if (required_size > token_information_length)
{
return STATUS_BUFFER_TOO_SMALL;
}
c.emu.write_memory(token_information, TOKEN_ELEVATION{
.TokenIsElevated = 0,
});
return STATUS_SUCCESS;
}
if (token_information_class == TokenIsAppContainer)
{
constexpr auto required_size = sizeof(ULONG);
return_length.write(required_size);
if (required_size > token_information_length)
{
return STATUS_BUFFER_TOO_SMALL;
}
emulator_object<ULONG>{c.emu, token_information}.write(0);
return STATUS_SUCCESS;
}
if (token_information_class == TokenSecurityAttributes)
{
constexpr auto required_size = sizeof(TOKEN_SECURITY_ATTRIBUTES_INFORMATION);
return_length.write(required_size);
if (required_size > token_information_length)
{
return STATUS_BUFFER_TOO_SMALL;
}
c.emu.write_memory(token_information, TOKEN_SECURITY_ATTRIBUTES_INFORMATION{
.Version = 0,
.AttributeCount = 0,
});
return STATUS_SUCCESS;
}
if (token_information_class == TokenBnoIsolation)
{
constexpr auto required_size = sizeof(TOKEN_BNO_ISOLATION_INFORMATION);
return_length.write(required_size);
if (required_size > token_information_length)
{
return STATUS_BUFFER_TOO_SMALL;
}
c.emu.write_memory(token_information, TOKEN_BNO_ISOLATION_INFORMATION{
.IsolationPrefix = nullptr,
.IsolationEnabled = 0,
});
return STATUS_SUCCESS;
}
printf("Unsupported token info class: %lX\n", token_information_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtDxgkIsFeatureEnabled()
{
//puts("NtDxgkIsFeatureEnabled not supported");
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtQueryInstallUILanguage()
{
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.base_allocator.reserve<GDI_SHARED_MEMORY>().ptr();
}
});
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtGetMUIRegistryInfo()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtUserRegisterWindowMessage()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtUserGetThreadState()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtIsUILanguageComitted()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtUpdateWnfStateData()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtGetNlsSectionPtr()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& c, const handle port_handle,
const ULONG /*flags*/,
const emulator_object<PORT_MESSAGE> /*send_message*/,
const emulator_object<ALPC_MESSAGE_ATTRIBUTES>
/*send_message_attributes*/
,
const emulator_object<PORT_MESSAGE> receive_message,
const emulator_object<SIZE_T> /*buffer_length*/,
const emulator_object<ALPC_MESSAGE_ATTRIBUTES>
/*receive_message_attributes*/,
const emulator_object<LARGE_INTEGER> /*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<PORT_DATA_ENTRY> data{c.emu, receive_message.value() + 0x48};
const auto dest = data.read();
const auto base = reinterpret_cast<uint64_t>(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<uint64_t> base_address,
const emulator_object<LCID> default_locale_id,
const emulator_object<LARGE_INTEGER> /*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<CONTEXT> 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 handle process_handle,
NTSTATUS exit_status)
{
if (process_handle == 0)
{
for (auto& thread : c.proc.threads | std::views::values)
{
if (&thread != c.proc.active_thread)
{
thread.exit_status = exit_status;
}
}
return STATUS_SUCCESS;
}
if (process_handle == CURRENT_PROCESS)
{
c.proc.exit_status = exit_status;
c.emu.stop();
return STATUS_SUCCESS;
}
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtReadFile(const syscall_context& c, const handle file_handle, const uint64_t /*event*/,
const uint64_t /*apc_routine*/,
const uint64_t /*apc_context*/,
const emulator_object<IO_STATUS_BLOCK> io_status_block,
uint64_t buffer, const ULONG length,
const emulator_object<LARGE_INTEGER> /*byte_offset*/,
const emulator_object<ULONG> /*key*/)
{
const auto* f = c.proc.files.get(file_handle);
if (!f)
{
return STATUS_INVALID_HANDLE;
}
std::string temp_buffer{};
temp_buffer.resize(length);
const auto bytes_read = fread(temp_buffer.data(), 1, temp_buffer.size(), f->handle);
if (io_status_block)
{
IO_STATUS_BLOCK block{};
block.Information = bytes_read;
io_status_block.write(block);
}
c.emu.write_memory(buffer, temp_buffer.data(), temp_buffer.size());
return STATUS_SUCCESS;
}
NTSTATUS handle_NtWriteFile(const syscall_context& c, const handle file_handle, const uint64_t /*event*/,
const uint64_t /*apc_routine*/,
const uint64_t /*apc_context*/,
const emulator_object<IO_STATUS_BLOCK> io_status_block,
uint64_t buffer, const ULONG length,
const emulator_object<LARGE_INTEGER> /*byte_offset*/,
const emulator_object<ULONG> /*key*/)
{
std::string temp_buffer{};
temp_buffer.resize(length);
c.emu.read_memory(buffer, temp_buffer.data(), temp_buffer.size());
if (file_handle == STDOUT_HANDLE)
{
if (io_status_block)
{
IO_STATUS_BLOCK block{};
block.Information = length;
io_status_block.write(block);
}
if (!temp_buffer.ends_with("\n"))
{
temp_buffer.push_back('\n');
}
c.win_emu.on_stdout(temp_buffer);
c.win_emu.logger.info("%.*s", static_cast<int>(temp_buffer.size()), temp_buffer.data());
return STATUS_SUCCESS;
}
const auto* f = c.proc.files.get(file_handle);
if (!f)
{
return STATUS_INVALID_HANDLE;
}
const auto bytes_written = fwrite(temp_buffer.data(), 1, temp_buffer.size(), f->handle);
if (io_status_block)
{
IO_STATUS_BLOCK block{};
block.Information = bytes_written;
io_status_block.write(block);
}
return STATUS_SUCCESS;
}
const wchar_t* map_mode(const ACCESS_MASK desired_access, const ULONG create_disposition)
{
const auto* mode = L"";
switch (create_disposition)
{
case FILE_CREATE:
case FILE_SUPERSEDE:
if (desired_access & GENERIC_WRITE)
{
mode = L"wb";
}
break;
case FILE_OPEN:
case FILE_OPEN_IF:
if (desired_access & GENERIC_WRITE)
{
mode = L"r+b";
}
else if (desired_access & GENERIC_READ || desired_access & SYNCHRONIZE)
{
mode = L"rb";
}
break;
case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
if (desired_access & GENERIC_WRITE)
{
mode = L"w+b";
}
break;
default:
mode = L"";
break;
}
if (desired_access & FILE_APPEND_DATA)
{
mode = L"a+b";
}
return mode;
}
NTSTATUS handle_NtCreateFile(const syscall_context& c, const emulator_object<handle> file_handle,
ACCESS_MASK desired_access,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes,
const emulator_object<IO_STATUS_BLOCK> /*io_status_block*/,
const emulator_object<LARGE_INTEGER> /*allocation_size*/, ULONG /*file_attributes*/,
ULONG /*share_access*/, ULONG create_disposition, ULONG create_options,
uint64_t ea_buffer,
ULONG ea_length)
{
const auto attributes = object_attributes.read();
auto filename = read_unicode_string(c.emu, attributes.ObjectName);
auto printer = utils::finally([&]
{
c.win_emu.logger.print(color::dark_gray, "--> Opening file: %S\n", filename.c_str());
});
constexpr std::wstring_view device_prefix = L"\\Device\\";
if (filename.starts_with(device_prefix))
{
const io_device_creation_data data{
.buffer = ea_buffer,
.length = ea_length,
};
auto device_name = filename.substr(device_prefix.size());
io_device_container container{std::move(device_name), c.win_emu, data};
const auto handle = c.proc.devices.store(std::move(container));
file_handle.write(handle);
return STATUS_SUCCESS;
}
handle root_handle{};
root_handle.bits = reinterpret_cast<uint64_t>(attributes.RootDirectory);
if (root_handle.value.is_pseudo && (filename == L"\\Reference" || filename == L"\\Connect"))
{
file_handle.write(root_handle);
return STATUS_SUCCESS;
}
file f{};
f.name = std::move(filename);
if (attributes.RootDirectory)
{
const auto* root = c.proc.files.get(reinterpret_cast<uint64_t>(attributes.RootDirectory));
if (!root)
{
return STATUS_INVALID_HANDLE;
}
f.name = root->name + f.name;
}
printer.cancel();
if (f.name.ends_with(L"\\") || create_options & FILE_DIRECTORY_FILE)
{
c.win_emu.logger.print(color::dark_gray, "--> Opening folder: %S\n", f.name.c_str());
if (create_disposition & FILE_CREATE)
{
std::error_code ec{};
std::filesystem::create_directory(f.name, ec);
if (ec)
{
return STATUS_ACCESS_DENIED;
}
}
else if (!std::filesystem::is_directory(f.name))
{
return STATUS_OBJECT_NAME_NOT_FOUND;
}
const auto handle = c.proc.files.store(std::move(f));
file_handle.write(handle);
return STATUS_SUCCESS;
}
c.win_emu.logger.print(color::dark_gray, "--> Opening file: %S\n", f.name.c_str());
const auto* mode = map_mode(desired_access, create_disposition);
if (!mode || wcslen(mode) == 0)
{
return STATUS_NOT_SUPPORTED;
}
FILE* file{};
const auto error = _wfopen_s(&file, f.name.c_str(), mode);
if (!file)
{
switch (error)
{
case ENOENT:
return STATUS_OBJECT_NAME_NOT_FOUND;
case EACCES:
return STATUS_ACCESS_DENIED;
case EISDIR:
return STATUS_FILE_IS_A_DIRECTORY;
default:
return STATUS_NOT_SUPPORTED;
}
}
f.handle = file;
const auto handle = c.proc.files.store(std::move(f));
file_handle.write(handle);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtQueryAttributesFile(const syscall_context& c,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes,
const emulator_object<FILE_BASIC_INFORMATION> file_information)
{
if (!object_attributes)
{
return STATUS_INVALID_PARAMETER;
}
const auto attributes = object_attributes.read();
if (!attributes.ObjectName)
{
return STATUS_INVALID_PARAMETER;
}
const auto filename = read_unicode_string(c.emu, attributes.ObjectName);
struct _stat64 file_stat{};
if (_wstat64(filename.c_str(), &file_stat) != 0)
{
return STATUS_OBJECT_NAME_NOT_FOUND;
}
file_information.access([&](FILE_BASIC_INFORMATION& info)
{
info.CreationTime = convert_unix_to_windows_time(file_stat.st_atime);
info.LastAccessTime = convert_unix_to_windows_time(file_stat.st_atime);
info.LastWriteTime = convert_unix_to_windows_time(file_stat.st_mtime);
info.ChangeTime = info.LastWriteTime;
info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
});
return STATUS_SUCCESS;
}
NTSTATUS handle_NtOpenFile(const syscall_context& c,
const emulator_object<handle> file_handle,
const ACCESS_MASK desired_access,
const emulator_object<OBJECT_ATTRIBUTES> object_attributes,
const emulator_object<IO_STATUS_BLOCK> io_status_block,
const ULONG share_access,
const ULONG open_options)
{
return handle_NtCreateFile(c, file_handle, desired_access, object_attributes, io_status_block, {c.emu}, 0,
share_access, FILE_OPEN, open_options, 0, 0);
}
NTSTATUS handle_NtQueryObject(const syscall_context&, const handle /*handle*/,
const OBJECT_INFORMATION_CLASS /*object_information_class*/,
const emulator_pointer /*object_information*/,
const ULONG /*object_information_length*/,
const emulator_object<ULONG> /*return_length*/)
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtQueryInformationJobObject()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtSetSystemInformation()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtAccessCheck()
{
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> /*unicode_string_parameter_mask*/,
const emulator_object<DWORD> /*parameters*/,
const HARDERROR_RESPONSE_OPTION /*valid_response_option*/,
const emulator_object<HARDERROR_RESPONSE> response)
{
if (response)
{
response.write(ResponseAbort);
}
printf("Hard error: %X\n", static_cast<uint32_t>(error_status));
c.proc.exception_rip = c.emu.read_instruction_pointer();
c.emu.stop();
return STATUS_SUCCESS;
}
NTSTATUS handle_NtRaiseException(const syscall_context& c,
const emulator_object<EXCEPTION_RECORD> /*exception_record*/,
const emulator_object<CONTEXT> thread_context, BOOLEAN handle_exception)
{
if (handle_exception)
{
puts("Unhandled exceptions not supported yet!");
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
c.proc.exception_rip = thread_context.read().Rip;
c.emu.stop();
return STATUS_SUCCESS;
}
NTSTATUS handle_NtCreateSemaphore(const syscall_context& c, const emulator_object<handle> semaphore_handle,
const ACCESS_MASK /*desired_access*/,
const emulator_object<OBJECT_ATTRIBUTES> 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);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtAddAtomEx(const syscall_context& c, const uint64_t atom_name, const ULONG length,
const emulator_object<RTL_ATOM> 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<uint16_t> 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, const handle process_handle,
const uint64_t base_address)
{
if (process_handle != CURRENT_PROCESS)
{
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;
}
NTSTATUS handle_NtCreateThreadEx(const syscall_context& c, const emulator_object<handle> thread_handle,
const ACCESS_MASK /*desired_access*/,
const emulator_object<OBJECT_ATTRIBUTES> /*object_attributes*/,
const handle process_handle, const uint64_t start_routine,
const uint64_t argument, const ULONG /*create_flags*/, const SIZE_T /*zero_bits*/,
const SIZE_T stack_size, const SIZE_T /*maximum_stack_size*/,
const emulator_object<PS_ATTRIBUTE_LIST> attribute_list)
{
if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
const auto h = c.proc.create_thread(c.emu, start_routine, argument, stack_size);
thread_handle.write(h);
if (!attribute_list)
{
return STATUS_SUCCESS;
}
const auto* thread = c.proc.threads.get(h);
const emulator_object<PS_ATTRIBUTE> attributes{
c.emu, attribute_list.value() + offsetof(PS_ATTRIBUTE_LIST, Attributes)
};
const auto total_length = attribute_list.read().TotalLength;
constexpr auto entry_size = sizeof(PS_ATTRIBUTE);
constexpr auto header_size = sizeof(PS_ATTRIBUTE_LIST) - entry_size;
const auto attribute_count = (total_length - header_size) / entry_size;
for (size_t i = 0; i < attribute_count; ++i)
{
attributes.access([&](const PS_ATTRIBUTE& attribute)
{
const auto type = attribute.Attribute & ~PS_ATTRIBUTE_THREAD;
if (type == PsAttributeClientId)
{
const auto client_id = thread->teb->read().ClientId;
write_attribute(c.emu, attribute, client_id);
}
else if (type == PsAttributeTebAddress)
{
write_attribute(c.emu, attribute, thread->teb->ptr());
}
else
{
printf("Unsupported thread attribute type: %llX\n", type);
}
}, i);
}
return STATUS_SUCCESS;
}
NTSTATUS handle_NtQueryDebugFilterState()
{
return FALSE;
}
NTSTATUS handle_NtWaitForMultipleObjects(const syscall_context& c, const ULONG count,
const emulator_object<handle> handles, const WAIT_TYPE wait_type,
const BOOLEAN alertable, const emulator_object<LARGE_INTEGER> timeout)
{
if (alertable)
{
c.win_emu.logger.print(color::gray, "Alertable NtWaitForMultipleObjects not supported yet!\n");
}
if (wait_type != WaitAny && wait_type != WaitAll)
{
puts("Wait type not supported!");
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
auto& t = c.win_emu.current_thread();
t.await_objects.clear();
t.await_any = wait_type == WaitAny;
for (ULONG i = 0; i < count; ++i)
{
const auto h = handles.read(i);
if (h.value.type != handle_types::thread && h.value.type != handle_types::event)
{
c.win_emu.logger.print(color::gray, "Unsupported handle type for NtWaitForMultipleObjects: %d!\n",
h.value.type);
return STATUS_NOT_SUPPORTED;
}
}
if (timeout.value() && !t.await_time.has_value())
{
t.await_time = convert_delay_interval_to_time_point(timeout.read());
}
c.win_emu.yield_thread();
return STATUS_SUCCESS;
}
NTSTATUS handle_NtWaitForSingleObject(const syscall_context& c, const handle h,
const BOOLEAN alertable,
const emulator_object<LARGE_INTEGER> timeout)
{
if (alertable)
{
c.win_emu.logger.print(color::gray, "Alertable NtWaitForSingleObject not supported yet!\n");
}
if (h.value.type != handle_types::thread && h.value.type != handle_types::event)
{
c.win_emu.logger.print(color::gray,
"Unsupported handle type for NtWaitForSingleObject: %d!\n", h.value.type);
return STATUS_NOT_SUPPORTED;
}
auto& t = c.win_emu.current_thread();
t.await_objects = {h};
t.await_any = false;
if (timeout.value() && !t.await_time.has_value())
{
t.await_time = convert_delay_interval_to_time_point(timeout.read());
}
c.win_emu.yield_thread();
return STATUS_SUCCESS;
}
NTSTATUS handle_NtTerminateThread(const syscall_context& c, const handle thread_handle,
const NTSTATUS exit_status)
{
auto* thread = !thread_handle.bits
? c.proc.active_thread
: c.proc.threads.get(thread_handle);
if (!thread)
{
return STATUS_INVALID_HANDLE;
}
thread->exit_status = exit_status;
if (thread == c.proc.active_thread)
{
c.win_emu.yield_thread();
}
return STATUS_SUCCESS;
}
NTSTATUS handle_NtDelayExecution(const syscall_context& c, const BOOLEAN alertable,
const emulator_object<LARGE_INTEGER> delay_interval)
{
if (alertable)
{
puts("Alertable NtDelayExecution not supported yet!");
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
auto& t = c.win_emu.current_thread();
t.await_time = convert_delay_interval_to_time_point(delay_interval.read());
c.win_emu.yield_thread();
return STATUS_SUCCESS;
}
NTSTATUS handle_NtAlertThreadByThreadId(const syscall_context& c, const uint64_t thread_id)
{
for (auto& t : c.proc.threads)
{
if (t.second.id == thread_id)
{
t.second.alerted = true;
return STATUS_SUCCESS;
}
}
return STATUS_INVALID_HANDLE;
}
NTSTATUS handle_NtAlertThreadByThreadIdEx(const syscall_context& c, const uint64_t thread_id,
const emulator_object<RTL_SRWLOCK> lock)
{
if (lock.value())
{
c.win_emu.logger.print(color::gray, "NtAlertThreadByThreadIdEx with lock not supported yet!");
//c.emu.stop();
//return STATUS_NOT_SUPPORTED;
}
return handle_NtAlertThreadByThreadId(c, thread_id);
}
NTSTATUS handle_NtWaitForAlertByThreadId(const syscall_context& c, const uint64_t,
const emulator_object<LARGE_INTEGER> timeout)
{
auto& t = c.win_emu.current_thread();
t.waiting_for_alert = true;
if (timeout.value() && !t.await_time.has_value())
{
t.await_time = convert_delay_interval_to_time_point(timeout.read());
}
c.win_emu.yield_thread();
return STATUS_SUCCESS;
}
NTSTATUS handle_NtGetCurrentProcessorNumberEx(const syscall_context&,
const emulator_object<PROCESSOR_NUMBER> processor_number)
{
constexpr PROCESSOR_NUMBER number{};
processor_number.write(number);
return STATUS_SUCCESS;
}
}
void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& handler_mapping)
{
#define add_handler(syscall) \
do \
{ \
handler_mapping[#syscall] = make_syscall_handler<handle_##syscall>(); \
} 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(NtOpenThreadTokenEx);
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(NtOpenProcessTokenEx);
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);
add_handler(NtOpenEvent);
add_handler(NtGetMUIRegistryInfo);
add_handler(NtIsUILanguageComitted);
add_handler(NtQueryInstallUILanguage);
add_handler(NtUpdateWnfStateData);
add_handler(NtRaiseException);
add_handler(NtQueryInformationJobObject);
add_handler(NtSetSystemInformation);
add_handler(NtQueryInformationFile);
add_handler(NtCreateThreadEx);
add_handler(NtQueryDebugFilterState);
add_handler(NtWaitForSingleObject);
add_handler(NtTerminateThread);
add_handler(NtDelayExecution);
add_handler(NtWaitForAlertByThreadId);
add_handler(NtAlertThreadByThreadIdEx);
add_handler(NtAlertThreadByThreadId);
add_handler(NtReadFile);
add_handler(NtSetInformationFile);
add_handler(NtUserRegisterWindowMessage);
add_handler(NtQueryValueKey);
add_handler(NtQueryKey);
add_handler(NtGetNlsSectionPtr);
add_handler(NtAccessCheck);
add_handler(NtCreateKey);
add_handler(NtNotifyChangeKey);
add_handler(NtGetCurrentProcessorNumberEx);
add_handler(NtQueryObject);
add_handler(NtQueryAttributesFile);
add_handler(NtWaitForMultipleObjects);
#undef add_handler
}