Compare commits

...

8 Commits

Author SHA1 Message Date
Maurice Heumann
d70ab2607e Add support for user callbacks (#691)
This PR is my attempt to add support for user callbacks in the emulator.
User callbacks allow the emulator to call guest callbacks from syscalls,
and when the callback finishes running, control returns to the syscall
through the completion method. I've also added a test and implemented
the NtUserEnumDisplayMonitors syscall.

One thing to note is that this implementation isn't faithful to how the
Windows kernel does it, since the kernel uses the KernelCallbackTable
and the `ntdll!KiUserCallbackDispatch` method, and this implementation
currently just calls the callback directly.
2026-01-10 13:59:24 +01:00
Maurice Heumann
f7eb8a2b2d Fix memory_manager serialization (#699)
This PR fixes `memory_manager` serialization. In particular, it fixes
running `WOW64` programs from snapshots.
2026-01-10 13:58:39 +01:00
ssvine
c49226d7c1 Fix memory_manager serialization 2026-01-10 14:13:09 +03:00
Maurice Heumann
83a6b65add Fix user_handle_table memory handling (#696)
This PR fixes `user_handle_table` for WOW64. Otherwise we get AV during
32-bit `user32.dll` initialization.
2026-01-09 14:45:25 +01:00
ssvine
dd31b6344f Fix user_handle_table memory handling 2026-01-09 10:51:09 +03:00
Igor Pissolati
d33ef2d93f Avoid memory hook being triggered twice for the same callback 2026-01-08 16:59:09 -03:00
Igor Pissolati
d5d73f42c9 Improve clarity in dispatch_user_callback 2026-01-07 15:01:02 -03:00
Igor Pissolati
9fdc2a4ce6 Add support for user callbacks 2026-01-06 10:14:02 -03:00
15 changed files with 441 additions and 24 deletions

View File

@@ -167,8 +167,15 @@ namespace
const auto var_ptr = get_function_argument(win_emu.emu(), index);
if (var_ptr && !is_int_resource(var_ptr))
{
const auto str = read_string<CharType>(win_emu.memory, var_ptr);
print_string(win_emu.log, str);
try
{
const auto str = read_string<CharType>(win_emu.memory, var_ptr);
print_string(win_emu.log, str);
}
catch (...)
{
print_string(win_emu.log, "[failed to read]");
}
}
}

View File

@@ -59,6 +59,12 @@ class typed_emulator : public emulator
return result;
}
void write_stack(const size_t index, const pointer_type& value)
{
const auto sp = this->read_stack_pointer();
this->write_memory(sp + (index * pointer_size), &value, sizeof(value));
}
void push_stack(const pointer_type& value)
{
const auto sp = this->read_stack_pointer() - pointer_size;

View File

@@ -509,6 +509,26 @@ namespace
return true;
}
bool validate_primary_monitor(MONITORINFOEXA& mi)
{
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_monitor_info()
{
const POINT pt = {0, 0};
@@ -526,22 +546,35 @@ namespace
return false;
}
if (std::string_view(mi.szDevice) != R"(\\.\DISPLAY1)")
return validate_primary_monitor(mi);
}
BOOL CALLBACK monitor_enum_proc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
{
auto* valid = reinterpret_cast<bool*>(dwData);
MONITORINFOEXA mi;
mi.cbSize = sizeof(mi);
if (!GetMonitorInfoA(hMonitor, &mi))
{
return FALSE;
}
*valid = validate_primary_monitor(mi);
return *valid ? TRUE : FALSE;
}
bool test_user_callback()
{
bool valid = false;
if (!EnumDisplayMonitors(nullptr, nullptr, monitor_enum_proc, reinterpret_cast<LPARAM>(&valid)))
{
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;
return valid;
}
bool test_time_zone()
@@ -957,9 +990,9 @@ int main(const int argc, const char* argv[])
RUN_TEST(test_interrupts, "Interrupts")
}
RUN_TEST(test_tls, "TLS")
RUN_TEST(test_socket, "Socket")
RUN_TEST(test_apc, "APC")
RUN_TEST(test_user_callback, "User Callback")
return valid ? 0 : 1;
}

View File

