HyperDbg/hyperdbg/hyperkd/code/debugger/events/Termination.c
2026-04-25 18:02:48 +02:00

1863 lines
55 KiB
C

/**
* @file Termination.c
* @author Sina Karvandi (sina@hyperdbg.org)
* @brief Implementation of debugger functions for terminating events
* @details
*
* @version 0.1
* @date 2020-08-16
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
/**
* @brief Termination function for external-interrupts
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateExternalInterruptEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
if (DebuggerEventListCount(&g_Events->ExternalInterruptOccurredEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastSetDisableExternalInterruptExitingOnlyOnClearingInterruptEventsAllCores();
}
else
{
ExtensionCommandUnsetExternalInterruptExitingOnlyOnClearingInterruptEventsAllCores();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->ExternalInterruptOccurredEventsHead;
while (&g_Events->ExternalInterruptOccurredEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Broadcast to disable on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastSetDisableExternalInterruptExitingOnlyOnClearingInterruptEventsAllCores();
}
else
{
ExtensionCommandUnsetExternalInterruptExitingOnlyOnClearingInterruptEventsAllCores();
}
}
}
/**
* @brief Termination function for hidden hook read/write/execute
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateHiddenHookReadAndWriteAndExecuteEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
if (InputFromVmxRoot)
{
//
// EPT hooking tag is same as event tag, so we can use it to unhook
//
TerminateEptHookUnHookAllHooksByHookingTagFromVmxRootAndApplyInvalidation(Event->Tag);
}
else
{
//
// EPT hooking tag is same as event tag, so we can use it to unhook
//
ConfigureEptHookUnHookAllByHookingTag(Event->Tag);
}
}
/**
* @brief Termination function for hidden hook (hidden breakpoints)
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateHiddenHookExecCcEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
//
// Because there are different EPT hooks, like READ, WRITE, READ WRITE,
// DETOURS INLINE HOOK, HIDDEN BREAKPOINT HOOK and all of them are
// unhooked with a same routine, we will not check whether the list of
// all of them is empty or not and instead, we remove just a single
// hook, this way is better as hidden hooks and ept modifications are
// not dependent to a single bit and if we remove or add any other hook
// then it won't cause any problem for other hooks
//
//
// In this hook Event->OptionalParam1 is the virtual address of the
// target address that we put hook on it
//
if (InputFromVmxRoot)
{
TerminateEptHookUnHookSingleAddressFromVmxRootAndApplyInvalidation(Event->Options.OptionalParam1,
(UINT64)NULL);
}
else
{
ConfigureEptHookUnHookSingleAddress(Event->Options.OptionalParam1,
(UINT64)NULL,
Event->ProcessId);
}
}
/**
* @brief Termination function for hidden hook (detours)
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateHiddenHookExecDetoursEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
//
// Because there are different EPT hooks, like READ, WRITE, READ WRITE,
// DETOURS INLINE HOOK, HIDDEN BREAKPOINT HOOK and all of them are
// unhooked with a same routine, we will not check whether the list of
// all of them is empty or not and instead, we remove just a single
// hook, this way is better as hidden hooks and ept modifications are
// not dependent to a single bit and if we remove or add any other hook
// then it won't cause any problem for other hooks
//
//
// In HIDDEN_HOOK_EXEC_DETOURS, OptionalParam1 points to a physical
// address that represent the virtual address, we have to convert
// this address to virtual address as the unhook routine works on
// virtual addresses
//
if (InputFromVmxRoot)
{
TerminateEptHookUnHookSingleAddressFromVmxRootAndApplyInvalidation((UINT64)NULL,
Event->Options.OptionalParam1);
}
else
{
ConfigureEptHookUnHookSingleAddress((UINT64)NULL,
Event->Options.OptionalParam1,
Event->ProcessId);
}
}
/**
* @brief Termination function for msr read events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateRdmsrExecutionEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
if (DebuggerEventListCount(&g_Events->RdmsrInstructionExecutionEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetMsrBitmapReadAllCores();
}
else
{
ExtensionCommandResetChangeAllMsrBitmapReadAllCores();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->RdmsrInstructionExecutionEventsHead;
while (&g_Events->RdmsrInstructionExecutionEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Broadcast to reset msr bitmap on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetMsrBitmapReadAllCores();
}
else
{
ExtensionCommandResetChangeAllMsrBitmapReadAllCores();
}
}
}
/**
* @brief Termination function for msr write events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateWrmsrExecutionEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
if (DebuggerEventListCount(&g_Events->WrmsrInstructionExecutionEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetMsrBitmapWriteAllCores();
}
else
{
ExtensionCommandResetAllMsrBitmapWriteAllCores();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->WrmsrInstructionExecutionEventsHead;
while (&g_Events->WrmsrInstructionExecutionEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Broadcast to reset msr bitmap on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetMsrBitmapWriteAllCores();
}
else
{
ExtensionCommandResetAllMsrBitmapWriteAllCores();
}
}
}
/**
* @brief Termination function for exception events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateExceptionEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
if (DebuggerEventListCount(&g_Events->ExceptionOccurredEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetExceptionBitmapOnlyOnClearingExceptionEventsAllCores();
}
else
{
ExtensionCommandResetExceptionBitmapAllCores();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->ExceptionOccurredEventsHead;
while (&g_Events->ExceptionOccurredEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Broadcast to reset exception bitmap on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetExceptionBitmapOnlyOnClearingExceptionEventsAllCores();
}
else
{
ExtensionCommandResetExceptionBitmapAllCores();
}
}
}
/**
* @brief Termination function for IN instruction events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateInInstructionExecutionEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
//
// For this event we should also check for out instructions events too
// because both of them are emulated by a single bit in vmx controls
// so if there is anything in out events list then we can remove all
// the events
//
if (DebuggerEventListCount(&g_Events->InInstructionExecutionEventsHead) > 1 ||
DebuggerEventListCount(&g_Events->OutInstructionExecutionEventsHead) >= 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetIoBitmapAllCores();
}
else
{
ExtensionCommandIoBitmapResetAllCores();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->InInstructionExecutionEventsHead;
while (&g_Events->InInstructionExecutionEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Broadcast to reset i/o bitmap on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetIoBitmapAllCores();
}
else
{
ExtensionCommandIoBitmapResetAllCores();
}
}
}
/**
* @brief Termination function for OUT Instructions events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateOutInstructionExecutionEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
//
// For this event we should also check for out instructions events too
// because both of them are emulated by a single bit in vmx controls
// so if there is anything in out events list then we can remove all
// the events
//
if (DebuggerEventListCount(&g_Events->OutInstructionExecutionEventsHead) > 1 ||
DebuggerEventListCount(&g_Events->InInstructionExecutionEventsHead) >= 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetIoBitmapAllCores();
}
else
{
ExtensionCommandIoBitmapResetAllCores();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->OutInstructionExecutionEventsHead;
while (&g_Events->OutInstructionExecutionEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Broadcast to reset i/o bitmap on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastResetIoBitmapAllCores();
}
else
{
ExtensionCommandIoBitmapResetAllCores();
}
}
}
/**
* @brief Termination function for VMCALL Instruction events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateVmcallExecutionEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
UNREFERENCED_PARAMETER(Event);
UNREFERENCED_PARAMETER(InputFromVmxRoot);
if (DebuggerEventListCount(&g_Events->VmcallInstructionExecutionEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// Nothing we can do for this event type, let it work because of other events
//
return;
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// We set the global variable related to the vmcall to FALSE
// so the vm-exit handler, no longer triggers events related
// to the vmcalls (still they cause vm-exits as vmcall is an
// unconditional instruction for vm-exit)
//
VmFuncSetTriggerEventForVmcalls(FALSE);
}
}
/**
* @brief Termination function for user-mode, kernel-mode exec trap events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateExecTrapModeChangedEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
if (DebuggerEventListCount(&g_Events->TrapExecutionModeChangedEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// Just remove the process id from the watching list
//
ConfigureExecTrapRemoveProcessFromWatchingList(Event->ProcessId);
//
// Nothing we can do for this event type, let it work because of other events
//
return;
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// We have to uninitialize the event
// If the debugger is in the Debugger Mode, we prefer not to uninitialize the
// exec mode trap because re-activating it needs a special command (preactivate)
// to be used so if the user needs to re-create such a functionality again
// after removing it, then it's not needed to re-run the 'preactivate' command
// again
//
if (!InputFromVmxRoot)
{
ConfigureUninitializeExecTrapOnAllProcessors();
}
//
// Remove the process id from the watching list
// THIS SHOULD BE DONE AFTER UNINITIALIZING THE TRAPS BECAUSE IT MIGHT END UP TO AN ERROR
//
ConfigureExecTrapRemoveProcessFromWatchingList(Event->ProcessId);
}
}
/**
* @brief Termination function for CPUID Instruction events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateCpuidExecutionEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
UNREFERENCED_PARAMETER(Event);
UNREFERENCED_PARAMETER(InputFromVmxRoot);
if (DebuggerEventListCount(&g_Events->CpuidInstructionExecutionEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// Nothing we can do for this event type, let it work because of other events
//
return;
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// We set the global variable related to the cpuid to FALSE
// so the vm-exit handler, no longer triggers events related
// to the cpuids (still they cause vm-exits as cpuid is an
// unconditional instruction for vm-exit)
//
VmFuncSetTriggerEventForCpuids(FALSE);
}
}
/**
* @brief Termination function for RDTSC/RDTSCP Instruction events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateTscEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
if (DebuggerEventListCount(&g_Events->TscInstructionExecutionEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableRdtscExitingForClearingTscEventsAllCores();
}
else
{
ExtensionCommandDisableRdtscExitingForClearingEventsAllCores();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->TscInstructionExecutionEventsHead;
while (&g_Events->TscInstructionExecutionEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Disable it on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableRdtscExitingForClearingTscEventsAllCores();
}
else
{
ExtensionCommandDisableRdtscExitingForClearingEventsAllCores();
}
}
}
/**
* @brief Termination function for RDPMC Instruction events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminatePmcEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
if (DebuggerEventListCount(&g_Events->PmcInstructionExecutionEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableRdpmcExitingAllCores();
}
else
{
ExtensionCommandDisableRdpmcExitingAllCores();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->PmcInstructionExecutionEventsHead;
while (&g_Events->PmcInstructionExecutionEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Disable it on all the cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableRdpmcExitingAllCores();
}
else
{
ExtensionCommandDisableRdpmcExitingAllCores();
}
}
}
/**
* @brief Termination function for MOV to control registers events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateControlRegistersEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
if (DebuggerEventListCount(&g_Events->ControlRegisterModifiedEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableMov2CrExitingForClearingCrEventsAllCores(&Event->Options);
}
else
{
ExtensionCommandDisableMov2ControlRegsExitingForClearingEventsAllCores(Event);
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->ControlRegisterModifiedEventsHead;
while (&g_Events->ControlRegisterModifiedEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Disable it on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableMov2CrExitingForClearingCrEventsAllCores(&Event->Options);
}
else
{
ExtensionCommandDisableMov2ControlRegsExitingForClearingEventsAllCores(Event);
}
}
}
/**
* @brief Termination function for MOV to debug registers events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateDebugRegistersEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
if (DebuggerEventListCount(&g_Events->DebugRegistersAccessedEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableMov2DrExitingForClearingDrEventsAllCores();
}
else
{
ExtensionCommandDisableMov2DebugRegsExitingForClearingEventsAllCores();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->DebugRegistersAccessedEventsHead;
while (&g_Events->DebugRegistersAccessedEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Disable it on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableMov2DrExitingForClearingDrEventsAllCores();
}
else
{
ExtensionCommandDisableMov2DebugRegsExitingForClearingEventsAllCores();
}
}
}
/**
* @brief Termination function for SYSCALL Instruction events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateSyscallHookEferEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
//
// For this event we should also check for sysret instructions events too
// because both of them are emulated by a single bit in vmx controls
// and an MSR so if there is anything in out events list then we can
// remove all the events
//
if (DebuggerEventListCount(&g_Events->SyscallHooksEferSyscallEventsHead) > 1 ||
DebuggerEventListCount(&g_Events->SyscallHooksEferSysretEventsHead) >= 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableEferSyscallEventsAllCores();
}
else
{
DebuggerEventDisableEferOnAllProcessors();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->SyscallHooksEferSyscallEventsHead;
while (&g_Events->SyscallHooksEferSyscallEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Disable it on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableEferSyscallEventsAllCores();
}
else
{
DebuggerEventDisableEferOnAllProcessors();
}
}
}
/**
* @brief Termination function for SYSRET Instruction events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateSysretHookEferEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
PLIST_ENTRY TempList = 0;
DEBUGGER_EVENT_AND_ACTION_RESULT ResultsToReturn = {0};
//
// For this event we should also check for syscall instructions events too
// because both of them are emulated by a single bit in vmx controls
// and an MSR so if there is anything in out events list then we can
// remove all the events
//
if (DebuggerEventListCount(&g_Events->SyscallHooksEferSysretEventsHead) > 1 ||
DebuggerEventListCount(&g_Events->SyscallHooksEferSyscallEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// For this purpose, first we disable all the events by
// disabling all of them
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableEferSyscallEventsAllCores();
}
else
{
DebuggerEventDisableEferOnAllProcessors();
}
//
// Then we iterate through the list of this event to re-apply
// the previous events
//
TempList = &g_Events->SyscallHooksEferSysretEventsHead;
while (&g_Events->SyscallHooksEferSysretEventsHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGER_EVENT CurrentEvent = CONTAINING_RECORD(TempList, DEBUGGER_EVENT, EventsOfSameTypeList);
//
// We have to check because we don't want to re-apply
// the terminated event
//
if (CurrentEvent->Tag != Event->Tag)
{
//
// re-apply the event
//
DebuggerApplyEvent(CurrentEvent, &ResultsToReturn, InputFromVmxRoot);
if (!ResultsToReturn.IsSuccessful)
{
LogInfo("Err, unable to re-apply previous events");
}
}
}
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// Disable it on all cores
//
if (InputFromVmxRoot)
{
HaltedBroadcastDisableEferSyscallEventsAllCores();
}
else
{
DebuggerEventDisableEferOnAllProcessors();
}
}
}
/**
* @brief Check and modify state of exception bitmap
*
* @param CoreId Core specific resource
* @param BitmapMask The current bitmask of the resource
* @param PassOver The pass over option
*
* @return BOOLEAN
*/
BOOLEAN
TerminateQueryDebuggerResourceExceptionBitmap(UINT32 CoreId,
UINT32 * BitmapMask,
PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
//
// Check if the integrity check is because of clearing
// events or not, if it's for clearing events, the debugger
// will automatically set
//
if (!(PassOver & PASSING_OVER_EXCEPTION_EVENTS))
{
//
// we have to check for !exception events and apply the mask
//
*BitmapMask = *BitmapMask | DebuggerExceptionEventBitmapMask(CoreId);
}
//
// Check if it's because of disabling !syscall or !sysret commands
// or not, if it's because of clearing #UD in these events then we
// can ignore the checking for this command, otherwise, we have to
// check it
//
if (!(PassOver & PASSING_OVER_UD_EXCEPTIONS_FOR_SYSCALL_SYSRET_HOOK))
{
//
// Check if the debugger has events relating to syscall or sysret,
// if no, we can safely ignore #UDs, otherwise, #UDs should be
// activated
//
if (DebuggerEventListCountByEventType(SYSCALL_HOOK_EFER_SYSCALL, CoreId) != 0 ||
DebuggerEventListCountByEventType(SYSCALL_HOOK_EFER_SYSRET, CoreId) != 0)
{
//
// #UDs should be activated
//
*BitmapMask = *BitmapMask | (1 << EXCEPTION_VECTOR_UNDEFINED_OPCODE);
}
}
//
// Check for kernel or user debugger's presence
//
if (DebuggerQueryDebuggerStatus())
{
*BitmapMask = *BitmapMask | (1 << EXCEPTION_VECTOR_BREAKPOINT);
*BitmapMask = *BitmapMask | (1 << EXCEPTION_VECTOR_DEBUG_BREAKPOINT);
}
//
// Check for intercepting #DB by threads tracer
//
if (KdQueryDebuggerQueryThreadOrProcessTracingDetailsByCoreId(CoreId,
DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_DEBUG_REGISTER_INTERCEPTION))
{
*BitmapMask = *BitmapMask | (1 << EXCEPTION_VECTOR_DEBUG_BREAKPOINT);
}
//
// Do not terminate the operation
//
return FALSE;
}
/**
* @brief Check and modify state of external interrupt exiting
*
* @param CoreId Core specific resource
* @param PassOver The pass over option
*
* @return BOOLEAN
*/
BOOLEAN
TerminateQueryDebuggerResourceExternalInterruptExiting(UINT32 CoreId,
PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
//
// Check if the integrity check is because of clearing
// events or not, if it's for clearing events, the debugger
// will automatically set
//
if (!(PassOver & PASSING_OVER_INTERRUPT_EVENTS))
{
//
// we have to check for !interrupt events and decide whether to
// ignore this event or not
//
if (DebuggerEventListCountByEventType(EXTERNAL_INTERRUPT_OCCURRED, CoreId) != 0)
{
//
// We should ignore this unset, because !interrupt is enabled for this core
//
return TRUE;
}
}
//
// Check if it should remain active for thread or process changing or not
//
if (KdQueryDebuggerQueryThreadOrProcessTracingDetailsByCoreId(CoreId,
DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_INTERRUPTS_FOR_THREAD_CHANGE) ||
KdQueryDebuggerQueryThreadOrProcessTracingDetailsByCoreId(CoreId,
DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_INTERRUPTS_FOR_PROCESS_CHANGE))
{
//
// Terminate the operation
//
return TRUE;
}
//
// Not terminate it
//
return FALSE;
}
/**
* @brief Check and modify state of TSC exiting
*
* @param CoreId Core specific resource
* @param PassOver The pass over option
*
* @return BOOLEAN
*/
BOOLEAN
TerminateQueryDebuggerResourceTscExiting(UINT32 CoreId,
PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
//
// Check if the integrity check is because of clearing
// events or not, if it's for clearing events, the debugger
// will automatically set
//
if (!(PassOver & PASSING_OVER_TSC_EVENTS))
{
//
// we have to check for !tsc events and decide whether to
// ignore this event or not
//
if (DebuggerEventListCountByEventType(TSC_INSTRUCTION_EXECUTION, CoreId) != 0)
{
//
// We should ignore this unset, because !tsc is enabled for this core
//
return TRUE;
}
}
//
// Not terminate it
//
return FALSE;
}
/**
* @brief Check and modify state of mov 2 debug regs exiting
*
* @param CoreId Core specific resource
* @param PassOver The pass over option
*
* @return BOOLEAN
*/
BOOLEAN
TerminateQueryDebuggerResourceMov2DebugRegExiting(UINT32 CoreId,
PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
//
// Check if the integrity check is because of clearing
// events or not, if it's for clearing events, the debugger
// will automatically set
//
if (!(PassOver & PASSING_OVER_MOV_TO_HW_DEBUG_REGS_EVENTS))
{
//
// we have to check for !dr events and decide whether to
// ignore this event or not
//
if (DebuggerEventListCountByEventType(DEBUG_REGISTERS_ACCESSED, CoreId) != 0)
{
//
// We should ignore this unset, because !dr is enabled for this core
//
return TRUE;
}
}
//
// Check if thread switching is enabled or not
//
if (KdQueryDebuggerQueryThreadOrProcessTracingDetailsByCoreId(CoreId,
DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_DEBUG_REGISTER_INTERCEPTION))
{
//
// We should ignore it as we want this to switch to new thread
//
return TRUE;
}
//
// Not terminate
//
return FALSE;
}
/**
* @brief Check and modify state of move to control register exiting
*
* @param CoreId Core specific resource
* @param PassOver The pass over option
*
* @return BOOLEAN
*/
BOOLEAN
TerminateQueryDebuggerResourceMovControlRegsExiting(UINT32 CoreId,
PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
//
// Check if the integrity check is because of clearing
// events or not, if it's for clearing events, the debugger
// will automatically set
//
if (!(PassOver & PASSING_OVER_MOV_TO_CONTROL_REGS_EVENTS))
{
//
// we have to check for !dr events and decide whether to
// ignore this event or not
//
if (DebuggerEventListCountByEventType(CONTROL_REGISTER_MODIFIED, CoreId) != 0)
{
//
// We should ignore this unset, because !crwrite is enabled for this core
//
return TRUE;
}
}
//
// Not terminate
//
return FALSE;
}
/**
* @brief Check and modify state of move to cr3 control register exiting
*
* @param CoreId Core specific resource
* @param PassOver The pass over option
*
* @return BOOLEAN
*/
BOOLEAN
TerminateQueryDebuggerResourceMovToCr3Exiting(UINT32 CoreId,
PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
UNREFERENCED_PARAMETER(PassOver);
//
// Check if process switching is enabled or not
//
if (KdQueryDebuggerQueryThreadOrProcessTracingDetailsByCoreId(CoreId,
DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_WAITING_FOR_MOV_CR3_VM_EXITS))
{
//
// We should ignore it as we want this to switch to new process
//
return TRUE;
}
//
// Do not terminate
//
return FALSE;
}
/**
* @brief Check and modify state of save and load debug controls (DR7 and IA32_DEBUGCTLS)
* on exit and entry VM controls
*
* @param CoreId Core specific resource
* @param PassOver The pass over option
*
* @return BOOLEAN
*/
BOOLEAN
TerminateQueryDebuggerResourceSaveAndLoadDebugControls(UINT32 CoreId,
PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
UNREFERENCED_PARAMETER(PassOver);
//
// Check if debug register load and save on entry and exit controls are needed for thread interception
//
if (ThreadQueryDebugRegisterInterceptionStateByCoreId(CoreId))
{
//
// We should ignore it as we want this to interception of thread to work
//
return TRUE;
}
//
// Query the hypertrace project about this controls since there might be using this load and save controls
//
if (HyperTraceLbrQueryStateOfLbrSaveAndLoadVmExitAndEntryControls(CoreId))
{
//
// We should ignore it since LBR feature of hypertrace is still using it
//
return TRUE;
}
//
// Do not terminate
//
return FALSE;
}
/**
* @brief Remove single hook from the hooked pages list and invalidate TLB
* @details Should be called from vmx root-mode
*
* @param VirtualAddress Virtual address to unhook (optional)
* @param PhysAddress Physical address to unhook (optional)
*
* @return BOOLEAN If unhook was successful it returns true or if it was not successful returns false
*/
BOOLEAN
TerminateEptHookUnHookSingleAddressFromVmxRootAndApplyInvalidation(UINT64 VirtualAddress,
UINT64 PhysAddress)
{
BOOLEAN Result = FALSE;
EPT_SINGLE_HOOK_UNHOOKING_DETAILS TargetUnhookingDetails = {0};
//
// Perform unhooking directly from VMX-root mode
//
Result = ConfigureEptHookUnHookSingleAddressFromVmxRoot(VirtualAddress,
PhysAddress,
&TargetUnhookingDetails);
if (Result == TRUE)
{
//
// It's the responsibility of the caller to restore EPT entries and
// invalidate EPT caches
//
if (TargetUnhookingDetails.CallerNeedsToRestoreEntryAndInvalidateEpt)
{
HaltedBroadcastUnhookSinglePageAllCores(&TargetUnhookingDetails);
}
//
// It's the responsibility of the caller to clear #BPs directly from
// VMX-root mode if applied from VMX-root mode
//
if (TargetUnhookingDetails.RemoveBreakpointInterception)
{
//
// The hook was the last hook and we can broadcast to
// not intercept #BPs anymore
//
HaltedBroadcastUnSetExceptionBitmapAllCores(EXCEPTION_VECTOR_BREAKPOINT);
}
return TRUE;
}
//
// The result of removing EPT hook was not okay
//
return FALSE;
}
/**
* @brief Remove all hooks from the hooked pages list and invalidate TLB using hooking tag
* @details Should be called from vmx root-mode
*
* @param HookingTag The hooking tag to unhook
*
* @return BOOLEAN If unhook was successful it returns true or if it was not successful returns false
*/
BOOLEAN
TerminateEptHookUnHookAllHooksByHookingTagFromVmxRootAndApplyInvalidation(UINT64 HookingTag)
{
BOOLEAN Result = FALSE;
BOOLEAN IsAtLeastOneHookRemoved = FALSE;
EPT_SINGLE_HOOK_UNHOOKING_DETAILS TargetUnhookingDetails = {0};
UnhookNextPossibleTag:
//
// Perform unhooking directly from VMX-root mode using hooking tag
//
Result = ConfigureEptHookUnHookSingleHookByHookingTagFromVmxRoot(HookingTag,
&TargetUnhookingDetails);
if (Result == TRUE)
{
//
// At least one hook was removed
//
IsAtLeastOneHookRemoved = TRUE;
//
// It's the responsibility of the caller to restore EPT entries and
// invalidate EPT caches
//
if (TargetUnhookingDetails.CallerNeedsToRestoreEntryAndInvalidateEpt)
{
HaltedBroadcastUnhookSinglePageAllCores(&TargetUnhookingDetails);
}
//
// It's the responsibility of the caller to clear #BPs directly from
// VMX-root mode if applied from VMX-root mode
//
if (TargetUnhookingDetails.RemoveBreakpointInterception)
{
//
// The hook was the last hook and we can broadcast to
// not intercept #BPs anymore
//
HaltedBroadcastUnSetExceptionBitmapAllCores(EXCEPTION_VECTOR_BREAKPOINT);
}
//
// Keep unhooking until there is no hook with the same hooking tag
//
goto UnhookNextPossibleTag;
}
//
// The result of removing EPT hook
//
return IsAtLeastOneHookRemoved;
}
/**
* @brief Termination query state of debugger
*
* @param CoreId Core specific resource
* @param ResourceType Type of resource
* @param Context The context specified to the resource
* @param PassOver The pass over option
*
* @return BOOLEAN If TRUE the caller might terminate the operation
*/
BOOLEAN
TerminateQueryDebuggerResource(UINT32 CoreId,
PROTECTED_HV_RESOURCES_TYPE ResourceType,
PVOID Context,
PROTECTED_HV_RESOURCES_PASSING_OVERS PassOver)
{
BOOLEAN Result = FALSE;
switch (ResourceType)
{
case PROTECTED_HV_RESOURCES_EXCEPTION_BITMAP:
Result = TerminateQueryDebuggerResourceExceptionBitmap(CoreId, (UINT32 *)Context, PassOver);
break;
case PROTECTED_HV_RESOURCES_EXTERNAL_INTERRUPT_EXITING:
Result = TerminateQueryDebuggerResourceExternalInterruptExiting(CoreId, PassOver);
break;
case PROTECTED_HV_RESOURCES_RDTSC_RDTSCP_EXITING:
Result = TerminateQueryDebuggerResourceTscExiting(CoreId, PassOver);
break;
case PROTECTED_HV_RESOURCES_MOV_TO_DEBUG_REGISTER_EXITING:
Result = TerminateQueryDebuggerResourceMov2DebugRegExiting(CoreId, PassOver);
break;
case PROTECTED_HV_RESOURCES_MOV_CONTROL_REGISTER_EXITING:
Result = TerminateQueryDebuggerResourceMovControlRegsExiting(CoreId, PassOver);
break;
case PROTECTED_HV_RESOURCES_MOV_TO_CR3_EXITING:
Result = TerminateQueryDebuggerResourceMovToCr3Exiting(CoreId, PassOver);
break;
case PROTECTED_HV_RESOURCES_SAVE_AND_LOAD_DEBUG_CONTROLS:
Result = TerminateQueryDebuggerResourceSaveAndLoadDebugControls(CoreId, PassOver);
break;
default:
Result = FALSE;
LogError("Err, invalid protected type");
break;
}
//
// Check termination result
//
return Result;
}
/**
* @brief Termination function for XSETBV Instruction events
*
* @param Event Target Event Object
* @param InputFromVmxRoot Whether the input comes from VMX root-mode or IOCTL
*
* @return VOID
*/
VOID
TerminateXsetbvExecutionEvent(PDEBUGGER_EVENT Event, BOOLEAN InputFromVmxRoot)
{
UNREFERENCED_PARAMETER(Event);
UNREFERENCED_PARAMETER(InputFromVmxRoot);
if (DebuggerEventListCount(&g_Events->XsetbvInstructionExecutionEventsHead) > 1)
{
//
// There are still other events in the queue (list), we should only remove
// this special event (not all events)
//
//
// Nothing we can do for this event type, let it work because of other events
//
return;
}
else
{
//
// Nothing else is in the list, we have to restore everything to default
// as the current event is the only event in the list
//
//
// We set the global variable related to the xsetbv to FALSE
// so the vm-exit handler, no longer triggers events related
// to the xsetbvs (still they cause vm-exits as xsetbv is an
// unconditional instruction for vm-exit)
//
VmFuncSetTriggerEventForXsetbvs(FALSE);
}
}