Skip to content

Commit

Permalink
ntdll: Call Wow64PrepareForException directly from KiUserExceptionDis…
Browse files Browse the repository at this point in the history
…patcher.

This requires moving the stack switch to Wow64PrepareForException.

Based on a patch by Paul Gofman.
  • Loading branch information
julliard committed Nov 30, 2023
1 parent ae32b2f commit 334f54c
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 27 deletions.
34 changes: 9 additions & 25 deletions dlls/ntdll/signal_x86_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,6 @@ NTSTATUS WINAPI dispatch_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
NTSTATUS status;
DWORD c;

if (pWow64PrepareForException) pWow64PrepareForException( rec, context );

TRACE_(seh)( "code=%lx flags=%lx addr=%p ip=%Ix\n",
rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Rip );
for (c = 0; c < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); c++)
Expand Down Expand Up @@ -610,34 +608,20 @@ NTSTATUS WINAPI dispatch_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
}


NTSTATUS WINAPI dispatch_wow_exception( EXCEPTION_RECORD *rec_ptr, CONTEXT *context_ptr )
{
char buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 128];
CONTEXT *context;
CONTEXT_EX *context_ex;
EXCEPTION_RECORD rec = *rec_ptr;

RtlInitializeExtendedContext( buffer, context_ptr->ContextFlags, &context_ex );
context = RtlLocateLegacyContext( context_ex, NULL );
RtlCopyContext( context, context_ptr->ContextFlags, context_ptr );
return dispatch_exception( &rec, context );
}


/*******************************************************************
* KiUserExceptionDispatcher (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( KiUserExceptionDispatcher,
"mov 0x98(%rsp),%rcx\n\t" /* context->Rsp */
"movw %cs,%ax\n\t"
"cmpw %ax,0x38(%rsp)\n\t" /* context->SegCs */
"je 1f\n\t"
"mov %rsp,%rdx\n\t" /* context */
"lea 0x4f0(%rsp),%rcx\n\t" /* rec */
"movq %r14,%rsp\n\t" /* switch to 64-bit stack */
"call " __ASM_NAME("dispatch_wow_exception") "\n\t"
"int3\n"
"cld\n\t"
/* some anticheats need this exact instruction here */
"mov " __ASM_NAME("pWow64PrepareForException") "(%rip),%rax\n\t"
"test %rax,%rax\n\t"
"jz 1f\n\t"
"mov %rsp,%rdx\n\t" /* context */
"lea 0x4f0(%rsp),%rcx\n\t" /* rec */
"call *%rax\n"
"1:\tmov 0xf8(%rsp),%rdx\n\t" /* context->Rip */
"mov 0x98(%rsp),%rcx\n\t" /* context->Rsp */
"mov %rdx,-0x8(%rcx)\n\t"
"mov %rbp,-0x10(%rcx)\n\t"
"mov %rdi,-0x18(%rcx)\n\t"
Expand Down
53 changes: 53 additions & 0 deletions dlls/ntdll/tests/wow64.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ static NTSTATUS (WINAPI *pRtlWow64GetThreadContext)(HANDLE,WOW64_CONTEXT*);
static NTSTATUS (WINAPI *pRtlWow64IsWowGuestMachineSupported)(USHORT,BOOLEAN*);
static NTSTATUS (WINAPI *pNtMapViewOfSectionEx)(HANDLE,HANDLE,PVOID*,const LARGE_INTEGER*,SIZE_T*,ULONG,ULONG,MEM_EXTENDED_PARAMETER*,ULONG);
#ifdef _WIN64
static NTSTATUS (WINAPI *pKiUserExceptionDispatcher)(EXCEPTION_RECORD*,CONTEXT*);
static NTSTATUS (WINAPI *pRtlWow64GetCpuAreaInfo)(WOW64_CPURESERVED*,ULONG,WOW64_CPU_AREA_INFO*);
static NTSTATUS (WINAPI *pRtlWow64GetThreadSelectorEntry)(HANDLE,THREAD_DESCRIPTOR_INFORMATION*,ULONG,ULONG*);
static CROSS_PROCESS_WORK_ENTRY * (WINAPI *pRtlWow64PopAllCrossProcessWorkFromWorkList)(CROSS_PROCESS_WORK_HDR*,BOOLEAN*);
Expand Down Expand Up @@ -100,6 +101,7 @@ static void init(void)
GET_PROC( RtlWow64GetThreadContext );
GET_PROC( RtlWow64IsWowGuestMachineSupported );
#ifdef _WIN64
GET_PROC( KiUserExceptionDispatcher );
GET_PROC( RtlWow64GetCpuAreaInfo );
GET_PROC( RtlWow64GetThreadSelectorEntry );
GET_PROC( RtlWow64PopAllCrossProcessWorkFromWorkList );
Expand Down Expand Up @@ -909,6 +911,7 @@ static void test_peb_teb(void)

