Compare commits

...

20 Commits

Author SHA1 Message Date
momo5502
ff99a1cb56 Log writes to the import table 2026-01-06 14:41:34 +01:00
Maurice Heumann
7c912146fb Cleanup common header includes (#690) 2026-01-06 11:46:07 +01:00
momo5502
45ac1fc32f Cleanup headers 2026-01-06 11:29:39 +01:00
Maurice Heumann
5d81d8bda9 Implement NtQueryMultipleValueKey (#684)
This PR implements `NtQueryMultipleValueKey` that is used in calls like
`RegQueryMultipleValues`.
2026-01-06 10:38:10 +01:00
Maurice Heumann
e46e8dcf7a more object syscalls (#687) 2026-01-06 10:30:47 +01:00
Maurice Heumann
1a613be667 ports: add noop port stub for WER (#689) 2026-01-06 10:22:49 +01:00
Maurice Heumann
d3af3c781c Add support for user_object/user_handle_table (#677)
Fixes #641

This PR is my attempt to add support for user32 objects and the user32
handle table. I also added a test, but as expected, it fails on Windows
2022. I’ll try to fix that another day, but feel free to review the code
😄
2026-01-06 10:21:58 +01:00
redthing1
17d1e23b60 syscalls.cpp: clang format 2026-01-05 23:15:02 -08:00
redthing1
1be74c4cde ports: add noop port stub for WER 2026-01-05 23:12:53 -08:00
redthing1
51034297bf object syscalls: wire up the new syscalls 2026-01-05 23:08:21 -08:00
redthing1
a2a119aec2 object syscalls: implement NtCompareObjects 2026-01-05 23:08:18 -08:00
redthing1
6f8dca6614 object syscalls: implement NtWaitForMultipleObjects32 2026-01-05 23:08:14 -08:00
redthing1
cafa0ebd0b NTSTATUS: add STATUS_NOT_SAME_OBJECT 2026-01-05 23:08:05 -08:00
Igor Pissolati
90b38b3bff Fix user_handle_table initialization 2026-01-05 09:01:05 -03:00
Igor Pissolati
5d59700ec7 Remove parse_number 2026-01-05 09:00:45 -03:00
ssvine
37c2184bfc Implement NtQueryMultipleValueKey 2026-01-05 13:11:35 +03:00
Igor Pissolati
d51f890197 Use vector instead of large array 2026-01-04 22:51:19 -03:00
Igor Pissolati
7112d619b4 Fix clang tidy 2026-01-04 16:28:30 -03:00
Igor Pissolati
5302900a9d Fix for older Windows builds 2026-01-04 16:01:13 -03:00
Igor Pissolati
de491ade0e Add support for user_object/user_handle_table 2026-01-02 19:46:04 -03:00
25 changed files with 948 additions and 18 deletions

View File

@@ -532,6 +532,29 @@ namespace
max = std::max(import_thunk, max); max = std::max(import_thunk, max);
} }
c.win_emu->emu().hook_memory_write(min, max - min, [&c](const uint64_t address, const void*, size_t) {
const auto& watched_module = *c.win_emu->mod_manager.executable;
const auto& accessor_module = *c.win_emu->mod_manager.executable;
const auto rip = c.win_emu->emu().read_instruction_pointer();
if (!accessor_module.contains(rip))
{
return;
}
const auto sym = watched_module.imports.find(address);
if (sym == watched_module.imports.end())
{
return;
}
const auto import_module = watched_module.imported_modules.at(sym->second.module_index);
c.win_emu->log.print(color::blue, "Import write access: %s (%s) at 0x%" PRIx64 " (%s)\n", sym->second.name.c_str(),
import_module.c_str(), rip, accessor_module.name.c_str());
});
c.win_emu->emu().hook_memory_read(min, max - min, [&c](const uint64_t address, const void*, size_t) { c.win_emu->emu().hook_memory_read(min, max - min, [&c](const uint64_t address, const void*, size_t) {
const auto& watched_module = *c.win_emu->mod_manager.executable; const auto& watched_module = *c.win_emu->mod_manager.executable;
const auto& accessor_module = *c.win_emu->mod_manager.executable; const auto& accessor_module = *c.win_emu->mod_manager.executable;

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include "kernel_mapped.hpp"
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) // NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
#define ACCESS_MASK DWORD #define ACCESS_MASK DWORD

View File

@@ -2,6 +2,12 @@
#include <cstdint> #include <cstdint>
#include "primitives.hpp"
#include "traits.hpp"
#include "unicode.hpp"
#include "status.hpp"
#include "process.hpp"
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) // NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
#ifndef NT_SUCCESS #ifndef NT_SUCCESS

View File

@@ -1,5 +1,8 @@
#pragma once #pragma once
#include "traits.hpp"
#include "primitives.hpp"
template <typename Traits> template <typename Traits>
struct EMU_WSABUF struct EMU_WSABUF
{ {

View File

@@ -17,6 +17,7 @@
#include "unicode.hpp" #include "unicode.hpp"
#include "status.hpp" #include "status.hpp"
#include "process.hpp" #include "process.hpp"
#include "user.hpp"
#include "kernel_mapped.hpp" #include "kernel_mapped.hpp"
#include "memory.hpp" #include "memory.hpp"
#include "file_management.hpp" #include "file_management.hpp"

View File

@@ -1,13 +1,14 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include "compiler.hpp"
// NOLINTBEGIN(modernize-use-using) // NOLINTBEGIN(modernize-use-using)
#ifdef OS_WINDOWS #ifdef OS_WINDOWS
#include "../utils/win.hpp" #include "../utils/win.hpp"
#include "winnt.h" #include <winnt.h>
#else #else
@@ -50,6 +51,14 @@ typedef union _LARGE_INTEGER
using BYTE = std::uint8_t; using BYTE = std::uint8_t;
#define CHAR BYTE #define CHAR BYTE
typedef struct _RECT
{
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT;
#endif #endif
using WORD = std::uint16_t; using WORD = std::uint16_t;

View File

@@ -110,4 +110,12 @@ struct KEY_VALUE_FULL_INFORMATION
char16_t Name[1]; char16_t Name[1];
}; };
struct KEY_VALUE_ENTRY
{
EmulatorTraits<Emu64>::PVOID ValueName;
ULONG DataLength;
ULONG DataOffset;
ULONG Type;
};
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) // NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

View File

@@ -50,6 +50,7 @@ using NTSTATUS = std::uint32_t;
#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL) #define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL) #define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L) #define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)
#define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS)0xC000009FL) #define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS)0xC000009FL)
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L) #define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
#define STATUS_PIPE_BUSY ((NTSTATUS)0xC00000AAL) #define STATUS_PIPE_BUSY ((NTSTATUS)0xC00000AAL)
@@ -78,6 +79,7 @@ using NTSTATUS = std::uint32_t;
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL) #define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
#define STATUS_CONNECTION_RESET ((NTSTATUS)0xC000020DL) #define STATUS_CONNECTION_RESET ((NTSTATUS)0xC000020DL)
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) #define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
#define STATUS_NOT_SAME_OBJECT ((NTSTATUS)0xC00001ACL)
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L) #define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
#define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS)0xC0000245L) #define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS)0xC0000245L)
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L) #define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include "kernel_mapped.hpp"
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) // NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
typedef enum _THREADINFOCLASS typedef enum _THREADINFOCLASS

