Improvements to locale syscalls, fix TimeZone query and add more syscalls (#223)

This PR aims to do the following:
- Add the `NtUserGetAtomName` syscall.
- Add stub handlers for `NtUserSetWindowsHookEx`,
`NtUserUnhookWindowsHookEx`, and `NtUserCreateWindowEx`.
- Implement `NtGetNlsSectionPtr` (for Code Pages), add
`NtQueryDefaultUILanguage`, and improve `NtQueryInstallUILanguage`.
- Handle `SystemTimeZoneInformation`,
`SystemCurrentTimeZoneInformation`, and
`SystemDynamicTimeZoneInformation` in `NtQuerySystemInformation`.
- Skip `ThreadAffinityMask` in `NtSetInformationThread`.
- Fix `NtConnectPort` to allow TimeZone queries to work properly. This
is probably the most problematic change here because it required messing
with the shared section and hardcoding an offset (2504). Initializing
the data in this offset seems necessary for calls like
`GetTimeZoneInformation` to succeed, so maybe there's no other way
around it.
This commit is contained in:
Maurice Heumann
2025-04-22 09:08:42 +02:00
committed by GitHub
9 changed files with 316 additions and 20 deletions

View File

@@ -453,6 +453,7 @@ union TEB_SAME_TEB_FLAGS_UNION
#ifndef OS_WINDOWS
using LCID = DWORD;
using LANGID = WORD;
#endif
typedef struct _TEB64
@@ -862,6 +863,44 @@ typedef struct _SYSTEM_TIMEOFDAY_INFORMATION64
ULONGLONG SleepTimeBias;
} SYSTEM_TIMEOFDAY_INFORMATION64, *PSYSTEM_TIMEOFDAY_INFORMATION64;
#ifndef OS_WINDOWS
typedef struct _SYSTEMTIME
{
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
#endif
typedef struct _SYSTEM_TIMEZONE_INFORMATION
{
LONG Bias;
ARRAY_CONTAINER<char16_t, 32> StandardName;
SYSTEMTIME StandardDate;
LONG StandardBias;
ARRAY_CONTAINER<char16_t, 32> DaylightName;
SYSTEMTIME DaylightDate;
LONG DaylightBias;
} SYSTEM_TIMEZONE_INFORMATION, *PSYSTEM_TIMEZONE_INFORMATION;
typedef struct _SYSTEM_DYNAMIC_TIMEZONE_INFORMATION
{
LONG Bias;
ARRAY_CONTAINER<char16_t, 32> StandardName;
SYSTEMTIME StandardDate;
LONG StandardBias;
ARRAY_CONTAINER<char16_t, 32> DaylightName;
SYSTEMTIME DaylightDate;
LONG DaylightBias;
ARRAY_CONTAINER<char16_t, 128> TimeZoneKeyName;
BOOLEAN DynamicDaylightTimeDisabled;
} SYSTEM_DYNAMIC_TIMEZONE_INFORMATION, *PSYSTEM_DYNAMIC_TIMEZONE_INFORMATION;
typedef struct _PROCESS_BASIC_INFORMATION64
{
NTSTATUS ExitStatus;

View File

@@ -129,7 +129,10 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->current_ip);
buffer.write(this->previous_ip);
buffer.write(this->shared_section_address);
buffer.write(this->shared_section_size);
buffer.write(this->dbwin_buffer);
buffer.write(this->dbwin_buffer_size);
buffer.write_optional(this->exception_rip);
buffer.write_optional(this->exit_status);
buffer.write(this->base_allocator);
@@ -164,7 +167,10 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->current_ip);
buffer.read(this->previous_ip);
buffer.read(this->shared_section_address);
buffer.read(this->shared_section_size);
buffer.read(this->dbwin_buffer);
buffer.read(this->dbwin_buffer_size);
buffer.read_optional(this->exception_rip);
buffer.read_optional(this->exit_status);
buffer.read(this->base_allocator);
@@ -312,3 +318,14 @@ bool process_context::delete_atom(uint16_t atom_id)
return true;
}
const std::u16string* process_context::get_atom_name(uint16_t atom_id) const
{
const auto it = atoms.find(atom_id);
if (it == atoms.end())
{
return nullptr;
}
return &it->second.name;
}

View File

