PATMGC.cpp revision 0ee0973d45375de2c19b5801ee3cf8fe09c45182
/* $Id$ */
/** @file
* PATM - Dynamic Guest OS Patching Manager - Guest Context
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PATM
#include "PATMInternal.h"
#include "PATMA.h"
#include <VBox/disopcode.h>
#include <stdlib.h>
#include <stdio.h>
/**
* #PF Virtual Handler callback for Guest access a page monitored by PATM
*
* @returns VBox status code (appropritate for trap handling and GC return).
* @param pVM VM Handle.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param pvRange The base address of the handled virtual range.
* @param offRange The offset of the access into this range.
* (If it's a EIP range this's the EIP, if not it's pvFault.)
*/
PATMGCDECL(int) PATMGCMonitorPage(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
{
return VINF_PATM_CHECK_PATCH_PAGE;
}
/**
* Checks if the write is located on a page with was patched before.
* (if so, then we are not allowed to turn on r/w)
*
* @returns VBox status
* @param pVM The VM to operate on.
* @param pRegFrame CPU context
* @param GCPtr GC pointer to write address
* @param cbWrite Nr of bytes to write
*
*/
PATMGCDECL(int) PATMGCHandleWriteToPatchPage(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPTR GCPtr, uint32_t cbWrite)
{
/* Quick boundary check */
)
return VERR_PATCH_NOT_FOUND;
pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(CTXSUFF(&pVM->patm.s.PatchLookupTree)->PatchTreeByPage, (RTGCPTR)pWritePageStart);
if ( !pPatchPage
&& pWritePageStart != pWritePageEnd
)
{
pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(CTXSUFF(&pVM->patm.s.PatchLookupTree)->PatchTreeByPage, (RTGCPTR)pWritePageEnd);
}
#ifdef LOG_ENABLED
if (pPatchPage)
Log2(("PATMIsWriteToPatchPage: Found page %VGv for write to %VGv %d bytes\n", pPatchPage->Core.Key, GCPtr, cbWrite));
#endif
if (pPatchPage)
{
{
/* This part of the page was not patched; try to emulate the instruction. */
if (rc == VINF_SUCCESS)
{
return VINF_SUCCESS;
}
}
/* Increase the invalid write counter for each patch that's registered for that page. */
{
pPatch->cInvalidWrites++;
}
return VINF_EM_RAW_EMULATE_INSTR;
}
return VERR_PATCH_NOT_FOUND;
}
/**
* Checks if the illegal instruction was caused by a patched instruction
*
* @returns VBox status
*
* @param pVM The VM handle.
* @param pCtxCore The relevant core context.
*/
{
int rc;
/* Very important check -> otherwise we have a security leak. */
AssertReturn(!pRegFrame->eflags.Bits.u1VM && (pRegFrame->ss & X86_SEL_RPL) == 1, VERR_ACCESS_DENIED);
/* OP_ILLUD2 in PATM generated code? */
{
LogFlow(("PATMGC: Pending action %x at %VGv\n", CTXSUFF(pVM->patm.s.pGCState)->uPendingAction, pRegFrame->eip));
/* Private PATM interface (@todo hack due to lack of anything generic). */
/* Parameters:
* eax = Pending action (currently PATM_ACTION_LOOKUP_ADDRESS)
* ecx = PATM_ACTION_MAGIC
*/
)
{
{
{
/* Parameters:
* edx = GC address to find
* edi = PATCHJUMPTABLE ptr
*/
AssertMsg(!pRegFrame->edi || PATMIsPatchGCAddr(pVM, (RTGCPTR)pRegFrame->edi), ("edx = %VGv\n", pRegFrame->edi));
if (pRec)
{
{
if (rc == VINF_SUCCESS)
{
return VINF_SUCCESS;
}
AssertFailed();
}
else
{
return VINF_SUCCESS;
}
}
else
{
#if 0
{
return VINF_SUCCESS;
}
#endif
return VINF_PATM_DUPLICATE_FUNCTION;
}
}
/* Parameters:
* edi = GC address to jump to
*/
/* Change EIP to the guest address the patch would normally jump to after setting IF. */
Assert(pVM->patm.s.CTXSUFF(pGCState)->Restore.uFlags == (PATM_RESTORE_EAX|PATM_RESTORE_ECX|PATM_RESTORE_EDI));
/* We are no longer executing PATM code; set PIF again. */
/* The caller will call trpmGCExitTrap, which will dispatch pending interrupts for us. */
return VINF_SUCCESS;
/* Parameters:
* edi = GC address to jump to
*/
Log(("PATMGC: Dispatch pending interrupt (iret); eip=%VGv->%VGv\n", pRegFrame->eip, pRegFrame->edi));
Assert(pVM->patm.s.CTXSUFF(pGCState)->Restore.uFlags == (PATM_RESTORE_EAX|PATM_RESTORE_ECX|PATM_RESTORE_EDI));
/* Change EIP to the guest address of the iret. */
/* We are no longer executing PATM code; set PIF again. */
return VINF_PATM_PENDING_IRQ_AFTER_IRET;
case PATM_ACTION_DO_V86_IRET:
{
if (VBOX_SUCCESS(rc))
{
/* We are no longer executing PATM code; set PIF again. */
#ifndef VBOX_RAW_V86
return VINF_EM_RESCHEDULE;
#else
#endif
/* does not return */
}
else
return rc;
}
#ifdef DEBUG
case PATM_ACTION_LOG_CLI:
Log(("PATMGC: CLI at %VGv (current IF=%d iopl=%d)\n", pRegFrame->eip, !!(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags & X86_EFL_IF), X86_EFL_GET_IOPL(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags) ));
return VINF_SUCCESS;
case PATM_ACTION_LOG_STI:
Log(("PATMGC: STI at %VGv (current IF=%d iopl=%d)\n", pRegFrame->eip, !!(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags & X86_EFL_IF), X86_EFL_GET_IOPL(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags) ));
return VINF_SUCCESS;
case PATM_ACTION_LOG_POPF_IF1:
Log(("PATMGC: POPF setting IF at %VGv (current IF=%d iopl=%d)\n", pRegFrame->eip, !!(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags & X86_EFL_IF), X86_EFL_GET_IOPL(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags)));
return VINF_SUCCESS;
case PATM_ACTION_LOG_POPF_IF0:
Log(("PATMGC: POPF at %VGv (current IF=%d iopl=%d)\n", pRegFrame->eip, !!(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags & X86_EFL_IF), X86_EFL_GET_IOPL(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags)));
return VINF_SUCCESS;
case PATM_ACTION_LOG_PUSHF:
Log(("PATMGC: PUSHF at %VGv (current IF=%d iopl=%d)\n", pRegFrame->eip, !!(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags & X86_EFL_IF), X86_EFL_GET_IOPL(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags) ));
return VINF_SUCCESS;
case PATM_ACTION_LOG_IF1:
return VINF_SUCCESS;
case PATM_ACTION_LOG_IRET:
{
if (rc == VINF_SUCCESS)
{
if ( (uEFlags & X86_EFL_VM)
{
if (uEFlags & X86_EFL_VM)
{
if (rc == VINF_SUCCESS)
{
Log(("PATMGC: IRET->VM stack frame: return address %04X:%VGv eflags=%08x ss:esp=%04X:%VGv\n", selCS, eip, uEFlags, selSS, esp));
Log(("PATMGC: IRET->VM stack frame: DS=%04X ES=%04X FS=%04X GS=%04X\n", selDS, selES, selFS, selGS));
}
}
else
Log(("PATMGC: IRET stack frame: return address %04X:%VGv eflags=%08x ss:esp=%04X:%VGv\n", selCS, eip, uEFlags, selSS, esp));
}
else
}
Log(("PATMGC: IRET from %VGv (IF->1) current eflags=%x\n", pRegFrame->eip, pVM->patm.s.CTXSUFF(pGCState)->uVMFlags));
return VINF_SUCCESS;
}
{
if (rc == VINF_SUCCESS)
{
if ( (uEFlags & X86_EFL_VM)
{
if (uEFlags & X86_EFL_VM)
{
if (rc == VINF_SUCCESS)
{
Log(("PATMGC: GATE->VM stack frame: return address %04X:%VGv eflags=%08x ss:esp=%04X:%VGv\n", selCS, eip, uEFlags, selSS, esp));
Log(("PATMGC: GATE->VM stack frame: DS=%04X ES=%04X FS=%04X GS=%04X\n", selDS, selES, selFS, selGS));
}
if (eip == 0x690)
{
return VINF_EM_RESCHEDULE;
}
}
else
Log(("PATMGC: GATE stack frame: return address %04X:%VGv eflags=%08x ss:esp=%04X:%VGv\n", selCS, eip, uEFlags, selSS, esp));
}
else
}
return VINF_SUCCESS;
}
case PATM_ACTION_LOG_RET:
Log(("PATMGC: RET to %VGv ESP=%VGv iopl=%d\n", pRegFrame->edx, pRegFrame->ebx, X86_EFL_GET_IOPL(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags)));
return VINF_SUCCESS;
case PATM_ACTION_LOG_CALL:
Log(("PATMGC: CALL to %VGv return addr %VGv ESP=%VGv iopl=%d\n", pVM->patm.s.CTXSUFF(pGCState)->GCCallPatchTargetAddr, pVM->patm.s.CTXSUFF(pGCState)->GCCallReturnAddr, pRegFrame->edx, X86_EFL_GET_IOPL(pVM->patm.s.CTXSUFF(pGCState)->uVMFlags)));
return VINF_SUCCESS;
#endif
default:
AssertFailed();
break;
}
}
else
AssertFailed();
}
AssertMsgFailed(("Unexpected OP_ILLUD2 in patch code at %VGv (pending action %x)!!!!\n", pRegFrame->eip, CTXSUFF(pVM->patm.s.pGCState)->uPendingAction));
return VINF_EM_RAW_EMULATE_INSTR;
}
/**
* Checks if the int 3 was caused by a patched instruction
*
* @returns VBox status
*
* @param pVM The VM handle.
* @param pCtxCore The relevant core context.
*/
{
int rc;
AssertReturn(!pRegFrame->eflags.Bits.u1VM && (pRegFrame->ss & X86_SEL_RPL) == 1, VERR_ACCESS_DENIED);
/* Int 3 in PATM generated code? (most common case) */
{
/* @note hardcoded assumption about it being a single byte int 3 instruction. */
return VINF_PATM_PATCH_INT3;
}
/** @todo could use simple caching here to speed things up. */
pRec = (PPATMPATCHREC)RTAvloGCPtrGet(&CTXSUFF(pVM->patm.s.PatchLookupTree)->PatchTree, (RTGCPTR)(pRegFrame->eip - 1)); /* eip is pointing to the instruction *after* 'int 3' already */
{
{
/* This is a special cli block that was turned into an int 3 patch. We jump to the generated code manually. */
return VINF_SUCCESS;
}
else
{
/* eip is pointing to the instruction *after* 'int 3' already */
Log(("PATMHandleInt3PatchTrap found int3 for %s at %VGv\n", patmGetInstructionString(pRec->patch.opcode, 0), pRegFrame->eip));
{
case OP_CPUID:
case OP_IRET:
break;
case OP_STR:
case OP_SGDT:
case OP_SLDT:
case OP_SIDT:
case OP_LSL:
case OP_LAR:
case OP_SMSW:
case OP_VERW:
case OP_VERR:
default:
return VINF_EM_RAW_EMULATE_INSTR;
}
cpu.mode = SELMIsSelector32Bit(pVM, pRegFrame->eflags, pRegFrame->cs, 0) ? CPUMODE_32BIT : CPUMODE_16BIT;
{
AssertFailed();
return VINF_EM_RAW_EMULATE_INSTR;
}
if (VBOX_FAILURE(rc))
{
return VINF_EM_RAW_EMULATE_INSTR;
}
if (rc != VINF_SUCCESS)
{
return VINF_EM_RAW_EMULATE_INSTR;
}
return VINF_SUCCESS;
}
}
return VERR_PATCH_NOT_FOUND;
}