View File

@@ -0,0 +1,117 @@
#pragma once
#include <cstdint>
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
struct USER_SERVERINFO
{
DWORD dwSRVIFlags;
uint64_t cHandleEntries;
uint8_t unknown[0x1000];
};
struct USER_DISPINFO
{
DWORD dwMonitorCount;
EMULATOR_CAST(uint64_t, USER_MONITOR*) pPrimaryMonitor;
uint8_t unknown[0xFF];
};
struct USER_HANDLEENTRY
{
uint64_t pHead;
uint64_t pOwner;
uint64_t unknown;
uint8_t bType;
uint8_t bFlags;
uint16_t wUniq;
};
static_assert(sizeof(USER_HANDLEENTRY) == 0x20);
struct USER_SHAREDINFO
{
uint64_t psi;
uint64_t aheList;
uint32_t HeEntrySize;
uint64_t pDispInfo;
uint8_t unknown[0xFF];
};
struct USER_THROBJHEAD
{
struct
{
uint64_t h;
uint32_t cLockObj;
} h;
uint64_t pti;
};
struct USER_THRDESKHEAD
{
USER_THROBJHEAD h;
uint64_t rpdesk;
uint64_t pSelf;
};
enum USER_HANDLETYPE : uint8_t
{
TYPE_FREE = 0,
TYPE_WINDOW = 1,
TYPE_MENU = 2,
TYPE_CURSOR = 3,
TYPE_SETWINDOWPOS = 4,
TYPE_HOOK = 5,
TYPE_CLIPDATA = 6,
TYPE_CALLPROC = 7,
TYPE_ACCELTABLE = 8,
TYPE_DDEACCESS = 9,
TYPE_DDECONV = 10,
TYPE_DDEXACT = 11,
TYPE_MONITOR = 12,
TYPE_KBDLAYOUT = 13,
TYPE_KBDFILE = 14,
TYPE_WINEVENTHOOK = 15,
TYPE_TIMER = 16,
TYPE_INPUTCONTEXT = 17,
TYPE_HIDDATA = 18,
TYPE_DEVICEINFO = 19,
TYPE_TOUCHINPUTINFO = 20,
TYPE_GESTUREINFOOBJ = 21,
TYPE_CTYPES = 22,
TYPE_GENERIC = 255
};
struct USER_MONITOR
{
EMULATOR_CAST(uint64_t, HMONITOR) hmon;
uint8_t unknown1[0x14];
RECT rcMonitor;
RECT rcWork;
union
{
struct
{
uint16_t monitorDpi;
uint16_t nativeDpi;
} b26;
struct
{
uint32_t unknown1;
uint16_t monitorDpi;
uint16_t nativeDpi;
uint16_t cachedDpi;
uint16_t unknown2;
RECT rcMonitorDpiAware;
} b20;
};
uint8_t unknown4[0xFF];
};
struct USER_WINDOW
{
uint8_t unknown[0xFF];
};
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

View File

@@ -7,6 +7,8 @@
#include <system_error> #include <system_error>
#include <variant> #include <variant>
#include "primitives.hpp"
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) // NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include "kernel_mapped.hpp"
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) // NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
using pointer = uint64_t; using pointer = uint64_t;

View File

