From 0b06d4c4a36e34b314c1886a30423aa9faa3faa2 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Fri, 19 Jun 2015 03:58:41 +0200 Subject: [PATCH] ntoskrnl: Add initial code to emulate memory access to USER_SHARED_DATA on x86_64. --- dlls/ntoskrnl.exe/instr.c | 263 ++++++++++++++++++++++++++++++++++- dlls/ntoskrnl.exe/ntoskrnl.c | 2 +- 2 files changed, 261 insertions(+), 4 deletions(-) diff --git a/dlls/ntoskrnl.exe/instr.c b/dlls/ntoskrnl.exe/instr.c index 45021c64faf..5a469cfcac4 100644 --- a/dlls/ntoskrnl.exe/instr.c +++ b/dlls/ntoskrnl.exe/instr.c @@ -4,6 +4,7 @@ * Copyright 1995 Alexandre Julliard * Copyright 2005 Ivan Leo Puoti * Copyright 2005 Laurent Pinchart + * Copyright 2014-2015 Sebastian Lackner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,17 +24,19 @@ #include "config.h" #include "wine/port.h" -#ifdef __i386__ - #include #include "windef.h" #include "winbase.h" #include "winternl.h" +#define WIN32_NO_STATUS +#include "ddk/wdm.h" #include "excpt.h" #include "wine/debug.h" #include "wine/exception.h" +#ifdef __i386__ + WINE_DEFAULT_DEBUG_CHANNEL(int); #include "pshpack1.h" @@ -475,4 +478,258 @@ LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs ) return EXCEPTION_CONTINUE_SEARCH; } -#endif /* __i386__ */ +#elif defined(__x86_64__) /* __i386__ */ + +WINE_DEFAULT_DEBUG_CHANNEL(int); + +#define REX_B 1 +#define REX_X 2 +#define REX_R 4 +#define REX_W 8 + +#define REGMODRM_MOD( regmodrm, rex ) ((regmodrm) >> 6) +#define REGMODRM_REG( regmodrm, rex ) (((regmodrm) >> 3) & 7) | (((rex) & REX_R) ? 8 : 0) +#define REGMODRM_RM( regmodrm, rex ) (((regmodrm) & 7) | (((rex) & REX_B) ? 8 : 0)) + +#define SIB_SS( sib, rex ) ((sib) >> 6) +#define SIB_INDEX( sib, rex ) (((sib) >> 3) & 7) | (((rex) & REX_R) ? 8 : 0) +#define SIB_BASE( sib, rex ) (((sib) & 7) | (((rex) & REX_B) ? 8 : 0)) + +/* keep in sync with dlls/ntdll/thread.c:thread_init */ +static const BYTE *wine_user_shared_data = (BYTE *)0x7ffe0000; +static const BYTE *user_shared_data = (BYTE *)0xfffff78000000000; + +static inline DWORD64 *get_int_reg( CONTEXT *context, int index ) +{ + return &context->Rax + index; /* index should be in range 0 .. 15 */ +} + +static inline int get_op_size( int long_op, int rex ) +{ + if (rex & REX_W) + return sizeof(DWORD64); + else if (long_op) + return sizeof(DWORD); + else + return sizeof(WORD); +} + +/* store an operand into a register */ +static void store_reg_word( CONTEXT *context, BYTE regmodrm, const BYTE *addr, int long_op, int rex ) +{ + int index = REGMODRM_REG( regmodrm, rex ); + BYTE *reg = (BYTE *)get_int_reg( context, index ); + memcpy( reg, addr, get_op_size( long_op, rex ) ); +} + +/* store an operand into a byte register */ +static void store_reg_byte( CONTEXT *context, BYTE regmodrm, const BYTE *addr, int rex ) +{ + int index = REGMODRM_REG( regmodrm, rex ); + BYTE *reg = (BYTE *)get_int_reg( context, index ); + if (!rex && index >= 4 && index < 8) reg -= (4 * sizeof(DWORD64) - 1); /* special case: ah, ch, dh, bh */ + *reg = *addr; +} + +/*********************************************************************** + * INSTR_GetOperandAddr + * + * Return the address of an instruction operand (from the mod/rm byte). + */ +static BYTE *INSTR_GetOperandAddr( CONTEXT *context, BYTE *instr, + int long_addr, int rex, int segprefix, int *len ) +{ + int mod, rm, ss = 0, off, have_sib = 0; + DWORD64 base = 0, index = 0; + +#define GET_VAL( val, type ) \ + { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); } + + *len = 0; + GET_VAL( &mod, BYTE ); + rm = REGMODRM_RM( mod, rex ); + mod = REGMODRM_MOD( mod, rex ); + + if (mod == 3) + return (BYTE *)get_int_reg( context, rm ); + + if ((rm & 7) == 4) + { + BYTE sib; + int id; + + GET_VAL( &sib, BYTE ); + rm = SIB_BASE( sib, rex ); + id = SIB_INDEX( sib, rex ); + ss = SIB_SS( sib, rex ); + + index = (id != 4) ? *get_int_reg( context, id ) : 0; + if (!long_addr) index &= 0xffffffff; + have_sib = 1; + } + + base = *get_int_reg( context, rm ); + if (!long_addr) base &= 0xffffffff; + + switch (mod) + { + case 0: + if (rm == 5) /* special case */ + { + base = have_sib ? 0 : context->Rip; + if (!long_addr) base &= 0xffffffff; + GET_VAL( &off, DWORD ); + base += (signed long)off; + } + break; + + case 1: /* 8-bit disp */ + GET_VAL( &off, BYTE ); + base += (signed char)off; + break; + + case 2: /* 32-bit disp */ + GET_VAL( &off, DWORD ); + base += (signed long)off; + break; + } + + /* FIXME: we assume that all segments have a base of 0 */ + return (BYTE *)(base + (index << ss)); +#undef GET_VAL +} + + +/*********************************************************************** + * emulate_instruction + * + * Emulate a privileged instruction. + * Returns exception continuation status. + */ +static DWORD emulate_instruction( EXCEPTION_RECORD *rec, CONTEXT *context ) +{ + int prefix, segprefix, prefixlen, len, long_op, long_addr, rex; + BYTE *instr; + + long_op = long_addr = 1; + instr = (BYTE *)context->Rip; + if (!instr) return ExceptionContinueSearch; + + /* First handle any possible prefix */ + + segprefix = -1; /* no seg prefix */ + rex = 0; /* no rex prefix */ + prefix = 1; + prefixlen = 0; + while(prefix) + { + switch(*instr) + { + case 0x2e: + segprefix = context->SegCs; + break; + case 0x36: + segprefix = context->SegSs; + break; + case 0x3e: + segprefix = context->SegDs; + break; + case 0x26: + segprefix = context->SegEs; + break; + case 0x64: + segprefix = context->SegFs; + break; + case 0x65: + segprefix = context->SegGs; + break; + case 0x66: + long_op = !long_op; /* opcode size prefix */ + break; + case 0x67: + long_addr = !long_addr; /* addr size prefix */ + break; + case 0xf0: /* lock */ + break; + case 0xf2: /* repne */ + break; + case 0xf3: /* repe */ + break; + default: + prefix = 0; /* no more prefixes */ + break; + } + if (*instr >= 0x40 && *instr < 0x50) /* rex */ + { + rex = *instr; + prefix = TRUE; + } + if (prefix) + { + instr++; + prefixlen++; + } + } + + /* Now look at the actual instruction */ + + switch(*instr) + { + case 0x8a: /* mov Eb, Gb */ + case 0x8b: /* mov Ev, Gv */ + { + BYTE *data = INSTR_GetOperandAddr( context, instr + 1, long_addr, + rex, segprefix, &len ); + unsigned int data_size = (*instr == 0x8b) ? get_op_size( long_op, rex ) : 1; + unsigned int offset = data - user_shared_data; + + if (offset <= sizeof(KSHARED_USER_DATA) - data_size) + { + switch (*instr) + { + case 0x8a: store_reg_byte( context, instr[1], wine_user_shared_data + offset, rex ); break; + case 0x8b: store_reg_word( context, instr[1], wine_user_shared_data + offset, long_op, rex ); break; + } + context->Rip += prefixlen + len + 1; + return ExceptionContinueExecution; + } + break; /* Unable to emulate it */ + } + } + return ExceptionContinueSearch; /* Unable to emulate it */ +} + + +/*********************************************************************** + * vectored_handler + * + * Vectored exception handler used to emulate protected instructions + * from 64-bit code. + */ +LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs ) +{ + EXCEPTION_RECORD *record = ptrs->ExceptionRecord; + CONTEXT *context = ptrs->ContextRecord; + + if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && + record->ExceptionInformation[0] == EXCEPTION_READ_FAULT) + { + if (emulate_instruction( record, context ) == ExceptionContinueExecution) + { + TRACE( "next instruction rip=%lx\n", context->Rip ); + TRACE( " rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx\n", + context->Rax, context->Rbx, context->Rcx, context->Rdx ); + TRACE( " rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n", + context->Rsi, context->Rdi, context->Rbp, context->Rsp ); + TRACE( " r8=%016lx r9=%016lx r10=%016lx r11=%016lx\n", + context->R8, context->R9, context->R10, context->R11 ); + TRACE( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", + context->R12, context->R13, context->R14, context->R15 ); + + return EXCEPTION_CONTINUE_EXECUTION; + } + } + return EXCEPTION_CONTINUE_SEARCH; +} + +#endif /* __x86_64__ */ diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 2051939bfad..35454a5b632 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -2069,7 +2069,7 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls( inst ); -#ifdef __i386__ +#if defined(__i386__) || defined(__x86_64__) handler = RtlAddVectoredExceptionHandler( TRUE, vectored_handler ); #endif KeQueryTickCount( &count ); /* initialize the global KeTickCount */