ntdll: Fix stack layout for KiUserCallbackDispatcher on i386.

This commit is contained in:
Alexandre Julliard 2023-11-30 17:43:14 +01:00
parent 3774b00f31
commit e60a97c1b4
3 changed files with 105 additions and 25 deletions

View file

@ -2175,6 +2175,61 @@ static void test_KiUserApcDispatcher(void)
VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect );
}
static void CDECL hook_KiUserCallbackDispatcher( void *eip, ULONG id, ULONG *args, ULONG len,
ULONG unk1, ULONG unk2, ULONG arg0, ULONG arg1 )
{
NTSTATUS (WINAPI *func)(void *, ULONG) = ((void **)NtCurrentTeb()->Peb->KernelCallbackTable)[id];
trace( "eip %p id %lx args %p (%x) len %lx unk1 %lx unk2 %lx args %lx,%lx\n",
eip, id, args, (char *)args - (char *)&eip, len, unk1, unk2, arg0, arg1 );
if (args[0] != arg0) /* new style with extra esp */
{
void *esp = (void *)arg0;
ok( args[0] == arg1, "wrong arg1 %lx / %lx\n", args[0], arg1 );
ok( (char *)esp - ((char *)args + len) < 0x10, "wrong esp offset %p / %p\n", esp, args );
}
if (eip && pRtlPcToFileHeader)
{
void *mod, *win32u = GetModuleHandleA("win32u.dll");
pRtlPcToFileHeader( eip, &mod );
if (win32u) ok( mod == win32u, "ret address %p not in win32u %p\n", eip, win32u );
else trace( "ret address %p in %p\n", eip, mod );
}
NtCallbackReturn( NULL, 0, func( args, len ));
}
static void test_KiUserCallbackDispatcher(void)
{
BYTE saved_code[7], patched_code[7];
DWORD old_protect;
BYTE *ptr;
BOOL ret;
ret = VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code),
PAGE_EXECUTE_READWRITE, &old_protect );
ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
memcpy( saved_code, pKiUserCallbackDispatcher, sizeof(saved_code) );
ptr = patched_code;
/* mov $hook_trampoline, %eax */
*ptr++ = 0xb8;
*(void **)ptr = hook_KiUserCallbackDispatcher;
ptr += sizeof(void *);
/* call *eax */
*ptr++ = 0xff;
*ptr++ = 0xd0;
memcpy( pKiUserCallbackDispatcher, patched_code, sizeof(patched_code) );
DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ));
memcpy( pKiUserCallbackDispatcher, saved_code, sizeof(saved_code));
VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), old_protect, &old_protect );
}
#elif defined(__x86_64__)
#define UNW_FLAG_NHANDLER 0
@ -12121,6 +12176,7 @@ START_TEST(exception)
test_prot_fault();
test_KiUserExceptionDispatcher();
test_KiUserApcDispatcher();
test_KiUserCallbackDispatcher();
test_extended_context();
test_copy_context();
test_set_live_context();

View file