@@ -160,6 +160,36 @@ namespace utils
this->write_span(std::span(vec)); this->write_span(std::span(vec));
} }
void write_vector(const std::vector<bool>& vec)
{
this->write(static_cast<uint64_t>(vec.size()));
uint8_t byte = 0;
uint8_t bit_index = 0;
for (const bool b : vec)
{
if (b)
{
byte |= (1u << bit_index);
}
++bit_index;
if (bit_index == 8)
{
this->write<uint8_t>(byte);
byte = 0;
bit_index = 0;
}
}
if (bit_index != 0)
{
this->write<uint8_t>(byte);
}
}
template <typename T> template <typename T>
void write_list(const std::list<T>& vec) void write_list(const std::list<T>& vec)
{ {
@@ -385,6 +415,25 @@ namespace utils
} }
} }
void read_vector(std::vector<bool>& result)
{
const auto bit_count = this->read<uint64_t>();
result.clear();
result.reserve(static_cast<size_t>(bit_count));
const auto size = (bit_count + 7) / 8;
for (uint64_t i = 0; i < size; ++i)
{
const auto byte = this->read<uint8_t>();
for (uint8_t bit = 0; bit < 8 && result.size() < bit_count; ++bit)
{
result.push_back((byte >> bit) & 1u);
}
}
}
template <typename T> template <typename T>
std::vector<T> read_vector() std::vector<T> read_vector()
{ {

View File

@@ -509,6 +509,41 @@ namespace
return true; return true;
} }
bool test_monitor_info()
{
const POINT pt = {0, 0};
const auto hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
if (!hMonitor)
{
return false;
}
MONITORINFOEXA mi;
mi.cbSize = sizeof(mi);
if (!GetMonitorInfoA(hMonitor, &mi))
{
return false;
}
if (std::string_view(mi.szDevice) != R"(\\.\DISPLAY1)")
{
return false;
}
if (mi.rcMonitor.left != 0 || mi.rcMonitor.top != 0 || mi.rcMonitor.right != 1920 || mi.rcMonitor.bottom != 1080)
{
return false;
}
if (!(mi.dwFlags & MONITORINFOF_PRIMARY))
{
return false;
}
return true;
}
bool test_time_zone() bool test_time_zone()
{ {
DYNAMIC_TIME_ZONE_INFORMATION current_dtzi = {}; DYNAMIC_TIME_ZONE_INFORMATION current_dtzi = {};
@@ -908,6 +943,7 @@ int main(const int argc, const char* argv[])
RUN_TEST(test_working_directory, "Working Directory") RUN_TEST(test_working_directory, "Working Directory")
RUN_TEST(test_registry, "Registry") RUN_TEST(test_registry, "Registry")
RUN_TEST(test_system_info, "System Info") RUN_TEST(test_system_info, "System Info")
RUN_TEST(test_monitor_info, "Monitor Info")
RUN_TEST(test_time_zone, "Time Zone") RUN_TEST(test_time_zone, "Time Zone")
RUN_TEST(test_threads, "Threads") RUN_TEST(test_threads, "Threads")
RUN_TEST(test_threads_winapi, "Threads WinAPI") RUN_TEST(test_threads_winapi, "Threads WinAPI")

View File

@@ -21,6 +21,7 @@ struct handle_types
token, token,
window, window,
timer, timer,
monitor,
}; };
}; };

View File

@@ -1,12 +1,13 @@
#include "std_include.hpp" #include "std_include.hpp"
#include "minidump_loader.hpp" #include "minidump_loader.hpp"
#include "windows_emulator.hpp" #include "windows_emulator.hpp"
#include "windows_objects.hpp" #include "windows_objects.hpp"
#include "emulator_thread.hpp" #include "emulator_thread.hpp"
#include "common/platform/unicode.hpp"
#include "common/platform/kernel_mapped.hpp"
#include "memory_utils.hpp" #include "memory_utils.hpp"
#include <platform/platform.hpp>
#include <minidump/minidump.hpp> #include <minidump/minidump.hpp>
namespace minidump_loader namespace minidump_loader

View File

@@ -15,6 +15,15 @@ namespace
return STATUS_NOT_SUPPORTED; return STATUS_NOT_SUPPORTED;
} }
}; };
struct noop_port : port
{
NTSTATUS handle_request(windows_emulator& /*win_emu*/, const lpc_request_context& c) override
{
c.recv_buffer_length = 0;
return STATUS_SUCCESS;
}
};
} }
std::unique_ptr<port> create_port(const std::u16string_view port) std::unique_ptr<port> create_port(const std::u16string_view port)
@@ -29,6 +38,11 @@ std::unique_ptr<port> create_port(const std::u16string_view port)
return create_dns_resolver(); return create_dns_resolver();
} }
if (port == u"\\WindowsErrorReportingServicePort")
{
return std::make_unique<noop_port>();
}
return std::make_unique<dummy_port>(); return std::make_unique<dummy_port>();
} }

View File