@@ -35,6 +35,48 @@ struct pending_apc
}
};
enum class callback_id : uint32_t
{
Invalid = 0,
NtUserEnumDisplayMonitors,
};
struct callback_frame
{
callback_id handler_id;
uint64_t rip;
uint64_t rsp;
uint64_t r10;
uint64_t rcx;
uint64_t rdx;
uint64_t r8;
uint64_t r9;
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->handler_id);
buffer.write(this->rip);
buffer.write(this->rsp);
buffer.write(this->r10);
buffer.write(this->rcx);
buffer.write(this->rdx);
buffer.write(this->r8);
buffer.write(this->r9);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->handler_id);
buffer.read(this->rip);
buffer.read(this->rsp);
buffer.read(this->r10);
buffer.read(this->rcx);
buffer.read(this->rdx);
buffer.read(this->r8);
buffer.read(this->r9);
}
};
class emulator_thread : public ref_counted_object
{
public:
@@ -105,6 +147,8 @@ class emulator_thread : public ref_counted_object
bool debugger_hide{false};
std::vector<callback_frame> callback_stack;
void mark_as_ready(NTSTATUS status);
bool is_await_time_over(utils::clock& clock) const
@@ -188,6 +232,8 @@ class emulator_thread : public ref_counted_object
buffer.write_vector(this->last_registers);
buffer.write(this->debugger_hide);
buffer.write_vector(this->callback_stack);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
@@ -236,6 +282,8 @@ class emulator_thread : public ref_counted_object
buffer.read_vector(this->last_registers);
buffer.read(this->debugger_hide);
buffer.read_vector(this->callback_stack);
}
void leak_memory()

View File

@@ -408,6 +408,8 @@ inline std::u16string read_unicode_string(emulator& emu, const uint64_t uc_strin
return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string});
}
/// Retrieves function arguments from registers or stack memory. This function assumes the stack pointer currently points to the
/// return address.
inline uint64_t get_function_argument(x86_64_emulator& emu, const size_t index, const bool is_syscall = false)
{
bool use_32bit_stack = false;
@@ -440,3 +442,50 @@ inline uint64_t get_function_argument(x86_64_emulator& emu, const size_t index,
return emu.read_stack(index + 1);
}
}
/// Sets function arguments in registers or stack memory. This function does not modify RSP and assumes the caller has already allocated
/// stack space and that RSP currently points to the return address.
inline void set_function_argument(x86_64_emulator& emu, const size_t index, const uint64_t value, const bool is_syscall = false)
{
bool use_32bit_stack = false;
if (!is_syscall)
{
const auto cs_selector = emu.reg<uint16_t>(x86_register::cs);
const auto bitness = segment_utils::get_segment_bitness(emu, cs_selector);
use_32bit_stack = bitness && *bitness == segment_utils::segment_bitness::bit32;
}
if (use_32bit_stack)
{
const auto esp = emu.reg<uint32_t>(x86_register::esp);
const auto address = static_cast<uint64_t>(esp) + static_cast<uint64_t>((index + 1) * sizeof(uint32_t));
emu.write_memory<uint32_t>(address, static_cast<uint32_t>(value));
return;
}
switch (index)
{
case 0:
emu.reg(is_syscall ? x86_register::r10 : x86_register::rcx, value);
break;
case 1:
emu.reg(x86_register::rdx, value);
break;
case 2:
emu.reg(x86_register::r8, value);
break;
case 3:
emu.reg(x86_register::r9, value);
break;
default:
emu.write_stack(index + 1, value);
break;
}
}
constexpr size_t aligned_stack_space(const size_t arg_count)
{
const size_t slots = (arg_count < 4) ? 4 : arg_count;
const size_t bytes = slots * sizeof(uint64_t);
return (bytes + 15) & ~15;
}

View File

@@ -122,6 +122,7 @@ memory_stats memory_manager::compute_memory_stats() const
void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const
{
buffer.write_atomic(this->layout_version_);
buffer.write(this->default_allocation_address_);
buffer.write_map(this->reserved_regions_);
if (is_snapshot)
@@ -157,6 +158,7 @@ void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer
}
buffer.read_atomic(this->layout_version_);
buffer.read(this->default_allocation_address_);
buffer.read_map(this->reserved_regions_);
if (is_snapshot)

View File