@@ -68,6 +68,7 @@ struct process_context
uint16_t add_or_find_atom(std::u16string name);
bool delete_atom(const std::u16string& name);
bool delete_atom(uint16_t atom_id);
const std::u16string* get_atom_name(uint16_t atom_id) const;
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
@@ -79,7 +80,10 @@ struct process_context
uint64_t current_ip{0};
uint64_t previous_ip{0};
uint64_t shared_section_address{0};
uint64_t shared_section_size{0};
uint64_t dbwin_buffer{0};
uint64_t dbwin_buffer_size{0};
std::optional<uint64_t> exception_rip{};
std::optional<NTSTATUS> exit_status{};
@@ -111,4 +115,4 @@ struct process_context
uint32_t spawned_thread_count{0};
handle_store<handle_types::thread, emulator_thread> threads{};
emulator_thread* active_thread{nullptr};
};
};

View File

@@ -118,11 +118,14 @@ namespace syscalls
emulator_object<LARGE_INTEGER> /*default_casing_table_size*/);
NTSTATUS handle_NtQueryDefaultLocale(const syscall_context&, BOOLEAN /*user_profile*/,
emulator_object<LCID> default_locale_id);
NTSTATUS handle_NtGetNlsSectionPtr();
NTSTATUS handle_NtGetNlsSectionPtr(const syscall_context& c, ULONG section_type, ULONG section_data,
emulator_pointer /*context_data*/, emulator_object<uint64_t> section_pointer,
emulator_object<ULONG> section_size);
NTSTATUS handle_NtGetMUIRegistryInfo();
NTSTATUS handle_NtIsUILanguageComitted();
NTSTATUS handle_NtUserGetKeyboardLayout();
NTSTATUS handle_NtQueryInstallUILanguage();
NTSTATUS handle_NtQueryDefaultUILanguage(const syscall_context&, emulator_object<LANGID> language_id);
NTSTATUS handle_NtQueryInstallUILanguage(const syscall_context&, emulator_object<LANGID> language_id);
// syscalls/memory.cpp:
NTSTATUS handle_NtQueryVirtualMemory(const syscall_context& c, handle process_handle, uint64_t base_address,
@@ -180,6 +183,14 @@ namespace syscalls
emulator_object<REMOTE_PORT_VIEW64> /*server_shared_memory*/,
emulator_object<ULONG> /*maximum_message_length*/, emulator_pointer connection_info,
emulator_object<ULONG> connection_info_length);
NTSTATUS handle_NtSecureConnectPort(const syscall_context& c, emulator_object<handle> client_port_handle,
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> server_port_name,
emulator_object<SECURITY_QUALITY_OF_SERVICE> security_qos,
emulator_object<PORT_VIEW64> client_shared_memory,
emulator_pointer /*server_sid*/,
emulator_object<REMOTE_PORT_VIEW64> server_shared_memory,
emulator_object<ULONG> maximum_message_length, emulator_pointer connection_info,
emulator_object<ULONG> connection_info_length);
NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& c, handle port_handle, ULONG /*flags*/,
emulator_object<PORT_MESSAGE64> /*send_message*/,
emulator_object<ALPC_MESSAGE_ATTRIBUTES>
@@ -554,6 +565,33 @@ namespace syscalls
return STATUS_SUCCESS;
}
NTSTATUS handle_NtUserGetAtomName(const syscall_context& c, const RTL_ATOM atom,
const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> atom_name)
{
const auto* name = c.proc.get_atom_name(atom);
if (!name)
{
return STATUS_INVALID_PARAMETER;
}
const size_t name_length = name->size() * 2;
const size_t max_length = name_length + 2;
bool too_small = false;
atom_name.access([&](UNICODE_STRING<EmulatorTraits<Emu64>>& str) {
if (str.MaximumLength < max_length)
{
too_small = true;
return;
}
str.Length = static_cast<USHORT>(name_length);
c.emu.write_memory(str.Buffer, name->data(), max_length);
});
return too_small ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
}
NTSTATUS handle_NtQueryDebugFilterState()
{
return FALSE;
@@ -660,6 +698,21 @@ namespace syscalls
{
return c.proc.delete_atom(read_unicode_string(c.emu, class_name));
}
NTSTATUS handle_NtUserSetWindowsHookEx()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtUserUnhookWindowsHookEx()
{
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtUserCreateWindowEx()
{
return STATUS_NOT_SUPPORTED;
}
}
void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& handler_mapping)
@@ -704,6 +757,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtApphelpCacheControl);
add_handler(NtCreateSection);
add_handler(NtConnectPort);
add_handler(NtSecureConnectPort);
add_handler(NtCreateFile);
add_handler(NtDeviceIoControlFile);
add_handler(NtQueryWnfStateData);
@@ -725,6 +779,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtAddAtomEx);
add_handler(NtAddAtom);
add_handler(NtDeleteAtom);
add_handler(NtUserGetAtomName);
add_handler(NtInitializeNlsFiles);
add_handler(NtUnmapViewOfSection);
add_handler(NtUnmapViewOfSectionEx);
@@ -740,6 +795,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtOpenEvent);
add_handler(NtGetMUIRegistryInfo);
add_handler(NtIsUILanguageComitted);
add_handler(NtQueryDefaultUILanguage);
add_handler(NtQueryInstallUILanguage);
add_handler(NtUpdateWnfStateData);
add_handler(NtRaiseException);
@@ -813,6 +869,9 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtUserGetProcessWindowStation);
add_handler(NtUserRegisterClassExWOW);
add_handler(NtUserUnregisterClass);
add_handler(NtUserSetWindowsHookEx);
add_handler(NtUserUnhookWindowsHookEx);
add_handler(NtUserCreateWindowEx);
#undef add_handler
}