@@ -9,7 +9,13 @@ namespace
{ {
NTSTATUS handle_request(windows_emulator& win_emu, const lpc_request_context& c) override NTSTATUS handle_request(windows_emulator& win_emu, const lpc_request_context& c) override
{ {
// TODO: Fix this. This is broken and wrong. uint32_t server_dll_index{};
win_emu.memory.read_memory(c.recv_buffer + 0x18, &server_dll_index, sizeof(server_dll_index));
if (server_dll_index != 3)
{
return STATUS_NOT_SUPPORTED;
}
try try
{ {
@@ -17,8 +23,13 @@ namespace
const auto dest = data.read(); const auto dest = data.read();
const auto base = dest.Base; const auto base = dest.Base;
const auto value = base + 0x10; const emulator_object<USER_SHAREDINFO> shared_obj{win_emu.emu(), base + 8};
win_emu.emu().write_memory(base + 8, &value, sizeof(value)); shared_obj.access([&](USER_SHAREDINFO& shared) {
shared.psi = win_emu.process.user_handles.get_server_info().value();
shared.aheList = win_emu.process.user_handles.get_handle_table().value();
shared.HeEntrySize = sizeof(USER_HANDLEENTRY);
shared.pDispInfo = win_emu.process.user_handles.get_display_info().value();
});
} }
catch (...) catch (...)
{ {

View File

@@ -176,12 +176,40 @@ namespace
return env_map; return env_map;
} }
uint32_t read_windows_build(registry_manager& registry)
{
const auto key = registry.get_key({R"(\Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion)"});
if (!key)
{
return 0;
}
for (size_t i = 0; const auto value = registry.get_value(*key, i); ++i)
{
if (value->type != REG_SZ)
{
continue;
}
if (value->name == "CurrentBuildNumber" || value->name == "CurrentBuild")
{
const auto* s = reinterpret_cast<const char16_t*>(value->data.data());
return static_cast<uint32_t>(std::strtoul(u16_to_u8(s).c_str(), nullptr, 10));
}
}
return 0;
}
} }
void process_context::setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry, void process_context::setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry,
const application_settings& app_settings, const mapped_module& executable, const mapped_module& ntdll, const application_settings& app_settings, const mapped_module& executable, const mapped_module& ntdll,
const apiset::container& apiset_container, const mapped_module* ntdll32) const apiset::container& apiset_container, const mapped_module* ntdll32)
{ {
this->windows_build_number = read_windows_build(registry);
setup_gdt(emu, memory); setup_gdt(emu, memory);
this->kusd.setup(); this->kusd.setup();
@@ -391,6 +419,34 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, regist
this->instrumentation_callback = 0; this->instrumentation_callback = 0;
this->default_register_set = emu.save_registers(); this->default_register_set = emu.save_registers();
this->user_handles.setup();
auto [h, monitor_obj] = this->user_handles.allocate_object<USER_MONITOR>(handle_types::monitor);
this->default_monitor_handle = h;
monitor_obj.access([&](USER_MONITOR& monitor) {
monitor.hmon = h.bits;
monitor.rcMonitor = {.left = 0, .top = 0, .right = 1920, .bottom = 1080};
monitor.rcWork = monitor.rcMonitor;
if (this->is_older_windows_build())
{
monitor.b20.monitorDpi = 96;
monitor.b20.nativeDpi = monitor.b20.monitorDpi;
monitor.b20.cachedDpi = monitor.b20.monitorDpi;
monitor.b20.rcMonitorDpiAware = monitor.rcMonitor;
}
else
{
monitor.b26.monitorDpi = 96;
monitor.b26.nativeDpi = monitor.b26.monitorDpi;
}
});
const auto user_display_info = this->user_handles.get_display_info();
user_display_info.access([&](USER_DISPINFO& display_info) {
display_info.dwMonitorCount = 1;
display_info.pPrimaryMonitor = monitor_obj.value();
});
} }
void process_context::serialize(utils::buffer_serializer& buffer) const void process_context::serialize(utils::buffer_serializer& buffer) const
@@ -408,6 +464,7 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
buffer.write(this->kusd); buffer.write(this->kusd);
buffer.write(this->is_wow64_process); buffer.write(this->is_wow64_process);
buffer.write(this->windows_build_number);
buffer.write(this->ntdll_image_base); buffer.write(this->ntdll_image_base);
buffer.write(this->ldr_initialize_thunk); buffer.write(this->ldr_initialize_thunk);
buffer.write(this->rtl_user_thread_start); buffer.write(this->rtl_user_thread_start);
@@ -416,6 +473,8 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
buffer.write(this->ki_user_exception_dispatcher); buffer.write(this->ki_user_exception_dispatcher);
buffer.write(this->instrumentation_callback); buffer.write(this->instrumentation_callback);
buffer.write(this->user_handles);
buffer.write(this->default_monitor_handle);
buffer.write(this->events); buffer.write(this->events);
buffer.write(this->files); buffer.write(this->files);
buffer.write(this->sections); buffer.write(this->sections);
@@ -454,6 +513,7 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
buffer.read(this->kusd); buffer.read(this->kusd);
buffer.read(this->is_wow64_process); buffer.read(this->is_wow64_process);
buffer.read(this->windows_build_number);
buffer.read(this->ntdll_image_base); buffer.read(this->ntdll_image_base);
buffer.read(this->ldr_initialize_thunk); buffer.read(this->ldr_initialize_thunk);
buffer.read(this->rtl_user_thread_start); buffer.read(this->rtl_user_thread_start);
@@ -462,6 +522,8 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
buffer.read(this->ki_user_exception_dispatcher); buffer.read(this->ki_user_exception_dispatcher);
buffer.read(this->instrumentation_callback); buffer.read(this->instrumentation_callback);
buffer.read(this->user_handles);
buffer.read(this->default_monitor_handle);
buffer.read(this->events); buffer.read(this->events);
buffer.read(this->files); buffer.read(this->files);
buffer.read(this->sections); buffer.read(this->sections);

View File

@@ -14,6 +14,7 @@
#include "windows_objects.hpp" #include "windows_objects.hpp"
#include "emulator_thread.hpp" #include "emulator_thread.hpp"
#include "port.hpp" #include "port.hpp"
#include "user_handle_table.hpp"
#include "apiset/apiset.hpp" #include "apiset/apiset.hpp"
@@ -66,7 +67,8 @@ struct process_context
base_allocator(emu), base_allocator(emu),
peb64(emu), peb64(emu),
process_params64(emu), process_params64(emu),
kusd(memory, clock) kusd(memory, clock),
user_handles(memory)
{ {
} }
@@ -86,10 +88,17 @@ struct process_context
void serialize(utils::buffer_serializer& buffer) const; void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer); void deserialize(utils::buffer_deserializer& buffer);
generic_handle_store* get_handle_store(handle handle);
// WOW64 support flag - set during process setup based on executable architecture // WOW64 support flag - set during process setup based on executable architecture
bool is_wow64_process{false}; bool is_wow64_process{false};
generic_handle_store* get_handle_store(handle handle); uint32_t windows_build_number{0};
bool is_older_windows_build() const
{
return windows_build_number < 26040;
}
callbacks* callbacks_{}; callbacks* callbacks_{};
@@ -118,6 +127,8 @@ struct process_context
std::optional<emulator_object<RTL_USER_PROCESS_PARAMETERS32>> process_params32; std::optional<emulator_object<RTL_USER_PROCESS_PARAMETERS32>> process_params32;
std::optional<uint64_t> rtl_user_thread_start32{}; std::optional<uint64_t> rtl_user_thread_start32{};
user_handle_table user_handles;
handle default_monitor_handle{};
handle_store<handle_types::event, event> events{}; handle_store<handle_types::event, event> events{};
handle_store<handle_types::file, file> files{}; handle_store<handle_types::file, file> files{};
handle_store<handle_types::section, section> sections{}; handle_store<handle_types::section, section> sections{};
@@ -125,7 +136,7 @@ struct process_context
handle_store<handle_types::semaphore, semaphore> semaphores{}; handle_store<handle_types::semaphore, semaphore> semaphores{};
handle_store<handle_types::port, port_container> ports{}; handle_store<handle_types::port, port_container> ports{};
handle_store<handle_types::mutant, mutant> mutants{}; handle_store<handle_types::mutant, mutant> mutants{};
handle_store<handle_types::window, window> windows{}; user_handle_store<handle_types::window, window> windows{user_handles};
handle_store<handle_types::timer, timer> timers{}; handle_store<handle_types::timer, timer> timers{};
handle_store<handle_types::registry, registry_key, 2> registry_keys{}; handle_store<handle_types::registry, registry_key, 2> registry_keys{};
std::map<uint16_t, atom_entry> atoms{}; std::map<uint16_t, atom_entry> atoms{};