@@ -420,7 +420,7 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, regist
this->default_register_set = emu.save_registers();
this->user_handles.setup();
this->user_handles.setup(is_wow64_process);
auto [h, monitor_obj] = this->user_handles.allocate_object<USER_MONITOR>(handle_types::monitor);
this->default_monitor_handle = h;
@@ -449,6 +449,66 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, regist
});
}
void process_context::setup_callback_hook(windows_emulator& win_emu, memory_manager& memory)
{
uint64_t sentinel_addr = this->callback_sentinel_addr;
if (!sentinel_addr)
{
using sentinel_type = std::array<uint8_t, 2>;
constexpr sentinel_type sentinel_opcodes{0x90, 0xC3}; // NOP, RET
auto sentinel_obj = this->base_allocator.reserve_page_aligned<sentinel_type>();
sentinel_addr = sentinel_obj.value();
this->callback_sentinel_addr = sentinel_addr;
win_emu.emu().write_memory(sentinel_addr, sentinel_opcodes.data(), sentinel_opcodes.size());
const auto sentinel_aligned_length = page_align_up(sentinel_addr + sentinel_opcodes.size()) - sentinel_addr;
memory.protect_memory(sentinel_addr, static_cast<size_t>(sentinel_aligned_length), memory_permission::all);
}
auto& emu = win_emu.emu();
emu.hook_memory_execution(sentinel_addr, [&](uint64_t) {
auto* t = this->active_thread;
if (!t || t->callback_stack.empty())
{
return;
}
const auto frame = t->callback_stack.back();
t->callback_stack.pop_back();
const auto callbacks_before = t->callback_stack.size();
const uint64_t guest_result = emu.reg(x86_register::rax);
emu.reg(x86_register::rip, frame.rip);
emu.reg(x86_register::rsp, frame.rsp);
emu.reg(x86_register::r10, frame.r10);
emu.reg(x86_register::rcx, frame.rcx);
emu.reg(x86_register::rdx, frame.rdx);
emu.reg(x86_register::r8, frame.r8);
emu.reg(x86_register::r9, frame.r9);
win_emu.dispatcher.dispatch_completion(win_emu, frame.handler_id, guest_result);
uint64_t target_rip = emu.reg(x86_register::rip);
emu.reg(x86_register::rip, this->callback_sentinel_addr + 1);
const bool new_callback_dispatched = t->callback_stack.size() > callbacks_before;
if (!new_callback_dispatched)
{
// Move past the syscall instruction
target_rip += 2;
}
const uint64_t ret_stack_ptr = frame.rsp - sizeof(emulator_pointer);
emu.write_memory(ret_stack_ptr, &target_rip, sizeof(target_rip));
emu.reg(x86_register::rsp, ret_stack_ptr);
});
}
void process_context::serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->shared_section_address);
@@ -496,6 +556,8 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
buffer.write(this->threads);
buffer.write(this->threads.find_handle(this->active_thread).bits);
buffer.write(this->callback_sentinel_addr);
}
void process_context::deserialize(utils::buffer_deserializer& buffer)
@@ -551,6 +613,8 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
buffer.read(this->threads);
this->active_thread = this->threads.get(buffer.read<uint64_t>());
buffer.read(this->callback_sentinel_addr);
}
generic_handle_store* process_context::get_handle_store(const handle handle)

View File

@@ -76,6 +76,8 @@ struct process_context
const mapped_module& executable, const mapped_module& ntdll, const apiset::container& apiset_container,
const mapped_module* ntdll32 = nullptr);
void setup_callback_hook(windows_emulator& win_emu, memory_manager& memory);
handle create_thread(memory_manager& memory, uint64_t start_address, uint64_t argument, uint64_t stack_size, uint32_t create_flags,
bool initial_thread = false);
@@ -147,6 +149,8 @@ struct process_context
handle_store<handle_types::thread, emulator_thread> threads{};
emulator_thread* active_thread{nullptr};
emulator_pointer callback_sentinel_addr{0};
// Extended parameters from last NtMapViewOfSectionEx call
// These can be used by other syscalls like NtAllocateVirtualMemoryEx
uint64_t last_extended_params_numa_node{0};

View File

