mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-09 17:19:26 +00:00
1863 lines
55 KiB
C
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);
|
|
}
|
|
}
|