View File

@@ -155,8 +155,11 @@ namespace syscalls
NTSTATUS handle_NtQueryObject(const syscall_context& c, handle handle, OBJECT_INFORMATION_CLASS object_information_class, NTSTATUS handle_NtQueryObject(const syscall_context& c, handle handle, OBJECT_INFORMATION_CLASS object_information_class,
emulator_pointer object_information, ULONG object_information_length, emulator_pointer object_information, ULONG object_information_length,
emulator_object<ULONG> return_length); emulator_object<ULONG> return_length);
NTSTATUS handle_NtCompareObjects(const syscall_context& c, handle first, handle second);
NTSTATUS handle_NtWaitForMultipleObjects(const syscall_context& c, ULONG count, emulator_object<handle> handles, WAIT_TYPE wait_type, NTSTATUS handle_NtWaitForMultipleObjects(const syscall_context& c, ULONG count, emulator_object<handle> handles, WAIT_TYPE wait_type,
BOOLEAN alertable, emulator_object<LARGE_INTEGER> timeout); BOOLEAN alertable, emulator_object<LARGE_INTEGER> timeout);
NTSTATUS handle_NtWaitForMultipleObjects32(const syscall_context& c, ULONG count, emulator_object<uint32_t> handles,
WAIT_TYPE wait_type, BOOLEAN alertable, emulator_object<LARGE_INTEGER> timeout);
NTSTATUS handle_NtWaitForSingleObject(const syscall_context& c, handle h, BOOLEAN alertable, emulator_object<LARGE_INTEGER> timeout); NTSTATUS handle_NtWaitForSingleObject(const syscall_context& c, handle h, BOOLEAN alertable, emulator_object<LARGE_INTEGER> timeout);
NTSTATUS handle_NtSetInformationObject(); NTSTATUS handle_NtSetInformationObject();
NTSTATUS handle_NtQuerySecurityObject(const syscall_context& c, handle /*h*/, SECURITY_INFORMATION /*security_information*/, NTSTATUS handle_NtQuerySecurityObject(const syscall_context& c, handle /*h*/, SECURITY_INFORMATION /*security_information*/,
@@ -230,6 +233,9 @@ namespace syscalls
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> value_name, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> value_name,
KEY_VALUE_INFORMATION_CLASS key_value_information_class, uint64_t key_value_information, ULONG length, KEY_VALUE_INFORMATION_CLASS key_value_information_class, uint64_t key_value_information, ULONG length,
emulator_object<ULONG> result_length); emulator_object<ULONG> result_length);
NTSTATUS handle_NtQueryMultipleValueKey(const syscall_context& c, handle key_handle, emulator_object<KEY_VALUE_ENTRY> value_entries,
ULONG entry_count, uint64_t value_buffer, emulator_object<ULONG> buffer_length,
emulator_object<ULONG> required_buffer_length);
NTSTATUS handle_NtCreateKey(const syscall_context& c, emulator_object<handle> key_handle, ACCESS_MASK desired_access, NTSTATUS handle_NtCreateKey(const syscall_context& c, emulator_object<handle> key_handle, ACCESS_MASK desired_access,
emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes, ULONG /*title_index*/, emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes, ULONG /*title_index*/,
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> /*class*/, ULONG /*create_options*/, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> /*class*/, ULONG /*create_options*/,
@@ -466,7 +472,7 @@ namespace syscalls
return STATUS_INVALID_HANDLE; return STATUS_INVALID_HANDLE;
} }
if (auto* e = c.win_emu.process.events.get(event)) if (auto* e = c.proc.events.get(event))
{ {
e->signaled = false; e->signaled = false;
} }
@@ -834,7 +840,7 @@ namespace syscalls
const hwnd /*parent*/, const hmenu /*menu*/, const hinstance /*instance*/, const pointer /*l_param*/, const hwnd /*parent*/, const hmenu /*menu*/, const hinstance /*instance*/, const pointer /*l_param*/,
const DWORD /*flags*/, const pointer /*acbi_buffer*/) const DWORD /*flags*/, const pointer /*acbi_buffer*/)
{ {
window win{}; auto [handle, win] = c.proc.windows.create(c.win_emu.memory);
win.x = x; win.x = x;
win.y = y; win.y = y;
win.width = width; win.width = width;
@@ -843,7 +849,7 @@ namespace syscalls
win.class_name = read_large_string(class_name); win.class_name = read_large_string(class_name);
win.name = read_large_string(window_name); win.name = read_large_string(window_name);
return c.proc.windows.store(std::move(win)).bits; return handle.bits;
} }
BOOL handle_NtUserDestroyWindow(const syscall_context& c, const hwnd window) BOOL handle_NtUserDestroyWindow(const syscall_context& c, const hwnd window)
@@ -1030,6 +1036,19 @@ namespace syscalls
{ {
return STATUS_NOT_SUPPORTED; return STATUS_NOT_SUPPORTED;
} }
BOOL handle_NtUserGetHDevName(const syscall_context& c, handle hdev, emulator_pointer device_name)
{
if (hdev != c.proc.default_monitor_handle)
{
return FALSE;
}
const std::u16string name = u"\\\\.\\DISPLAY1";
c.emu.write_memory(device_name, name.c_str(), (name.size() + 1) * sizeof(char16_t));
return TRUE;
}
} }
void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& handler_mapping) void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& handler_mapping)
@@ -1143,6 +1162,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtSetInformationFile); add_handler(NtSetInformationFile);
add_handler(NtUserRegisterWindowMessage); add_handler(NtUserRegisterWindowMessage);
add_handler(NtQueryValueKey); add_handler(NtQueryValueKey);
add_handler(NtQueryMultipleValueKey);
add_handler(NtQueryKey); add_handler(NtQueryKey);
add_handler(NtGetNlsSectionPtr); add_handler(NtGetNlsSectionPtr);
add_handler(NtAccessCheck); add_handler(NtAccessCheck);
@@ -1151,8 +1171,10 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtGetCurrentProcessorNumberEx); add_handler(NtGetCurrentProcessorNumberEx);
add_handler(NtGetCurrentProcessorNumber); add_handler(NtGetCurrentProcessorNumber);
add_handler(NtQueryObject); add_handler(NtQueryObject);
add_handler(NtCompareObjects);
add_handler(NtQueryAttributesFile); add_handler(NtQueryAttributesFile);
add_handler(NtWaitForMultipleObjects); add_handler(NtWaitForMultipleObjects);
add_handler(NtWaitForMultipleObjects32);
add_handler(NtCreateMutant); add_handler(NtCreateMutant);
add_handler(NtReleaseMutant); add_handler(NtReleaseMutant);
add_handler(NtDuplicateToken); add_handler(NtDuplicateToken);
@@ -1243,6 +1265,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtSetInformationDebugObject); add_handler(NtSetInformationDebugObject);
add_handler(NtRemoveProcessDebug); add_handler(NtRemoveProcessDebug);
add_handler(NtNotifyChangeDirectoryFileEx); add_handler(NtNotifyChangeDirectoryFileEx);
add_handler(NtUserGetHDevName);
#undef add_handler #undef add_handler
} }