@@ -22,6 +22,7 @@ void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer)
{
buffer.read_map(this->handlers_);
this->add_handlers();
this->add_callbacks();
}
void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const std::span<const std::byte> ntdll_data,
@@ -36,6 +37,7 @@ void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const std:
map_syscalls(this->handlers_, win32u_syscalls);
this->add_handlers();
this->add_callbacks();
}
void syscall_dispatcher::add_handlers()
@@ -140,6 +142,41 @@ void syscall_dispatcher::dispatch_callback(windows_emulator& win_emu, std::strin
}
}
void syscall_dispatcher::dispatch_completion(windows_emulator& win_emu, callback_id callback_id, uint64_t guest_result)
{
auto& emu = win_emu.emu();
const syscall_context c{
.win_emu = win_emu,
.emu = emu,
.proc = win_emu.process,
.write_status = true,
};
const auto entry = this->callbacks_.find(callback_id);
if (entry == this->callbacks_.end())
{
win_emu.log.error("Unknown callback: 0x%X\n", static_cast<uint32_t>(callback_id));
c.emu.stop();
return;
}
try
{
entry->second(c, guest_result);
}
catch (std::exception& e)
{
win_emu.log.error("Callback 0x%X threw an exception - %s\n", static_cast<int>(callback_id), e.what());
emu.stop();
}
catch (...)
{
win_emu.log.error("Callback 0x%X threw an unknown exception\n", static_cast<int>(callback_id));
emu.stop();
}
}
syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, const std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, const std::span<const std::byte> win32u_data)
{

View File

@@ -4,6 +4,7 @@
struct syscall_context;
using syscall_handler = void (*)(const syscall_context& c);
using callback_completion_handler = void (*)(const syscall_context& c, uint64_t guest_result);
struct syscall_handler_entry
{
@@ -22,6 +23,7 @@ class syscall_dispatcher
void dispatch(windows_emulator& win_emu);
static void dispatch_callback(windows_emulator& win_emu, std::string& syscall_name);
void dispatch_completion(windows_emulator& win_emu, callback_id callback_id, uint64_t guest_result);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
@@ -36,7 +38,9 @@ class syscall_dispatcher
private:
std::map<uint64_t, syscall_handler_entry> handlers_{};
std::map<callback_id, callback_completion_handler> callbacks_{};
static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping);
void add_handlers();
void add_callbacks();
};

View File

