Files
windows-user-space-emulator/src/windows-emulator/syscalls/process.cpp
2026-01-04 13:35:54 +01:00

425 lines
18 KiB
C++

#include "../std_include.hpp"
#include "../emulator_utils.hpp"
#include "../syscall_utils.hpp"
#include <utils/finally.hpp>
namespace syscalls
{
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;
}
const auto return_length_info = c.win_emu.memory.get_region_info(return_length.value());
switch (info_class)
{
case ProcessExecuteFlags:
return STATUS_NOT_SUPPORTED;
case ProcessGroupInformation:
case ProcessMitigationPolicy: {
// ProcessMitigationPolicy requires special handling because the caller
// specifies which policy to query via the Policy field in the input buffer.
// We need to read this field first to determine what's being queried.
// Ensure we have at least enough space to read the Policy field
if (process_information_length < sizeof(PROCESS_MITIGATION_POLICY))
{
return STATUS_BUFFER_TOO_SMALL;
}
// Read the policy type from the input buffer using safe emulator memory access
const emulator_object<PROCESS_MITIGATION_POLICY> policy_obj{c.emu, process_information};
const auto policy = policy_obj.read();
// We only support querying ProcessDynamicCodePolicy
if (policy != ProcessDynamicCodePolicy)
{
return STATUS_NOT_SUPPORTED;
}
return handle_query<PROCESS_MITIGATION_POLICY_RAW_DATA>(c.emu, process_information, process_information_length, return_length,
[policy](PROCESS_MITIGATION_POLICY_RAW_DATA& policy_data) {
policy_data.Policy = policy;
policy_data.Value = 0;
});
}
case ProcessEnclaveInformation:
return STATUS_NOT_SUPPORTED;
case ProcessTimes:
return handle_query<KERNEL_USER_TIMES>(c.emu, process_information, process_information_length, return_length,
[](KERNEL_USER_TIMES& t) {
t = {}; //
});
case ProcessCookie:
return handle_query<uint32_t>(c.emu, process_information, process_information_length, return_length, [](uint32_t& cookie) {
cookie = 0x01234567; //
});
case ProcessDebugObjectHandle:
c.win_emu.callbacks.on_suspicious_activity("Anti-debug check with ProcessDebugObjectHandle");
if ((process_information & 3) != 0)
{
return STATUS_DATATYPE_MISALIGNMENT;
}
if (return_length.value() == 0)
{
return STATUS_PORT_NOT_SET;
}
if (!return_length_info.is_reserved)
{
return STATUS_ACCESS_VIOLATION;
}
return handle_query<handle>(c.emu, process_information, process_information_length, return_length, [](handle& h) {
h = NULL_HANDLE;
return STATUS_PORT_NOT_SET;
});
case ProcessDebugFlags:
case ProcessWx86Information:
case ProcessDefaultHardErrorMode:
return handle_query<ULONG>(c.emu, process_information, process_information_length, return_length, [&](ULONG& res) {
res = (info_class == ProcessDebugFlags ? 1 : 0); //
});
case ProcessDebugPort:
c.win_emu.callbacks.on_suspicious_activity("Anti-debug check with ProcessDebugPort");
return handle_query<EmulatorTraits<Emu64>::PVOID>(c.emu, process_information, process_information_length, return_length,
[](EmulatorTraits<Emu64>::PVOID& ptr) {
ptr = 0; //
});
case ProcessDeviceMap:
return handle_query<EmulatorTraits<Emu64>::PVOID>(c.emu, process_information, process_information_length, return_length,
[](EmulatorTraits<Emu64>::PVOID& ptr) {
ptr = 0; //
});
case ProcessEnableAlignmentFaultFixup:
return handle_query<BOOLEAN>(c.emu, process_information, process_information_length, return_length, [](BOOLEAN& b) {
b = FALSE; //
});
case ProcessPriorityClass:
return handle_query<PROCESS_PRIORITY_CLASS>(c.emu, process_information, process_information_length, return_length,
[](PROCESS_PRIORITY_CLASS& c) {
c.Foreground = 1;
c.PriorityClass = 32; // Normal
});
case ProcessBasicInformation: {
const auto init_basic_info = [&](PROCESS_BASIC_INFORMATION64& basic_info) {
basic_info.PebBaseAddress = c.proc.peb64.value();
basic_info.UniqueProcessId = 1;
};
switch (process_information_length)
{
case sizeof(PROCESS_BASIC_INFORMATION64):
return handle_query<PROCESS_BASIC_INFORMATION64>(c.emu, process_information, process_information_length, return_length,
init_basic_info);
case sizeof(PROCESS_EXTENDED_BASIC_INFORMATION):
return handle_query<PROCESS_EXTENDED_BASIC_INFORMATION>(
c.emu, process_information, process_information_length, return_length,
[&](PROCESS_EXTENDED_BASIC_INFORMATION& ext_basic_info) {
ext_basic_info.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION);
init_basic_info(ext_basic_info.BasicInfo);
});
default:
return STATUS_INFO_LENGTH_MISMATCH;
}
}
case ProcessImageInformation:
return handle_query<SECTION_IMAGE_INFORMATION<EmulatorTraits<Emu64>>>(
c.emu, process_information, process_information_length, return_length,
[&](SECTION_IMAGE_INFORMATION<EmulatorTraits<Emu64>>& i) {
const auto& mod = *c.win_emu.mod_manager.executable;
const emulator_object<PEDosHeader_t> dos_header_obj{c.emu, mod.image_base};
const auto dos_header = dos_header_obj.read();
const emulator_object<PENTHeaders_t<uint64_t>> 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 = 0;
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;
});
case ProcessImageFileNameWin32: {
const auto peb = c.proc.peb64.read();
emulator_object<RTL_USER_PROCESS_PARAMETERS64> proc_params{c.emu, peb.ProcessParameters};
const auto params = proc_params.read();
const auto length = params.ImagePathName.Length + sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) + 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<EmulatorTraits<Emu64>>> info{c.emu, process_information};
info.access([&](UNICODE_STRING<EmulatorTraits<Emu64>>& str) {
const auto buffer_start = static_cast<uint64_t>(process_information) + sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>);
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 = buffer_start;
});
return STATUS_SUCCESS;
}
default:
c.win_emu.log.error("Unsupported process info class: %X\n", info_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
}
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 //
|| info_class == ProcessDynamicFunctionTableInformation //
|| info_class == ProcessPriorityBoost //
|| info_class == ProcessPriorityClassEx //
|| info_class == ProcessPriorityClass || info_class == ProcessAffinityMask)
{
return STATUS_SUCCESS;
}
if (info_class == ProcessExecuteFlags)
{
return STATUS_NOT_SUPPORTED;
}
if (info_class == ProcessTlsInformation)
{
if (process_information_length < sizeof(PROCESS_TLS_INFORMATION) ||
(process_information_length - (sizeof(PROCESS_TLS_INFORMATION) - sizeof(THREAD_TLS_INFORMATION))) %
sizeof(THREAD_TLS_INFORMATION))
{
return STATUS_INFO_LENGTH_MISMATCH;
}
constexpr auto thread_data_offset = offsetof(PROCESS_TLS_INFORMATION, ThreadData);
const emulator_object<THREAD_TLS_INFORMATION> data{c.emu, process_information + thread_data_offset};
PROCESS_TLS_INFORMATION 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;
const auto is_wow64 = c.win_emu.process.is_wow64_process;
const auto& thread = thread_iterator->second;
thread.teb64->access([&](TEB64& teb) {
entry.ThreadId = teb.ClientId.UniqueThread;
uint64_t tls_vector = teb.ThreadLocalStoragePointer;
const auto ptr_size = is_wow64 ? sizeof(EmulatorTraits<Emu32>::PVOID) : sizeof(EmulatorTraits<Emu64>::PVOID);
if (is_wow64)
{
if (!thread.teb32.has_value())
{
return;
}
thread.teb32->access([&tls_vector](const TEB32& teb32) { tls_vector = teb32.ThreadLocalStoragePointer; });
}
if (!tls_vector)
{
return;
}
if (tls_info.OperationType == ProcessTlsReplaceIndex)
{
const auto tls_entry_ptr = tls_vector + (tls_info.TlsIndex * ptr_size);
uint64_t old_entry{};
if (is_wow64)
{
old_entry = c.emu.read_memory<EmulatorTraits<Emu32>::PVOID>(tls_entry_ptr);
c.emu.write_memory<EmulatorTraits<Emu32>::PVOID>(tls_entry_ptr, static_cast<uint32_t>(entry.NewTlsData));
}
else
{
old_entry = c.emu.read_memory<EmulatorTraits<Emu64>::PVOID>(tls_entry_ptr);
c.emu.write_memory<EmulatorTraits<Emu64>::PVOID>(tls_entry_ptr, entry.NewTlsData);
}
entry.OldTlsData = old_entry;
}
else if (tls_info.OperationType == ProcessTlsReplaceVector)
{
const auto new_tls_vector = entry.NewTlsData;
for (uint32_t index = 0; index < tls_info.PreviousCount; ++index)
{
if (is_wow64)
{
const auto old_entry = c.emu.read_memory<uint32_t>(tls_vector + (index * ptr_size));
c.emu.write_memory(new_tls_vector + (index * ptr_size), old_entry);
}
else
{
const auto old_entry = c.emu.read_memory<uint64_t>(tls_vector + (index * ptr_size));
c.emu.write_memory(new_tls_vector + (index * ptr_size), old_entry);
}
}
if (is_wow64)
{
thread.teb32->access([&new_tls_vector](TEB32& teb32) {
teb32.ThreadLocalStoragePointer = static_cast<uint32_t>(new_tls_vector);
});
}
else
{
teb.ThreadLocalStoragePointer = new_tls_vector;
}
entry.OldTlsData = tls_vector;
}
});
}
return STATUS_SUCCESS;
}
if (info_class == ProcessInstrumentationCallback)
{
if (process_information_length != sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION))
{
return STATUS_BUFFER_OVERFLOW;
}
PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info;
c.emu.read_memory(process_information, &info, sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION));
c.win_emu.callbacks.on_suspicious_activity("Setting ProcessInstrumentationCallback");
c.proc.instrumentation_callback = info.Callback;
return STATUS_SUCCESS;
}
c.win_emu.log.error("Unsupported info process class: %X\n", info_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtOpenProcess()
{
return STATUS_NOT_SUPPORTED;
}
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_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;
}
}