View File

@@ -267,6 +267,11 @@ namespace syscalls
|| h.value.type == handle_types::event; || h.value.type == handle_types::event;
} }
NTSTATUS handle_NtCompareObjects(const syscall_context&, const handle first, const handle second)
{
return (first == second) ? STATUS_SUCCESS : STATUS_NOT_SAME_OBJECT;
}
NTSTATUS handle_NtWaitForMultipleObjects(const syscall_context& c, const ULONG count, const emulator_object<handle> handles, NTSTATUS handle_NtWaitForMultipleObjects(const syscall_context& c, const ULONG count, const emulator_object<handle> handles,
const WAIT_TYPE wait_type, const BOOLEAN alertable, const WAIT_TYPE wait_type, const BOOLEAN alertable,
const emulator_object<LARGE_INTEGER> timeout) const emulator_object<LARGE_INTEGER> timeout)
@@ -304,6 +309,44 @@ namespace syscalls
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
NTSTATUS handle_NtWaitForMultipleObjects32(const syscall_context& c, const ULONG count, const emulator_object<uint32_t> handles,
const WAIT_TYPE wait_type, const BOOLEAN alertable,
const emulator_object<LARGE_INTEGER> timeout)
{
if (wait_type != WaitAny && wait_type != WaitAll)
{
c.win_emu.log.error("Wait type not supported!\n");
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 raw_handle = handles.read(i);
const auto h = make_handle(static_cast<uint64_t>(raw_handle));
if (!is_awaitable_object_type(h))
{
c.win_emu.log.warn("Unsupported handle type for NtWaitForMultipleObjects32: %d!\n", h.value.type);
return STATUS_INVALID_HANDLE;
}
t.await_objects.push_back(h);
}
if (timeout.value() && !t.await_time.has_value())
{
t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), timeout.read());
}
c.win_emu.yield_thread(alertable);
return STATUS_SUCCESS;
}
NTSTATUS handle_NtWaitForSingleObject(const syscall_context& c, const handle h, const BOOLEAN alertable, NTSTATUS handle_NtWaitForSingleObject(const syscall_context& c, const handle h, const BOOLEAN alertable,
const emulator_object<LARGE_INTEGER> timeout) const emulator_object<LARGE_INTEGER> timeout)
{ {

View File

@@ -261,6 +261,86 @@ namespace syscalls
return STATUS_NOT_SUPPORTED; return STATUS_NOT_SUPPORTED;
} }
NTSTATUS handle_NtQueryMultipleValueKey(const syscall_context& c, const handle key_handle,
const emulator_object<KEY_VALUE_ENTRY> value_entries, const ULONG entry_count,
const uint64_t value_buffer, const emulator_object<ULONG> buffer_length,
const emulator_object<ULONG> required_buffer_length)
{
if (entry_count > 0x10000)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
const auto* key = c.proc.registry_keys.get(key_handle);
if (!key)
{
return STATUS_INVALID_HANDLE;
}
NTSTATUS status = STATUS_SUCCESS;
auto remaining_length = buffer_length.read();
ULONG required_length = 0;
ULONG written_bytes = 0;
for (ULONG i = 0; i < entry_count; i++)
{
auto entry = value_entries.read(i);
if (!entry.ValueName)
{
status = STATUS_INVALID_PARAMETER;
break;
}
const auto query_name = read_unicode_string(c.emu, entry.ValueName);
if (c.win_emu.callbacks.on_generic_access)
{
// TODO: Find a better way to log this
c.win_emu.callbacks.on_generic_access("Querying multiple value key ", query_name + u" (" + key->to_string() + u")");
}
const auto value = c.win_emu.registry.get_value(*key, u16_to_u8(query_name));
if (!value)
{
status = STATUS_OBJECT_NAME_NOT_FOUND;
break;
}
const auto data_length = static_cast<ULONG>(value->data.size());
if (status == STATUS_SUCCESS)
{
if (remaining_length >= data_length)
{
entry.DataOffset = written_bytes;
entry.DataLength = data_length;
entry.Type = value->type;
c.emu.write_memory(value_buffer + entry.DataOffset, value->data.data(), entry.DataLength);
value_entries.write(entry, i);
remaining_length -= data_length;
written_bytes += data_length;
}
else
{
status = STATUS_BUFFER_OVERFLOW;
}
}
required_length += data_length;
}
buffer_length.write(written_bytes);
if (required_buffer_length.value())
{
required_buffer_length.write(required_length);
}
return status;
}
NTSTATUS handle_NtCreateKey(const syscall_context& c, const emulator_object<handle> key_handle, const ACCESS_MASK desired_access, NTSTATUS handle_NtCreateKey(const syscall_context& c, const emulator_object<handle> key_handle, const ACCESS_MASK desired_access,
const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes, const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes,
const ULONG /*title_index*/, const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> /*class*/, const ULONG /*title_index*/, const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> /*class*/,

View File

@@ -0,0 +1,394 @@
#pragma once
#include "emulator_utils.hpp"
#include "handles.hpp"
class user_handle_table
{
public:
static constexpr uint32_t MAX_HANDLES = 0xFFFF;
user_handle_table(memory_manager& memory)
: memory_(&memory)
{
}
void setup()
{
used_indices_.resize(MAX_HANDLES, false);
const auto server_info_size = static_cast<size_t>(page_align_up(sizeof(USER_SERVERINFO)));
server_info_addr_ = memory_->allocate_memory(server_info_size, memory_permission::read);
const auto display_info_size = static_cast<size_t>(page_align_up(sizeof(USER_DISPINFO)));
display_info_addr_ = memory_->allocate_memory(display_info_size, memory_permission::read);
const emulator_object<USER_SERVERINFO> srv_obj(*memory_, server_info_addr_);
srv_obj.access([&](USER_SERVERINFO& srv) {
srv.cHandleEntries = MAX_HANDLES - 1; //
});
const auto handle_table_size = static_cast<size_t>(page_align_up(sizeof(USER_HANDLEENTRY) * MAX_HANDLES));
handle_table_addr_ = memory_->allocate_memory(handle_table_size, memory_permission::read);
}
emulator_object<USER_SHAREDINFO> get_server_info() const
{
return {*memory_, server_info_addr_};
}
emulator_object<USER_HANDLEENTRY> get_handle_table() const
{
return {*memory_, handle_table_addr_};
}
emulator_object<USER_DISPINFO> get_display_info() const
{
return {*memory_, display_info_addr_};
}
template <typename T>
std::pair<handle, emulator_object<T>> allocate_object(handle_types::type type)
{
const auto index = find_free_index();
const auto alloc_size = static_cast<size_t>(page_align_up(sizeof(T)));
const auto alloc_ptr = memory_->allocate_memory(alloc_size, memory_permission::read);
const emulator_object<T> alloc_obj(*memory_, alloc_ptr);
const emulator_object<USER_HANDLEENTRY> handle_table_obj(*memory_, handle_table_addr_);
handle_table_obj.access(
[&](USER_HANDLEENTRY& entry) {
entry.pHead = alloc_ptr;
entry.bType = get_native_type(type);
entry.wUniq = static_cast<uint16_t>(type << 7);
},
index);
used_indices_[index] = true;
return {make_handle(index, type, false), alloc_obj};
}
void free_index(uint32_t index)
{
if (index >= used_indices_.size() || !used_indices_[index])
{
return;
}
used_indices_[index] = false;
const emulator_object<USER_HANDLEENTRY> handle_table_obj(*memory_, handle_table_addr_);
handle_table_obj.access(
[&](USER_HANDLEENTRY& entry) {
memory_->release_memory(entry.pHead, 0);
entry = {};
},
index);
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(server_info_addr_);
buffer.write(handle_table_addr_);
buffer.write(display_info_addr_);
buffer.write_vector(used_indices_);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(server_info_addr_);
buffer.read(handle_table_addr_);
buffer.read(display_info_addr_);
buffer.read_vector(used_indices_);
}
private:
uint32_t find_free_index() const
{
for (uint32_t i = 1; i < used_indices_.size(); ++i)
{
if (!used_indices_[i])
{
return i;
}
}
throw std::runtime_error("No more user handles available");
}
static uint8_t get_native_type(handle_types::type type)
{
switch (type)
{
case handle_types::type::window:
return TYPE_WINDOW;
case handle_types::type::monitor:
return TYPE_MONITOR;
default:
throw std::runtime_error("Unhandled handle type!");
}
}
uint64_t server_info_addr_{};
uint64_t handle_table_addr_{};
uint64_t display_info_addr_{};
std::vector<bool> used_indices_{};
memory_manager* memory_{};
};
template <handle_types::type Type, typename T>
requires(utils::Serializable<T> && std::is_base_of_v<ref_counted_object, T>)
class user_handle_store : public generic_handle_store
{
public:
using index_type = uint32_t;
using value_map = std::map<index_type, T>;
explicit user_handle_store(user_handle_table& table)
: table_(&table)
{
}
std::pair<handle, T&> create(memory_interface& memory)
{
if (this->block_mutation_)
{
throw std::runtime_error("Mutation of user object store is blocked!");
}
auto [h, guest_obj] = table_->allocate_object<typename T::guest_type>(Type);
T new_obj(memory);
new_obj.guest = std::move(guest_obj);
const auto index = static_cast<uint32_t>(h.value.id);
const auto it = this->store_.emplace(index, std::move(new_obj)).first;
return {h, it->second};
}
bool block_mutation(bool blocked)
{
std::swap(this->block_mutation_, blocked);
return blocked;
}
handle make_handle(const index_type index) const
{
handle h{};
h.bits = 0;
h.value.is_pseudo = false;
h.value.type = Type;
h.value.id = index;
return h;
}
T* get_by_index(const uint32_t index)
{
const auto it = this->store_.find(index);
if (it == this->store_.end())
{
return nullptr;
}
return &it->second;
}
T* get(const handle_value h)
{
if (h.type != Type || h.is_pseudo)
{
return nullptr;
}
return this->get_by_index(static_cast<uint32_t>(h.id));
}
T* get(const handle h)
{
return this->get(h.value);
}
T* get(const uint64_t h)
{
handle hh{};
hh.bits = h;
return this->get(hh);
}
size_t size() const
{
return this->store_.size();
}
std::optional<handle> duplicate(const handle h) override
{
auto* entry = this->get(h);
if (!entry)
{
return std::nullopt;
}
++entry->ref_count;
return h;
}
std::pair<typename value_map::iterator, bool> erase(const typename value_map::iterator& entry)
{
if (this->block_mutation_)
{
throw std::runtime_error("Mutation of handle store is blocked!");
}
if (entry == this->store_.end())
{
return {entry, false};
}
if constexpr (handle_detail::has_deleter_function<T>())
{
if (!T::deleter(entry->second))
{
return {entry, true};
}
}
auto new_iter = this->store_.erase(entry);
return {new_iter, true};
}
bool erase(const handle_value h)
{
if (this->block_mutation_)
{
throw std::runtime_error("Mutation of user object store is blocked!");
}
if (h.type != Type || h.is_pseudo)
{
return false;
}
const auto index = static_cast<uint32_t>(h.id);
const auto entry = this->store_.find(index);
if (entry == this->store_.end())
{
return false;
}
if constexpr (handle_detail::has_deleter_function<T>())
{
if (!T::deleter(entry->second))
{
return false;
}
}
table_->free_index(index);
this->store_.erase(entry);
return true;
}
bool erase(const handle h) override
{
return this->erase(h.value);
}
bool erase(const uint64_t h)
{
handle hh{};
hh.bits = h;
return this->erase(hh);
}
bool erase(const T& value)
{
const auto entry = this->find(value);
if (entry == this->store_.end())
{
return false;
}
return this->erase(make_handle(entry->first));
}
typename value_map::iterator find(const T& value)
{
auto i = this->store_.begin();
for (; i != this->store_.end(); ++i)
{
if (&i->second == &value)
{
break;
}
}
return i;
}
typename value_map::const_iterator find(const T& value) const
{
auto i = this->store_.begin();
for (; i != this->store_.end(); ++i)
{
if (&i->second == &value)
{
break;
}
}
return i;
}
handle find_handle(const T& value) const
{
const auto entry = this->find(value);
if (entry == this->end())
{
return {};
}
return this->make_handle(entry->first);
}
handle find_handle(const T* value) const
{
if (!value)
{
return {};
}
return this->find_handle(*value);
}
typename value_map::iterator begin()
{
return this->store_.begin();
}
typename value_map::const_iterator begin() const
{
return this->store_.begin();
}
typename value_map::iterator end()
{
return this->store_.end();
}
typename value_map::const_iterator end() const
{
return this->store_.end();
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->block_mutation_);
buffer.write_map(this->store_);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->block_mutation_);
buffer.read_map(this->store_);
}
private:
user_handle_table* table_;
bool block_mutation_{false};
value_map store_{};
};