static void test_selectors(void)
{
#ifndef __arm__
THREAD_DESCRIPTOR_INFORMATION info;
NTSTATUS status;
ULONG base, limit, sel, retlen;
Expand Down Expand Up @@ -1043,6 +1046,7 @@ static void test_selectors(void)
}
}
#undef GET_ENTRY
#endif /* __arm__ */
}

static void test_image_mappings(void)
Expand Down Expand Up @@ -1384,6 +1388,20 @@ static void test_cpu_area(void)
else win_skip( "RtlWow64GetCpuAreaInfo not supported\n" );
}

static void test_exception_dispatcher(void)
{
#ifdef __x86_64__
BYTE *code = (BYTE *)pKiUserExceptionDispatcher;
void **hook;

/* cld; mov xxx(%rip),%rax */
ok( code[0] == 0xfc && code[1] == 0x48 && code[2] == 0x8b && code[3] == 0x05,
"wrong opcodes %02x %02x %02x %02x\n", code[0], code[1], code[2], code[3] );
hook = (void **)(code + 8 + *(int *)(code + 4));
ok( !*hook, "hook %p set to %p\n", hook, *hook );
#endif
}

#else /* _WIN64 */

static const BYTE call_func64_code[] =
Expand Down Expand Up @@ -2122,6 +2140,40 @@ static void test_cpu_area(void)

}

static void test_exception_dispatcher(void)
{
ULONG64 ptr, hook_ptr, hook, expect, res;
NTSTATUS status;
BYTE code[8];

if (!is_wow64) return;
if (!code_mem) return;
if (!ntdll_module) return;

ptr = get_proc_address64( ntdll_module, "KiUserExceptionDispatcher" );
ok( ptr, "KiUserExceptionDispatcher not found\n" );

if (pNtWow64ReadVirtualMemory64)
{
HANDLE process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId() );

ok( process != 0, "failed to open current process %lu\n", GetLastError() );
status = pNtWow64ReadVirtualMemory64( process, ptr, &code, sizeof(code), &res );
ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status );

/* cld; mov xxx(%rip),%rax */
ok( code[0] == 0xfc && code[1] == 0x48 && code[2] == 0x8b && code[3] == 0x05,
"wrong opcodes %02x %02x %02x %02x\n", code[0], code[1], code[2], code[3] );
hook_ptr = ptr + 8 + *(int *)(code + 4);
status = pNtWow64ReadVirtualMemory64( process, hook_ptr, &hook, sizeof(hook), &res );
ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status );

expect = get_proc_address64( wow64_module, "Wow64PrepareForException" );
ok( hook == expect, "hook %I64x set to %I64x / %I64x\n", hook_ptr, hook, expect );
NtClose( process );
}
}

#endif /* _WIN64 */