View File

@@ -34,8 +34,32 @@ namespace syscalls
return STATUS_SUCCESS;
}
NTSTATUS handle_NtGetNlsSectionPtr()
NTSTATUS handle_NtGetNlsSectionPtr(const syscall_context& c, ULONG section_type, ULONG section_data,
emulator_pointer /*context_data*/, emulator_object<uint64_t> section_pointer,
emulator_object<ULONG> section_size)
{
if (section_type == 11)
{
c.win_emu.log.print(color::dark_gray, "--> Code Page: %d\n", section_data);
const auto file_path = R"(C:\Windows\System32\C_)" + std::to_string(section_data) + ".NLS";
const auto locale_file = utils::io::read_file(c.win_emu.file_sys.translate(file_path));
if (locale_file.empty())
{
return STATUS_OBJECT_NAME_NOT_FOUND;
}
const auto size = static_cast<size_t>(page_align_up(locale_file.size()));
const auto section_memory = c.win_emu.memory.allocate_memory(size, memory_permission::read);
c.emu.write_memory(section_memory, locale_file.data(), locale_file.size());
section_pointer.write_if_valid(section_memory);
section_size.write_if_valid(static_cast<ULONG>(size));
return STATUS_SUCCESS;
}
c.win_emu.log.print(color::gray, "Unsupported section type: %X\n", section_type);
return STATUS_NOT_SUPPORTED;
}
@@ -54,8 +78,15 @@ namespace syscalls
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtQueryInstallUILanguage()
NTSTATUS handle_NtQueryDefaultUILanguage(const syscall_context&, emulator_object<LANGID> language_id)
{
return STATUS_NOT_SUPPORTED;
language_id.write(0x407);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtQueryInstallUILanguage(const syscall_context&, emulator_object<LANGID> language_id)
{
language_id.write(0x407);
return STATUS_SUCCESS;
}
}

View File

