diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index 0102af5b..bf6eddf8 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -408,6 +408,8 @@ inline std::u16string read_unicode_string(emulator& emu, const uint64_t uc_strin return read_unicode_string(emu, emulator_object>>{emu, uc_string}); } +/// Retrieves function arguments from registers or stack memory. This function assumes the caller has already allocated +/// stack space and that RSP 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; @@ -441,6 +443,8 @@ inline uint64_t get_function_argument(x86_64_emulator& emu, const size_t index, } } +/// 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; diff --git a/src/windows-emulator/user_callback_dispatch.hpp b/src/windows-emulator/user_callback_dispatch.hpp index ddfd689d..b74e647a 100644 --- a/src/windows-emulator/user_callback_dispatch.hpp +++ b/src/windows-emulator/user_callback_dispatch.hpp @@ -7,39 +7,41 @@ // 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 +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(args)), ...); +} + template void dispatch_user_callback(const syscall_context& c, callback_id completion_id, uint64_t func_address, Args... args) { - const uint64_t original_rsp = c.emu.read_stack_pointer(); - - // Save syscall argument registers BEFORE modifying anything const callback_frame frame{ .handler_id = completion_id, .rip = c.emu.read_instruction_pointer(), - .rsp = original_rsp, + .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), }; - - uint64_t stack_ptr = align_down(original_rsp, 16); - - constexpr size_t arg_count = sizeof...(Args); - const size_t allocation_size = aligned_stack_space(arg_count); - stack_ptr -= allocation_size; - - // Push the return address onto the stack (Simulating CALL) - stack_ptr -= sizeof(emulator_pointer); - c.emu.write_memory(stack_ptr, &c.proc.callback_sentinel_addr, sizeof(c.proc.callback_sentinel_addr)); - c.proc.active_thread->callback_stack.push_back(frame); - c.emu.reg(x86_register::rsp, stack_ptr); - - size_t index = 0; - (set_function_argument(c.emu, index++, static_cast(args)), ...); + prepare_call_stack(c.emu, c.proc.callback_sentinel_addr, args...); c.emu.reg(x86_register::rip, func_address); c.run_callback = true;