mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-19 11:43:56 +00:00
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.
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user