@@ -12,6 +12,7 @@ struct syscall_context
process_context& proc;
mutable bool write_status{true};
mutable bool retrigger_syscall{false};
mutable bool run_callback{false};
};
inline uint64_t get_syscall_argument(x86_64_emulator& emu, const size_t index)
@@ -132,15 +133,16 @@ T resolve_indexed_argument(x86_64_emulator& emu, size_t& index)
return resolve_argument<T>(emu, index++);
}
inline void write_syscall_result(const syscall_context& c, const uint64_t result, const uint64_t initial_ip)
inline void write_syscall_result(const syscall_context& c, const uint64_t result, const uint64_t initial_ip,
const bool is_callback_completion = false)
{
if (c.write_status && !c.retrigger_syscall)
if (c.write_status && !c.retrigger_syscall && !c.run_callback)
{
c.emu.reg<uint64_t>(x86_register::rax, result);
}
const auto new_ip = c.emu.read_instruction_pointer();
if (initial_ip != new_ip || c.retrigger_syscall)
if ((initial_ip != new_ip || c.retrigger_syscall || c.run_callback) && !is_callback_completion)
{
c.emu.reg(x86_register::rip, new_ip - 2);
}
@@ -176,6 +178,36 @@ syscall_handler make_syscall_handler()
return +[](const syscall_context& c) { forward_syscall(c, Handler); };
}
template <typename Result>
void forward_callback_completion(const syscall_context& c, const uint64_t /*guest_result*/, Result (*handler)())
{
const auto ip = c.emu.read_instruction_pointer();
const auto ret = handler();
write_syscall_result(c, static_cast<uint64_t>(ret), ip, true);
}
template <typename Result, typename GuestResult, typename... Args>
void forward_callback_completion(const syscall_context& c, const uint64_t guest_result,
Result (*handler)(const syscall_context&, GuestResult, Args...))
{
const auto ip = c.emu.read_instruction_pointer();
size_t index = 0;
std::tuple<const syscall_context&, GuestResult, Args...> func_args{
c, static_cast<GuestResult>(guest_result),
resolve_indexed_argument<std::remove_cv_t<std::remove_reference_t<Args>>>(c.emu, index)...};
const auto ret = std::apply(handler, std::move(func_args));
write_syscall_result(c, ret, ip, true);
}
template <auto Handler>
callback_completion_handler make_callback_completion_handler()
{
return +[](const syscall_context& c, const uint64_t guest_result) { forward_callback_completion(c, guest_result, Handler); };
}
template <typename T, typename Traits>
void write_attribute(emulator& emu, const PS_ATTRIBUTE<Traits>& attribute, const T& value)
{

View File

@@ -3,6 +3,7 @@
#include "cpu_context.hpp"
#include "emulator_utils.hpp"
#include "syscall_utils.hpp"
#include "user_callback_dispatch.hpp"
#include <numeric>
#include <cwctype>
@@ -1007,6 +1008,47 @@ namespace syscalls
return STATUS_UNSUCCESSFUL;
}
BOOL handle_NtUserEnumDisplayMonitors(const syscall_context& c, const hdc hdc_in, const uint64_t clip_rect_ptr, const uint64_t callback,
const uint64_t param)
{
if (!callback)
{
return FALSE;
}
const auto hmon = c.win_emu.process.default_monitor_handle.bits;
const auto display_info = c.proc.user_handles.get_display_info().read();
if (clip_rect_ptr)
{
RECT clip{};
c.emu.read_memory(clip_rect_ptr, &clip, sizeof(clip));
const emulator_object<USER_MONITOR> monitor_obj(c.emu, display_info.pPrimaryMonitor);
const auto monitor = monitor_obj.read();
auto effective_rc{monitor.rcMonitor};
effective_rc.left = std::max(effective_rc.left, clip.left);
effective_rc.top = std::max(effective_rc.top, clip.top);
effective_rc.right = std::min(effective_rc.right, clip.right);
effective_rc.bottom = std::min(effective_rc.bottom, clip.bottom);
if (effective_rc.right <= effective_rc.left || effective_rc.bottom <= effective_rc.top)
{
return TRUE;
}
}
const uint64_t rect_ptr = display_info.pPrimaryMonitor + offsetof(USER_MONITOR, rcMonitor);
dispatch_user_callback(c, callback_id::NtUserEnumDisplayMonitors, callback, hmon, hdc_in, rect_ptr, param);
return {};
}
BOOL completion_NtUserEnumDisplayMonitors(const syscall_context&, BOOL guest_result, const hdc /*hdc_in*/,
const uint64_t /*clip_rect_ptr*/, const uint64_t /*callback*/, const uint64_t /*param*/)
{
return guest_result;
}
NTSTATUS handle_NtAssociateWaitCompletionPacket()
{
return STATUS_SUCCESS;
@@ -1049,6 +1091,19 @@ namespace syscalls
return TRUE;
}
emulator_pointer handle_NtUserMapDesktopObject(const syscall_context& c, handle handle)
{
const auto index = handle.value.id;
if (index == 0 || index >= user_handle_table::MAX_HANDLES)
{
return 0;
}
const auto handle_entry = c.proc.user_handles.get_handle_table().read(static_cast<size_t>(index));
return handle_entry.pHead;
}
}
void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& handler_mapping)
@@ -1238,6 +1293,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtUserGetKeyboardType);
add_handler(NtUserEnumDisplayDevices);
add_handler(NtUserEnumDisplaySettings);
add_handler(NtUserEnumDisplayMonitors);
add_handler(NtUserSetProp);
add_handler(NtUserSetProp2);
add_handler(NtUserChangeWindowMessageFilterEx);
@@ -1266,6 +1322,20 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtRemoveProcessDebug);
add_handler(NtNotifyChangeDirectoryFileEx);
add_handler(NtUserGetHDevName);
add_handler(NtUserMapDesktopObject);
#undef add_handler
}
void syscall_dispatcher::add_callbacks()
{
#define add_callback(syscall) \
do \
{ \
this->callbacks_[callback_id::syscall] = make_callback_completion_handler<syscalls::completion_##syscall>(); \
} while (0)
add_callback(NtUserEnumDisplayMonitors);
#undef add_callback
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include "syscall_utils.hpp"
// TODO: Here we are calling guest functions directly, but this is not how it works in the real Windows kernel.
// In the real implementation, the kernel invokes ntdll!KiUserCallbackDispatcher and passes a callback
// index that refers to an entry in PEB->KernelCallbackTable. The dispatcher then looks up the function
// pointer in that table and invokes the corresponding user-mode callback.
template <typename... Args>
void prepare_call_stack(x86_64_emulator& emu, uint64_t return_address, Args... args)
{
constexpr size_t arg_count = sizeof...(Args);
const size_t stack_args_size = aligned_stack_space(arg_count);
const uint64_t current_rsp = emu.read_stack_pointer();
const uint64_t aligned_rsp = align_down(current_rsp, 16);
// We subtract the args size (including the shadow space) AND the size of the return address
const uint64_t new_rsp = aligned_rsp - stack_args_size - sizeof(emulator_pointer);
emu.reg(x86_register::rsp, new_rsp);
emu.write_memory(new_rsp, &return_address, sizeof(return_address));
size_t index = 0;
(set_function_argument(emu, index++, static_cast<uint64_t>(args)), ...);
}
template <typename... Args>
void dispatch_user_callback(const syscall_context& c, callback_id completion_id, uint64_t func_address, Args... args)
{
const callback_frame frame{
.handler_id = completion_id,
.rip = c.emu.read_instruction_pointer(),
.rsp = c.emu.read_stack_pointer(),
.r10 = c.emu.reg(x86_register::r10),
.rcx = c.emu.reg(x86_register::rcx),
.rdx = c.emu.reg(x86_register::rdx),
.r8 = c.emu.reg(x86_register::r8),
.r9 = c.emu.reg(x86_register::r9),
};
c.proc.active_thread->callback_stack.push_back(frame);
prepare_call_stack(c.emu, c.proc.callback_sentinel_addr, args...);
c.emu.reg(x86_register::rip, func_address);
c.run_callback = true;
}

View File

@@ -12,15 +12,17 @@ class user_handle_table
{
}
void setup()
void setup(const bool is_wow64_process)
{
this->is_wow64_process_ = is_wow64_process;
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);
server_info_addr_ = this->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);
display_info_addr_ = this->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) {
@@ -28,7 +30,7 @@ class user_handle_table
});
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);
handle_table_addr_ = this->allocate_memory(handle_table_size, memory_permission::read);
}
emulator_object<USER_SHAREDINFO> get_server_info() const
@@ -52,7 +54,7 @@ class user_handle_table
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 auto alloc_ptr = this->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_);
@@ -93,6 +95,7 @@ class user_handle_table
buffer.write(handle_table_addr_);
buffer.write(display_info_addr_);
buffer.write_vector(used_indices_);
buffer.write(is_wow64_process_);
}
void deserialize(utils::buffer_deserializer& buffer)
@@ -101,6 +104,7 @@ class user_handle_table
buffer.read(handle_table_addr_);
buffer.read(display_info_addr_);
buffer.read_vector(used_indices_);
buffer.read(is_wow64_process_);
}
private:
@@ -129,11 +133,19 @@ class user_handle_table
}
}
uint64_t allocate_memory(const size_t size, const nt_memory_permission permissions)
{
const auto allocation_base = this->is_wow64_process_ ? DEFAULT_ALLOCATION_ADDRESS_32BIT : DEFAULT_ALLOCATION_ADDRESS_64BIT;
const auto base = memory_->find_free_allocation_base(size, allocation_base);
return memory_->allocate_memory(size, permissions, false, base);
}
uint64_t server_info_addr_{};
uint64_t handle_table_addr_{};
uint64_t display_info_addr_{};
std::vector<bool> used_indices_{};
memory_manager* memory_{};
bool is_wow64_process_{};
};
template <handle_types::type Type, typename T>

View File

@@ -554,6 +554,7 @@ void windows_emulator::start(size_t count)
{
this->should_stop = false;
this->setup_process_if_necessary();
this->process.setup_callback_hook(*this, this->memory);
const auto use_count = count > 0;
const auto start_instructions = this->executed_instructions_;