/*++ Copyright (c) 2000 Microsoft Corporation Module Name: ia32emul.c Abstract: This module implements an x86 instruction decoder and emulator. Author: Samer Arafeh (samera) 30-Oct-2000 Environment: Kernel mode only. Revision History: --*/ #include "ki.h" #include "ia32def.h" #include "wow64t.h" #if DBG BOOLEAN KiIa32InstructionEmulationDbg = 0; #endif #define KiIa32GetX86Eflags(efl) efl.Value = __getReg(CV_IA64_AR24) #define KiIa32SetX86Eflags(efl) __setReg(CV_IA64_AR24, efl.Value) #define IA32_GETEFLAGS_CF(efl) (efl & 0x01UI64) // // Ia32 instruction handlers // NTSTATUS KiIa32InstructionAdc ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); NTSTATUS KiIa32InstructionAdd ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); NTSTATUS KiIa32InstructionArithmeticBitwiseHelper ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); NTSTATUS KiIa32InstructionBitTestHelper ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); NTSTATUS KiIa32InstructionOneParamHelper ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); NTSTATUS KiIa32InstructionXadd ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); NTSTATUS KiIa32InstructionXchg ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); NTSTATUS KiIa32InstructionCmpXchg ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); NTSTATUS KiIa32InstructionCmpXchg8b ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); NTSTATUS KiIa32InstructionMoveSeg ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ); // // Opcode Ids // typedef enum _IA32_OPCODE { Ia32_Adc, Ia32_Add, Ia32_And, Ia32_Bt, Ia32_Btc, Ia32_Btr, Ia32_Bts, Ia32_Cmpxchg, Ia32_Cmpxchg8b, Ia32_Dec, Ia32_Inc, Ia32_Neg, Ia32_Not, Ia32_Or, Ia32_Sbb, Ia32_Sub, Ia32_Xadd, Ia32_Xchg, Ia32_Xor, Ia32_MovToSeg, // // This needs always to be the last element // Ia32_LastOpcode } IA32_OPCODE; // // Array of Ia32 instruction handlers // NOTE : The following table must be in sync with the above enum. // typedef NTSTATUS (*IA32_INSTRUCTION_HANDLER) (PKTRAP_FRAME, PIA32_INSTRUCTION); IA32_INSTRUCTION_HANDLER KiIa32InstructionHandler [] = { KiIa32InstructionAdc, KiIa32InstructionAdd, KiIa32InstructionArithmeticBitwiseHelper, KiIa32InstructionBitTestHelper, KiIa32InstructionBitTestHelper, KiIa32InstructionBitTestHelper, KiIa32InstructionBitTestHelper, KiIa32InstructionCmpXchg, KiIa32InstructionCmpXchg8b, KiIa32InstructionOneParamHelper, KiIa32InstructionOneParamHelper, KiIa32InstructionOneParamHelper, KiIa32InstructionOneParamHelper, KiIa32InstructionArithmeticBitwiseHelper, KiIa32InstructionAdc, KiIa32InstructionAdd, KiIa32InstructionXadd, KiIa32InstructionXchg, KiIa32InstructionArithmeticBitwiseHelper, KiIa32InstructionMoveSeg, NULL }; #if DBG PCHAR KiIa32InstructionHandlerNames [] = { "KiIa32InstructionAdc", "KiIa32InstructionAdd", "KiIa32InstructionAnd", "KiIa32InstructionBt", "KiIa32InstructionBtc", "KiIa32InstructionBtr", "KiIa32InstructionBts", "KiIa32InstructionCmpXchg", "KiIa32InstructionCmpXchg8b", "KiIa32InstructionDec", "KiIa32InstructionInc", "KiIa32InstructionNeg", "KiIa32InstructionNot", "KiIa32InstructionOr", "KiIa32InstructionSbb", "KiIa32InstructionSub", "KiIa32InstructionXadd", "KiIa32InstructionXchg", "KiIa32InstructionXor", "KiIa32InstructionMoveSeg", NULL, }; #endif IA32_OPCODE_DESCRIPTION OpcodesDescription[] = { // // Adc // // Adc r/m8, imm8 { 0x80, 0x00, 0x02, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Adc }, // Adc r/m, imm { 0x81, 0x00, 0x02, 0x11, IA32_PARAM_RM_IMM, Ia32_Adc }, // Adc r/m, imm8 (sign) { 0x83, 0x00, 0x02, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Adc }, // Adc r/m8, r8 { 0x10, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Adc }, // Adc r/m, r { 0x11, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Adc }, // Adc r, r/m8 { 0x12, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Adc }, // Adc r, r/m { 0x13, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Adc }, // // Add // // Add r/m8, imm8 { 0x80, 0x00, 0x00, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Add }, // Add r/m, imm { 0x81, 0x00, 0x00, 0x11, IA32_PARAM_RM_IMM, Ia32_Add }, // Add r/m, imm8 (sign) { 0x83, 0x00, 0x00, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Add }, // Add r/m8, r8 { 0x00, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Add }, // Add r/m, r { 0x01, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Add }, // Add r, r/m8 { 0x02, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Add }, // Add r, r/m { 0x03, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Add }, // // And // // And r/m8, imm8 { 0x80, 0x00, 0x04, 0x11, IA32_PARAM_RM8_IMM8, Ia32_And }, // And r/m, imm { 0x81, 0x00, 0x04, 0x11, IA32_PARAM_RM_IMM, Ia32_And }, // And r/m, imm8 { 0x83, 0x00, 0x04, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_And }, // And r/m8, r8 { 0x20, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_And }, // And rm, r { 0x21, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_And }, // And r8, r/m8 { 0x22, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_And }, // And r, r/m { 0x23, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_And }, // // Or // // Or r/m8, imm8 { 0x80, 0x00, 0x01, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Or }, // Or r/m, imm { 0x81, 0x00, 0x01, 0x11, IA32_PARAM_RM_IMM, Ia32_Or }, // Or r/m, imm8 { 0x83, 0x00, 0x01, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Or }, // Or r/m8, r8 { 0x08, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Or }, // Or rm, r { 0x09, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Or }, // Or r8, r/m8 { 0x0a, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Or }, // Or r, r/m { 0x0b, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Or }, // // Xor // // Xor r/m8, imm8 { 0x80, 0x00, 0x06, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Xor }, // Xor r/m, imm { 0x81, 0x00, 0x06, 0x11, IA32_PARAM_RM_IMM, Ia32_Xor }, // Xor r/m, imm8 { 0x83, 0x00, 0x06, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Xor }, // Xor r/m8, r8 { 0x30, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Xor }, // Xor rm, r { 0x31, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Xor }, // Xor r8, r/m8 { 0x32, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Xor }, // Xor r, r/m { 0x33, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Xor }, // // Inc // // Inc r/m8 { 0xfe, 0x00, 0x00, 0x11, IA32_PARAM_RM8, Ia32_Inc }, // Inc r/m { 0xff, 0x00, 0x00, 0x11, IA32_PARAM_RM, Ia32_Inc }, // // Dec // // Dec r/m8 { 0xfe, 0x00, 0x01, 0x11, IA32_PARAM_RM8, Ia32_Dec }, // Dec r/m { 0xff, 0x00, 0x01, 0x11, IA32_PARAM_RM, Ia32_Dec }, // // Xchg // // Xchg r/m8, r { 0x86, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Xchg }, // Xchg r/m, r { 0x87, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Xchg }, // // Cmpxchg // // Cmpxchg r/m8, r { 0x0f, 0xb0, 0x00, 0x02, IA32_PARAM_RM8_R, Ia32_Cmpxchg }, // Cmpxchg r/m, r { 0x0f, 0xb1, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Cmpxchg }, // // Cmpxchg8b // // Cmpxchg8b m64 { 0x0f, 0xc7, 0x01, 0x12, IA32_PARAM_RM, Ia32_Cmpxchg8b }, // // Xadd // // Xadd r/m8, r { 0x0f, 0xc0, 0x00, 0x02, IA32_PARAM_RM8_R, Ia32_Xadd }, // Xadd r/m, r { 0x0f, 0xc1, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Xadd }, // // Neg // // Neg r/m8 { 0xf6, 0x00, 0x03, 0x11, IA32_PARAM_RM8, Ia32_Neg }, // Neg r/m { 0xf7, 0x00, 0x03, 0x11, IA32_PARAM_RM, Ia32_Neg }, // // Not // // Not r/m8 { 0xf6, 0x00, 0x02, 0x11, IA32_PARAM_RM8, Ia32_Not }, // Not r/m { 0xf7, 0x00, 0x02, 0x11, IA32_PARAM_RM, Ia32_Not }, // // Bt (Bit Test) // // Bt r/m, r { 0x0f, 0xa3, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Bt }, // Bt r/m, imm8 { 0x0f, 0xba, 0x04, 0x12, IA32_PARAM_RM_IMM8SIGN, Ia32_Bt }, // // Btc // // Btc r/m, r { 0x0f, 0xbb, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Btc }, // Btc r/m, imm8 { 0x0f, 0xba, 0x07, 0x12, IA32_PARAM_RM_IMM8SIGN, Ia32_Btc }, // // Btr // // Btr r/m, r { 0x0f, 0xb3, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Btr }, // Btr r/m, imm8 { 0x0f, 0xba, 0x06, 0x12, IA32_PARAM_RM_IMM8SIGN, Ia32_Btr }, // // Bts // // Bts r/m, r { 0x0f, 0xab, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Bts }, // Bts r/m, imm8 { 0x0f, 0xba, 0x05, 0x12, IA32_PARAM_RM_IMM8SIGN, Ia32_Bts }, // // Sub // // Sub r/m8, imm8 { 0x80, 0x00, 0x05, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Sub }, // Sub r/m, imm { 0x81, 0x00, 0x05, 0x11, IA32_PARAM_RM_IMM, Ia32_Sub }, // Sub r/m, imm8 (sign) { 0x83, 0x00, 0x05, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Sub }, // Sub r/m8, r8 { 0x28, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Sub }, // Sub r/m, r { 0x29, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Sub }, // Sub r, r/m8 { 0x2a, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Sub }, // Sub r, r/m { 0x2b, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Sub }, // // Sbb // // Sbb r/m8, imm8 { 0x80, 0x00, 0x03, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Sbb }, // Sbb r/m, imm { 0x81, 0x00, 0x03, 0x11, IA32_PARAM_RM_IMM, Ia32_Sbb }, // Sbb r/m, imm8 (sign) { 0x83, 0x00, 0x03, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Sbb }, // Sbb r/m8, r8 { 0x18, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Sbb }, // Sbb r/m, r { 0x19, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Sbb }, // Sbb r, r/m8 { 0x1a, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Sbb }, // Sbb r, r/m { 0x1b, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Sbb }, // // Mov // // Mov seg-reg, r/m8 { 0x8e, 0x00, 0x00, 0x01, IA32_PARAM_SEGREG_RM8, Ia32_MovToSeg }, // Mov seg-reg, r/m { 0x8e, 0x00, 0x00, 0x01, IA32_PARAM_SEGREG_RM, Ia32_MovToSeg }, }; // // Fast mutex that will serialize access to the instruction // emulator when the lock prefix is set. // FAST_MUTEX KiIa32MisalignedLockFastMutex; #define KiIa32AcquireMisalignedLockFastMutex() ExAcquireFastMutex(&KiIa32MisalignedLockFastMutex) #define KiIa32ReleaseMisalignedLockFastMutex() ExReleaseFastMutex(&KiIa32MisalignedLockFastMutex) // // This table contains the offset into the KTRAP_FRAME // for the appropriate register. This table is based on the // needs of the x86 instruction R/M bits // const ULONG RegOffsetTable[8] = { FIELD_OFFSET(KTRAP_FRAME, IntV0), // EAX FIELD_OFFSET(KTRAP_FRAME, IntT2), // ECX FIELD_OFFSET(KTRAP_FRAME, IntT3), // EDX FIELD_OFFSET(KTRAP_FRAME, IntT4), // EBX FIELD_OFFSET(KTRAP_FRAME, IntSp), // ESP FIELD_OFFSET(KTRAP_FRAME, IntTeb), // EBP FIELD_OFFSET(KTRAP_FRAME, IntT5), // ESI FIELD_OFFSET(KTRAP_FRAME, IntT6) // EDI }; ULONG_PTR GetX86RegOffset ( IN PKTRAP_FRAME TrapFrame, IN ULONG RegisterBase ) /*++ Routine Description: Retreives the offset into the aliased ia64 register for the ia32 register inside the trap frame. Arguments: TrapFrame - Pointer to TrapFrame on the stack. RegisterBase - Register number to retrieve the offset for. Return Value: Address of ia64 alias register for the ia32 register. --*/ { return (ULONG_PTR)((PCHAR)TrapFrame + RegOffsetTable[RegisterBase]); } ULONG GetX86Reg ( IN PKTRAP_FRAME TrapFrame, IN ULONG RegisterBase ) /*++ Routine Description: Retreives the ia32 register value. Arguments: TrapFrame - Pointer to TrapFrame on the stack. RegisterBase - Register number to retrieve the value for. Return Value: Ia32 register context. --*/ { return (ULONG)(*(PULONG_PTR)GetX86RegOffset(TrapFrame, RegisterBase)); } NTSTATUS KiIa32InitializeLockFastMutex ( VOID ) /*++ Routine Description: Initializes the misaligned lock fast mutex. Used to serialize access if the r/m address is misaligned. Arguments: None. Return Value: NTSTATUS. --*/ { ExInitializeFastMutex (&KiIa32MisalignedLockFastMutex); return STATUS_SUCCESS; } LONG KiIa32ComputeSIBAddress( IN PKTRAP_FRAME Frame, IN LONG Displacement, IN UCHAR Sib, IN UCHAR ModRm ) /*++ Routine Description: Compute an effective address based on the SIB bytes in an instruction using the register values in the trap frame Arguments: Frame - Pointer to iA32 TrapFrame in the stack. Displacement - The value of the displacement byte. If no displacement, this value should be passed in as zero. Sib - The sib byte that is causing all the trouble. ModRm - ModRm instruction value Return Value: The effective address to use for the memory operation --*/ { LONG Base; LONG Index; LONG Scale; // // First get the base address that we will be using // if ((Sib & MI_SIB_BASEMASK) == 5) { // // Handle the special case where we don't use EBP for the base // // // EBP is an implicit reg-base if the Mod is not zero. // if ((ModRm >> MI_MODSHIFT) != 0) { Base = GetX86Reg (Frame, IA32_REG_EBP); } else { Base = 0; } } else { Base = GetX86Reg (Frame, (Sib & MI_SIB_BASEMASK) >> MI_SIB_BASESHIFT); } // // Now get the Index // if ((Sib & MI_SIB_INDEXMASK) == MI_SIB_INDEXNONE) { // // Handle the special case where we don't have an index // Index = 0; } else { Index = GetX86Reg (Frame, (Sib & MI_SIB_INDEXMASK) >> MI_SIB_INDEXSHIFT); } Scale = 1 << ((Sib & MI_SIB_SSMASK) >> MI_SIB_SSSHIFT); return (Base + (Index * Scale) + Displacement); } BOOLEAN KiIa32Compute32BitEffectiveAddress( IN PKTRAP_FRAME Frame, IN OUT PUCHAR *InstAddr, OUT PUINT_PTR Addr, OUT PBOOLEAN RegisterMode ) /*++ Routine Description: Compute an effective address based on bytes in memory and the register values passed in via the ia64 stack frame. The addressing mode is assumed to be 32-bit. Arguments: Frame - Pointer to iA32 TrapFrame in the stack InstAddr - Pointer to the first byte after the opcode. Addr - Effective address. RegisterMode - Indicates whether the effective address is inside a register or memory. Return Value: Returns TRUE if able to compute the EA, else returns FALSE. Note: Does not verify permission on an Effective Address. It only computes the value and lets someone else worry if the process should have access to that memory location. --*/ { UNALIGNED ULONG * UlongAddress; UCHAR ModRm; UCHAR Sib = 0; LONG UNALIGNED *DisplacementPtr; BOOLEAN ReturnCode = TRUE; // // This needs to be a signed value. Start off assuming no displacement // LONG Displacement = 0; try { ModRm = *(*InstAddr)++; // // handle the register case first // if ((ModRm >> MI_MODSHIFT) == 3) { // // yup, we have a register - the easy case... // *Addr = GetX86RegOffset (Frame, ModRm & MI_RMMASK); *RegisterMode = TRUE; return ReturnCode; } *RegisterMode = FALSE; // // See if we have a SIB // if ((ModRm & MI_RMMASK) == 4) { Sib = *(*InstAddr)++; } // // Now decode the destination bits // switch (ModRm >> MI_MODSHIFT) { case 0: // // We have an indirect through a register // switch (ModRm & MI_RMMASK) { case 4: // // Deal with the SIB // *Addr = KiIa32ComputeSIBAddress (Frame, Displacement, Sib, ModRm); break; case 5: // // We have a 32-bit indirect... // UlongAddress = (UNALIGNED ULONG *)*InstAddr; *Addr = *UlongAddress; *InstAddr = (PUCHAR) (UlongAddress + 1); break; default: // // The default case is get the address from the register // *Addr = GetX86Reg (Frame, (ModRm & MI_RMMASK)); break; } break; case 1: // // we have an 8 bit displacement, so grab the next byte // Displacement = (signed char) (*(*InstAddr)++); if ((ModRm & MI_RMMASK) == 4) { // // Have a SIB, so do that // *Addr = KiIa32ComputeSIBAddress (Frame, Displacement, Sib, ModRm); } else { // // No SIB, life is easy // *Addr = GetX86Reg (Frame, (ModRm & MI_RMMASK)) + Displacement; } break; case 2: // // we have a 32-bit displacement, so grab the next 4 bytes // DisplacementPtr = (PLONG) (*InstAddr); Displacement = *DisplacementPtr++; *InstAddr = (PUCHAR)DisplacementPtr; if ((ModRm & MI_RMMASK) == 4) { // // Have a SIB, so do that // *Addr = KiIa32ComputeSIBAddress (Frame, Displacement, Sib, ModRm); } else { // // No SIB, life is easy // *Addr = GetX86Reg (Frame, (ModRm & MI_RMMASK)) + Displacement; } break; default: // // we should have handled case 3 (register access) // before getting here... // ReturnCode = FALSE; break; } } except (EXCEPTION_EXECUTE_HANDLER) { ReturnCode = FALSE; #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32Compute32BitEffectiveAddress - Exception %lx\n", GetExceptionCode()); } #endif } // // Make sure the address stays within 4GB range // if (ReturnCode == TRUE) { *Addr = (*Addr & 0x000000007fffffffI64); } return ReturnCode; } BOOLEAN KiIa32Compute16BitEffectiveAddress ( IN PKTRAP_FRAME Frame, IN OUT PUCHAR *InstAddr, OUT PUINT_PTR Addr, OUT PBOOLEAN RegisterMode ) /*++ Routine Description: Compute an effective address based on bytes in memory and the register values passed in via the ia64 stack frame. The addressing mode is assumed to be 16-bit. Arguments: Frame - Pointer to iA32 TrapFrame in the stack. InstAddr - Pointer to the first byte after the opcode. Addr - Effective address. RegisterMode - Indicates whether the effective address is inside a register or memory. Return Value: Returns TRUE if able to compute the EA, else returns FALSE. Note: Does not verify permission on an Effective Address. It only computes the value and lets someone else worry if the process should have access to that memory location. --*/ { UCHAR ModRm; UCHAR DisplacementType = IA32_DISP_NONE; USHORT UNALIGNED *Disp16; LONG EffectiveAddress = 0; BOOLEAN ReturnCode = TRUE; try { // // Read in the Mod/Rm and increment the instruction address // ModRm = *(*InstAddr)++; *RegisterMode = FALSE; // // First pass // switch (ModRm >> MI_MODSHIFT) { case 0: if ((ModRm & MI_RMMASK) == 6) { Disp16 = (USHORT UNALIGNED *) InstAddr; *Addr = *Disp16; *InstAddr = (*InstAddr + 2); return ReturnCode; } DisplacementType = IA32_DISP_NONE; break; case 1: DisplacementType = IA32_DISP8; break; case 2: DisplacementType = IA32_DISP16; break; case 3: *Addr = GetX86RegOffset (Frame, ModRm & MI_RMMASK); *RegisterMode = TRUE; return ReturnCode; } // // Second pass // switch (ModRm & MI_RMMASK) { case 0: EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBX) & 0xffff) + (GetX86Reg(Frame, IA32_REG_ESI) & 0xffff) ; break; case 1: EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBX) & 0xffff) + (GetX86Reg(Frame, IA32_REG_EDI) & 0xffff) ; break; case 2: EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBP) & 0xffff) + (GetX86Reg(Frame, IA32_REG_ESI) & 0xffff) ; break; case 3: EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBP) & 0xffff) + (GetX86Reg(Frame, IA32_REG_EDI) & 0xffff) ; break; case 4: EffectiveAddress = (GetX86Reg(Frame, IA32_REG_ESI) & 0xffff); break; case 5: EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EDI) & 0xffff); break; case 6: EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBP) & 0xffff); break; case 7: EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBX) & 0xffff); break; } // // Read the displacement, if any // if (DisplacementType != IA32_DISP_NONE) { switch (DisplacementType) { case IA32_DISP8: { EffectiveAddress += (LONG) (**InstAddr); *InstAddr = *InstAddr + 1; } break; case IA32_DISP16: { Disp16 = (USHORT UNALIGNED *) InstAddr; EffectiveAddress += (LONG) *Disp16; *InstAddr = *InstAddr + 2; } break; default: #if DBG DbgPrint("KE: KiIa32Compute16BitEffectiveAddress - Invalid displacement type %lx\n", DisplacementType); #endif ReturnCode = FALSE; break; } } *Addr = EffectiveAddress; } except (EXCEPTION_EXECUTE_HANDLER) { #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32Compute16BitEffectiveAddress - Exception %lx\n", GetExceptionCode()); } #endif ReturnCode = FALSE; } // // Make sure the address stays within 4GB range // if (ReturnCode == TRUE) { *Addr = (*Addr & 0x000000007fffffffI64); } return ReturnCode; } NTSTATUS KiIa32UpdateFlags ( IN PIA32_INSTRUCTION Instruction, IN ULONGLONG Operand1, IN ULONGLONG Result, IN ULONG Ia32Eflags ) /*++ Routine Description: Updates the Ia32 specified eflags according to the result value. Arguments: Instruction - Pointer to the instruction being processed. Operand1 - First operand (value) of the instruction being emulated. Result - Result value. Ia32Eflags - Specific flags to update based on the result value. Return Value: NTSTATUS --*/ { ULONGLONG Temp = 0; IA32_EFLAGS Eflags = Instruction->Eflags; // // Sanitize the destination value. // Result = (Result & MAXULONG); if ((Ia32Eflags & IA32_EFLAGS_CF) != 0) { if (Result > Instruction->OperandMask) { Eflags.u.cf = 1; } else { Eflags.u.cf = 0; } } if ((Ia32Eflags & IA32_EFLAGS_OF) != 0) { if (((Operand1 & Result) & 0x80000000UI64) != 0) { Eflags.u.of = 1; } else { Eflags.u.of = 0; } } if ((Ia32Eflags & IA32_EFLAGS_SF) != 0) { switch (Instruction->OperandSize) { case 0xff: Temp = 0x80UI64; break; case 0xffff: Temp = 0x8000UI64; break; case 0xffffffff: Temp = 0x80000000UI64; break; } if (Result & Temp) { Eflags.u.sf = 1; } else { Eflags.u.sf = 0; } } if ((Ia32Eflags & IA32_EFLAGS_ZF) != 0) { if (Result == 0) { Eflags.u.zf = 1; } else { Eflags.u.zf = 0; } } if ((Ia32Eflags & IA32_EFLAGS_AF) != 0) { Eflags.u.af = (((Operand1 ^ Result) >> 4) & 0x01UI64); } // // This needs to be the last one as it modifies the 'Result' // if ((Ia32Eflags & IA32_EFLAGS_PF) != 0) { Result = Result & Instruction->OperandMask; Temp = 0; while (Result) { Result = (Result & (Result - 1)); Temp++; } if ((Temp & 0x01UI64) == 0) { Eflags.u.pf = 1; } else { Eflags.u.pf = 1; } } // // Reset reserved values. // Eflags.u.v1 = 1; Eflags.u.v2 = 0; Eflags.u.v3 = 0; Eflags.u.v4 = 0; // // Sanitize the flags // Eflags.Value = SANITIZE_AR24_EFLAGS (Eflags.Value, UserMode); Instruction->Eflags = Eflags; return STATUS_SUCCESS; } NTSTATUS KiIa32UpdateResult ( IN PIA32_INSTRUCTION Instruction, IN PIA32_OPERAND DestinationOperand, IN ULONGLONG Result ) /*++ Routine Description: Writes the result value taking into consideration operand size. Arguments: Instruction - Pointer to the instruction being processed. DestinationOperand - Operand to receive the result. Result - Result value to write Return Value: NTSTATUS --*/ { UNALIGNED USHORT *UshortPtr; UNALIGNED ULONG *UlongPtr; NTSTATUS NtStatus = STATUS_SUCCESS; // // Update results according to operand size // try { if (DestinationOperand->RegisterMode == FALSE) { if (DestinationOperand->v > MM_MAX_WOW64_ADDRESS) { return STATUS_ACCESS_VIOLATION; } } switch (Instruction->OperandSize) { case OPERANDSIZE_ONEBYTE: *(PUCHAR)DestinationOperand->v = (UCHAR)Result; break; case OPERANDSIZE_TWOBYTES: UshortPtr = (UNALIGNED USHORT *) DestinationOperand->v; *UshortPtr = (USHORT)Result; break; case OPERANDSIZE_FOURBYTES: UlongPtr =(UNALIGNED ULONG *) DestinationOperand->v; *UlongPtr = (ULONG)Result; break; default: #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32UpdateResult() - Invalid operand size - %lx - %p\n", Instruction->OperandSize, Instruction); } #endif NtStatus = STATUS_UNSUCCESSFUL; break; } } except (EXCEPTION_EXECUTE_HANDLER) { NtStatus = GetExceptionCode (); #if DBG DbgPrint("KE: KiIa32UpdateResult - Exception %lx - %p\n", NtStatus, Instruction); #endif } return NtStatus; } NTSTATUS KiIa32ReadOperand1 ( IN PIA32_INSTRUCTION Instruction, OUT PULONGLONG Operand1 ) /*++ Routine Description: Reads the first (destination) operand of an instruction. Arguments: Instruction - Pointer to the instruction being processed. Operand1 - Buffer to receive the operand value. Return Value: NTSTATUS --*/ { UNALIGNED ULONG *UlongPtr; UNALIGNED USHORT *UshortPtr; NTSTATUS NtStatus = STATUS_SUCCESS; try { switch (Instruction->Description->Type) { case IA32_PARAM_RM_IMM8SIGN: case IA32_PARAM_RM_IMM: case IA32_PARAM_RM_R: case IA32_PARAM_R_RM8: case IA32_PARAM_R_RM: case IA32_PARAM_RM: case IA32_PARAM_SEGREG_RM: if (Instruction->OperandSize == OPERANDSIZE_TWOBYTES) { UshortPtr = (UNALIGNED USHORT *) Instruction->Operand1.v; *Operand1 = (ULONGLONG) *UshortPtr; } else { UlongPtr = (UNALIGNED ULONG *) Instruction->Operand1.v; *Operand1 = (ULONGLONG) *UlongPtr; } break; case IA32_PARAM_RM8_IMM8: case IA32_PARAM_RM8_R: case IA32_PARAM_RM8: case IA32_PARAM_SEGREG_RM8: *Operand1 = (ULONGLONG) (*(PUCHAR)Instruction->Operand1.v); break; default: #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32ReadRm - Invalid opcode type %lx - %p\n", Instruction->Description->Type, Instruction); } NtStatus = STATUS_UNSUCCESSFUL; #endif break; } } except (EXCEPTION_EXECUTE_HANDLER) { NtStatus = GetExceptionCode (); #if DBG DbgPrint("KE: KiIa32ReadOperand1 - Exception %lx - %p\n", NtStatus, Instruction); #endif } return NtStatus; } NTSTATUS KiIa32ReadOperand2 ( IN PIA32_INSTRUCTION Instruction, OUT PULONGLONG Operand2 ) /*++ Routine Description: Reads the second (source) operand of an instruction. Arguments: Instruction - Pointer to the instruction being processed. Operand1 - Buffer to receive the operand value. Return Value: NTSTATUS --*/ { UNALIGNED ULONG *UlongPtr; UNALIGNED USHORT *UshortPtr; NTSTATUS NtStatus = STATUS_SUCCESS; try { switch (Instruction->Description->Type) { case IA32_PARAM_RM8_IMM8: case IA32_PARAM_RM_IMM8SIGN: *Operand2 = (UCHAR)Instruction->Operand2.v; break; case IA32_PARAM_RM_IMM: *Operand2 = Instruction->Operand2.v & Instruction->OperandMask; break; case IA32_PARAM_RM8_R: case IA32_PARAM_R_RM8: *Operand2 = (ULONGLONG)(*(PUCHAR)Instruction->Operand2.v); break; case IA32_PARAM_RM_R: case IA32_PARAM_R_RM: if (Instruction->OperandSize == OPERANDSIZE_TWOBYTES) { UshortPtr = (UNALIGNED USHORT *) Instruction->Operand2.v; *Operand2 = (ULONGLONG) *UshortPtr; } else { UlongPtr = (UNALIGNED ULONG *) Instruction->Operand2.v; *Operand2 = (ULONGLONG) *UlongPtr; } break; case IA32_PARAM_SEGREG_RM8: case IA32_PARAM_SEGREG_RM: break; default: #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32ReadOperand2 - Invalid type %lx - %p\n", Instruction->Description->Type, Instruction); } NtStatus = STATUS_UNSUCCESSFUL; #endif break; } } except (EXCEPTION_EXECUTE_HANDLER) { NtStatus = GetExceptionCode (); #if DBG DbgPrint("KE: KiIa32ReadOperand2 - Exception %lx - %p\n", NtStatus, Instruction); #endif } return NtStatus; } NTSTATUS KiIa32InstructionAddWithIncrement ( IN PIA32_INSTRUCTION Instruction, IN ULONG Increment ) /*++ Routine Description: Common routine implementing Ia32 add, adc, sub and sbb instructions. Arguments: Instruction - Pointer to the instruction being processed. Increment - Specifies the carry value. Return Value: NTSTATUS --*/ { ULONGLONG UlongDst; ULONGLONG UlongSrc; ULONGLONG Operand1; UCHAR Imm8; char SignImm8; BOOLEAN Subtract; NTSTATUS NtStatus; switch (Instruction->Description->Opcode) { case Ia32_Add: case Ia32_Adc: Subtract = FALSE; break; case Ia32_Sub: case Ia32_Sbb: Subtract = TRUE; break; default: #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32InstructionAddWithIncrement - Invalid opcode %lx - %p\n", Instruction->Description->Opcode, Instruction); } #endif return STATUS_UNSUCCESSFUL; break; } NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst); if (NT_SUCCESS (NtStatus)) { Operand1 = UlongDst; NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc); if (NT_SUCCESS (NtStatus)) { switch (Instruction->Description->Type) { case IA32_PARAM_RM_IMM8SIGN: SignImm8 = (char) UlongSrc; if (Subtract) UlongDst = (UlongDst - (Increment + SignImm8)); else UlongDst = UlongDst + Increment + SignImm8; break; case IA32_PARAM_RM8_IMM8: Imm8 = (UCHAR) UlongSrc; if (Subtract) UlongDst = (UlongDst - (Increment + Imm8)); else UlongDst = UlongDst + Increment + Imm8; break; case IA32_PARAM_RM_IMM: default: if (Subtract) UlongDst = (UlongDst - (Increment + UlongSrc)); else UlongDst = UlongDst + Increment + UlongSrc; break; } // // Update results according to operand size // NtStatus = KiIa32UpdateResult ( Instruction, &Instruction->Operand1, UlongDst ); // // Eflags update // if (NT_SUCCESS (NtStatus)) { KiIa32UpdateFlags ( Instruction, Operand1, UlongDst, (IA32_EFLAGS_CF | IA32_EFLAGS_SF | IA32_EFLAGS_OF | IA32_EFLAGS_PF | IA32_EFLAGS_ZF | IA32_EFLAGS_AF) ); } } } return NtStatus; } NTSTATUS KiIa32InstructionAdc ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Adc instruction handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { return KiIa32InstructionAddWithIncrement ( Instruction, (ULONG)Instruction->Eflags.u.cf); UNREFERENCED_PARAMETER (TrapFrame); } NTSTATUS KiIa32InstructionAdd ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Add instruction handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { return KiIa32InstructionAddWithIncrement ( Instruction, 0); UNREFERENCED_PARAMETER (TrapFrame); } NTSTATUS KiIa32InstructionArithmeticBitwiseHelper ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: And, Or & Xor instructions handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { ULONGLONG UlongDst; ULONGLONG UlongSrc; ULONGLONG Operand1; NTSTATUS NtStatus; NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst); if (NT_SUCCESS (NtStatus)) { Operand1 = UlongDst; NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc); if (NT_SUCCESS (NtStatus)) { switch (Instruction->Description->Opcode) { case Ia32_And: UlongDst = UlongDst & UlongSrc; break; case Ia32_Or: UlongDst = UlongDst | UlongSrc; break; case Ia32_Xor: UlongDst = UlongDst ^ UlongSrc; break; default: #if DBG NtStatus = STATUS_UNSUCCESSFUL; if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32InstructionBitwiseHelper - Invalid operation %lx - %p\n", Instruction->Description->Opcode, Instruction); } #endif break; } if (NT_SUCCESS (NtStatus)) { NtStatus = KiIa32UpdateResult ( Instruction, &Instruction->Operand1, UlongDst ); if (NT_SUCCESS (NtStatus)) { NtStatus = KiIa32UpdateFlags ( Instruction, Operand1, UlongDst, (IA32_EFLAGS_SF | IA32_EFLAGS_PF | IA32_EFLAGS_ZF) ); Instruction->Eflags.u.cf = 0; Instruction->Eflags.u.of = 0; } } } } return NtStatus; UNREFERENCED_PARAMETER (TrapFrame); } NTSTATUS KiIa32InstructionBitTestHelper ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Bt, Bts, Btr & Btc instructions handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { ULONGLONG UlongDst; ULONGLONG UlongSrc; ULONGLONG BitTestResult; NTSTATUS NtStatus; NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc); if (NT_SUCCESS (NtStatus)) { if (Instruction->Operand2.RegisterMode == TRUE) { if (Instruction->Prefix.b.AddressOverride == 1) { Instruction->Operand1.v += ((UlongSrc >> 4) << 1); UlongSrc &= 0x0f; } else { Instruction->Operand1.v += ((UlongSrc >> 5) << 2); UlongSrc &= 0x1f; } } NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst); if (NT_SUCCESS (NtStatus)) { BitTestResult = (UlongDst & (1 << UlongSrc)); if (BitTestResult) { Instruction->Eflags.u.cf = 1; } else { Instruction->Eflags.u.cf = 0; } switch (Instruction->Description->Opcode) { case Ia32_Btc: UlongDst ^= (1 << UlongSrc); break; case Ia32_Btr: UlongDst &= (~(1 << UlongSrc)); break; case Ia32_Bts: UlongDst |= (1 << UlongSrc); break; } NtStatus = KiIa32UpdateResult ( Instruction, &Instruction->Operand1, UlongDst ); } } return NtStatus; UNREFERENCED_PARAMETER (TrapFrame); } NTSTATUS KiIa32InstructionOneParamHelper ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Inc, Dec, Neg & Not instructions handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { UCHAR Opcode; ULONG FlagsAffected = 0; ULONGLONG UlongDst; ULONGLONG UlongSrc; NTSTATUS NtStatus; NtStatus = KiIa32ReadOperand1 ( Instruction, &UlongDst ); if (NT_SUCCESS (NtStatus)) { UlongSrc = UlongDst; Opcode = Instruction->Description->Opcode; switch (Opcode) { case Ia32_Inc: UlongDst += 1; break; case Ia32_Dec: UlongDst -= 1; break; case Ia32_Neg: UlongDst = -(LONGLONG)UlongDst; break; case Ia32_Not: UlongDst = ~UlongDst; break; } NtStatus = KiIa32UpdateResult ( Instruction, &Instruction->Operand1, UlongDst ); if (NT_SUCCESS (NtStatus)) { switch (Opcode) { case Ia32_Inc: case Ia32_Dec: FlagsAffected = (IA32_EFLAGS_SF | IA32_EFLAGS_PF | IA32_EFLAGS_ZF); break; case Ia32_Neg: if (UlongDst == 0) Instruction->Eflags.u.cf = 0; else Instruction->Eflags.u.cf = 1; FlagsAffected = (IA32_EFLAGS_SF | IA32_EFLAGS_PF | IA32_EFLAGS_ZF | IA32_EFLAGS_AF | IA32_EFLAGS_OF); break; } if (FlagsAffected != 0) { NtStatus = KiIa32UpdateFlags ( Instruction, UlongSrc, UlongDst, FlagsAffected ); } } } return NtStatus; UNREFERENCED_PARAMETER (TrapFrame); } NTSTATUS KiIa32InstructionXadd ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Xadd instruction handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { ULONGLONG UlongDst; ULONGLONG UlongSrc; ULONGLONG Operand1; ULONGLONG Temp; NTSTATUS NtStatus; NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst); if (NT_SUCCESS (NtStatus)) { Operand1 = UlongDst; NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc); if (NT_SUCCESS (NtStatus)) { Temp = UlongDst; UlongDst += UlongSrc; NtStatus = KiIa32UpdateResult ( Instruction, &Instruction->Operand1, UlongDst ); if (NT_SUCCESS (NtStatus)) { NtStatus = KiIa32UpdateResult ( Instruction, &Instruction->Operand2, Temp ); if (NT_SUCCESS (NtStatus)) { NtStatus = KiIa32UpdateFlags ( Instruction, Operand1, UlongDst, (IA32_EFLAGS_CF | IA32_EFLAGS_SF | IA32_EFLAGS_PF | IA32_EFLAGS_ZF | IA32_EFLAGS_OF | IA32_EFLAGS_AF) ); } } } } return NtStatus; UNREFERENCED_PARAMETER (TrapFrame); } NTSTATUS KiIa32InstructionXchg ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Xchg instruction handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { ULONGLONG UlongDst; ULONGLONG UlongSrc; NTSTATUS NtStatus; NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst); if (NT_SUCCESS (NtStatus)) { NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc); if (NT_SUCCESS (NtStatus)) { NtStatus = KiIa32UpdateResult ( Instruction, &Instruction->Operand1, UlongSrc ); if (NT_SUCCESS (NtStatus)) { NtStatus = KiIa32UpdateResult ( Instruction, &Instruction->Operand2, UlongDst ); } } } return NtStatus; UNREFERENCED_PARAMETER (TrapFrame); } NTSTATUS KiIa32InstructionCmpXchg ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Cmpxchg instruction handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { ULONGLONG UlongDst; ULONGLONG UlongSrc; ULONGLONG Accumulator; IA32_OPERAND AccumulatorOperand; NTSTATUS NtStatus; NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst); if (NT_SUCCESS (NtStatus)) { NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc); if (NT_SUCCESS (NtStatus)) { Accumulator = GetX86Reg (TrapFrame, IA32_REG_EAX); Accumulator &= Instruction->OperandMask; if (Accumulator == UlongDst) { Instruction->Eflags.u.zf = 1; NtStatus = KiIa32UpdateResult ( Instruction, &Instruction->Operand1, UlongSrc ); } else { Instruction->Eflags.u.zf = 0; AccumulatorOperand.RegisterMode = TRUE; AccumulatorOperand.v = GetX86RegOffset (TrapFrame, IA32_REG_EAX); NtStatus = KiIa32UpdateResult ( Instruction, &AccumulatorOperand, UlongDst ); } if (NT_SUCCESS (NtStatus)) { NtStatus = KiIa32UpdateFlags ( Instruction, UlongDst, UlongDst, (IA32_EFLAGS_CF | IA32_EFLAGS_SF | IA32_EFLAGS_PF | IA32_EFLAGS_OF | IA32_EFLAGS_AF) ); } } } return NtStatus; } NTSTATUS KiIa32InstructionCmpXchg8b ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Cmpxchg8b instruction handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { UNALIGNED ULONGLONG *UlongDst; UNALIGNED ULONGLONG *UlongSrc; ULONGLONG EdxEax; EdxEax = GetX86Reg (TrapFrame, IA32_REG_EDX); EdxEax <<= 32; EdxEax |= GetX86Reg (TrapFrame, IA32_REG_EAX); UlongDst = (PULONGLONG)Instruction->Operand1.v; if (*UlongDst == EdxEax) { Instruction->Eflags.u.zf = 1; *UlongDst = ((((ULONGLONG) GetX86Reg (TrapFrame, IA32_REG_ECX)) << 32) | ((ULONGLONG) GetX86Reg (TrapFrame, IA32_REG_EBX))); } else { Instruction->Eflags.u.zf = 0; UlongSrc = (PULONGLONG) GetX86RegOffset (TrapFrame, IA32_REG_EDX); *UlongSrc = ((*UlongDst) >> 32); UlongSrc = (PULONGLONG) GetX86RegOffset (TrapFrame, IA32_REG_EAX); *UlongSrc = ((*UlongDst) & 0xffffffff); } return STATUS_SUCCESS; } NTSTATUS KiIa32InstructionMoveSeg ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Mov Seg-Reg instruction handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { return STATUS_NOT_IMPLEMENTED; UNREFERENCED_PARAMETER (TrapFrame); UNREFERENCED_PARAMETER (Instruction); } NTSTATUS KiIa32LocateInstruction ( IN PKTRAP_FRAME TrapFrame, OUT PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Searches the OpcodeDescription table for matching instruction. Fills any relevant prefix values inside the Instruction structure. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to an Instruction structure to receive the opcode description. Return Value: NTSTATUS --*/ { BOOLEAN PrefixLoop; BOOLEAN Match; UCHAR ByteValue; UCHAR ByteBuffer[4]; PUCHAR RegOpcodeByte; PIA32_OPCODE_DESCRIPTION OpcodeDescription; ULONG Count; NTSTATUS NtStatus = STATUS_SUCCESS; PrefixLoop = TRUE; while (PrefixLoop) { try { ByteValue = ProbeAndReadUchar ((PUCHAR)Instruction->Eip); } except (EXCEPTION_EXECUTE_HANDLER) { NtStatus = GetExceptionCode(); break; } switch (ByteValue) { case MI_LOCK_PREFIX: Instruction->Prefix.b.Lock = 1; break; case MI_REPNE_PREFIX: Instruction->Prefix.b.RepNe = 1; break; case MI_REP_PREFIX: Instruction->Prefix.b.Rep = 1; break; case MI_SEGCS_PREFIX: Instruction->Prefix.b.CsOverride = 1; break; case MI_SEGSS_PREFIX: Instruction->Prefix.b.SsOverride = 1; break; case MI_SEGDS_PREFIX: Instruction->Prefix.b.DsOverride = 1; break; case MI_SEGES_PREFIX: Instruction->Prefix.b.EsOverride = 1; break; case MI_SEGFS_PREFIX: Instruction->Prefix.b.FsOverride = 1; break; case MI_SEGGS_PREFIX: Instruction->Prefix.b.GsOverride = 1; break; case MI_OPERANDSIZE_PREFIX: Instruction->Prefix.b.SizeOverride = 1; break; case MI_ADDRESSOVERRIDE_PREFIX: Instruction->Prefix.b.AddressOverride = 1; break; default: PrefixLoop = FALSE; break; } if (PrefixLoop == TRUE) { Instruction->Eip++; } } try { RtlCopyMemory(ByteBuffer, Instruction->Eip, sizeof (ByteBuffer)); } except (EXCEPTION_EXECUTE_HANDLER) { NtStatus = GetExceptionCode(); } if (NT_SUCCESS (NtStatus)) { // // Locate the opcode // Match = FALSE; OpcodeDescription = OpcodesDescription; Count = (sizeof (OpcodesDescription) / sizeof (IA32_OPCODE_DESCRIPTION)); while (Count != 0) { Count--; if (OpcodeDescription->Byte1 == ByteBuffer[0]) { Match = TRUE; if (OpcodeDescription->Count.m.Bytes == 2) { RegOpcodeByte = &ByteBuffer[2]; if (OpcodeDescription->Byte2 != ByteBuffer[1]) { Match = FALSE; } } else { RegOpcodeByte = &ByteBuffer[1]; } if ((Match == TRUE) && (OpcodeDescription->Count.m.RegOpcode)) { if (OpcodeDescription->Byte3 != ((*RegOpcodeByte & MI_REGMASK) >> MI_REGSHIFT)) { Match = FALSE; } } if (Match == TRUE) { break; } } OpcodeDescription++; } if (Match != TRUE) { #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32LocateInstruction - Unable to locate instruction %p\n", Instruction); } #endif NtStatus = STATUS_UNSUCCESSFUL; } if (NT_SUCCESS (NtStatus)) { Instruction->Description = OpcodeDescription; Instruction->Eip += OpcodeDescription->Count.m.Bytes; } } return NtStatus; UNREFERENCED_PARAMETER (TrapFrame); } NTSTATUS KiIa32DecodeInstruction ( IN PKTRAP_FRAME TrapFrame, OUT PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Decodes the instruction prefixes and operands. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to an Instruction structure to receive the opcode description. Return Value: NTSTATUS --*/ { UCHAR InstructionType; UCHAR ModRm; UNALIGNED USHORT *UnalignedUshort; UNALIGNED ULONG *UnalignedUlong; IA32_OPERAND Temp; BOOLEAN ReturnCode; NTSTATUS NtStatus; // // Check instruction pointer validity // if (TrapFrame->StIIP > MM_MAX_WOW64_ADDRESS) { return STATUS_ACCESS_VIOLATION; } // // Initialize the instruction pointer // Instruction->Eip = (PCHAR) TrapFrame->StIIP; KiIa32GetX86Eflags (Instruction->Eflags); // // Locate a description for the instruction // NtStatus = KiIa32LocateInstruction (TrapFrame, Instruction); if (NT_SUCCESS (NtStatus)) { // // Let's parse the arguments // InstructionType = Instruction->Description->Type; switch (InstructionType) { case IA32_PARAM_RM8_IMM8: case IA32_PARAM_RM8_R: case IA32_PARAM_R_RM8: case IA32_PARAM_RM8: case IA32_PARAM_SEGREG_RM8: Instruction->OperandSize = OPERANDSIZE_ONEBYTE; Instruction->OperandMask = 0xff; break; case IA32_PARAM_RM_IMM: case IA32_PARAM_RM_IMM8SIGN: case IA32_PARAM_RM_R: case IA32_PARAM_R_RM: case IA32_PARAM_RM: if (Instruction->Prefix.b.SizeOverride) { Instruction->OperandSize = OPERANDSIZE_TWOBYTES; Instruction->OperandMask = 0xffff; } else { Instruction->OperandSize = OPERANDSIZE_FOURBYTES; Instruction->OperandMask = 0xffffffff; } break; break; case IA32_PARAM_SEGREG_RM: Instruction->OperandSize = OPERANDSIZE_TWOBYTES; Instruction->OperandMask = 0xffff; break; default: #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32DecodeInstruction - Invalid Instruction type %lx, %p\n", Instruction->Description->Type, Instruction); } #endif return STATUS_UNSUCCESSFUL; break; } try { ModRm = ProbeAndReadUchar ((PUCHAR)Instruction->Eip); } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // // Eip should be pointing now at the bytes following the opcode // if (Instruction->Prefix.b.AddressOverride == 0) { ReturnCode = KiIa32Compute32BitEffectiveAddress ( TrapFrame, (PUCHAR *)&Instruction->Eip, &Instruction->Operand1.v, &Instruction->Operand1.RegisterMode ); } else { ReturnCode = KiIa32Compute16BitEffectiveAddress ( TrapFrame, (PUCHAR *)&Instruction->Eip, &Instruction->Operand1.v, &Instruction->Operand1.RegisterMode ); } if (ReturnCode != TRUE) { NtStatus = STATUS_UNSUCCESSFUL; } if (Instruction->Prefix.b.FsOverride) { try { Instruction->Operand1.v += (ULONGLONG)NtCurrentTeb32(); } except (EXCEPTION_EXECUTE_HANDLER) { NtStatus = GetExceptionCode (); #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32DecodeInstruction - Exception while reading NtCurrentTeb32() - %p\n", Instruction); } #endif } } // // Read in more args // if (NT_SUCCESS (NtStatus)) { switch (InstructionType) { case IA32_PARAM_RM8_IMM8: case IA32_PARAM_RM_IMM8SIGN: try { Instruction->Operand2.v = (ULONG_PTR) ProbeAndReadUchar ((PUCHAR)Instruction->Eip); Instruction->Eip += 1; } except (EXCEPTION_EXECUTE_HANDLER) { NtStatus = GetExceptionCode(); } break; case IA32_PARAM_RM_IMM: try { if (Instruction->OperandSize == OPERANDSIZE_TWOBYTES) { UnalignedUshort = (UNALIGNED USHORT *) Instruction->Eip; Instruction->Operand2.v = (ULONG_PTR) *UnalignedUshort; Instruction->Eip += sizeof (USHORT); } else { UnalignedUlong = (UNALIGNED ULONG *) Instruction->Eip; Instruction->Operand2.v = (ULONG_PTR) *UnalignedUlong; Instruction->Eip += sizeof (ULONG); } } except (EXCEPTION_EXECUTE_HANDLER) { NtStatus = GetExceptionCode(); } break; case IA32_PARAM_RM8_R: case IA32_PARAM_R_RM8: case IA32_PARAM_RM_R: case IA32_PARAM_R_RM: Instruction->Operand2.v = GetX86RegOffset ( TrapFrame, (ModRm & MI_REGMASK) >> MI_REGSHIFT); Instruction->Operand2.RegisterMode = TRUE; break; case IA32_PARAM_RM8: case IA32_PARAM_RM: case IA32_PARAM_SEGREG_RM8: case IA32_PARAM_SEGREG_RM: break; default: NtStatus = STATUS_UNSUCCESSFUL; #if DBG DbgPrint("KE: KiIa32DecodeInstruction - Invalid instruction type %lx - %p\n", InstructionType, Instruction); #endif break; } // // Adjust operands order // if (NT_SUCCESS (NtStatus)) { switch (InstructionType) { case IA32_PARAM_R_RM8: case IA32_PARAM_R_RM: Temp = Instruction->Operand2; Instruction->Operand2 = Instruction->Operand1; Instruction->Operand1 = Temp; break; } } } } return NtStatus; } NTSTATUS KiIa32ExecuteInstruction ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Executes the instruction handler. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { NTSTATUS NtStatus; #if DBG if (KiIa32InstructionEmulationDbg) { DbgPrint("KE: KiIa32ExecuteInstruction - Calling %s %lx, %lx. Instruction = %p\n", KiIa32InstructionHandlerNames[Instruction->Description->Opcode], Instruction->Operand1.v, Instruction->Operand2.v, Instruction); } #endif NtStatus = KiIa32InstructionHandler[Instruction->Description->Opcode] ( TrapFrame, Instruction ); // // If all is good... // if (NT_SUCCESS (NtStatus)) { TrapFrame->StIIP = (ULONGLONG) Instruction->Eip; KiIa32SetX86Eflags (Instruction->Eflags); } return NtStatus; } NTSTATUS KiIa32EmulateInstruction ( IN PKTRAP_FRAME TrapFrame, IN PIA32_INSTRUCTION Instruction ) /*++ Routine Description: Emulates the instruction and emulates the lock prefix, if any. Arguments: TrapFrame - Pointer to TrapFrame. Instruction - Pointer to the instruction being processed. Return Value: NTSTATUS --*/ { NTSTATUS NtStatus; // // Acquire the lock mutex // if (Instruction->Prefix.b.Lock) { if (ExAcquireRundownProtection (&PsGetCurrentThread()->RundownProtect) == FALSE) { return STATUS_UNSUCCESSFUL; } KiIa32AcquireMisalignedLockFastMutex (); } try { NtStatus = KiIa32ExecuteInstruction (TrapFrame, Instruction); } except (EXCEPTION_EXECUTE_HANDLER) { NtStatus = GetExceptionCode(); } // // Release the lock mutex // if (Instruction->Prefix.b.Lock) { KiIa32ReleaseMisalignedLockFastMutex (); ExReleaseRundownProtection (&PsGetCurrentThread()->RundownProtect); } return NtStatus; } NTSTATUS KiIa32InterceptUnalignedLock ( IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: Handles misaligned lock interception raised by the iVE. Arguments: TrapFrame - Pointer to TrapFrame. Return Value: NTSTATUS --*/ { NTSTATUS NtStatus; IA32_INSTRUCTION Instruction; RtlZeroMemory (&Instruction, sizeof (Instruction)); // // Decode the faulting instruction // NtStatus = KiIa32DecodeInstruction (TrapFrame, &Instruction); if (NT_SUCCESS (NtStatus)) { // // xchg instruction asserts the lock by default // if (Instruction.Description->Opcode == Ia32_Xchg) { Instruction.Prefix.b.Lock = 1; } // // Execute the x86 instruction by emulating its behaviour // NtStatus = KiIa32EmulateInstruction (TrapFrame, &Instruction); } if (NtStatus == STATUS_UNSUCCESSFUL) { NtStatus = STATUS_PRIVILEGED_INSTRUCTION; } return NtStatus; } NTSTATUS KiIa32ValidateInstruction ( IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: This routine valiates the instruction that we trapped for. Currently, the following instructions are checked: - mov ss, r/m : the register/memory is validated to contain a valid stack-selector value. NOTE: This routine is only called for trap instructions (i.e. IIP is incremented after the fault). Arguments: TrapFrame - Pointer to TrapFrame. Return Value: NTSTATUS --*/ { NTSTATUS NtStatus; IA32_INSTRUCTION Instruction; ULONGLONG UlongSrc; ULONGLONG StIIP; RtlZeroMemory (&Instruction, sizeof (Instruction)); // // Adjust the instruction // StIIP = TrapFrame->StIIP; TrapFrame->StIIP = TrapFrame->StIIPA; // // Decode the faulting instruction // NtStatus = KiIa32DecodeInstruction (TrapFrame, &Instruction); if (NT_SUCCESS (NtStatus)) { // // Parse the opcode here // switch (Instruction.Description->Opcode) { case Ia32_MovToSeg: { // // Validate the stack-selector being loaded // NtStatus = KiIa32ReadOperand1 (&Instruction, &UlongSrc); if (NT_SUCCESS (NtStatus)) { // // If not a valid selector value // if ((UlongSrc != 0x23) && (UlongSrc != 0x1b) && (UlongSrc != 0x3b)) { NtStatus = STATUS_ILLEGAL_INSTRUCTION; } } } break; default: NtStatus = STATUS_ILLEGAL_INSTRUCTION; break; } } else { NtStatus = STATUS_ILLEGAL_INSTRUCTION; } // // Restore the saved IIP // if (NT_SUCCESS (NtStatus)) { TrapFrame->StIIP = StIIP; } return NtStatus; }