@@ -4,6 +4,18 @@
namespace syscalls
{
struct CSR_API_CONNECTINFO
{
uint64_t SharedSectionBase;
uint64_t SharedStaticServerData;
uint64_t SharedSectionHeap;
ULONG DebugFlags;
ULONG SizeOfPebData;
ULONG SizeOfTebData;
ULONG NumberOfServerDllNames;
EMULATOR_CAST(uint64_t, HANDLE) ServerProcessId;
};
NTSTATUS handle_NtConnectPort(const syscall_context& c, const emulator_object<handle> client_port_handle,
const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> server_port_name,
const emulator_object<SECURITY_QUALITY_OF_SERVICE> /*security_qos*/,
@@ -21,9 +33,34 @@ namespace syscalls
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());
if (p.name == u"\\Windows\\ApiPort")
{
CSR_API_CONNECTINFO connect_info{};
const auto expected_connect_length = connection_info_length.read();
if (expected_connect_length < sizeof(CSR_API_CONNECTINFO))
{
return STATUS_BUFFER_TOO_SMALL;
}
// TODO: Use client_shared_memory to get the section entry and get the address from it?
connect_info.SharedSectionBase = c.proc.shared_section_address;
c.emu.write_memory(c.proc.shared_section_address + 2504,
0xFFFFFFFF); // BaseStaticServerData->TermsrvClientTimeZoneId
const auto static_server_data =
c.win_emu.memory.allocate_memory(0x10000, memory_permission::read_write);
connect_info.SharedStaticServerData = static_server_data;
c.emu.write_memory(static_server_data + 8, connect_info.SharedSectionBase);
c.emu.write_memory(connection_info, &connect_info, sizeof(connect_info));
}
else
{
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_VIEW64& view) {
@@ -39,6 +76,20 @@ namespace syscalls
return STATUS_SUCCESS;
}
NTSTATUS handle_NtSecureConnectPort(const syscall_context& c, emulator_object<handle> client_port_handle,
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> server_port_name,
emulator_object<SECURITY_QUALITY_OF_SERVICE> security_qos,
emulator_object<PORT_VIEW64> client_shared_memory,
emulator_pointer /*server_sid*/,
emulator_object<REMOTE_PORT_VIEW64> server_shared_memory,
emulator_object<ULONG> maximum_message_length, emulator_pointer connection_info,
emulator_object<ULONG> connection_info_length)
{
return handle_NtConnectPort(c, client_port_handle, server_port_name, security_qos, client_shared_memory,
server_shared_memory, maximum_message_length, connection_info,
connection_info_length);
}
NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& c, const handle port_handle, const ULONG /*flags*/,
const emulator_object<PORT_MESSAGE64> /*send_message*/,
const emulator_object<ALPC_MESSAGE_ATTRIBUTES>

View File

@@ -64,12 +64,26 @@ namespace syscalls
if (filename == u"\\Windows\\SharedSection")
{
constexpr auto shared_section_size = 0x10000;
const auto address = c.win_emu.memory.find_free_allocation_base(shared_section_size);
c.win_emu.memory.allocate_memory(address, shared_section_size, memory_permission::read_write);
c.proc.shared_section_address = address;
c.proc.shared_section_size = shared_section_size;
section_handle.write(SHARED_SECTION);
return STATUS_SUCCESS;
}
if (filename == u"DBWIN_BUFFER")
{
constexpr auto dbwin_buffer_section_size = 0x1000;
const auto address = c.win_emu.memory.find_free_allocation_base(dbwin_buffer_section_size);
c.win_emu.memory.allocate_memory(address, dbwin_buffer_section_size, memory_permission::read_write);
c.proc.dbwin_buffer = address;
c.proc.dbwin_buffer_size = dbwin_buffer_section_size;
section_handle.write(DBWIN_BUFFER);
return STATUS_SUCCESS;
}
@@ -120,10 +134,8 @@ namespace syscalls
if (section_handle == SHARED_SECTION)
{
constexpr auto shared_section_size = 0x10000;
const auto address = c.win_emu.memory.find_free_allocation_base(shared_section_size);
c.win_emu.memory.allocate_memory(address, shared_section_size, memory_permission::read_write);
const auto shared_section_size = c.proc.shared_section_size;
const auto address = c.proc.shared_section_address;
const std::u16string_view windows_dir = c.proc.kusd.get().NtSystemRoot.arr;
const auto windows_dir_size = windows_dir.size() * 2;
@@ -168,11 +180,8 @@ namespace syscalls
if (section_handle == DBWIN_BUFFER)
{
constexpr auto dbwin_buffer_section_size = 0x1000;
const auto address = c.win_emu.memory.find_free_allocation_base(dbwin_buffer_section_size);
c.win_emu.memory.allocate_memory(address, dbwin_buffer_section_size, memory_permission::read_write);
c.proc.dbwin_buffer = address;
const auto dbwin_buffer_section_size = c.proc.dbwin_buffer_size;
const auto address = c.proc.dbwin_buffer;
if (view_size)
{
@@ -255,10 +264,17 @@ namespace syscalls
return STATUS_INVALID_PARAMETER;
}
if (base_address == c.proc.shared_section_address)
{
c.proc.shared_section_address = 0;
c.win_emu.memory.release_memory(base_address, static_cast<size_t>(c.proc.shared_section_size));
return STATUS_SUCCESS;
}
if (base_address == c.proc.dbwin_buffer)
{
c.proc.dbwin_buffer = 0;
c.win_emu.memory.release_memory(base_address, 0x1000);
c.win_emu.memory.release_memory(base_address, static_cast<size_t>(c.proc.dbwin_buffer_size));
return STATUS_SUCCESS;
}

View File

@@ -115,10 +115,88 @@ namespace syscalls
return handle_query<SYSTEM_TIMEOFDAY_INFORMATION64>(c.emu, system_information, system_information_length,
return_length,
[&](SYSTEM_TIMEOFDAY_INFORMATION64& info) {
memset(&info, 0, sizeof(info));
info.BootTime.QuadPart = 0;
info.TimeZoneId = 0x00000002;
// TODO: Fill
});
case SystemTimeZoneInformation:
case SystemCurrentTimeZoneInformation:
return handle_query<SYSTEM_TIMEZONE_INFORMATION>(
c.emu, system_information, system_information_length, return_length,
[&](SYSTEM_TIMEZONE_INFORMATION& tzi) {
memset(&tzi, 0, sizeof(tzi));
tzi.Bias = -60;
tzi.StandardBias = 0;
tzi.DaylightBias = -60;
constexpr std::u16string_view std_name{u"W. Europe Standard Time"};
memcpy(&tzi.StandardName.arr[0], std_name.data(), std_name.size() * sizeof(char16_t));
constexpr std::u16string_view dlt_name{u"W. Europe Daylight Time"};
memcpy(&tzi.DaylightName.arr[0], dlt_name.data(), dlt_name.size() * sizeof(char16_t));
// Standard Time: Last Sunday in October, 03:00
tzi.StandardDate.wMonth = 10;
tzi.StandardDate.wDayOfWeek = 0;
tzi.StandardDate.wDay = 5;
tzi.StandardDate.wHour = 3;
tzi.StandardDate.wMinute = 0;
tzi.StandardDate.wSecond = 0;
tzi.StandardDate.wMilliseconds = 0;
// Daylight Time: Last Sunday in March, 02:00
tzi.DaylightDate.wMonth = 3;
tzi.DaylightDate.wDayOfWeek = 0;
tzi.DaylightDate.wDay = 5;
tzi.DaylightDate.wHour = 2;
tzi.DaylightDate.wMinute = 0;
tzi.DaylightDate.wSecond = 0;
tzi.DaylightDate.wMilliseconds = 0;
});
case SystemDynamicTimeZoneInformation:
return handle_query<SYSTEM_DYNAMIC_TIMEZONE_INFORMATION>(
c.emu, system_information, system_information_length, return_length,
[&](SYSTEM_DYNAMIC_TIMEZONE_INFORMATION& dtzi) {
memset(&dtzi, 0, sizeof(dtzi));
dtzi.Bias = -60;
dtzi.StandardBias = 0;
dtzi.DaylightBias = -60;
constexpr std::u16string_view std_name{u"W. Europe Standard Time"};
memcpy(&dtzi.StandardName.arr[0], std_name.data(), std_name.size() * sizeof(char16_t));
constexpr std::u16string_view dlt_name{u"W. Europe Daylight Time"};
memcpy(&dtzi.DaylightName.arr[0], dlt_name.data(), dlt_name.size() * sizeof(char16_t));
constexpr std::u16string_view key_name{u"W. Europe Standard Time"};
memcpy(&dtzi.TimeZoneKeyName.arr[0], key_name.data(), key_name.size() * sizeof(char16_t));
// Standard Time: Last Sunday in October, 03:00
dtzi.StandardDate.wMonth = 10;
dtzi.StandardDate.wDayOfWeek = 0;
dtzi.StandardDate.wDay = 5;
dtzi.StandardDate.wHour = 3;
dtzi.StandardDate.wMinute = 0;
dtzi.StandardDate.wSecond = 0;
dtzi.StandardDate.wMilliseconds = 0;
// Daylight Time: Last Sunday in March, 02:00
dtzi.DaylightDate.wMonth = 3;
dtzi.DaylightDate.wDayOfWeek = 0;
dtzi.DaylightDate.wDay = 5;
dtzi.DaylightDate.wHour = 2;
dtzi.DaylightDate.wMinute = 0;
dtzi.DaylightDate.wSecond = 0;
dtzi.DaylightDate.wMilliseconds = 0;
dtzi.DynamicDaylightTimeDisabled = FALSE;
});
case SystemRangeStartInformation:
return handle_query<SYSTEM_RANGE_START_INFORMATION64>(c.emu, system_information, system_information_length,
return_length,

View File

@@ -18,7 +18,8 @@ namespace syscalls
return STATUS_INVALID_HANDLE;
}
if (info_class == ThreadSchedulerSharedDataSlot || info_class == ThreadBasePriority)
if (info_class == ThreadSchedulerSharedDataSlot || info_class == ThreadBasePriority ||
info_class == ThreadAffinityMask)
{
return STATUS_SUCCESS;
}