HyperDbg/hyperdbg/hyperhv/code/vmm/vmx/ProtectedHv.c

752 lines
20 KiB
C

/**
* @file ProtectedHv.c
* @author Sina Karvandi (sina@hyperdbg.org)
* @brief File for protected hypervisor resources
* @details Protected Hypervisor Routines are those resource that
* are used in different parts of the debugger or hypervisor,
* these resources need extra checks to avoid integrity problems
*
* @version 0.1
* @date 2021-10-04
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
/**
* @brief Add extra mask to this resource and write it
* @details As exception bitmap is a protected resource, this
* routine makes sure that modifying exception bitmap won't
* break the debugger's integrity
*
* @param VCpu The virtual processor's state
* @param CurrentMask The mask that debugger wants to write
* @param PassOver Adds some pass over to the checks
* thus we won't check for exceptions
*
* @return VOID
*/
VOID
ProtectedHvChangeExceptionBitmapWithIntegrityCheck(VIRTUAL_MACHINE_STATE * VCpu, UINT32 CurrentMask, PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
//
// Ask the top-level module to reshape the mask
//
if (VmmCallbackQueryTerminateProtectedResource(VCpu->CoreId,
PROTECTED_HV_RESOURCES_EXCEPTION_BITMAP,
&CurrentMask,
PassOver))
{
return;
}
//
// Check for syscall callback (masking #DBs and #BPs)
//
if (g_SyscallCallbackStatus)
{
CurrentMask |= 1 << EXCEPTION_VECTOR_DEBUG_BREAKPOINT;
CurrentMask |= 1 << EXCEPTION_VECTOR_BREAKPOINT;
}
//
// Check for possible EPT Hooks (Hidden Breakpoints)
//
if (EptHookGetCountOfEpthooks(FALSE) != 0)
{
CurrentMask |= 1 << EXCEPTION_VECTOR_BREAKPOINT;
}
//
// Write the final value
//
HvWriteExceptionBitmap(CurrentMask);
}
/**
* @brief Set exception bitmap in VMCS
* @details Should be called in vmx-root
*
* @param VCpu The virtual processor's state
* @param IdtIndex Interrupt Descriptor Table index of exception
* @return VOID
*/
VOID
ProtectedHvSetExceptionBitmap(VIRTUAL_MACHINE_STATE * VCpu, UINT32 IdtIndex)
{
UINT32 ExceptionBitmap = 0;
//
// Read the current bitmap
//
ExceptionBitmap = HvReadExceptionBitmap();
if (IdtIndex == DEBUGGER_EVENT_EXCEPTIONS_ALL_FIRST_32_ENTRIES)
{
ExceptionBitmap = 0xffffffff;
}
else
{
ExceptionBitmap |= 1 << IdtIndex;
}
//
// Set the new value
//
ProtectedHvChangeExceptionBitmapWithIntegrityCheck(VCpu, ExceptionBitmap, PASSING_OVER_NONE);
}
/**
* @brief Unset exception bitmap in VMCS
* @details Should be called in vmx-root
*
* @param VCpu The virtual processor's state
* @param IdtIndex Interrupt Descriptor Table index of exception
* @return VOID
*/
VOID
ProtectedHvUnsetExceptionBitmap(VIRTUAL_MACHINE_STATE * VCpu, UINT32 IdtIndex)
{
UINT32 ExceptionBitmap = 0;
//
// Read the current bitmap
//
ExceptionBitmap = HvReadExceptionBitmap();
if (IdtIndex == DEBUGGER_EVENT_EXCEPTIONS_ALL_FIRST_32_ENTRIES)
{
ExceptionBitmap = 0x0;
}
else
{
ExceptionBitmap &= ~(1 << IdtIndex);
}
//
// Set the new value
//
ProtectedHvChangeExceptionBitmapWithIntegrityCheck(VCpu, ExceptionBitmap, PASSING_OVER_NONE);
}
/**
* @brief Reset exception bitmap in VMCS because of clearing
* !exception commands
* @details Should be called in vmx-root
* @param VCpu The virtual processor's state
*
* @return VOID
*/
VOID
ProtectedHvResetExceptionBitmapToClearEvents(VIRTUAL_MACHINE_STATE * VCpu)
{
UINT32 ExceptionBitmap = 0;
//
// Set the new value
//
ProtectedHvChangeExceptionBitmapWithIntegrityCheck(VCpu, ExceptionBitmap, PASSING_OVER_EXCEPTION_EVENTS);
}
/**
* @brief Reset exception bitmap in VMCS because of clearing
* !exception commands
* @details Should be called in vmx-root
* @param VCpu The virtual processor's state
*
* @return VOID
*/
VOID
ProtectedHvRemoveUndefinedInstructionForDisablingSyscallSysretCommands(VIRTUAL_MACHINE_STATE * VCpu)
{
UINT32 ExceptionBitmap = 0;
//
// Read the current bitmap
//
ExceptionBitmap = HvReadExceptionBitmap();
//
// Unset exception bitmap for #UD
//
ExceptionBitmap &= ~(1 << EXCEPTION_VECTOR_UNDEFINED_OPCODE);
//
// Set the new value
//
ProtectedHvChangeExceptionBitmapWithIntegrityCheck(VCpu, ExceptionBitmap, PASSING_OVER_UD_EXCEPTIONS_FOR_SYSCALL_SYSRET_HOOK);
}
/**
* @brief Set the External Interrupt Exiting
*
* @param VCpu The virtual processor's state
* @param Set Set or unset the External Interrupt Exiting
* @param PassOver Adds some pass over to the checks
* thus we won't check for interrupts
* @return VOID
*/
VOID
ProtectedHvApplySetExternalInterruptExiting(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set, PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
UINT32 PinBasedControls = 0;
UINT32 VmExitControls = 0;
//
// The protected checks are only performed if the "Set" is "FALSE",
// because if sb wants to set it to "TRUE" then we're no need to
// worry about it as it remains enabled
//
if (Set == FALSE)
{
//
// Ask the top-level driver whether to terminate this operation or not
//
if (VmmCallbackQueryTerminateProtectedResource(VCpu->CoreId,
PROTECTED_HV_RESOURCES_EXTERNAL_INTERRUPT_EXITING,
NULL,
PassOver))
{
return;
}
}
//
// In order to enable External Interrupt Exiting we have to set
// IA32_VMX_PINBASED_CTLS_EXTERNAL_INTERRUPT_EXITING_FLAG in vmx
// pin-based controls (PIN_BASED_VM_EXEC_CONTROL) and also
// we should enable IA32_VMX_EXIT_CTLS_ACKNOWLEDGE_INTERRUPT_ON_EXIT_FLAG
// on vmx vm-exit controls (VMCS_CTRL_VMEXIT_CONTROLS), also this function
// might not always be successful if the guest is not in the interruptible
// state so it wait for and interrupt-window exiting to re-inject
// the interrupt into the guest
//
//
// Read the previous flags
//
VmxVmread32P(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, &PinBasedControls);
VmxVmread32P(VMCS_CTRL_PRIMARY_VMEXIT_CONTROLS, &VmExitControls);
if (Set)
{
PinBasedControls |= IA32_VMX_PINBASED_CTLS_EXTERNAL_INTERRUPT_EXITING_FLAG;
VmExitControls |= IA32_VMX_EXIT_CTLS_ACKNOWLEDGE_INTERRUPT_ON_EXIT_FLAG;
}
else
{
PinBasedControls &= ~IA32_VMX_PINBASED_CTLS_EXTERNAL_INTERRUPT_EXITING_FLAG;
VmExitControls &= ~IA32_VMX_EXIT_CTLS_ACKNOWLEDGE_INTERRUPT_ON_EXIT_FLAG;
}
//
// Set the new value
//
VmxVmwrite64(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, PinBasedControls);
VmxVmwrite64(VMCS_CTRL_PRIMARY_VMEXIT_CONTROLS, VmExitControls);
}
/**
* @brief Set the External Interrupt Exiting
*
* @param VCpu The virtual processor's state
* @param Set Set or unset the External Interrupt Exiting
* @return VOID
*/
VOID
ProtectedHvSetExternalInterruptExiting(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set)
{
ProtectedHvApplySetExternalInterruptExiting(VCpu, Set, PASSING_OVER_NONE);
}
/**
* @brief Clear events of !interrupt
*
* @return VOID
*/
VOID
ProtectedHvExternalInterruptExitingForDisablingInterruptCommands(VIRTUAL_MACHINE_STATE * VCpu)
{
ProtectedHvApplySetExternalInterruptExiting(VCpu, FALSE, PASSING_OVER_INTERRUPT_EVENTS);
}
/**
* @brief Set vm-exit for tsc instructions (rdtsc/rdtscp)
* @details Should be called in vmx-root
*
* @param VCpu The virtual processor's state
* @param Set Set or unset the vm-exits
* @param PassOver Adds some pass over to the checks
* thus we won't check for tsc
* @return VOID
*/
VOID
ProtectedHvSetTscVmexit(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set, PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
UINT32 CpuBasedVmExecControls = 0;
//
// The protected checks are only performed if the "Set" is "FALSE",
// because if sb wants to set it to "TRUE" then we're no need to
// worry about it as it remains enabled
//
if (Set == FALSE)
{
//
// Check the top-level driver's state
//
if (VmmCallbackQueryTerminateProtectedResource(VCpu->CoreId,
PROTECTED_HV_RESOURCES_RDTSC_RDTSCP_EXITING,
NULL,
PassOver))
{
return;
}
}
//
// Read the previous flags
//
VmxVmread32P(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, &CpuBasedVmExecControls);
if (Set)
{
CpuBasedVmExecControls |= IA32_VMX_PROCBASED_CTLS_RDTSC_EXITING_FLAG;
}
else
{
CpuBasedVmExecControls &= ~IA32_VMX_PROCBASED_CTLS_RDTSC_EXITING_FLAG;
}
//
// Set the new value
//
VmxVmwrite64(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, CpuBasedVmExecControls);
}
/**
* @brief Set vm-exit for mov to debug registers
* @details Should be called in vmx-root
*
* @param VCpu The virtual processor's state
* @param Set Set or unset the vm-exits
* @param PassOver Adds some pass over to the checks
* thus we won't check for dr
* @return VOID
*/
VOID
ProtectedHvSetMovDebugRegsVmexit(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set, PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
UINT32 CpuBasedVmExecControls = 0;
//
// The protected checks are only performed if the "Set" is "FALSE",
// because if sb wants to set it to "TRUE" then we're no need to
// worry about it as it remains enabled
//
if (Set == FALSE)
{
//
// Check the top-level driver's state
//
if (VmmCallbackQueryTerminateProtectedResource(VCpu->CoreId,
PROTECTED_HV_RESOURCES_MOV_TO_DEBUG_REGISTER_EXITING,
NULL,
PassOver))
{
return;
}
}
//
// Read the previous flags
//
VmxVmread32P(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, &CpuBasedVmExecControls);
if (Set)
{
CpuBasedVmExecControls |= IA32_VMX_PROCBASED_CTLS_MOV_DR_EXITING_FLAG;
}
else
{
CpuBasedVmExecControls &= ~IA32_VMX_PROCBASED_CTLS_MOV_DR_EXITING_FLAG;
}
//
// Set the new value
//
VmxVmwrite64(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, CpuBasedVmExecControls);
}
/**
* @brief Set vm-exit for mov to cr0 / cr4 register
* @details Should be called in vmx-root
*
* @param Set or unset the vm-exits
* @param ControlRegister
* @param MaskRegister
* @return VOID
*/
VOID
ProtectedHvSetMovToCrVmexit(BOOLEAN Set, UINT64 ControlRegister, UINT64 MaskRegister)
{
if (ControlRegister == VMX_EXIT_QUALIFICATION_REGISTER_CR0)
{
if (Set)
{
VmxVmwrite64(VMCS_CTRL_CR0_GUEST_HOST_MASK, MaskRegister);
VmxVmwrite64(VMCS_CTRL_CR0_READ_SHADOW, CpuReadCr0());
}
else
{
VmxVmwrite64(VMCS_CTRL_CR0_GUEST_HOST_MASK, 0);
VmxVmwrite64(VMCS_CTRL_CR0_READ_SHADOW, 0);
}
}
else if (ControlRegister == VMX_EXIT_QUALIFICATION_REGISTER_CR4)
{
if (Set)
{
VmxVmwrite64(VMCS_CTRL_CR4_GUEST_HOST_MASK, MaskRegister);
VmxVmwrite64(VMCS_CTRL_CR4_READ_SHADOW, CpuReadCr0());
}
else
{
VmxVmwrite64(VMCS_CTRL_CR4_GUEST_HOST_MASK, 0);
VmxVmwrite64(VMCS_CTRL_CR4_READ_SHADOW, 0);
}
}
}
/**
* @brief Set vm-exit for mov to control registers
* @details Should be called in vmx-root
*
* @param VCpu The virtual processor's state
* @param Set or unset the vm-exits
* @param PassOver Adds some pass over to the checks
* @param Control Register
* @param Mask Register
* @return VOID
*/
VOID
ProtectedHvSetMovControlRegsVmexit(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set, PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver, UINT64 ControlRegister, UINT64 MaskRegister)
{
//
// The protected checks are only performed if the "Set" is "FALSE",
// because if sb wants to set it to "TRUE" then we're no need to
// worry about it as it remains enabled
//
if (Set == FALSE)
{
//
// Check the state of top-level driver
//
if (VmmCallbackQueryTerminateProtectedResource(VCpu->CoreId,
PROTECTED_HV_RESOURCES_MOV_CONTROL_REGISTER_EXITING,
NULL,
PassOver))
{
return;
}
}
ProtectedHvSetMovToCrVmexit(Set, ControlRegister, MaskRegister);
}
/**
* @brief Set vm-exit for mov to cr3 register
* @details Should be called in vmx-root
*
* @param VCpu The virtual processor's state
* @param Set Set or unset the vm-exits
* @param PassOver Adds some pass over to the checks
* thus we won't check for dr
* @return VOID
*/
VOID
ProtectedHvSetMovToCr3Vmexit(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set, PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
UINT32 CpuBasedVmExecControls = 0;
//
// The protected checks are only performed if the "Set" is "FALSE",
// because if sb wants to set it to "TRUE" then we're no need to
// worry about it as it remains enabled
//
if (Set == FALSE)
{
//
// Check the top-level driver's state
//
if (VmmCallbackQueryTerminateProtectedResource(VCpu->CoreId,
PROTECTED_HV_RESOURCES_MOV_TO_CR3_EXITING,
NULL,
PassOver))
{
return;
}
//
// Check if the trap execution is enabled or not, and whether the
// uninitialization phase started or not
//
if (g_ExecTrapInitialized && !g_ExecTrapUnInitializationStarted)
{
//
// The VMM needs mov2cr3s
//
return;
}
}
//
// Read the previous flags
//
VmxVmread32P(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, &CpuBasedVmExecControls);
if (Set)
{
CpuBasedVmExecControls |= IA32_VMX_PROCBASED_CTLS_CR3_LOAD_EXITING_FLAG;
}
else
{
CpuBasedVmExecControls &= ~IA32_VMX_PROCBASED_CTLS_CR3_LOAD_EXITING_FLAG;
}
//
// Set the new value
//
VmxVmwrite64(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, CpuBasedVmExecControls);
}
/**
* @brief Set LOAD DEBUG CONTROLS on Vm-entry controls
*
* @param VCpu
* @param Set Set or unset
* @param PassOver
*
* @return VOID
*/
VOID
ProtectedHvSetLoadDebugControlsIntegrityCheck(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set, PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
UINT32 VmentryControls = 0;
//
// The protected checks are only performed if the "Set" is "FALSE",
// because if sb wants to set it to "TRUE" then we're no need to
// worry about it as it remains enabled
//
if (Set == FALSE)
{
//
// Check the top-level driver's state
//
if (VmmCallbackQueryTerminateProtectedResource(VCpu->CoreId,
PROTECTED_HV_RESOURCES_SAVE_AND_LOAD_DEBUG_CONTROLS,
NULL,
PassOver))
{
return;
}
}
//
// Read the previous flags
//
VmxVmread32P(VMCS_CTRL_VMENTRY_CONTROLS, &VmentryControls);
if (Set)
{
VmentryControls |= IA32_VMX_ENTRY_CTLS_LOAD_DEBUG_CONTROLS_FLAG;
}
else
{
VmentryControls &= ~IA32_VMX_ENTRY_CTLS_LOAD_DEBUG_CONTROLS_FLAG;
}
//
// Set the new value
//
VmxVmwrite32(VMCS_CTRL_VMENTRY_CONTROLS, VmentryControls);
}
/**
* @brief Set SAVE DEBUG CONTROLS on Vm-exit controls
*
* @param VCpu
* @param Set Set or unset
* @param PassOver
*
* @return VOID
*/
VOID
ProtectedHvSetSaveDebugControlsIntegrityCheck(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set, PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
UINT32 VmexitControls = 0;
//
// The protected checks are only performed if the "Set" is "FALSE",
// because if sb wants to set it to "TRUE" then we're no need to
// worry about it as it remains enabled
//
if (Set == FALSE)
{
//
// Check the top-level driver's state
//
if (VmmCallbackQueryTerminateProtectedResource(VCpu->CoreId,
PROTECTED_HV_RESOURCES_SAVE_AND_LOAD_DEBUG_CONTROLS,
NULL,
PassOver))
{
return;
}
}
//
// Read the previous flags
//
VmxVmread32P(VMCS_CTRL_PRIMARY_VMEXIT_CONTROLS, &VmexitControls);
if (Set)
{
VmexitControls |= IA32_VMX_EXIT_CTLS_SAVE_DEBUG_CONTROLS_FLAG;
}
else
{
VmexitControls &= ~IA32_VMX_EXIT_CTLS_SAVE_DEBUG_CONTROLS_FLAG;
}
//
// Set the new value
//
VmxVmwrite32(VMCS_CTRL_PRIMARY_VMEXIT_CONTROLS, VmexitControls);
}
/**
* @brief Set LOAD DEBUG CONTROLS on VM-entry controls
*
* @param VCpu
* @param Set Set or unset
*
* @return VOID
*/
VOID
ProtectedHvSetSaveDebugControls(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set)
{
ProtectedHvSetSaveDebugControlsIntegrityCheck(VCpu, Set, PASSING_OVER_NONE);
}
/**
* @brief Set SAVE DEBUG CONTROLS on VM-exit controls
*
* @param VCpu
* @param Set Set or unset
*
* @return VOID
*/
VOID
ProtectedHvSetLoadDebugControls(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set)
{
ProtectedHvSetLoadDebugControlsIntegrityCheck(VCpu, Set, PASSING_OVER_NONE);
}
/**
* @brief Set the RDTSC/P Exiting
*
* @param VCpu The virtual processor's state
* @param Set Set or unset the RDTSC/P Exiting
* @return VOID
*/
VOID
ProtectedHvSetRdtscExiting(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set)
{
ProtectedHvSetTscVmexit(VCpu, Set, PASSING_OVER_NONE);
}
/**
* @brief Clear events of !tsc
* @param VCpu The virtual processor's state
*
* @return VOID
*/
VOID
ProtectedHvDisableRdtscExitingForDisablingTscCommands(VIRTUAL_MACHINE_STATE * VCpu)
{
ProtectedHvSetTscVmexit(VCpu, FALSE, PASSING_OVER_TSC_EVENTS);
}
/**
* @brief Set MOV to HW Debug Regs Exiting
*
* @param VCpu The virtual processor's state
* @param Set Set or unset the MOV to HW Debug Regs Exiting
* @return VOID
*/
VOID
ProtectedHvSetMovDebugRegsExiting(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set)
{
ProtectedHvSetMovDebugRegsVmexit(VCpu, Set, PASSING_OVER_NONE);
}
/**
* @brief Clear events of !dr
* @param VCpu The virtual processor's state
*
* @return VOID
*/
VOID
ProtectedHvDisableMovDebugRegsExitingForDisablingDrCommands(VIRTUAL_MACHINE_STATE * VCpu)
{
ProtectedHvSetMovDebugRegsVmexit(VCpu, FALSE, PASSING_OVER_MOV_TO_HW_DEBUG_REGS_EVENTS);
}
/**
* @brief Clear events of !crwrite
*
* @param VCpu The virtual processor's state
* @param Control Register
* @param Mask Register
* @return VOID
*/
VOID
ProtectedHvDisableMovControlRegsExitingForDisablingCrCommands(VIRTUAL_MACHINE_STATE * VCpu, UINT64 ControlRegister, UINT64 MaskRegister)
{
ProtectedHvSetMovControlRegsVmexit(VCpu, FALSE, PASSING_OVER_MOV_TO_CONTROL_REGS_EVENTS, ControlRegister, MaskRegister);
}
/**
* @brief Set MOV to CR3 Exiting
*
* @param VCpu The virtual processor's state
* @param Set Set or unset the MOV to CR3 Exiting
* @return VOID
*/
VOID
ProtectedHvSetMov2Cr3Exiting(VIRTUAL_MACHINE_STATE * VCpu, BOOLEAN Set)
{
ProtectedHvSetMovToCr3Vmexit(VCpu, Set, PASSING_OVER_NONE);
}
/**
* @brief Set MOV to CR0/4 Exiting
*
* @param Set or unset the MOV to CR0/4 Exiting
* @param Control Register
* @param Mask Register
* @return VOID
*/
VOID
ProtectedHvSetMov2CrExiting(BOOLEAN Set, UINT64 ControlRegister, UINT64 MaskRegister)
{
ProtectedHvSetMovToCrVmexit(Set, ControlRegister, MaskRegister);
}