/****************************************************************************** * Module: ProtectedInstructions.c * * Revision: 1.00 * * Date: 4/28/2000 * * Author: Goran Devic * ******************************************************************************* Module Description: Instructions that can not be executed natively are virtualized in this file. They should return RETURN_ADVANCE_EIP if the eip should be advanced, or RETURN_KEEP_EIP if the eip should not be advanced. (as example, when virtualized instruction explicitly change eip). ******************************************************************************* * Changes: * * DATE DESCRIPTION OF CHANGES AUTHOR * * -------- --------------------------------------------------- ----------- * * 4/28/2000 Original Goran Devic * * -------- --------------------------------------------------- ----------- * ******************************************************************************* * Include Files * ******************************************************************************/ #include <strmini.h> // Include driver level C functions #include <ntddk.h> // Include ddk function header file #include "Vm.h" // Include root header file #include "Scanner.h" // Include scanner header file #include "DisassemblerInterface.h" // Include disassembler header file /****************************************************************************** * Global Variables * ******************************************************************************/ #if 1 #undef Breakpoint #define Breakpoint #endif /****************************************************************************** * External functions * ******************************************************************************/ extern DWORD fn_iret(DWORD flags, BYTE bModrm); /****************************************************************************** * Local Defines, Variables and Macros * ******************************************************************************/ extern DWORD CurrentInstrLen; #define RETURN_ADVANCE_EIP 0 #define RETURN_KEEP_EIP 1 typedef DWORD (*TProtected)(DWORD flags, BYTE bModrm); /****************************************************************************** * Functions * ******************************************************************************/ /* These virtual instructions are called from DisassemblerForVirtualization.c */ static DWORD fn_native(DWORD flags, BYTE bModrm) { // This one is error-catcher ASSERT(0); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_cli(DWORD flags, BYTE bModrm) { CPU.eflags &= ~IF_MASK; return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_clts(DWORD flags, BYTE bModrm) { CPU.cr0 &= ~BITMASK(CR0_TS_BIT); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_hlt(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_inb(DWORD flags, BYTE bModrm) // IN AL,Ib { BYTE bPort; DWORD value; Breakpoint; bPort = VmGetNextBYTE(); value = (*Device.IO[ Device.IOIndexMap[bPort] ].pInCallback)(bPort, 1); *(BYTE *)&pMonitor->guestTSS.eax = (BYTE)(value & 0xFF); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_inv(DWORD flags, BYTE bModrm) // IN eAX,Ib (opsize) { BYTE bPort; DWORD value; Breakpoint; bPort = VmGetNextBYTE(); if( flags & DIS_DATA32 ) { // 32 bit data size value = (*Device.IO[ Device.IOIndexMap[bPort] ].pInCallback)(bPort, 4); pMonitor->guestTSS.eax = value; } else { // 16 bit data size value = (*Device.IO[ Device.IOIndexMap[bPort] ].pInCallback)(bPort, 2); *(WORD *)&pMonitor->guestTSS.eax = LOWORD(value); } return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_inal(DWORD flags, BYTE bModrm) // IN AL,DX { WORD wPort; DWORD value; Breakpoint; wPort = LOWORD(pMonitor->guestTSS.edx); value = (*Device.IO[ Device.IOIndexMap[wPort] ].pInCallback)(wPort, 1); *(BYTE *)&pMonitor->guestTSS.eax = (BYTE)(value & 0xFF); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_inax(DWORD flags, BYTE bModrm) // IN eAX,DX (opsize) { WORD wPort; DWORD value; Breakpoint; wPort = LOWORD(pMonitor->guestTSS.edx); if( flags & DIS_DATA32 ) { // 32 bit data size value = (*Device.IO[ Device.IOIndexMap[wPort] ].pInCallback)(wPort, 4); pMonitor->guestTSS.eax = value; } else { // 16 bit data size value = (*Device.IO[ Device.IOIndexMap[wPort] ].pInCallback)(wPort, 2); *(WORD *)&pMonitor->guestTSS.eax = LOWORD(value); } return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_insb(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_insv(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_int_n(DWORD flags, BYTE bModrm) { BYTE bIntNumber; // Advance eip so the CPU_Interrupt() can push following instruction // on the stack. Also, do not advance eip on return. Int n instruction // is always 2 bytes long. pMonitor->guestTSS.eip += 2; bIntNumber = VmGetNextBYTE(); CPU_Interrupt(bIntNumber); return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_into(DWORD flags, BYTE bModrm) { // Advance eip manually to push it on the stack if the interrupt should be // executed, or simply proceed. INTO is 1 byte long. pMonitor->guestTSS.eip += 1; // Generate interrupt 4 only if overflow flag is set if( pMonitor->guestTSS.eflags & BITMASK(OF_BIT) ) { CPU_Exception(EXCEPTION_INTO, 0); } return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_invd(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_invlpg(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= // Load Global Descriptor Table //============================================================================= static DWORD fn_lgdt(DWORD flags, BYTE bModrm) { DWORD dwVmGDT; TDescriptor *pGDT_descriptor; // Get the address of the gdt table to load dwVmGDT = DecodeMemoryPointer( &flags, bModrm ); // Translate the VM address to driver address pGDT_descriptor = (TDescriptor *) VmLinearToDriverLinear(dwVmGDT); ASSERT(pGDT_descriptor); // Fetch base address and limit of the new VM GDT descriptor // and store in our virtual guest processor state structure CPU.guestGDT.base = pGDT_descriptor->base; CPU.guestGDT.limit = pGDT_descriptor->limit; ASSERT(CPU.guestGDT.limit); ASSERT(CPU.guestGDT.limit < GDT_SEL_VM_TOP); // In 16-bit operand mode, zero out upper 8 bits of the base address if( (flags & DIS_DATA32)==0 ) CPU.guestGDT.base &= 0x00FFFFFF; // Get the driver pointer to actual GDT table CPU.pGDT = (TGDT_Gate *) VmLinearToDriverLinear(CPU.guestGDT.base); ASSERT(CPU.pGDT); // Register system region RenewSystemRegion( SYSREG_GDT, CPU.guestGDT.base, CPU.guestGDT.limit + 1 ); // Copy all vm gdt entries into master monitor gdt table TranslateVmGdtToMonitorGdt(); INFO(("GDT loaded: %08X Limit %04X", CPU.guestGDT.base, CPU.guestGDT.limit)); return( RETURN_ADVANCE_EIP ); } //============================================================================= // Load Interrupt Descriptor Table //============================================================================= static DWORD fn_lidt(DWORD flags, BYTE bModrm) { DWORD dwVmIDT; TDescriptor *pIDT_descriptor; // Get the linear VM address of the IDT table to load dwVmIDT = DecodeMemoryPointer( &flags, bModrm ); // Translate the VM address to driver address pIDT_descriptor = (TDescriptor *) VmLinearToDriverLinear(dwVmIDT); ASSERT(pIDT_descriptor); // Fetch base address and limit of the new VM IDT descriptor // and store in our virtual guest processor state structure CPU.guestIDT.base = pIDT_descriptor->base; CPU.guestIDT.limit = pIDT_descriptor->limit; ASSERT(CPU.guestIDT.limit < 256 * sizeof(TIDT_Gate)); // In 16-bit operand mode, zero out upper 8 bits of the base address if( (flags & DIS_DATA32)==0 ) CPU.guestIDT.base &= 0x00FFFFFF; // Get the driver pointer to actual IDT table CPU.pIDT = (TIDT_Gate *) VmLinearToDriverLinear(CPU.guestIDT.base); ASSERT(CPU.pIDT); // Register system region // TODO: Why do we need to track writes to VM IDT ??? // RenewSystemRegion( SYSREG_IDT, CPU.guestIDT.base, CPU.guestIDT.limit + 1 ); INFO(("IDT loaded: %08X Limit %04X", CPU.guestIDT.base, CPU.guestIDT.limit)); return( RETURN_ADVANCE_EIP ); } //============================================================================= // Load Task Register; also set up TSS structure //============================================================================= static DWORD fn_ltr(DWORD flags, BYTE bModrm) { WORD wTask; DWORD dwVmTSSBase; DWORD dwVmTSSLimit; // Get the GDT entry describing the task state structure wTask = DecodeEw( &flags, bModrm ); // Store the guest Task Register away since we will be using // our own monitor task state structure instead CPU.guestTR = wTask; INFO(("TR loaded: %04X", CPU.guestTR)); dwVmTSSBase = GetGDTBaseLimit(&dwVmTSSLimit, &pMonitor->GDT[ wTask >> 3 ]); // Register system region // TODO: Why do we need to track writes to VM TSS ??? // RenewSystemRegion( SYSREG_TSS, dwVmTSSBase, dwVmTSSLimit ); // Get the driver pointer to the virtual TSS CPU.pTSS = (TSS_Struct *) VmLinearToDriverLinear(dwVmTSSBase); ASSERT(CPU.pTSS); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_mov_to_cr(DWORD flags, BYTE bModrm) { DWORD cr, value; ASSERT((bModrm & 0xC0)==0xC0); // Get new value from a general-purpose register ( Rm field ) cr = (bModrm >> 3) & 7; value = *(((DWORD *)&pMonitor->guestTSS.eax) + (bModrm & 0x7) ); LoadCR( cr, value ); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_lldt(DWORD flags, BYTE bModrm) { WORD wSel; wSel = DecodeEw( &flags, bModrm ); if( wSel == 0 ) { pMonitor->guestLDT = wSel; return( RETURN_ADVANCE_EIP ); } if( CPU.CPU_mode != CPU_REAL_MODE ) { if( CPU.CPU_mode==CPU_KERNEL_MODE ) { if( (wSel & SEL_TI_MASK)==0 ) { if( pMonitor->GDT[wSel >> 3].present == TRUE ) { if( pMonitor->GDT[wSel >> 3].type==DESC_TYPE_LDT ) { pMonitor->guestLDT = wSel; return( RETURN_ADVANCE_EIP ); } else CPU_Exception( EXCEPTION_GPF, wSel & 0xFFFC ); } else CPU_Exception( EXCEPTION_SEG_NOT_PRESENT, wSel & 0xFFFC ); } else CPU_Exception( EXCEPTION_GPF, wSel & 0xFFFC ); } else CPU_Exception( EXCEPTION_GPF, 0 ); } else CPU_Exception( EXCEPTION_INVALID_OPCODE, 0 ); return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_lmsw(DWORD flags, BYTE bModrm) { DWORD value; value = (DWORD) DecodeEw( &flags, bModrm ); if( (CPU.cr0 & BITMASK(CR0_PE_BIT)) && ((value & BITMASK(CR0_PE_BIT))==0) ) WARNING(("Used lmsw to switch back to real mode")); // This instruction can load only the low nibble of CR0: PE, MP, EM and TS bits value &= 0x0F; value |= CPU.cr0 & ~0x0F; LoadCR( 0, value ); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_mov_to_dr(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_mov_from_dr(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_outb(DWORD flags, BYTE bModrm) // Ib,AL { BYTE bPort; DWORD value; Breakpoint; bPort = VmGetNextBYTE(); value = pMonitor->guestTSS.eax & 0xFF; (*Device.IO[ Device.IOIndexMap[bPort] ].pOutCallback)(bPort, value, 1); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_outv(DWORD flags, BYTE bModrm) // Ib,eAX (opsize) { BYTE bPort; DWORD value; Breakpoint; bPort = VmGetNextBYTE(); if( flags & DIS_DATA32 ) { // 32 bit data size value = pMonitor->guestTSS.eax; (*Device.IO[ Device.IOIndexMap[bPort] ].pOutCallback)(bPort, value, 4); } else { // 16 bit data size value = LOWORD(pMonitor->guestTSS.eax); (*Device.IO[ Device.IOIndexMap[bPort] ].pOutCallback)(bPort, value, 2); } return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_outal(DWORD flags, BYTE bModrm) // DX,AL { WORD wPort; DWORD value; Breakpoint; wPort = LOWORD(pMonitor->guestTSS.edx); value = pMonitor->guestTSS.eax & 0xFF; (*Device.IO[ Device.IOIndexMap[wPort] ].pOutCallback)(wPort, value, 1); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_outax(DWORD flags, BYTE bModrm) // DX,eAX { WORD wPort; DWORD value; Breakpoint; wPort = LOWORD(pMonitor->guestTSS.edx); if( flags & DIS_DATA32 ) { // 32 bit data size value = pMonitor->guestTSS.eax; (*Device.IO[ Device.IOIndexMap[wPort] ].pOutCallback)(wPort, value, 4); } else { // 16 bit data size value = LOWORD(pMonitor->guestTSS.eax); (*Device.IO[ Device.IOIndexMap[wPort] ].pOutCallback)(wPort, value, 2); } return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_outsb(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_outsv(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_rdmsr(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_rdmpc(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_rdtsc(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_sti(DWORD flags, BYTE bModrm) { INFO(("Virtual interrupts enabled")); CPU.eflags |= IF_MASK; return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_wbinvd(DWORD flags, BYTE bModrm) { // Serializing instruction that flushes internal and external caches return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_wrmsr(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_call_near(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_call_far(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_call_neari(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_call_fari(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_cpuid(DWORD flags, BYTE bModrm) { TRACE(("CPUID instruction")); switch( pMonitor->guestTSS.eax ) { case 0: pMonitor->guestTSS.eax = CPU.cpuid[0][0]; pMonitor->guestTSS.ebx = CPU.cpuid[0][1]; pMonitor->guestTSS.ecx = CPU.cpuid[0][2]; pMonitor->guestTSS.edx = CPU.cpuid[0][3]; break; case 1: pMonitor->guestTSS.eax = CPU.cpuid[1][0]; pMonitor->guestTSS.ebx = CPU.cpuid[1][1]; pMonitor->guestTSS.ecx = CPU.cpuid[1][2]; pMonitor->guestTSS.edx = CPU.cpuid[1][3]; break; case 2: pMonitor->guestTSS.eax = CPU.cpuid[2][0]; pMonitor->guestTSS.ebx = CPU.cpuid[2][1]; pMonitor->guestTSS.ecx = CPU.cpuid[2][2]; pMonitor->guestTSS.edx = CPU.cpuid[2][3]; break; default: WARNING(("Invalid eax of %X for cpuid", pMonitor->guestTSS.eax)); } return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_jmp_near(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_jmp_near2(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_jmp_neari(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_jmp_far(DWORD flags, BYTE bModrm) { WORD wSel; // Normally, this instruction would be executed single-step, but since // the Phoenix system BIOS does a weird thing and enters protected mode, // but never reloads CS register, we need to virtualize this behaviour. // // EA cd/cp Ap -> ptr16:16/ptr16:32 (opsize)(seg) // We need to virtualize this instruction since the CS can still be segment // even when PE=1 // This instruction effectively flushes the CS selector into the right mode if( CPU.CPU_mode==CPU_KERNEL_MODE ) { CPU.fSelPMode[ 1 ] = TRUE; if( flags & DIS_DATA32 ) pMonitor->guestTSS.eip = VmGetNextDWORD(); // 32-bit jump else pMonitor->guestTSS.eip = (DWORD) VmGetNextWORD(); // 16-bit jump wSel = VmGetNextWORD(); // Run ring 0 guest in ring 1 ASSERT((wSel & SEL_TI_MASK)==0); // Make sure it is GDT wSel |= SEL_RPL1_MASK; pMonitor->guestTSS.cs = wSel; if( wSel >= GDT_SEL_VM_TOP ) { ERROR(("VM is using selector %04X. Increase GDT_MAX and recompile.", wSel)); ASSERT(0); } } else // Real or v86 mode { CPU.fSelPMode[ 1 ] = FALSE; pMonitor->guestTSS.eip = (DWORD) VmGetNextWORD(); pMonitor->guestTSS.cs = VmGetNextWORD(); } return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_jmp_fari(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_lar(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= // Loading of selector:register value pair: lds, les, lfs, lgs, lss // Even if we let those run natively, they will GPF since the RPL of the // selector value is 0 //============================================================================= #define SEL_INDEX_ES 0 #define SEL_INDEX_CS 1 #define SEL_INDEX_SS 2 #define SEL_INDEX_DS 3 #define SEL_INDEX_FS 4 #define SEL_INDEX_GS 5 static void LoadSelRegPair( DWORD indexSel, DWORD flags, BYTE bModrm ) { DWORD dwVmSelOfs, *pSelOfs, dwOffset, reg; WORD wSel; // These are virtualized in pmode only! ASSERT(CPU.CPU_mode==CPU_KERNEL_MODE); // Get the address of the SEL:OFS to load dwVmSelOfs = DecodeMemoryPointer( &flags, bModrm ); // Translate the VM address to driver address pSelOfs = (DWORD *) VmLinearToDriverLinear(dwVmSelOfs); ASSERT(pSelOfs); reg = (bModrm >> 3) & 7; // Which register to load (offset) // Fetch the new selector/offset and store them if( flags & DIS_DATA32 ) // 32 bit data { dwOffset = *pSelOfs++; *(((DWORD *)&pMonitor->guestTSS.eax) + reg ) = dwOffset; } else // 16 bit data { dwOffset = (DWORD) *((WORD *)pSelOfs)++; *(WORD *)(((DWORD *)&pMonitor->guestTSS.eax) + reg ) = (WORD) dwOffset; } wSel = *(WORD *)pSelOfs; wSel |= SEL_RPL1_MASK; // Make ring0 become ring 1 *(((DWORD *)&pMonitor->guestTSS.es) + indexSel) = wSel; // TODO: Do we really need to keep track of selector mode? CPU.fSelPMode[] CPU.fSelPMode[ indexSel ] = TRUE; } //============================================================================= static DWORD fn_lds(DWORD flags, BYTE bModrm) { LoadSelRegPair(SEL_INDEX_DS, flags, bModrm); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_les(DWORD flags, BYTE bModrm) { LoadSelRegPair(SEL_INDEX_ES, flags, bModrm); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_lfs(DWORD flags, BYTE bModrm) { LoadSelRegPair(SEL_INDEX_FS, flags, bModrm); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_lgs(DWORD flags, BYTE bModrm) { LoadSelRegPair(SEL_INDEX_GS, flags, bModrm); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_lss(DWORD flags, BYTE bModrm) { LoadSelRegPair(SEL_INDEX_SS, flags, bModrm); return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_lsl(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_mov_from_cr(DWORD flags, BYTE bModrm) // Rd,Cd { DWORD value; DWORD Cr; ASSERT((bModrm & 0xC0)==0xC0); Cr = (bModrm >> 3) & 7; // Reg field is control register switch( Cr ) { case 0: value = CPU.cr0; break; case 3: value = CPU.cr3; break; default: ASSERT(0); } *(((DWORD *)&pMonitor->guestTSS.eax) + (bModrm & 0x7) ) = value; return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_mov_to_seg(DWORD flags, BYTE bModrm) { WORD wNewValue; DWORD indexSeg; wNewValue = DecodeEw( &flags, bModrm ); indexSeg = (bModrm >> 3) & 7; if( CPU.CPU_mode==CPU_KERNEL_MODE ) { if( wNewValue >= GDT_SEL_VM_TOP ) { ERROR(("VM is using selector %04X. Increase GDT_MAX and recompile.", wNewValue)); ASSERT(0); } wNewValue |= SEL_RPL1_MASK; // Make ring0 become ring 1 CPU.fSelPMode[ indexSeg ] = TRUE; } else CPU.fSelPMode[ indexSeg ] = FALSE; *(((DWORD *)&pMonitor->guestTSS.es) + indexSeg) = wNewValue; return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_mov_from_seg(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_pop_ds(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_pop_es(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_pop_fs(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_pop_ss(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_pop_gs(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_popf(DWORD flags, BYTE bModrm) { // Pop flags word or dword depending on the stack segment/selector switch( CPU.CPU_mode ) { case CPU_REAL_MODE: case CPU_V86_MODE: CPU.eflags = CPU_PopWord(); pMonitor->guestTSS.eflags = EFLAGS_SET_CONSTANTS(CPU.eflags & EFLAGS_ACTIVE_MASK) | VM_MASK | IF_MASK; break; case CPU_KERNEL_MODE: if( flags & DIS_DATA32 ) // POPFD CPU.eflags = CPU_PopDword(); else // POPF CPU.eflags = (CPU.eflags & ~0xFFFF) | (CPU_PopWord() & 0xFFFF); pMonitor->guestTSS.eflags = EFLAGS_SET_CONSTANTS(CPU.eflags & EFLAGS_ACTIVE_MASK) | IF_MASK; break; } return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_push_cs(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_push_ss(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_push_ds(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_push_es(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_push_fs(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_push_gs(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_pushf(DWORD flags, BYTE bModrm) { DWORD dwFlags; // Push flags word or dword depending on the stack segment/selector dwFlags = LOWORD((pMonitor->guestTSS.eflags & EFLAGS_ACTIVE_MASK) | (CPU.eflags & ~EFLAGS_ACTIVE_MASK)); switch( CPU.CPU_mode ) { case CPU_REAL_MODE: case CPU_V86_MODE: CPU_PushWord((WORD) (dwFlags & 0xFFFF)); break; case CPU_KERNEL_MODE: if( flags & DIS_DATA32 ) // PUSHFD CPU_PushDword(dwFlags); else // PUSHF CPU_PushWord((WORD) (dwFlags & 0xFFFF)); break; } return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_ret_near(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_ret_far(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_ret_near_pop(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_ret_far_pop(DWORD flags, BYTE bModrm) { return( RETURN_KEEP_EIP ); } //============================================================================= static DWORD fn_sgdt(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_sidt(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_sldt(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_smsw(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_str(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_verr(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= static DWORD fn_verw(DWORD flags, BYTE bModrm) { return( RETURN_ADVANCE_EIP ); } //============================================================================= /****************************************************************************** * List of opcodes to catch and emulate in various ways * ******************************************************************************/ TProtected Protected[] = { fn_native, // 0x00 fn_cli, // 0x01 // FA - fn_clts, // 0x02 // 0F 06 - fn_hlt, // 0x03 // F4 - fn_inb, // 0x04 // E4 ib Al, Ib -> Al, imm8 fn_inv, // 0x05 // E5 ib eAX, Ib -> AX/EAX, imm8 (opsize) fn_inal, // 0x06 // EC AL, DX -> AL, DX fn_inax, // 0x07 // ED eAX, DX -> AX/EAX, DX (opsize) fn_insb, // 0x08 // 6C Yb, DX -> ES:(E)DI, DX (seg) (opsize) (rep) fn_insv, // 0x09 // 6D Yv, DX -> ES:(E)DI, DX (seg) (opsize) (rep) fn_int_n, // 0x0A // CD ib Ib fn_into, // 0x0B // CE - fn_invd, // 0x0C // 0F 08 - fn_invlpg, // 0x0D // 0F 01/7 M -> linear address (ignore instruction) fn_lgdt, // 0x0E // 0F 01/2 Ms -> linear address fn_lidt, // 0x0F // 0F 01/3 Ms -> linear address fn_lldt, // 0x10 // 0F 00/2 Ew -> rm16 selector fn_lmsw, // 0x11 // 0F 01/6 Ew -> rm16 fn_ltr, // 0x12 // 0F 00/3 Ew -> rm16 fn_mov_to_cr, // 0x13 // 0F 22/r Cd, Rd -> CR,r32 fn_mov_to_dr, // 0x14 // 0F 23/r Dd, Rd -> DR,r32 fn_mov_from_dr, // 0x15 // 0F 21/r Rd, Dd -> r32,DR fn_outb, // 0x16 // E6 ib Ib, AL -> imm8,AL fn_outv, // 0x17 // E7 ib Ib, eAX -> imm8,AX/EAX (opsize) fn_outal, // 0x18 // EE DX, AL -> DX, AL fn_outax, // 0x19 // EF DX, eAX -> DX, AX/EAX (opsize) fn_outsb, // 0x1A // 6E DX, Xb -> DX, DS:(E)SI (seg) (opsize) (rep) fn_outsv, // 0x1B // 6F DX, Xv -> DX, DS:(E)SI (seg) (opsize) (rep) fn_rdmsr, // 0x1C // 0F 32 - fn_rdmpc, // 0x1D // 0F 33 - fn_rdtsc, // 0x1E // 0F 31 - fn_sti, // 0x1F // FB - fn_wbinvd, // 0x20 // 0F 09 - fn_wrmsr, // 0x21 // 0F 30 - fn_call_near, // 0x22 // E8 cv/cd Jv -> rel16/rel32 disp (opsize) fn_call_far, // 0x23 // 9A cd/cp Ap -> ptr16:16/ptr16:32 (opsize)(seg) fn_call_neari, // 0x24 // FF /2 Ev -> rm16/rm32 (opsize) fn_call_fari, // 0x25 // FF /3 Ep -> m16:16/m16:32 (opsize) fn_cpuid, // 0x26 // 0F A2 - fn_iret, // 0x27 // CF - 16 and 32 bit version (opsize) fn_jmp_near, // 0x28 // EB cb Jb -> rel8 fn_jmp_near2, // 0x29 // E9 cw/cd Jv -> rel16/rel32 (opsize) fn_jmp_neari, // 0x2A // FF /4 Ev -> rm16/rm32 (opsize) fn_jmp_far, // 0x2B // EA cd/cp Ap -> ptr16:16/ptr16:32 (opsize)(seg) fn_jmp_fari, // 0x2C // FF /5 Ep -> m16:16/m16:32 (opsize) fn_lar, // 0x2D // 0F 02 /r Gv, Ew -> r16/r32,rm16/rm32 (opsize) fn_lds, // 0x2E // C5 /r Gv, Mp -> DS:r16/DS:r32 (opsize) fn_les, // 0x2F // C4 /r Gv, Mp -> ES:r16/ES:r32 (opsize) fn_lfs, // 0x30 // 0F B4 /r Gv, Mp -> FS:r16/FS:r32 (opsize) fn_lgs, // 0x31 // 0F B5 /r Gv, Mp -> GS:r16/GS:r32 (opsize) fn_lss, // 0x32 // 0F B2 /r Gv, Mp -> SS:r16/SS:r32 (opsize) fn_lsl, // 0x33 // 0F 03 /r Gv, Ew -> r16/r32,rm16/rm32 (opsize) fn_mov_from_cr, // 0x34 // 0F 20 /r Rd, Cd -> r32,CR fn_mov_to_seg, // 0x35 // 8E /r Sw, Ew -> seg,rm16 fn_mov_from_seg, // 0x36 // 8C /r Ew, Sw -> rm16,seg fn_pop_ds, // 0x37 // 1F - fn_pop_es, // 0x38 // 07 - fn_pop_fs, // 0x39 // 0F A1 - fn_pop_ss, // 0x3A // 17 - fn_pop_gs, // 0x3B // 0F A9 - fn_popf, // 0x3C // 9D - 16 and 32 bit version (opsize) fn_push_cs, // 0x3D // 0E - fn_push_ss, // 0x3E // 16 - fn_push_ds, // 0x3F // 1E - fn_push_es, // 0x40 // 06 - fn_push_fs, // 0x41 // 0F A0 - fn_push_gs, // 0x42 // 0F A8 - fn_pushf, // 0x43 // 9C - 16 and 32 bit version (opsize) fn_ret_near, // 0x44 // C3 - fn_ret_far, // 0x45 // CB - fn_ret_near_pop, // 0x46 // C2 iw Iw -> imm16 fn_ret_far_pop, // 0x47 // CA iw Iw -> imm16 fn_sgdt, // 0x48 // 0F 01 /0 Ms -> linear address fn_sidt, // 0x49 // 0F 01 /1 Ms -> linear address fn_sldt, // 0x4A // 0F 00 /0 Ew -> rm16/rm32 * (opsize) fn_smsw, // 0x4B // 0F 01 /4 Ew -> rm16/rm32 * (opsize) fn_str, // 0x4C // 0F 00 /1 Ew -> rm16/rm32 * (opsize) fn_verr, // 0x4D // 0F 00 /4 Ew -> rm16 fn_verw // 0x4E // 0F 00 /5 Ew -> rm16 };