View File

@@ -54,19 +54,47 @@ struct event : ref_counted_object
} }
}; };
struct window : ref_counted_object template <typename GuestType>
struct user_object : ref_counted_object
{
using guest_type = GuestType;
emulator_object<GuestType> guest;
user_object(memory_interface& memory)
: guest(memory)
{
}
void serialize_object(utils::buffer_serializer& buffer) const override
{
buffer.write(this->guest);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
{
buffer.read(this->guest);
}
};
struct window : user_object<USER_WINDOW>
{ {
uint32_t thread_id{}; uint32_t thread_id{};
std::u16string name{}; std::u16string name{};
std::u16string class_name{}; std::u16string class_name{};
int32_t width; int32_t width{};
int32_t height; int32_t height{};
int32_t x; int32_t x{};
int32_t y; int32_t y{};
std::unordered_map<std::u16string, uint64_t> props; std::unordered_map<std::u16string, uint64_t> props;
window(memory_interface& memory)
: user_object(memory)
{
}
void serialize_object(utils::buffer_serializer& buffer) const override void serialize_object(utils::buffer_serializer& buffer) const override
{ {
user_object::serialize_object(buffer);
buffer.write(this->thread_id); buffer.write(this->thread_id);
buffer.write(this->name); buffer.write(this->name);
buffer.write(this->class_name); buffer.write(this->class_name);
@@ -79,6 +107,7 @@ struct window : ref_counted_object
void deserialize_object(utils::buffer_deserializer& buffer) override void deserialize_object(utils::buffer_deserializer& buffer) override
{ {
user_object::deserialize_object(buffer);
buffer.read(this->thread_id); buffer.read(this->thread_id);
buffer.read(this->name); buffer.read(this->name);
buffer.read(this->class_name); buffer.read(this->class_name);
@@ -186,7 +215,6 @@ struct file : ref_counted_object
utils::file_handle handle{}; utils::file_handle handle{};
std::u16string name{}; std::u16string name{};
std::optional<file_enumeration_state> enumeration_state{}; std::optional<file_enumeration_state> enumeration_state{};
std::optional<std::u16string> deferred_rename;
bool is_file() const bool is_file() const
{ {