@ -465,6 +465,19 @@ struct apc_stack_layout
C_ASSERT( offsetof(struct apc_stack_layout, context) == 0x14 );
C_ASSERT( sizeof(struct apc_stack_layout) == 0x308 );
/* stack layout when calling KiUserCallbackDispatcher */
struct callback_stack_layout
{
ULONG eip; /* 000 */
ULONG id; /* 004 */
void *args; /* 008 */
ULONG len; /* 00c */
ULONG unk[2]; /* 010 */
ULONG esp; /* 018 */
BYTE args_data[0]; /* 01c */
};
C_ASSERT( sizeof(struct callback_stack_layout) == 0x1c );
struct syscall_frame
{
WORD syscall_flags; /* 000 */
@ -1595,8 +1608,8 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context
/***********************************************************************
* call_user_mode_callback
*/
extern NTSTATUS call_user_mode_callback( ULONG id, void *args, ULONG len, void **ret_ptr,
ULONG *ret_len, void *func, TEB *teb );
extern NTSTATUS call_user_mode_callback( ULONG user_esp, void **ret_ptr, ULONG *ret_len,
void *func, TEB *teb );
__ASM_GLOBAL_FUNC( call_user_mode_callback,
"pushl %ebp\n\t"
__ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
@ -1609,7 +1622,7 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback,
__ASM_CFI(".cfi_rel_offset %esi,-8\n\t")
"pushl %edi\n\t"
__ASM_CFI(".cfi_rel_offset %edi,-12\n\t")
"movl 0x20(%ebp),%edx\n\t" /* teb */
"movl 0x18(%ebp),%edx\n\t" /* teb */
"pushl 0(%edx)\n\t" /* teb->Tib.ExceptionList */
"subl $0x380,%esp\n\t" /* sizeof(struct syscall_frame) */
"andl $~63,%esp\n\t"
@ -1620,14 +1633,9 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback,
"movl %eax,(%esp)\n\t"
"movl %ecx,0x3c(%esp)\n\t" /* frame->prev_frame */
"movl %esp,0x1f8(%edx)\n\t" /* x86_thread_data()->syscall_frame */
"movl 0x1c(%ebp),%ecx\n\t" /* func */
"movl 0x0c(%ebp),%edx\n\t" /* args */
"movl 0x14(%ebp),%ecx\n\t" /* func */
/* switch to user stack */
"leal -4(%edx),%esp\n\t"
"pushl 0x10(%ebp)\n\t" /* len */
"pushl %edx\n\t" /* args */
"pushl 0x08(%ebp)\n\t" /* id */
"pushl $0\n\t"
"movl 8(%ebp),%esp\n\t"
"xorl %ebp,%ebp\n\t"
"jmpl *%ecx" )
@ -1654,9 +1662,9 @@ __ASM_GLOBAL_FUNC( user_mode_callback_return,
"movl 8(%esp),%edi\n\t" /* ret_len */
"movl 12(%esp),%eax\n\t" /* status */
"leal -16(%ebp),%esp\n\t"
"movl 0x14(%ebp),%ecx\n\t" /* ret_ptr */
"movl 0x0c(%ebp),%ecx\n\t" /* ret_ptr */
"movl %esi,(%ecx)\n\t"
"movl 0x18(%ebp),%ecx\n\t" /* ret_len */
"movl 0x10(%ebp),%ecx\n\t" /* ret_len */
"movl %edi,(%ecx)\n\t"
"popl 0(%edx)\n\t" /* teb->Tib.ExceptionList */
"popl %edi\n\t"
@ -1698,14 +1706,19 @@ __ASM_GLOBAL_FUNC( user_mode_abort_thread,
NTSTATUS KeUserModeCallback( ULONG id, const void *args, ULONG len, void **ret_ptr, ULONG *ret_len )
{
struct syscall_frame *frame = x86_thread_data()->syscall_frame;
void *args_data = (void *)((frame->esp - len) & ~15);
ULONG esp = (frame->esp - offsetof(struct callback_stack_layout, args_data[len])) & ~3;
struct callback_stack_layout *stack = (struct callback_stack_layout *)esp;
if ((char *)ntdll_get_thread_data()->kernel_stack + min_kernel_stack > (char *)&frame)
return STATUS_STACK_OVERFLOW;
memcpy( args_data, args, len );
return call_user_mode_callback( id, args_data, len, ret_ptr, ret_len,
pKiUserCallbackDispatcher, NtCurrentTeb() );
stack->eip = frame->eip;
stack->id = id;
stack->args = stack->args_data;
stack->len = len;
stack->esp = frame->esp;
memcpy( stack->args_data, args, len );
return call_user_mode_callback( esp, ret_ptr, ret_len, pKiUserCallbackDispatcher, NtCurrentTeb() );
}

View file

@ -1095,20 +1095,31 @@ NTSTATUS WINAPI Wow64KiUserCallbackDispatcher( ULONG id, void *args, ULONG len,
{
case IMAGE_FILE_MACHINE_I386:
{
/* stack layout when calling 32-bit KiUserCallbackDispatcher */
struct callback_stack_layout32
{
ULONG eip; /* 000 */
ULONG id; /* 004 */
ULONG args; /* 008 */
ULONG len; /* 00c */
ULONG unk[2]; /* 010 */
ULONG esp; /* 018 */
BYTE args_data[0]; /* 01c */
} *stack;
I386_CONTEXT orig_ctx, ctx = { CONTEXT_I386_FULL };
void *args_data;
ULONG *stack;
C_ASSERT( sizeof(struct callback_stack_layout32) == 0x1c );
pBTCpuGetContext( GetCurrentThread(), GetCurrentProcess(), NULL, &ctx );
orig_ctx = ctx;
stack = args_data = ULongToPtr( (ctx.Esp - len) & ~15 );
memcpy( args_data, args, len );
*(--stack) = 0;
*(--stack) = len;
*(--stack) = PtrToUlong( args_data );
*(--stack) = id;
*(--stack) = 0xdeadbabe;
stack = ULongToPtr( (ctx.Esp - offsetof(struct callback_stack_layout32,args_data[len])) & ~15 );
stack->eip = ctx.Eip;
stack->id = id;
stack->args = PtrToUlong( stack->args_data );
stack->len = len;
stack->esp = ctx.Esp;
memcpy( stack->args_data, args, len );
ctx.Esp = PtrToUlong( stack );
ctx.Eip = pLdrSystemDllInitBlock->pKiUserCallbackDispatcher;