Expand All @@ -2142,4 +2194,5 @@ START_TEST(wow64)
test_syscalls();
#endif
test_cpu_area();
test_exception_dispatcher();
}
35 changes: 34 additions & 1 deletion dlls/wow64/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ static void (WINAPI *pBTCpuProcessInit)(void);
static NTSTATUS (WINAPI *pBTCpuSetContext)(HANDLE,HANDLE,void *,void *);
static void (WINAPI *pBTCpuThreadInit)(void);
static void (WINAPI *pBTCpuSimulate)(void);
static NTSTATUS (WINAPI *pBTCpuResetToConsistentState)( EXCEPTION_POINTERS * );
static void * (WINAPI *p__wine_get_unix_opcode)(void);
static void * (WINAPI *pKiRaiseUserExceptionDispatcher)(void);
void (WINAPI *pBTCpuNotifyFlushInstructionCache2)( const void *, SIZE_T ) = NULL;
Expand All @@ -111,6 +110,7 @@ void (WINAPI *pBTCpuNotifyMemoryDirty)( void *, SIZE_T ) = NULL;
void (WINAPI *pBTCpuNotifyMemoryFree)( void *, SIZE_T, ULONG ) = NULL;
void (WINAPI *pBTCpuNotifyMemoryProtect)( void *, SIZE_T, ULONG ) = NULL;
void (WINAPI *pBTCpuNotifyUnmapViewOfSection)( void * ) = NULL;
NTSTATUS (WINAPI *pBTCpuResetToConsistentState)( EXCEPTION_POINTERS * ) = NULL;
void (WINAPI *pBTCpuUpdateProcessorInformation)( SYSTEM_CPU_INFORMATION * ) = NULL;
void (WINAPI *pBTCpuThreadTerm)( HANDLE ) = NULL;

Expand Down Expand Up @@ -1154,12 +1154,45 @@ void WINAPI Wow64LdrpInitialize( CONTEXT *context )
/**********************************************************************
* Wow64PrepareForException (wow64.@)
*/
#ifdef __x86_64__
__ASM_GLOBAL_FUNC( Wow64PrepareForException,
"sub $0x38,%rsp\n\t"
"mov %rcx,%r10\n\t" /* rec */
"movw %cs,%ax\n\t"
"cmpw %ax,0x38(%rdx)\n\t" /* context->SegCs */
"je 1f\n\t" /* already in 64-bit mode? */
/* copy arguments to 64-bit stack */
"mov %rsp,%rsi\n\t"
"mov 0x98(%rdx),%rcx\n\t" /* context->Rsp */
"sub %rsi,%rcx\n\t" /* stack size */
"sub %rcx,%r14\n\t" /* reserve same size on 64-bit stack */
"and $~0x0f,%r14\n\t"
"mov %r14,%rdi\n\t"
"shr $3,%rcx\n\t"
"rep; movsq\n\t"
/* update arguments to point to the new stack */
"mov %r14,%rax\n\t"
"sub %rsp,%rax\n\t"
"add %rax,%r10\n\t" /* rec */
"add %rax,%rdx\n\t" /* context */
/* switch to 64-bit stack */
"mov %r14,%rsp\n"
/* build EXCEPTION_POINTERS structure and call BTCpuResetToConsistentState */
"1:\tlea 0x20(%rsp),%rcx\n\t" /* pointers */
"mov %r10,(%rcx)\n\t" /* rec */
"mov %rdx,8(%rcx)\n\t" /* context */
"mov " __ASM_NAME("pBTCpuResetToConsistentState") "(%rip),%rax\n\t"
"call *%rax\n\t"
"add $0x38,%rsp\n\t"
"ret" )
#else
void WINAPI Wow64PrepareForException( EXCEPTION_RECORD *rec, CONTEXT *context )
{
EXCEPTION_POINTERS ptrs = { rec, context };

pBTCpuResetToConsistentState( &ptrs );
}
#endif


/**********************************************************************
Expand Down
2 changes: 1 addition & 1 deletion dlls/wow64/wow64.spec
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@ stub Wow64NotifyUnsimulateComplete
@ stdcall Wow64PassExceptionToGuest(ptr)
@ stub Wow64PrepareForDebuggerAttach
@ stdcall Wow64PrepareForException(ptr ptr)
@ stdcall -norelay Wow64PrepareForException(ptr ptr)
@ stdcall Wow64ProcessPendingCrossProcessItems()
@ stdcall Wow64RaiseException(long ptr)
@ stub Wow64ShallowThunkAllocObjectAttributes32TO64_FNC
Expand Down

0 comments on commit 334f54c

Please sign in to comment.