/** * @file Kd.c * @author Sina Karvandi (sina@hyperdbg.org) * @author Alee Amini (alee@hyperdbg.org) * @brief Routines related to kernel mode debugging * @details * @version 0.1 * @date 2020-12-20 * * @copyright This project is released under the GNU Public License v3. * */ #include "pch.h" /** * @brief initialize kernel debugger * @details this function should be called on vmx non-root * * @return VOID */ VOID KdInitializeKernelDebugger() { // // Allocate DPC routine // // for (SIZE_T i = 0; i < CoreCount; i++) // { // g_DbgState[i].KdDpcObject = PlatformMemAllocateNonPagedPool(sizeof(KDPC)); // // if (g_DbgState[i].KdDpcObject == NULL) // { // LogError("Err, allocating dpc holder for debuggee"); // return; // } // } // // Enable vm-exit on Hardware debug exceptions and breakpoints // so, intercept #DBs and #BP by changing exception bitmap (one core) // BroadcastEnableDbAndBpExitingAllCores(); // // Reset pause break requests // RtlZeroMemory(&g_IgnoreBreaksToDebugger, sizeof(DEBUGGEE_REQUEST_TO_IGNORE_BREAKS_UNTIL_AN_EVENT)); // // Initial the needed pools for instant events // KdInitializeInstantEventPools(); // // Indicate that the kernel debugger is active // g_KernelDebuggerState = TRUE; } /** * @brief uninitialize kernel debugger * * @details this function should be called on vmx non-root * * @return VOID */ VOID KdUninitializeKernelDebugger() { ULONG ProcessorsCount; if (g_KernelDebuggerState) { ProcessorsCount = KeQueryActiveProcessorCount(0); // // Indicate that the kernel debugger is not active // g_KernelDebuggerState = FALSE; // // Reset pause break requests // RtlZeroMemory(&g_IgnoreBreaksToDebugger, sizeof(DEBUGGEE_REQUEST_TO_IGNORE_BREAKS_UNTIL_AN_EVENT)); // // Remove all active breakpoints // BreakpointRemoveAllBreakpoints(); // // Disable vm-exit on Hardware debug exceptions and breakpoints // so, not intercept #DBs and #BP by changing exception bitmap (one core) // BroadcastDisableDbAndBpExitingAllCores(); } } /** * @brief Checks whether the immediate messaging mechism is * needed or not * @param OperationCode * * @return BOOLEAN */ BOOLEAN KdCheckImmediateMessagingMechanism(UINT32 OperationCode) { return (g_KernelDebuggerState && !(OperationCode & OPERATION_MANDATORY_DEBUGGEE_BIT)); } /** * @brief Initialize the required pools for instant events * * @return VOID */ VOID KdInitializeInstantEventPools() { // // Request pages to be allocated for regular instant events // PoolManagerRequestAllocation(REGULAR_INSTANT_EVENT_CONDITIONAL_BUFFER, MAXIMUM_REGULAR_INSTANT_EVENTS, INSTANT_REGULAR_EVENT_BUFFER); // // Request pages to be allocated for regular instant events's actions // PoolManagerRequestAllocation(REGULAR_INSTANT_EVENT_ACTION_BUFFER, MAXIMUM_REGULAR_INSTANT_EVENTS, INSTANT_REGULAR_EVENT_ACTION_BUFFER); #if MAXIMUM_BIG_INSTANT_EVENTS >= 1 // // Request pages to be allocated for big instant events // PoolManagerRequestAllocation(BIG_INSTANT_EVENT_CONDITIONAL_BUFFER, MAXIMUM_BIG_INSTANT_EVENTS, INSTANT_BIG_EVENT_BUFFER); // // Request pages to be allocated for big instant events's actions // PoolManagerRequestAllocation(BIG_INSTANT_EVENT_ACTION_BUFFER, MAXIMUM_BIG_INSTANT_EVENTS, INSTANT_BIG_EVENT_ACTION_BUFFER); #endif // MAXIMUM_BIG_INSTANT_EVENTS // // Pre-allocate pools for possible EPT hooks // Because there are possible init EPT hook structures, we only allocate the // maximum number of regular instant event subtracted from the initial pages // ConfigureEptHookReservePreallocatedPoolsForEptHooks(MAXIMUM_REGULAR_INSTANT_EVENTS - MAXIMUM_NUMBER_OF_INITIAL_PREALLOCATED_EPT_HOOKS); if (!PoolManagerCheckAndPerformAllocationAndDeallocation()) { LogWarning("Warning, cannot allocate the pre-allocated pools for EPT hooks"); // // BTW, won't fail the starting phase because of this // } } /** * @brief A test function for DPC * @param Dpc * @param DeferredContext * @param SystemArgument1 * @param SystemArgument2 * * @return VOID */ VOID KdDummyDPC(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) { UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument1); UNREFERENCED_PARAMETER(SystemArgument2); LogInfo("I'm here %llx\n", DeferredContext); } /** * @brief Add a DPC to dpc queue * @param Routine * @param Parameter * @param ProcessorNumber * * @return VOID */ VOID KdFireDpc(PVOID Routine, PVOID Parameter) { ULONG CurrentCore = KeGetCurrentProcessorNumberEx(NULL); KeInitializeDpc(g_DbgState[CurrentCore].KdDpcObject, (PKDEFERRED_ROUTINE)Routine, Parameter); KeInsertQueueDpc(g_DbgState[CurrentCore].KdDpcObject, NULL, NULL); } /** * @brief Query for process/thread interception status * @param CoreId * @param TracingType * * @return BOOLEAN whether it's activated or not */ BOOLEAN KdQueryDebuggerQueryThreadOrProcessTracingDetailsByCoreId(UINT32 CoreId, DEBUGGER_THREAD_PROCESS_TRACING TracingType) { BOOLEAN Result = FALSE; PROCESSOR_DEBUGGING_STATE * DbgState = &g_DbgState[CoreId]; switch (TracingType) { case DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_INTERRUPTS_FOR_THREAD_CHANGE: Result = DbgState->ThreadOrProcessTracingDetails.InterceptClockInterruptsForThreadChange; break; case DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_INTERRUPTS_FOR_PROCESS_CHANGE: Result = DbgState->ThreadOrProcessTracingDetails.InterceptClockInterruptsForProcessChange; break; case DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_DEBUG_REGISTER_INTERCEPTION: Result = DbgState->ThreadOrProcessTracingDetails.DebugRegisterInterceptionState; break; case DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_WAITING_FOR_MOV_CR3_VM_EXITS: Result = DbgState->ThreadOrProcessTracingDetails.IsWatingForMovCr3VmExits; break; default: LogError("Err, debugger encountered an unknown query type for querying process or thread interception details"); break; } return Result; } /** * @brief Query to ignore handling mov 2 debug regs exiting * @param CoreId * * @return BOOLEAN whether it's activated or not */ BOOLEAN KdQueryIgnoreHandlingMov2DebugRegs(UINT32 CoreId) { // // Handle access to debug registers, if we should not ignore it, it is // because on detecting thread scheduling we ignore the hardware debug // registers modifications // return KdQueryDebuggerQueryThreadOrProcessTracingDetailsByCoreId(CoreId, DEBUGGER_THREAD_PROCESS_TRACING_INTERCEPT_CLOCK_DEBUG_REGISTER_INTERCEPTION); } /** * @brief calculate the checksum of received buffer from debugger * * @param Buffer * @param LengthReceived * @return BYTE */ _Use_decl_annotations_ BYTE KdComputeDataChecksum(PVOID Buffer, UINT32 Length) { BYTE CalculatedCheckSum = 0; BYTE Temp = 0; while (Length--) { Temp = *(BYTE *)Buffer; CalculatedCheckSum = CalculatedCheckSum + Temp; Buffer = (PVOID)((UINT64)Buffer + 1); } return CalculatedCheckSum; } /** * @brief Sends a HyperDbg response packet to the debugger * * @param PacketType * @param Response * @param OptionalBuffer * @param OptionalBufferLength * @return BOOLEAN */ _Use_decl_annotations_ BOOLEAN KdResponsePacketToDebugger( DEBUGGER_REMOTE_PACKET_TYPE PacketType, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION Response, CHAR * OptionalBuffer, UINT32 OptionalBufferLength) { DEBUGGER_REMOTE_PACKET Packet = {0}; BOOLEAN Result = FALSE; // // Make the packet's structure // Packet.Indicator = INDICATOR_OF_HYPERDBG_PACKET; Packet.TypeOfThePacket = PacketType; // // Set the requested action // Packet.RequestedActionOfThePacket = Response; // // Send the serial packets to the debugger // if (OptionalBuffer == NULL || OptionalBufferLength == 0) { Packet.Checksum = KdComputeDataChecksum((PVOID)((UINT64)&Packet + 1), sizeof(DEBUGGER_REMOTE_PACKET) - sizeof(BYTE)); // // Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, // if not we use the windows spinlock // ScopedSpinlock( DebuggerResponseLock, Result = SerialConnectionSend((CHAR *)&Packet, sizeof(DEBUGGER_REMOTE_PACKET))); } else { Packet.Checksum = KdComputeDataChecksum((PVOID)((UINT64)&Packet + 1), sizeof(DEBUGGER_REMOTE_PACKET) - sizeof(BYTE)); Packet.Checksum += KdComputeDataChecksum((PVOID)OptionalBuffer, OptionalBufferLength); // // Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, // if not we use the windows spinlock // ScopedSpinlock( DebuggerResponseLock, Result = SerialConnectionSendTwoBuffers((CHAR *)&Packet, sizeof(DEBUGGER_REMOTE_PACKET), OptionalBuffer, OptionalBufferLength)); } if (g_IgnoreBreaksToDebugger.PauseBreaksUntilSpecialMessageSent && g_IgnoreBreaksToDebugger.SpeialEventResponse == Response) { // // Set it to false by zeroing it // RtlZeroMemory(&g_IgnoreBreaksToDebugger, sizeof(DEBUGGEE_REQUEST_TO_IGNORE_BREAKS_UNTIL_AN_EVENT)); } return Result; } /** * @brief Sends a HyperDbg logging response packet to the debugger * * @param OptionalBuffer * @param OptionalBufferLength * @param OperationCode * @return BOOLEAN */ _Use_decl_annotations_ BOOLEAN KdLoggingResponsePacketToDebugger( CHAR * OptionalBuffer, UINT32 OptionalBufferLength, UINT32 OperationCode) { DEBUGGER_REMOTE_PACKET Packet = {0}; BOOLEAN Result = FALSE; // // Make the packet's structure // Packet.Indicator = INDICATOR_OF_HYPERDBG_PACKET; Packet.TypeOfThePacket = DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER; // // Set the requested action // Packet.RequestedActionOfThePacket = DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_LOGGING_MECHANISM; // // Calculate checksum // Packet.Checksum = KdComputeDataChecksum((PVOID)((UINT64)&Packet + 1), sizeof(DEBUGGER_REMOTE_PACKET) - sizeof(BYTE)); Packet.Checksum += KdComputeDataChecksum((PVOID)&OperationCode, sizeof(UINT32)); Packet.Checksum += KdComputeDataChecksum((PVOID)OptionalBuffer, OptionalBufferLength); // // Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, // if not we use the windows spinlock // ScopedSpinlock( DebuggerResponseLock, Result = SerialConnectionSendThreeBuffers((CHAR *)&Packet, sizeof(DEBUGGER_REMOTE_PACKET), (CHAR *)&OperationCode, sizeof(UINT32), OptionalBuffer, OptionalBufferLength)); return Result; } /** * @brief Regular step-over, step one instruction to the debuggee if * there is a call then it jumps the call * * @param LastRip Last RIP register * @param IsNextInstructionACall * @param CallLength * * @return VOID */ VOID KdRegularStepOver(UINT64 LastRip, BOOLEAN IsNextInstructionACall, UINT32 CallLength) { UINT64 NextAddressForHardwareDebugBp = 0; ULONG ProcessorsCount; // LogInfo("Last Rip: %llx, IsNextInstructionACall: %s, Call length: %x", // LastRip, // IsNextInstructionACall ? "true" : "false", // CallLength); if (IsNextInstructionACall) { // // It's a call, we should put a hardware debug register breakpoint // on the next instruction // NextAddressForHardwareDebugBp = LastRip + CallLength; ProcessorsCount = KeQueryActiveProcessorCount(0); // // Store the detail of the hardware debug register to avoid trigger // in other processes // g_HardwareDebugRegisterDetailsForStepOver.Address = NextAddressForHardwareDebugBp; g_HardwareDebugRegisterDetailsForStepOver.ProcessId = HANDLE_TO_UINT32(PsGetCurrentProcessId()); g_HardwareDebugRegisterDetailsForStepOver.ThreadId = HANDLE_TO_UINT32(PsGetCurrentThreadId()); // // Add hardware debug breakpoints on all core on vm-entry // for (SIZE_T i = 0; i < ProcessorsCount; i++) { g_DbgState[i].HardwareDebugRegisterForStepping = NextAddressForHardwareDebugBp; } } else { // // Any instruction other than call (regular step) // TracingRegularStepInInstruction(); } } /** * @brief Handles debug events when kernel-debugger is attached * * @param DbgState The state of the debugger on the current core * @param TrapSetByDebugger Shows whether a trap set by debugger or not * * @return VOID */ VOID KdHandleDebugEventsWhenKernelDebuggerIsAttached(PROCESSOR_DEBUGGING_STATE * DbgState, BOOLEAN TrapSetByDebugger) { DEBUGGER_TRIGGERED_EVENT_DETAILS TargetContext = {0}; BOOLEAN IgnoreDebugEvent = FALSE; UINT64 LastVmexitRip = VmFuncGetLastVmexitRip(DbgState->CoreId); // // It's a breakpoint and should be handled by the kernel debugger // TargetContext.Context = (PVOID)LastVmexitRip; if (TrapSetByDebugger) { // // *** Handle a regular trap flag (most of the times as a result of stepping) set by the debugger *** // // // Check and handle if there is a software defined breakpoint // if (!BreakpointCheckAndHandleDebuggerDefinedBreakpoints(DbgState, LastVmexitRip, DEBUGGEE_PAUSING_REASON_DEBUGGEE_STEPPED, FALSE)) { if (g_HardwareDebugRegisterDetailsForStepOver.Address != (UINT64)NULL) { // // Check if it's caused by a step-over hardware debug breakpoint or not // if (LastVmexitRip == g_HardwareDebugRegisterDetailsForStepOver.Address) { if (g_HardwareDebugRegisterDetailsForStepOver.ProcessId == HANDLE_TO_UINT32(PsGetCurrentProcessId()) && g_HardwareDebugRegisterDetailsForStepOver.ThreadId == HANDLE_TO_UINT32(PsGetCurrentThreadId())) { // // It's a step caused by a debug register breakpoint step-over // RtlZeroMemory(&g_HardwareDebugRegisterDetailsForStepOver, sizeof(HARDWARE_DEBUG_REGISTER_DETAILS)); } else { // // Should be ignored because it's a hardware debug register that is // likely triggered by other thread // IgnoreDebugEvent = TRUE; // // Also, we should re-apply the hardware debug breakpoint on this thread // SetDebugRegisters(DEBUGGER_DEBUG_REGISTER_FOR_STEP_OVER, BREAK_ON_INSTRUCTION_FETCH, FALSE, g_HardwareDebugRegisterDetailsForStepOver.Address); } } } if (!IgnoreDebugEvent) { // // Handle a regular step // KdHandleBreakpointAndDebugBreakpoints(DbgState, DEBUGGEE_PAUSING_REASON_DEBUGGEE_STEPPED, &TargetContext); } } } else { // // It's a regular debug break event // KdHandleBreakpointAndDebugBreakpoints(DbgState, DEBUGGEE_PAUSING_REASON_DEBUGGEE_HARDWARE_DEBUG_REGISTER_HIT, &TargetContext); } } /** * @brief before halting any core, all the tasks will be applied to all * cores including the main core * @details these tasks will be applied in vmx-root * * @param DbgState The state of the debugger on the current core * * @return VOID */ VOID KdApplyTasksPreHaltCore(PROCESSOR_DEBUGGING_STATE * DbgState) { // // Check to unset mov to cr3 vm-exits // if (DbgState->ThreadOrProcessTracingDetails.InitialSetProcessChangeEvent == TRUE) { // // Disable process change detection // ProcessEnableOrDisableThreadChangeMonitor(DbgState, FALSE, DbgState->ThreadOrProcessTracingDetails.InitialSetByClockInterrupt); } // // Check to unset change thread alerts // if (DbgState->ThreadOrProcessTracingDetails.InitialSetThreadChangeEvent == TRUE) { // // Disable thread change alerts // ThreadEnableOrDisableThreadChangeMonitor(DbgState, FALSE, DbgState->ThreadOrProcessTracingDetails.InitialSetByClockInterrupt); } } /** * @brief before continue any core, all the tasks will be applied to all * cores including the main core * @details these tasks will be applied in vmx-root * * @param DbgState The state of the debugger on the current core * * @return VOID */ VOID KdApplyTasksPostContinueCore(PROCESSOR_DEBUGGING_STATE * DbgState) { // // Check to apply hardware debug register breakpoints for step-over // if (DbgState->HardwareDebugRegisterForStepping != (UINT64)NULL) { SetDebugRegisters(DEBUGGER_DEBUG_REGISTER_FOR_STEP_OVER, BREAK_ON_INSTRUCTION_FETCH, FALSE, DbgState->HardwareDebugRegisterForStepping); DbgState->HardwareDebugRegisterForStepping = (UINT64)NULL; } } /** * @brief continue the debuggee, this function guarantees that all other cores * are continued (except current core) * @param DbgState The state of the debugger on the current core * @param SpeialEventResponse * @param PauseBreaksUntilSpecialMessageSent * * @return VOID */ _Use_decl_annotations_ VOID KdContinueDebuggee(PROCESSOR_DEBUGGING_STATE * DbgState, BOOLEAN PauseBreaksUntilSpecialMessageSent, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION SpeialEventResponse) { if (PauseBreaksUntilSpecialMessageSent) { g_IgnoreBreaksToDebugger.PauseBreaksUntilSpecialMessageSent = TRUE; g_IgnoreBreaksToDebugger.SpeialEventResponse = SpeialEventResponse; } // // Check if we should enable interrupts in this core or not, // we have another same check in SWITCHING CORES too // VmFuncCheckAndEnableExternalInterrupts(DbgState->CoreId); // // Unlock all the cores // ULONG ProcessorsCount = KeQueryActiveProcessorCount(0); for (SIZE_T i = 0; i < ProcessorsCount; i++) { SpinlockUnlock(&g_DbgState[i].Lock); } } /** * @brief continue the debuggee, just the current operating core * @param DbgState The state of the debugger on the current core * * @return VOID */ VOID KdContinueDebuggeeJustCurrentCore(PROCESSOR_DEBUGGING_STATE * DbgState) { // // In the case of any halting event, the processor won't send NMIs // to other cores if this field is set // DbgState->DoNotNmiNotifyOtherCoresByThisCore = TRUE; // // Unlock the current core // SpinlockUnlock(&DbgState->Lock); } /** * @brief change the current operating core to new core * * @param DbgState The state of the debugger on the current core * @param ChangeCorePacket * @return BOOLEAN */ BOOLEAN KdSwitchCore(PROCESSOR_DEBUGGING_STATE * DbgState, DEBUGGEE_CHANGE_CORE_PACKET * ChangeCorePacket) { ULONG ProcessorsCount = KeQueryActiveProcessorCount(0); if (DbgState->CoreId == ChangeCorePacket->NewCore) { // // The operating core and the target core is the same, no need for further action // ChangeCorePacket->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL; return FALSE; // Return FALSE to not unlock anything } // // Check if core is valid or not // if (ChangeCorePacket->NewCore >= ProcessorsCount) { // // Invalid core count // ChangeCorePacket->Result = DEBUGGER_ERROR_PREPARING_DEBUGGEE_INVALID_CORE_IN_REMOTE_DEBUGGE; return FALSE; } // // *** Core is valid *** // // // Check to see whether this core is locked or not // if (!KdCheckTargetCoreIsLocked(ChangeCorePacket->NewCore)) { ChangeCorePacket->Result = DEBUGGER_ERROR_TARGET_SWITCHING_CORE_IS_NOT_LOCKED; return FALSE; } // // Check if we should enable interrupts in this core or not // VmFuncCheckAndEnableExternalInterrupts(DbgState->CoreId); // // Unset the current operating core (this is not important as if we // return from the operating function then the it will be unset // automatically but as we want to not have two operating cores // at the same time so we unset it here too) // DbgState->MainDebuggingCore = FALSE; // // Set new operating core // g_DbgState[ChangeCorePacket->NewCore].MainDebuggingCore = TRUE; // // Unlock the new core // *** We should not unlock the spinlock here as the other core might // simultaneously start sending packets and corrupt our packets *** // ChangeCorePacket->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL; return TRUE; } /** * @brief Check the user-mode priority buffer * * @return VOID */ VOID KdCheckUserModePriorityBuffers() { // // Check if the priority buffer is full or not // if (LogCallbackCheckIfBufferIsFull(TRUE)) { LogWarning("Warning, the user-mode priority buffers are full, thus the new action replaces " "previously unserviced actions. As the result, some functionalities might not work correctly!\n" "For more information please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n"); } } /** * @brief Notify user-mode to unload the debuggee and close the connections * * @return VOID */ VOID KdCloseConnectionAndUnloadDebuggee() { // // Check if the priority buffer is full or not // KdCheckUserModePriorityBuffers(); // // Send one byte buffer and operation codes // LogCallbackSendBuffer(OPERATION_COMMAND_FROM_DEBUGGER_CLOSE_AND_UNLOAD_VMM, "$", sizeof(CHAR), TRUE); } /** * @brief Notify user-mode to re-send (reload) the symbol packets * @param SymPacket * * @return VOID */ _Use_decl_annotations_ VOID KdReloadSymbolDetailsInDebuggee(PDEBUGGEE_SYMBOL_REQUEST_PACKET SymPacket) { // // Check if the priority buffer is full or not // KdCheckUserModePriorityBuffers(); // // Send one byte buffer and operation codes // LogCallbackSendBuffer(OPERATION_COMMAND_FROM_DEBUGGER_RELOAD_SYMBOL, SymPacket, sizeof(DEBUGGEE_SYMBOL_REQUEST_PACKET), TRUE); } /** * @brief Notify user-mode to about new user-input buffer * @param Descriptor * @param Len * * @return VOID */ VOID KdNotifyDebuggeeForUserInput(DEBUGGEE_USER_INPUT_PACKET * Descriptor, UINT32 Len) { // // Check if the priority buffer is full or not // KdCheckUserModePriorityBuffers(); // // Send user-input buffer along with operation code to // the user-mode // LogCallbackSendBuffer(OPERATION_DEBUGGEE_USER_INPUT, Descriptor, Len, TRUE); } /** * @brief Send the result of formats command to the kernel debugger * @param Value * * @return VOID */ VOID KdSendFormatsFunctionResult(UINT64 Value) { DEBUGGEE_FORMATS_PACKET FormatsPacket = {0}; FormatsPacket.Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL; FormatsPacket.Value = Value; // // Kernel debugger is active, we should send the bytes over serial // KdResponsePacketToDebugger( DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_FORMATS, (CHAR *)&FormatsPacket, sizeof(DEBUGGEE_FORMATS_PACKET)); } /** * @brief Notify debugger that the execution of command finished * @param CoreId * * @return VOID */ VOID KdSendCommandFinishedSignal(UINT32 CoreId) { // // Halt other cores again // KdHandleBreakpointAndDebugBreakpointsCallback(CoreId, DEBUGGEE_PAUSING_REASON_DEBUGGEE_COMMAND_EXECUTION_FINISHED, NULL); } /** * @brief Tries to get the lock and won't return until successfully get the lock * * @param DbgState The state of the debugger on the current core * * @return VOID */ _Use_decl_annotations_ VOID KdHandleHaltsWhenNmiReceivedFromVmxRoot(PROCESSOR_DEBUGGING_STATE * DbgState) { // // During the debugging of HyperDbg, we realized that whenever an // event is set, one core might (and will) get the lock of the // debugging and other cores that are triggering the same event // go to the wait for the debugging lock // As we send NMIs to all cores to notify them and halt them, // a core might be in VMX-root and receive the NMI // Handling the NMI and halting from the NMI handlers is not // possible as the stack of the Windows' NMI handler routine // is not big enough to handle HyperDbg's command dispatching // routines, so if the user switches to the cores that are halted // from an NMI handler then we ran out of stack and debugger crashes // It is also not possible to change the stack to a bigger stack because // we're not really interested in allocating other memories for the stack // and also the target stack will not be a valid Windows stack as it's // supposed to run with NMI handling routines. // // By the way, I conclude to let the NMI handler finish its normal // execution and then we check for the possible pausing reasons. // // The pausing scenario should be checked two cases, // 1. If the current core is stuck in spinlock of getting // the debug lock // 2. If the current core is making it self ready for the vm-entry // // In these two cases we should check for the possible halting of the core // // // Handle halt of the current core as an NMI // KdHandleNmi(DbgState); // // Set the indication to false as we handled it // DbgState->NmiState.NmiCalledInVmxRootRelatedToHaltDebuggee = FALSE; } /** * @brief Tries to get the lock and won't return until successfully get the lock * * @param DbgState The state of the debugger on the current core * @param Lock The lock variable * * @return VOID */ VOID KdCustomDebuggerBreakSpinlockLock(PROCESSOR_DEBUGGING_STATE * DbgState, volatile LONG * Lock) { UINT32 Wait = 1; // // *** Lock handling breaks *** // while (!SpinlockTryLock(Lock)) { for (UINT32 i = 0; i < Wait; ++i) { CpuPause(); } // // check if the core needs to be locked // if (DbgState->NmiState.WaitingToBeLocked) { // // We should ignore one MTF as we touched MTF and it's not usable anymore // VmFuncChangeIgnoreOneMtfState(DbgState->CoreId, TRUE); // // Handle break of the core // if (DbgState->NmiState.NmiCalledInVmxRootRelatedToHaltDebuggee) { // // Handle it like an NMI is received from VMX root // KdHandleHaltsWhenNmiReceivedFromVmxRoot(DbgState); } else { // // Handle halt of the current core as an NMI // KdHandleNmi(DbgState); } } // // Don't call "pause" too many times. If the wait becomes too big, // clamp it to the MaxWait. // if (Wait * 2 > 65536) { Wait = 65536; } else { Wait = Wait * 2; } } } /** * @brief Handle broadcast NMIs for halting cores in vmx-root mode * * @param CoreId * @param IsOnVmxNmiHandler * * @return VOID */ VOID KdHandleNmiBroadcastDebugBreaks(UINT32 CoreId, BOOLEAN IsOnVmxNmiHandler) { // // Get the current debugging state // PROCESSOR_DEBUGGING_STATE * DbgState = &g_DbgState[CoreId]; // // We use it as a global flag (for both vmx-root and vmx non-root), because // generally it doesn't have any use case in vmx-root (IsOnVmxNmiHandler == FALSE) // but in some cases, we might set the MTF but another vm-exit receives before // MTF and in that place if it tries to trigger and event, then the MTF is not // handled and the core is not locked properly, just waits to get the handle // of the "DebuggerHandleBreakpointLock", so we check this flag there // DbgState->NmiState.WaitingToBeLocked = TRUE; if (IsOnVmxNmiHandler) { // // Indicate that it's called from NMI handle, and it relates to // halting the debuggee // DbgState->NmiState.NmiCalledInVmxRootRelatedToHaltDebuggee = TRUE; // // If the core was in the middle of spinning on the spinlock // of getting the debug lock, this mechanism is not needed, // but if the core is not spinning there or the core is processing // a random vm-exit, then we inject an immediate vm-exit after vm-entry // or inject a DPC // this is used for two reasons. // // 1. first, we will get the registers (context) to halt the core // 2. second, it guarantees that if the NMI arrives within any // instruction in vmx-root mode, then we injected an immediate // vm-exit and we won't miss any cpu cycle in the guest // // KdFireDpc(KdHaltCoreInTheCaseOfHaltedFromNmiInVmxRoot, NULL); VmFuncSetMonitorTrapFlag(TRUE); } else { // // Handle core break // KdHandleNmi(DbgState); } } /** * @brief Handle #DBs and #BPs for kernel debugger * @details This function can be used in vmx-root * * @param CoreId * @param Reason * @param EventDetails * * @return VOID */ _Use_decl_annotations_ VOID KdHandleBreakpointAndDebugBreakpointsCallback(UINT32 CoreId, DEBUGGEE_PAUSING_REASON Reason, PDEBUGGER_TRIGGERED_EVENT_DETAILS EventDetails) { PROCESSOR_DEBUGGING_STATE * DbgState = &g_DbgState[CoreId]; KdHandleBreakpointAndDebugBreakpoints(DbgState, Reason, EventDetails); } /** * @brief Handle NMI state for MTF * @param DbgState The state of the debugger on the current core * * @details This function should be called in vmx-root mode * @return BOOLEAN */ BOOLEAN KdCheckAndHandleNmiStateForMtf(PROCESSOR_DEBUGGING_STATE * DbgState) { BOOLEAN Result = FALSE; if (DbgState->NmiState.WaitingToBeLocked) { // // The NMI wait is handled here // Result = TRUE; // // Handle break of the core // if (DbgState->NmiState.NmiCalledInVmxRootRelatedToHaltDebuggee) { // // Handle it like an NMI is received from VMX root // KdHandleHaltsWhenNmiReceivedFromVmxRoot(DbgState); } else { // // Handle halt of the current core as an NMI // KdHandleNmi(DbgState); } } return Result; } /** * @brief Handle instrumentation step-in for kernel debugger * @details This function will be called from vmx-root mode * * @param DbgState The state of the debugger on the current core * * @return VOID */ VOID KdHandleInstrumentationStepIn(PROCESSOR_DEBUGGING_STATE * DbgState) { // // Check for tracing instructions // if (DbgState->TracingMode) { // // Handle callback for tracing instructions // TracingHandleMtf(DbgState); } else { // // Only 16 bit is needed however, vmwrite might write on other bits // and corrupt other variables, that's why we get 64bit // UINT64 CsSel = NULL64_ZERO; DEBUGGER_TRIGGERED_EVENT_DETAILS TargetContext = {0}; UINT64 LastVmexitRip = VmFuncGetLastVmexitRip(DbgState->CoreId); // // Check if the cs selector changed or not, which indicates that the // execution changed from user-mode to kernel-mode or kernel-mode to // user-mode // CsSel = VmFuncGetCsSelector(); KdCheckGuestOperatingModeChanges(DbgState->InstrumentationStepInTrace.CsSel, (UINT16)CsSel); // // Unset the MTF flag and previous cs selector // DbgState->InstrumentationStepInTrace.CsSel = 0; // // Check and handle if there is a software defined breakpoint // if (!BreakpointCheckAndHandleDebuggerDefinedBreakpoints(DbgState, LastVmexitRip, DEBUGGEE_PAUSING_REASON_DEBUGGEE_STEPPED, TRUE)) { // // Handle the step (if the disassembly ignored here, it means the debugger wants to use it // as a tracking mechanism, so we'll change the reason for that) // TargetContext.Context = (PVOID)LastVmexitRip; KdHandleBreakpointAndDebugBreakpoints(DbgState, DbgState->IgnoreDisasmInNextPacket ? DEBUGGEE_PAUSING_REASON_DEBUGGEE_TRACKING_STEPPED : DEBUGGEE_PAUSING_REASON_DEBUGGEE_STEPPED, &TargetContext); } } } /** * @brief Handle Monitor Trap Flag (MTF) callback for kernel debugger * @details This function will be called from vmx-root mode * * @param CoreId * * @return BOOLEAN */ BOOLEAN KdHandleMtfCallback(UINT32 CoreId) { PROCESSOR_DEBUGGING_STATE * DbgState = &g_DbgState[CoreId]; BOOLEAN IsMtfHandled = FALSE; // // *** Check if we need to re-apply a breakpoint or not // We check it separately because the guest might step // instructions on an MTF so we want to check for the step too *** // if (BreakpointCheckAndHandleReApplyingBreakpoint(DbgState)) { // // Check for re-enabling external interrupts // VmFuncEnableAndCheckForPreviousExternalInterrupts(DbgState->CoreId); // // MTF is handled // IsMtfHandled = TRUE; } // // *** Check for instrumentation step-in *** // if (VmFuncQueryInstrumentationStepInState(DbgState->CoreId)) { // // Unset the MTF instrumentation state (might be changed in the caller) // VmFuncUnsetInstrumentationStepInState(DbgState->CoreId); // // Handle MTF in the debugger // KdHandleInstrumentationStepIn(DbgState); // // MTF is handled // IsMtfHandled = TRUE; } // // check the condition of passing the execution to NMIs // // This one wastes one week of my life! // During the testing we realized the !epthook command in Debugger Mode // is not working. After some tests, it's because if in the middle of a // command in vmx-root and NMI is sent and the debugger waits for another // MTF, we'll ignore that MTF and a new MTF is not set again. // That's why we moved this check here so every command that needs a task // from MTF is doing its tasks and when we reached here, the check for halting // the debuggee in MTF is performed // else if (KdCheckAndHandleNmiStateForMtf(DbgState)) { // // MTF is handled // IsMtfHandled = TRUE; } return IsMtfHandled; } /** * @brief Handle #DBs and #BPs for kernel debugger * @details This function can be used in vmx-root * * @param DbgState The state of the debugger on the current core * @param Reason * @param EventDetails * * @return VOID */ _Use_decl_annotations_ VOID KdHandleBreakpointAndDebugBreakpoints(PROCESSOR_DEBUGGING_STATE * DbgState, DEBUGGEE_PAUSING_REASON Reason, PDEBUGGER_TRIGGERED_EVENT_DETAILS EventDetails) { // // Lock handling breaks // KdCustomDebuggerBreakSpinlockLock(DbgState, &DebuggerHandleBreakpointLock); // // Check if we should ignore this break request or not // if (g_IgnoreBreaksToDebugger.PauseBreaksUntilSpecialMessageSent) { // // Unlock the above core // SpinlockUnlock(&DebuggerHandleBreakpointLock); // // Not continue anymore as the break should be ignored // return; } // // Set it as the main core // DbgState->MainDebuggingCore = TRUE; // // Lock current core // DbgState->NmiState.WaitingToBeLocked = FALSE; SpinlockLock(&DbgState->Lock); // // Set the halting reason // g_DebuggeeHaltReason = Reason; // // Set the context and tag, and stage // if (EventDetails != NULL) { RtlCopyMemory(&g_EventTriggerDetail, EventDetails, sizeof(DEBUGGER_TRIGGERED_EVENT_DETAILS)); } if (DbgState->DoNotNmiNotifyOtherCoresByThisCore == TRUE) { // // Unset to avoid future not notifying events // DbgState->DoNotNmiNotifyOtherCoresByThisCore = FALSE; } else { // // Make sure, nobody is in the middle of sending anything // SpinlockLock(&DebuggerResponseLock); // // Broadcast NMI with the intention of halting cores // VmFuncNmiBroadcastRequest(DbgState->CoreId); // // Unlock the sending response lock to perform regular debugging // SpinlockUnlock(&DebuggerResponseLock); } // // All the cores should go and manage through the following function // KdManageSystemHaltOnVmxRoot(DbgState, EventDetails); // // Clear the halting reason // g_DebuggeeHaltReason = DEBUGGEE_PAUSING_REASON_NOT_PAUSED; // // Clear the context, tag, and stage // RtlZeroMemory(&g_EventTriggerDetail, sizeof(DEBUGGER_TRIGGERED_EVENT_DETAILS)); // // Unlock handling breaks // if (DbgState->MainDebuggingCore) { DbgState->MainDebuggingCore = FALSE; SpinlockUnlock(&DebuggerHandleBreakpointLock); } } /** * @brief Handle NMI Vm-exits * @param DbgState The state of the debugger on the current core * * @details This function should be called in vmx-root mode * @return VOID */ _Use_decl_annotations_ VOID KdHandleNmi(PROCESSOR_DEBUGGING_STATE * DbgState) { // // Test // // LogInfo("NMI Arrived on : %d \n",CurrentProcessorIndex); // // Not the main debugging core // DbgState->MainDebuggingCore = FALSE; // // Lock current core // DbgState->NmiState.WaitingToBeLocked = FALSE; SpinlockLock(&DbgState->Lock); // // All the cores should go and manage through the following function // KdManageSystemHaltOnVmxRoot(DbgState, NULL); // // Unlock handling breaks // if (DbgState->MainDebuggingCore) { DbgState->MainDebuggingCore = FALSE; SpinlockUnlock(&DebuggerHandleBreakpointLock); } } /** * @brief apply a guaranteed step one instruction to the debuggee * * @param DbgState The state of the debugger on the current core * * @return VOID */ VOID KdGuaranteedStepInstruction(PROCESSOR_DEBUGGING_STATE * DbgState) { // // Only 16 bit is needed however, vmwrite might write on other bits // and corrupt other variables, that's why we get 64bit // UINT64 CsSel = (UINT64)NULL; // // Read cs to have a trace of the execution mode of running application // in the debuggee // CsSel = VmFuncGetCsSelector(); DbgState->InstrumentationStepInTrace.CsSel = (UINT16)CsSel; // // Set an indicator of instrumentation step-in MTF // VmFuncSetInstrumentationStepInState(DbgState->CoreId); // // Not unset MTF again // VmFuncChangeMtfUnsettingState(DbgState->CoreId, TRUE); // // Disable external interrupts and interrupt Window // VmFuncDisableExternalInterruptsAndInterruptWindow(DbgState->CoreId); // // Set the MTF flag // VmFuncSetMonitorTrapFlag(TRUE); } /** * @brief Check if the execution mode (kernel-mode to user-mode or user-mode * to kernel-mode) changed * * @param PreviousCsSelector * @param CurrentCsSelector * * @return BOOLEAN */ BOOLEAN KdCheckGuestOperatingModeChanges(UINT16 PreviousCsSelector, UINT16 CurrentCsSelector) { PreviousCsSelector = PreviousCsSelector & ~3; CurrentCsSelector = CurrentCsSelector & ~3; // // Check if the execution modes are the same or not // if (PreviousCsSelector == CurrentCsSelector) { // // Execution modes are not changed // return FALSE; } if ((PreviousCsSelector == KGDT64_R3_CODE || PreviousCsSelector == KGDT64_R3_CMCODE) && CurrentCsSelector == KGDT64_R0_CODE) { // // User-mode -> Kernel-mode // LogInfo("User-mode -> Kernel-mode\n"); } else if ((CurrentCsSelector == KGDT64_R3_CODE || CurrentCsSelector == KGDT64_R3_CMCODE) && PreviousCsSelector == KGDT64_R0_CODE) { // // Kernel-mode to user-mode // LogInfo("Kernel-mode -> User-mode\n"); } else if (CurrentCsSelector == KGDT64_R3_CODE && PreviousCsSelector == KGDT64_R3_CMCODE) { // // A heaven's gate (User-mode 32-bit code -> User-mode 64-bit code) // LogInfo("32-bit User-mode -> 64-bit User-mode (Heaven's gate)\n"); } else if (PreviousCsSelector == KGDT64_R3_CODE && CurrentCsSelector == KGDT64_R3_CMCODE) { // // A heaven's gate (User-mode 64-bit code -> User-mode 32-bit code) // LogInfo("64-bit User-mode -> 32-bit User-mode (Return from Heaven's gate)\n"); } else { LogError("Err, unknown changes in cs selector during the instrumentation step-in\n"); } // // Execution modes are changed // return TRUE; } /** * @brief Send event registration buffer to user-mode to register the event * @param EventDetailHeader * @param DebuggerEventAndActionResult * * @return BOOLEAN Shows whether the debuggee should be continued or not */ BOOLEAN KdPerformRegisterEvent(PDEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET EventDetailHeader, DEBUGGER_EVENT_AND_ACTION_RESULT * DebuggerEventAndActionResult) { #if EnableInstantEventMechanism DEBUGGER_GENERAL_EVENT_DETAIL * GeneralEventDetail = NULL; GeneralEventDetail = (PDEBUGGER_GENERAL_EVENT_DETAIL)(((CHAR *)EventDetailHeader) + sizeof(DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET)); // // Check to see whether all cores are halted (in instant event) // if (!KdCheckAllCoresAreLocked()) { DebuggerEventAndActionResult->IsSuccessful = FALSE; DebuggerEventAndActionResult->Error = DEBUGGER_ERROR_NOT_ALL_CORES_ARE_LOCKED_FOR_APPLYING_INSTANT_EVENT; } else { // // Parse event from the VMX-root mode // DebuggerParseEvent(GeneralEventDetail, DebuggerEventAndActionResult, TRUE); } return FALSE; #else // // Check if the priority buffer is full or not // KdCheckUserModePriorityBuffers(); LogCallbackSendBuffer(OPERATION_DEBUGGEE_REGISTER_EVENT, ((CHAR *)EventDetailHeader + sizeof(DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET)), EventDetailHeader->Length, TRUE); return TRUE; #endif // EnableInstantEventMechanism } /** * @brief Send action buffer to user-mode to be added to the event * @param ActionDetailHeader * @param DebuggerEventAndActionResult * * @return BOOLEAN Shows whether the debuggee should be continued or not */ BOOLEAN KdPerformAddActionToEvent(PDEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET ActionDetailHeader, DEBUGGER_EVENT_AND_ACTION_RESULT * DebuggerEventAndActionResult) { #if EnableInstantEventMechanism DEBUGGER_GENERAL_ACTION * GeneralActionDetail = NULL; GeneralActionDetail = (PDEBUGGER_GENERAL_ACTION)(((CHAR *)ActionDetailHeader) + sizeof(DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET)); // // Parse action from the VMX-root mode // DebuggerParseAction(GeneralActionDetail, DebuggerEventAndActionResult, TRUE); return FALSE; #else // // Check if the priority buffer is full or not // KdCheckUserModePriorityBuffers(); LogCallbackSendBuffer(OPERATION_DEBUGGEE_ADD_ACTION_TO_EVENT, ((CHAR *)ActionDetailHeader + sizeof(DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET)), ActionDetailHeader->Length, TRUE); return TRUE; #endif // EnableInstantEventMechanism } /** * @brief Query state of the RFLAG's traps * * @return VOID */ VOID KdQueryRflagTrapState() { // // show the number of items // LogInfo("Number of valid entries: 0x%x\n" "(Please be aware that only top 0x%x items are considered valid. " "There could be other items present in the array, but they are not valid.)", g_TrapFlagState.NumberOfItems, g_TrapFlagState.NumberOfItems); for (SIZE_T i = 0; i < MAXIMUM_NUMBER_OF_THREAD_INFORMATION_FOR_TRAPS; i++) { LogInfo("g_TrapFlagState.ThreadInformation[%d].ProcessId = %x | ThreadId = %x", i, g_TrapFlagState.ThreadInformation[i].Fields.ProcessId, g_TrapFlagState.ThreadInformation[i].Fields.ThreadId); } } /** * @brief Check whether all cores are locked or not * * @return BOOLEAN */ BOOLEAN KdCheckAllCoresAreLocked() { ULONG ProcessorsCount; ProcessorsCount = KeQueryActiveProcessorCount(0); // // Query core debugging Lock info // for (SIZE_T i = 0; i < ProcessorsCount; i++) { if (!SpinlockCheckLock(&g_DbgState[i].Lock)) { // // We found one core that is not locked // return FALSE; } } // // Reaching here means all cores are locked // return TRUE; } /** * @brief Check whether a specific target core is locked or not * @param CoreNumber * * @return BOOLEAN */ BOOLEAN KdCheckTargetCoreIsLocked(UINT32 CoreNumber) { // // Query core debugging Lock info // if (!SpinlockCheckLock(&g_DbgState[CoreNumber].Lock)) { // // This core is not locked // return FALSE; } else { // // Target core is locked // return TRUE; } } /** * @brief Query state of the system * * @return VOID */ VOID KdQuerySystemState() { ULONG ProcessorsCount; ProcessorsCount = KeQueryActiveProcessorCount(0); // // Query core debugging Lock info // Log("================================================ Debugging Lock Info ================================================\n"); for (SIZE_T i = 0; i < ProcessorsCount; i++) { if (SpinlockCheckLock(&g_DbgState[i].Lock)) { LogInfo("Core : %d is locked", i); } else { LogInfo("Core : %d isn't locked", i); } } // // Query if the core is halted (or NMI is received) when the debuggee // was in the vmx-root mode // Log("\n================================================ NMI Receiver State =======+=========================================\n"); for (SIZE_T i = 0; i < ProcessorsCount; i++) { if (g_DbgState[i].NmiState.NmiCalledInVmxRootRelatedToHaltDebuggee) { LogInfo("Core : %d - called from an NMI that is called in VMX-root mode", i); } else { LogInfo("Core : %d - not called from an NMI handler (through the immediate VM-exit mechanism)", i); } } } /** * @brief unlock the target core * * @param DbgState The state of the debugger on the target core * * @return VOID */ VOID KdUnlockTheHaltedCore(PROCESSOR_DEBUGGING_STATE * DbgState) { SpinlockUnlock(&DbgState->Lock); } /** * @brief check the lock state of the target core * * @param DbgState The state of the debugger on the target core * * @return BOOLEAN */ BOOLEAN KdCheckTheHaltedCore(PROCESSOR_DEBUGGING_STATE * DbgState) { return SpinlockCheckLock(&DbgState->Lock); } /** * @brief routines to break page-in * * @param DbgState The state of the debugger on the current core * @param PageinRequest * * @return BOOLEAN */ BOOLEAN KdBringPagein(PROCESSOR_DEBUGGING_STATE * DbgState, PDEBUGGER_PAGE_IN_REQUEST PageinRequest) { // // Inject page-fault range // VmFuncEventInjectPageFaultRangeAddress(DbgState->CoreId, PageinRequest->VirtualAddressFrom, PageinRequest->VirtualAddressTo, PageinRequest->PageFaultErrorCode); // // Also, set the RFLAGS.TF to intercept the process (thread) again after inject #PF // VmFuncSetRflagTrapFlag(TRUE); // // Unset the trap flag next time that it's triggered (on current thread/process) // if (!BreakpointRestoreTheTrapFlagOnceTriggered(HANDLE_TO_UINT32(PsGetCurrentProcessId()), HANDLE_TO_UINT32(PsGetCurrentThreadId()))) { // // Adjust the flags for showing there was error // PageinRequest->KernelStatus = DEBUGGER_ERROR_THE_TRAP_FLAG_LIST_IS_FULL; return FALSE; } else { // // Adjust the flags for showing the successful #PF injection // PageinRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; return TRUE; } } /** * @brief Perform the test packet's operation * * @param DbgState The state of the debugger on the current core * @param TestQueryPacket test packet request * * @return VOID */ VOID KdPerformTheTestPacketOperation(PROCESSOR_DEBUGGING_STATE * DbgState, DEBUGGER_DEBUGGER_TEST_QUERY_BUFFER * TestQueryPacket) { // // Dispatch the request // switch (TestQueryPacket->RequestType) { case TEST_QUERY_HALTING_CORE_STATUS: // // Query state of the system // KdQuerySystemState(); TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; case TEST_QUERY_TRAP_STATE: // // Query state of the trap // KdQueryRflagTrapState(); TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; case TEST_QUERY_PREALLOCATED_POOL_STATE: // // Query state of pre-allocated pools // PoolManagerShowPreAllocatedPools(); TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; case TEST_SETTING_TARGET_TASKS_ON_HALTED_CORES_SYNCHRONOUS: case TEST_SETTING_TARGET_TASKS_ON_HALTED_CORES_ASYNCHRONOUS: // // Send request for the target task to the halted cores (synchronized and unsynchronized) // HaltedCoreBroadcastTaskAllCores(DbgState, DEBUGGER_HALTED_CORE_TASK_TEST, TRUE, TestQueryPacket->RequestType == TEST_SETTING_TARGET_TASKS_ON_HALTED_CORES_SYNCHRONOUS ? TRUE : FALSE, (PVOID)0x55); TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; case TEST_SETTING_TARGET_TASKS_ON_TARGET_HALTED_CORES: // // Validate core number // if (!CommonValidateCoreNumber((UINT32)TestQueryPacket->Context)) { // // Core number is invalid // TestQueryPacket->KernelStatus = DEBUGGER_ERROR_INVALID_CORE_ID; } else { // // Send request for the target task to the target halted core // HaltedCoreRunTaskOnSingleCore((UINT32)TestQueryPacket->Context, DEBUGGER_HALTED_CORE_TASK_TEST, TRUE, (PVOID)0x85); TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } break; case TEST_BREAKPOINT_TURN_OFF_BPS: // // Turn off the breakpoint interception // g_InterceptBreakpoints = TRUE; TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; case TEST_BREAKPOINT_TURN_ON_BPS: // // Turn on the breakpoint interception // g_InterceptBreakpoints = FALSE; TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; case TEST_BREAKPOINT_TURN_OFF_BPS_AND_EVENTS_FOR_COMMANDS_IN_REMOTE_COMPUTER: // // Turn off the breakpoints and events interception before executing the commands in the remote computer // g_InterceptBreakpointsAndEventsForCommandsInRemoteComputer = TRUE; TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; case TEST_BREAKPOINT_TURN_ON_BPS_AND_EVENTS_FOR_COMMANDS_IN_REMOTE_COMPUTER: // // Turn on the breakpoints and events interception after finishing the commands in the remote computer // g_InterceptBreakpointsAndEventsForCommandsInRemoteComputer = FALSE; TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; case TEST_BREAKPOINT_TURN_OFF_DBS: // // Turn off the debug break interception // g_InterceptDebugBreaks = TRUE; TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; case TEST_BREAKPOINT_TURN_ON_DBS: // // Turn on the debug break interception // g_InterceptDebugBreaks = FALSE; TestQueryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; break; default: // // Query index not found // TestQueryPacket->KernelStatus = DEBUGGER_ERROR_UNKNOWN_TEST_QUERY_RECEIVED; break; } } /** * @brief Perform modify the state of short-circuiting * * @param DbgState The state of the debugger on the current core * @param ShortCircuitingEvent * * @return VOID */ VOID KdPerformSettingTheStateOfShortCircuiting(PROCESSOR_DEBUGGING_STATE * DbgState, PDEBUGGER_SHORT_CIRCUITING_EVENT ShortCircuitingEvent) { // // Perform the short-circuiting changes // if (ShortCircuitingEvent->IsShortCircuiting) { DbgState->ShortCircuitingEvent = TRUE; } else { DbgState->ShortCircuitingEvent = FALSE; } // // The status was okay // ShortCircuitingEvent->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } /** * @brief Perform modify and query events * @param ModifyAndQueryEvent * * @return BOOLEAN Shows whether the debuggee should be continued or not */ BOOLEAN KdPerformEventQueryAndModification(PDEBUGGER_MODIFY_EVENTS ModifyAndQueryEvent) { BOOLEAN IsForAllEvents = FALSE; BOOLEAN ContinueDebugger = FALSE; // // Check if the tag is valid or not // if (ModifyAndQueryEvent->Tag == DEBUGGER_MODIFY_EVENTS_APPLY_TO_ALL_TAG) { IsForAllEvents = TRUE; } else if (!DebuggerIsTagValid(ModifyAndQueryEvent->Tag)) { // // Tag is invalid // ModifyAndQueryEvent->KernelStatus = DEBUGGER_ERROR_MODIFY_EVENTS_INVALID_TAG; return FALSE; } // // *************************************************************************** // // // Check if it's a query state command // if (ModifyAndQueryEvent->TypeOfAction == DEBUGGER_MODIFY_EVENTS_QUERY_STATE) { // // check if tag is valid or not // if (!DebuggerIsTagValid(ModifyAndQueryEvent->Tag)) { ModifyAndQueryEvent->KernelStatus = DEBUGGER_ERROR_TAG_NOT_EXISTS; } else { // // Set event state // if (DebuggerQueryStateEvent(ModifyAndQueryEvent->Tag)) { ModifyAndQueryEvent->IsEnabled = TRUE; } else { ModifyAndQueryEvent->IsEnabled = FALSE; } // // The function was successful // ModifyAndQueryEvent->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } } else if (ModifyAndQueryEvent->TypeOfAction == DEBUGGER_MODIFY_EVENTS_ENABLE) { if (IsForAllEvents) { // // Enable all events // DebuggerEnableOrDisableAllEvents(TRUE); } else { // // Enable just one event // DebuggerEnableEvent(ModifyAndQueryEvent->Tag); } // // The function was successful // ModifyAndQueryEvent->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } else if (ModifyAndQueryEvent->TypeOfAction == DEBUGGER_MODIFY_EVENTS_DISABLE) { if (IsForAllEvents) { // // Disable all events // DebuggerEnableOrDisableAllEvents(FALSE); } else { // // Disable just one event // DebuggerDisableEvent(ModifyAndQueryEvent->Tag); } // // The function was successful // ModifyAndQueryEvent->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } else if (ModifyAndQueryEvent->TypeOfAction == DEBUGGER_MODIFY_EVENTS_CLEAR) { #if EnableInstantEventMechanism // // For clearing events, we just disable them here and after that // we'll send a DPC to the user-mode to clear the event // // This is because we want to make sure that no other core is in middle // of handling anything (so the structures and the track of the debugger // might be lost) // // For example, the debugger might be (and will be) paused while other cores // are halted but in the middle of handling an EPT, and if we remove the EPT // hook directly, it might break the operation of the other cores, but when // we disable it then we're sure no other cores get a chance to trigger the // event // if (IsForAllEvents) { // // Disable all events // DebuggerEnableOrDisableAllEvents(FALSE); } else { // // Disable just one event // DebuggerDisableEvent(ModifyAndQueryEvent->Tag); } // // Send the DPC to remove the event at next run // // // Send one byte buffer and operation codes to the user-mode // This buffer won't notify the debugger and silently removes // the event(s) // if (!LogCallbackCheckIfBufferIsFull(TRUE)) { LogCallbackSendBuffer(OPERATION_DEBUGGEE_CLEAR_EVENTS_WITHOUT_NOTIFYING_DEBUGGER, ModifyAndQueryEvent, sizeof(DEBUGGER_MODIFY_EVENTS), TRUE); // // The function was successful // ModifyAndQueryEvent->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } else { // // The event is already disabled, but we cannot deliver the user-mode // to clear the buffer because the buffers are either not served or // too much buffers are added to queue // ModifyAndQueryEvent->KernelStatus = DEBUGGER_ERROR_THE_TARGET_EVENT_IS_DISABLED_BUT_CANNOT_BE_CLEARED_PRIRITY_BUFFER_IS_FULL; } #else // // Check if the priority buffer is full or not // KdCheckUserModePriorityBuffers(); // // Send one byte buffer and operation codes to the user-mode // LogCallbackSendBuffer(OPERATION_DEBUGGEE_CLEAR_EVENTS, ModifyAndQueryEvent, sizeof(DEBUGGER_MODIFY_EVENTS), TRUE); ContinueDebugger = TRUE; #endif // EnableInstantEventMechanism } else { // // Invalid parameter specified in Action // ModifyAndQueryEvent->KernelStatus = DEBUGGER_ERROR_MODIFY_EVENTS_INVALID_TYPE_OF_ACTION; } // // In all of the cases except clearing event while instant event // mechanism is disabled, we shouldn't continue the debugger and // keep the debugger in the halt state // return ContinueDebugger; } /** * @brief This function applies commands from the debugger to the debuggee * @details when we reach here, we are on the first core * @param DbgState The state of the debugger on the current core * * @return VOID */ VOID KdDispatchAndPerformCommandsFromDebugger(PROCESSOR_DEBUGGING_STATE * DbgState) { PDEBUGGEE_CHANGE_CORE_PACKET ChangeCorePacket; PDEBUGGEE_STEP_PACKET SteppingPacket; PDEBUGGER_FLUSH_LOGGING_BUFFERS FlushPacket; PDEBUGGER_CALLSTACK_REQUEST CallstackPacket; PDEBUGGER_SINGLE_CALLSTACK_FRAME CallstackFrameBuffer; PDEBUGGER_DEBUGGER_TEST_QUERY_BUFFER TestQueryPacket; PDEBUGGEE_REGISTER_READ_DESCRIPTION ReadRegisterPacket; PDEBUGGEE_REGISTER_WRITE_DESCRIPTION WriteRegisterPacket; PDEBUGGER_READ_MEMORY ReadMemoryPacket; PDEBUGGER_EDIT_MEMORY EditMemoryPacket; PDEBUGGEE_DETAILS_AND_SWITCH_PROCESS_PACKET ChangeProcessPacket; PDEBUGGEE_DETAILS_AND_SWITCH_THREAD_PACKET ChangeThreadPacket; PDEBUGGEE_SCRIPT_PACKET ScriptPacket; PDEBUGGEE_USER_INPUT_PACKET UserInputPacket; PDEBUGGER_SEARCH_MEMORY SearchQueryPacket; PDEBUGGEE_BP_PACKET BpPacket; PDEBUGGER_READ_PAGE_TABLE_ENTRIES_DETAILS PtePacket; PSMI_OPERATION_PACKETS SmiOperationPacket; PHYPERTRACE_LBR_DUMP_PACKETS HyperTraceLbrdumpPacket; PHYPERTRACE_PT_OPERATION_PACKETS HyperTracePtOperationPacket; PDEBUGGER_APIC_REQUEST ApicPacket; PINTERRUPT_DESCRIPTOR_TABLE_ENTRIES_PACKETS IdtEntryPacket; PDEBUGGER_PAGE_IN_REQUEST PageinPacket; PDEBUGGER_VA2PA_AND_PA2VA_COMMANDS Va2paPa2vaPacket; PDEBUGGEE_BP_LIST_OR_MODIFY_PACKET BpListOrModifyPacket; PDEBUGGEE_SYMBOL_REQUEST_PACKET SymReloadPacket; PDEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET EventRegPacket; PDEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET AddActionPacket; PDEBUGGER_MODIFY_EVENTS QueryAndModifyEventPacket; PDEBUGGER_SHORT_CIRCUITING_EVENT ShortCircuitingEventPacket; UINT32 SizeToSend = 0; BOOLEAN UnlockTheNewCore = FALSE; UINT32 ReturnSize = 0; DEBUGGEE_RESULT_OF_SEARCH_PACKET SearchPacketResult = {0}; DEBUGGER_EVENT_AND_ACTION_RESULT DebuggerEventAndActionResult = {0}; PDEBUGGEE_PCITREE_REQUEST_RESPONSE_PACKET PcitreePacket = {0}; PDEBUGGEE_PCIDEVINFO_REQUEST_RESPONSE_PACKET PcidevinfoPacket = {0}; while (TRUE) { BOOLEAN EscapeFromTheLoop = FALSE; CHAR * RecvBuffer = &DbgState->KdRecvBuffer[0]; UINT32 RecvBufferLength = 0; PDEBUGGER_REMOTE_PACKET TheActualPacket = (PDEBUGGER_REMOTE_PACKET)RecvBuffer; // // Zero the receiving buffer // RtlZeroMemory(RecvBuffer, MaxSerialPacketSize); // // Receive the buffer in polling mode // if (!SerialConnectionRecvBuffer(RecvBuffer, &RecvBufferLength)) { // // Invalid buffer // continue; } if (TheActualPacket->Indicator == INDICATOR_OF_HYPERDBG_PACKET) { // // Check checksum // if (KdComputeDataChecksum((PVOID)&TheActualPacket->Indicator, RecvBufferLength - sizeof(BYTE)) != TheActualPacket->Checksum) { LogError("Err, checksum is invalid"); continue; } // // Check if the packet type is correct // if (TheActualPacket->TypeOfThePacket != DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT) { // // sth wrong happened, the packet is not belonging to use // nothing to do, just wait again // LogError("Err, unknown packet received from the debugger\n"); continue; } // // It's a HyperDbg packet // switch (TheActualPacket->RequestedActionOfThePacket) { case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CONTINUE: // // Unlock other cores // KdContinueDebuggee(DbgState, FALSE, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_NO_ACTION); // // No need to wait for new commands // EscapeFromTheLoop = TRUE; break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_STEP: SteppingPacket = (DEBUGGEE_STEP_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); switch (SteppingPacket->StepType) { case DEBUGGER_REMOTE_STEPPING_REQUEST_INSTRUMENTATION_STEP_IN: case DEBUGGER_REMOTE_STEPPING_REQUEST_INSTRUMENTATION_STEP_IN_FOR_TRACKING: // // Guaranteed step in (i command) or used for tracking (creating call tree) // // // Indicate a step // KdGuaranteedStepInstruction(DbgState); // // Unlock just on core // KdContinueDebuggeeJustCurrentCore(DbgState); if (SteppingPacket->StepType == DEBUGGER_REMOTE_STEPPING_REQUEST_INSTRUMENTATION_STEP_IN_FOR_TRACKING) { DbgState->IgnoreDisasmInNextPacket = TRUE; } // // No need to wait for new commands // EscapeFromTheLoop = TRUE; break; case DEBUGGER_REMOTE_STEPPING_REQUEST_STEP_OVER: case DEBUGGER_REMOTE_STEPPING_REQUEST_STEP_OVER_FOR_GU: case DEBUGGER_REMOTE_STEPPING_REQUEST_STEP_OVER_FOR_GU_LAST_INSTRUCTION: // // Step-over (p command) // KdRegularStepOver( VmFuncGetLastVmexitRip(DbgState->CoreId), SteppingPacket->IsCurrentInstructionACall, SteppingPacket->CallLength); // // Unlock other cores // KdContinueDebuggee(DbgState, FALSE, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_NO_ACTION); if (SteppingPacket->StepType == DEBUGGER_REMOTE_STEPPING_REQUEST_STEP_OVER_FOR_GU) { DbgState->IgnoreDisasmInNextPacket = TRUE; } // // Continue to the debuggee // EscapeFromTheLoop = TRUE; break; case DEBUGGER_REMOTE_STEPPING_REQUEST_STEP_IN: // // Step in (t command) // // // Indicate a step-in // TracingRegularStepInInstruction(); // // Unlock other cores // KdContinueDebuggee(DbgState, FALSE, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_NO_ACTION); // // Continue to the debuggee // EscapeFromTheLoop = TRUE; break; default: break; } break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CLOSE_AND_UNLOAD_DEBUGGEE: // // Send the close buffer // KdCloseConnectionAndUnloadDebuggee(); // // Unlock other cores // KdContinueDebuggee(DbgState, FALSE, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_NO_ACTION); // // No need to wait for new commands // EscapeFromTheLoop = TRUE; break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CHANGE_CORE: ChangeCorePacket = (DEBUGGEE_CHANGE_CORE_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Switch to new core // if (KdSwitchCore(DbgState, ChangeCorePacket)) { // // No need to wait for new commands // EscapeFromTheLoop = TRUE; // // Unlock the new core // UnlockTheNewCore = TRUE; } // // Send the result of switching core back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_CHANGING_CORE, (CHAR *)ChangeCorePacket, sizeof(DEBUGGEE_CHANGE_CORE_PACKET)); // // Because we don't want two cores to send the same packets simultaneously // if (UnlockTheNewCore) { UnlockTheNewCore = FALSE; SpinlockUnlock(&g_DbgState[ChangeCorePacket->NewCore].Lock); } break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_FLUSH_BUFFERS: FlushPacket = (DEBUGGER_FLUSH_LOGGING_BUFFERS *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Flush the buffers // DebuggerCommandFlush(FlushPacket); // // Send the result of flushing back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_FLUSH, (CHAR *)FlushPacket, sizeof(DEBUGGER_FLUSH_LOGGING_BUFFERS)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CALLSTACK: CallstackPacket = (DEBUGGER_CALLSTACK_REQUEST *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); CallstackFrameBuffer = (DEBUGGER_SINGLE_CALLSTACK_FRAME *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET) + sizeof(DEBUGGER_CALLSTACK_REQUEST)); // // If the address is null, we use the current RSP register // if (CallstackPacket->BaseAddress == (UINT64)NULL) { CallstackPacket->BaseAddress = DbgState->Regs->rsp; } // // Feel the callstack frames the buffers // if (CallstackWalkthroughStack(CallstackFrameBuffer, &CallstackPacket->FrameCount, CallstackPacket->BaseAddress, CallstackPacket->Size, CallstackPacket->Is32Bit)) { CallstackPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } else { CallstackPacket->KernelStatus = DEBUGGER_ERROR_UNABLE_TO_GET_CALLSTACK; } // // Send the result of flushing back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_CALLSTACK, (CHAR *)CallstackPacket, (UINT32)CallstackPacket->BufferSize); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_TEST_QUERY: TestQueryPacket = (DEBUGGER_DEBUGGER_TEST_QUERY_BUFFER *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the test packet operation // KdPerformTheTestPacketOperation(DbgState, TestQueryPacket); // // Send the result of query system state to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_TEST_QUERY, (CHAR *)TestQueryPacket, sizeof(DEBUGGER_DEBUGGER_TEST_QUERY_BUFFER)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_READ_REGISTERS: ReadRegisterPacket = (DEBUGGEE_REGISTER_READ_DESCRIPTION *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Read registers // if (DebuggerCommandReadRegisters(DbgState->Regs, ReadRegisterPacket)) { ReadRegisterPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } else { ReadRegisterPacket->KernelStatus = DEBUGGER_ERROR_INVALID_REGISTER_NUMBER; } if (ReadRegisterPacket->RegisterId == DEBUGGEE_SHOW_ALL_REGISTERS) { SizeToSend = sizeof(DEBUGGEE_REGISTER_READ_DESCRIPTION) + sizeof(GUEST_REGS) + sizeof(GUEST_EXTRA_REGISTERS); } else { SizeToSend = sizeof(DEBUGGEE_REGISTER_READ_DESCRIPTION); } // // Send the result of reading registers back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_READING_REGISTERS, (CHAR *)ReadRegisterPacket, SizeToSend); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_WRITE_REGISTER: WriteRegisterPacket = (DEBUGGEE_REGISTER_WRITE_DESCRIPTION *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Write register // if (SetRegValue(DbgState->Regs, WriteRegisterPacket->RegisterId, WriteRegisterPacket->Value)) { WriteRegisterPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } else { WriteRegisterPacket->KernelStatus = DEBUGGER_ERROR_INVALID_REGISTER_NUMBER; } // // Send the result of writing register back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_WRITE_REGISTER, (CHAR *)WriteRegisterPacket, sizeof(DEBUGGEE_REGISTER_WRITE_DESCRIPTION)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_READ_MEMORY: ReadMemoryPacket = (DEBUGGER_READ_MEMORY *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Read memory // if (DebuggerCommandReadMemoryVmxRoot(ReadMemoryPacket, (PVOID)((UINT64)ReadMemoryPacket + sizeof(DEBUGGER_READ_MEMORY)), &ReturnSize)) { ReadMemoryPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } else { ReadMemoryPacket->KernelStatus = DEBUGGER_ERROR_INVALID_ADDRESS; } ReadMemoryPacket->ReturnLength = ReturnSize; // // Send the result of reading memory back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_READING_MEMORY, (CHAR *)ReadMemoryPacket, sizeof(DEBUGGER_READ_MEMORY) + ReturnSize); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_EDIT_MEMORY: EditMemoryPacket = (PDEBUGGER_EDIT_MEMORY)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Edit memory // DebuggerCommandEditMemoryVmxRoot(EditMemoryPacket); // // Send the result of reading memory back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_EDITING_MEMORY, (CHAR *)EditMemoryPacket, sizeof(DEBUGGER_EDIT_MEMORY)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CHANGE_PROCESS: ChangeProcessPacket = (DEBUGGEE_DETAILS_AND_SWITCH_PROCESS_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Interpret the process packet // ProcessInterpretProcess(DbgState, ChangeProcessPacket); // // Send the result of switching process back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_CHANGING_PROCESS, (CHAR *)ChangeProcessPacket, sizeof(DEBUGGEE_DETAILS_AND_SWITCH_PROCESS_PACKET)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CHANGE_THREAD: ChangeThreadPacket = (DEBUGGEE_DETAILS_AND_SWITCH_THREAD_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Interpret the thread packet // ThreadInterpretThread(DbgState, ChangeThreadPacket); // // Send the result of switching thread back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_CHANGING_THREAD, (CHAR *)ChangeThreadPacket, sizeof(DEBUGGEE_DETAILS_AND_SWITCH_THREAD_PACKET)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_RUN_SCRIPT: ScriptPacket = (DEBUGGEE_SCRIPT_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Run the script in debuggee // if (DebuggerPerformRunScript(DbgState, NULL, ScriptPacket, &g_EventTriggerDetail)) { // // Set status // ScriptPacket->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } else { // // Set status // ScriptPacket->Result = DEBUGGER_ERROR_PREPARING_DEBUGGEE_TO_RUN_SCRIPT; } // // Send the result of running script back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_RUNNING_SCRIPT, (CHAR *)ScriptPacket, sizeof(DEBUGGEE_SCRIPT_PACKET)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_USER_INPUT_BUFFER: UserInputPacket = (DEBUGGEE_USER_INPUT_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Send the user-input to user-mode debuggee // KdNotifyDebuggeeForUserInput(((DEBUGGEE_USER_INPUT_PACKET *)UserInputPacket), sizeof(DEBUGGEE_USER_INPUT_PACKET) + UserInputPacket->CommandLen); // // Continue Debuggee // KdContinueDebuggee(DbgState, FALSE, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_NO_ACTION); EscapeFromTheLoop = TRUE; break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_SEARCH_QUERY: SearchQueryPacket = (DEBUGGER_SEARCH_MEMORY *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the search in debuggee debuggee // Call the search wrapper // if (SearchAddressWrapper(NULL, SearchQueryPacket, SearchQueryPacket->Address, SearchQueryPacket->Address + SearchQueryPacket->Length, TRUE, &SearchPacketResult.CountOfResults)) { // // The search was successful // SearchPacketResult.Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL; } else { // // There was an error, probably the address was not valid // SearchPacketResult.Result = DEBUGGER_ERROR_INVALID_ADDRESS; } // // Send the result of the 's*' back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RELOAD_SEARCH_QUERY, (CHAR *)&SearchPacketResult, sizeof(DEBUGGEE_RESULT_OF_SEARCH_PACKET)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_REGISTER_EVENT: EventRegPacket = (DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Parsing the event either in the VMX-root mode or pass it to the user-mode // if (KdPerformRegisterEvent(EventRegPacket, &DebuggerEventAndActionResult)) { // // Continue Debuggee (Send the event buffer to user-mode debuggee) // KdContinueDebuggee(DbgState, TRUE, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_REGISTERING_EVENT); EscapeFromTheLoop = TRUE; } else { // // Send the response of event registration to the debugger // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_REGISTERING_EVENT, (CHAR *)&DebuggerEventAndActionResult, sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT)); } break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_ADD_ACTION_TO_EVENT: AddActionPacket = (DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Parsing the action either in the VMX-root mode or pass it to the user-mode // if (KdPerformAddActionToEvent(AddActionPacket, &DebuggerEventAndActionResult)) { // // Continue Debuggee (Send the action buffer to user-mode debuggee) // KdContinueDebuggee(DbgState, TRUE, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_ADDING_ACTION_TO_EVENT); EscapeFromTheLoop = TRUE; } else { // // Send the response of event registration to the debugger // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_ADDING_ACTION_TO_EVENT, (CHAR *)&DebuggerEventAndActionResult, sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT)); } break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_QUERY_AND_MODIFY_EVENT: QueryAndModifyEventPacket = (DEBUGGER_MODIFY_EVENTS *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the action and check if we should continue the debuggee or not // if (KdPerformEventQueryAndModification(QueryAndModifyEventPacket)) { // // Continue Debuggee // KdContinueDebuggee(DbgState, TRUE, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_QUERY_AND_MODIFY_EVENT); EscapeFromTheLoop = TRUE; } else { // // Send the response of event query and modification (anything other than clear) // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_QUERY_AND_MODIFY_EVENT, (CHAR *)QueryAndModifyEventPacket, sizeof(DEBUGGER_MODIFY_EVENTS)); } break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_SET_SHORT_CIRCUITING_STATE: ShortCircuitingEventPacket = (DEBUGGER_SHORT_CIRCUITING_EVENT *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the action // KdPerformSettingTheStateOfShortCircuiting(DbgState, ShortCircuitingEventPacket); // // Send the response of short-circuiting event // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_SHORT_CIRCUITING_STATE, (CHAR *)ShortCircuitingEventPacket, sizeof(DEBUGGER_SHORT_CIRCUITING_EVENT)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_BP: BpPacket = (DEBUGGEE_BP_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the action // This is on the vmx-root mode for the kernel debugger, thus, no need to switch // to the target process memory layout as we are already in it // BreakpointAddNew(BpPacket, FALSE); // // Send the result of the 'bp' back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_BP, (CHAR *)BpPacket, sizeof(DEBUGGEE_BP_PACKET)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_SYMBOL_QUERY_PTE: PtePacket = (DEBUGGER_READ_PAGE_TABLE_ENTRIES_DETAILS *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Get the page table details (it's in vmx-root) // ExtensionCommandPte(PtePacket, TRUE); // // Send the result of the '!pte' back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_PTE, (CHAR *)PtePacket, sizeof(DEBUGGER_READ_PAGE_TABLE_ENTRIES_DETAILS)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_PERFORM_SMI_OPERATION: SmiOperationPacket = (SMI_OPERATION_PACKETS *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the SMI operations (it's in vmx-root) // VmFuncSmmPerformSmiOperation(SmiOperationPacket, TRUE); // // Send the result of the '!smi' back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_SMI_OPERATION_REQUESTS, (CHAR *)SmiOperationPacket, SIZEOF_SMI_OPERATION_PACKETS); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_PERFORM_HYPERTRACE_LBR_DUMP: HyperTraceLbrdumpPacket = (HYPERTRACE_LBR_DUMP_PACKETS *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the HyperTrace LBR dump (it's in vmx-root) // HyperTraceLbrPerformDump(HyperTraceLbrdumpPacket); // // Send the result of the HyperTrace LBR dump back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_HYPERTRACE_LBR_DUMP_REQUESTS, (CHAR *)HyperTraceLbrdumpPacket, SIZEOF_HYPERTRACE_LBR_DUMP_PACKETS); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_PERFORM_HYPERTRACE_PT_OPERATION: HyperTracePtOperationPacket = (HYPERTRACE_PT_OPERATION_PACKETS *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the HyperTrace PT operations (it's in vmx-root) // HyperTracePtPerformOperation(HyperTracePtOperationPacket); // // Send the result of the HyperTrace PT back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_HYPERTRACE_PT_OPERATION_REQUESTS, (CHAR *)HyperTracePtOperationPacket, SIZEOF_HYPERTRACE_PT_OPERATION_PACKETS); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_PERFORM_ACTIONS_ON_APIC: ApicPacket = (DEBUGGER_APIC_REQUEST *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Call APIC handler (size to send is computed by this function) // SizeToSend = ExtensionCommandPerformActionsForApicRequests(ApicPacket); // // Send the result of the APIC requests back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_APIC_REQUESTS, (CHAR *)ApicPacket, SizeToSend); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_READ_IDT_ENTRIES: IdtEntryPacket = (INTERRUPT_DESCRIPTOR_TABLE_ENTRIES_PACKETS *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Call IDT query handler (read from VMX root-mode) // ExtensionCommandPerformQueryIdtEntriesRequest(IdtEntryPacket, TRUE); // // Send the result of the IDT entries requests to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_QUERY_IDT_ENTRIES_REQUESTS, (CHAR *)IdtEntryPacket, SIZEOF_INTERRUPT_DESCRIPTOR_TABLE_ENTRIES_PACKETS); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_INJECT_PAGE_FAULT: PageinPacket = (DEBUGGER_PAGE_IN_REQUEST *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform bringing the pages in (it's in vmx-root) // KdBringPagein(DbgState, PageinPacket); // // Send the result of the '.pagein' back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_BRINGING_PAGES_IN, (CHAR *)PageinPacket, sizeof(DEBUGGER_PAGE_IN_REQUEST)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_QUERY_PA2VA_AND_VA2PA: Va2paPa2vaPacket = (DEBUGGER_VA2PA_AND_PA2VA_COMMANDS *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the virtual to physical or physical to virtual address // conversion (it's on vmx-root mode) // ExtensionCommandVa2paAndPa2va(Va2paPa2vaPacket, TRUE); // // Send the result of the '!va2pa' or '!pa2va' back to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_VA2PA_AND_PA2VA, (CHAR *)Va2paPa2vaPacket, sizeof(DEBUGGER_VA2PA_AND_PA2VA_COMMANDS)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_LIST_OR_MODIFY_BREAKPOINTS: BpListOrModifyPacket = (DEBUGGEE_BP_LIST_OR_MODIFY_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Perform the action // No need to switch to the target process memory layout as we are already in it // BreakpointListOrModify(BpListOrModifyPacket, FALSE); // // Send the result of modify or list breakpoints to the debuggee // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_LIST_OR_MODIFY_BREAKPOINTS, (CHAR *)BpListOrModifyPacket, sizeof(DEBUGGEE_BP_LIST_OR_MODIFY_PACKET)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_SYMBOL_RELOAD: SymReloadPacket = (DEBUGGEE_SYMBOL_REQUEST_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Send the reload symbol request buffer // KdReloadSymbolDetailsInDebuggee(SymReloadPacket); // // Unlock other cores // KdContinueDebuggee(DbgState, FALSE, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_NO_ACTION); // // No need to wait for new commands // EscapeFromTheLoop = TRUE; break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_QUERY_PCITREE: PcitreePacket = (DEBUGGEE_PCITREE_REQUEST_RESPONSE_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Enumerate PCI tree // ExtensionCommandPcitree(PcitreePacket, TRUE); // // Send the result of '!pcitree' back to the debugger // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_PCITREE, (CHAR *)PcitreePacket, sizeof(DEBUGGEE_PCITREE_REQUEST_RESPONSE_PACKET)); break; case DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_QUERY_PCIDEVINFO: PcidevinfoPacket = (DEBUGGEE_PCIDEVINFO_REQUEST_RESPONSE_PACKET *)(((CHAR *)TheActualPacket) + sizeof(DEBUGGER_REMOTE_PACKET)); // // Retrieve PCI device info (CAM) // ExtensionCommandPcidevinfo(PcidevinfoPacket, TRUE); // // Send the result back to the debugger // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_PCIDEVINFO, (CHAR *)PcidevinfoPacket, sizeof(DEBUGGEE_PCIDEVINFO_REQUEST_RESPONSE_PACKET)); break; default: LogError("Err, unknown packet action received from the debugger\n"); break; } } else { // // It's not a HyperDbg packet, the packet is probably deformed // LogError("Err, it's not a HyperDbg packet, the packet is probably deformed\n"); continue; } // // If we have to leave the loop, we apply it here // if (EscapeFromTheLoop) { break; } } } /** * @brief determines if the guest was in 32-bit user-mode or 64-bit (long mode) * @details this function should be called from vmx-root * * @return BOOLEAN */ BOOLEAN KdIsGuestOnUsermode32Bit() { // // Only 16 bit is needed however, vmwrite might write on other bits // and corrupt other variables, that's why we get 64bit // UINT64 CsSel = (UINT64)NULL; // // Read guest's cs selector // CsSel = VmFuncGetCsSelector(); if (CsSel == KGDT64_R0_CODE) { // // 64-bit kernel-mode // return FALSE; } else if ((CsSel & ~3) == KGDT64_R3_CODE) { // // 64-bit user-mode // return FALSE; } else if ((CsSel & ~3) == KGDT64_R3_CMCODE) { // // 32-bit user-mode // return TRUE; } else { LogError("Err, unknown value for cs, cannot determine wow64 mode"); } // // By default, 64-bit // return FALSE; } /** * @brief manage system halt on vmx-root mode * @details This function should only be called from KdHandleBreakpointAndDebugBreakpoints * @param DbgState The state of the debugger on the current core * @param EventDetails * @param MainCore the core that triggered the event * * @return VOID */ VOID KdManageSystemHaltOnVmxRoot(PROCESSOR_DEBUGGING_STATE * DbgState, PDEBUGGER_TRIGGERED_EVENT_DETAILS EventDetails) { DEBUGGEE_KD_PAUSED_PACKET PausePacket; ULONG ExitInstructionLength = 0; RFLAGS Rflags = {0}; UINT64 LastVmexitRip = 0; // // Perform Pre-halt tasks // KdApplyTasksPreHaltCore(DbgState); StartAgain: // // We check for receiving buffer (unhalting) only on the // first core and not on every cores // if (DbgState->MainDebuggingCore) { // // *** Current Operating Core *** // RtlZeroMemory(&PausePacket, sizeof(DEBUGGEE_KD_PAUSED_PACKET)); // // Get the last RIP for vm-exit handler // LastVmexitRip = VmFuncGetRip(); // // Set the halt reason // PausePacket.PausingReason = g_DebuggeeHaltReason; // // Set the current core // PausePacket.CurrentCore = DbgState->CoreId; // // Set the RIP and mode of execution // PausePacket.Rip = LastVmexitRip; PausePacket.IsProcessorOn32BitMode = KdIsGuestOnUsermode32Bit(); // // Set disassembly state // PausePacket.IgnoreDisassembling = DbgState->IgnoreDisasmInNextPacket; DbgState->IgnoreDisasmInNextPacket = FALSE; // // Set rflags for finding the results of conditional jumps // Rflags.AsUInt = VmFuncGetRflags(); PausePacket.Rflags = Rflags.AsUInt; // // Set the event tag (if it's an event) // if (EventDetails != NULL) { PausePacket.EventTag = EventDetails->Tag; PausePacket.EventCallingStage = EventDetails->Stage; } // // Read the instruction len hint // if (DbgState->InstructionLengthHint != 0) { ExitInstructionLength = DbgState->InstructionLengthHint; } else { // // Reading instruction length (VMCS_VMEXIT_INSTRUCTION_LENGTH) won't work for anything that is not instruction exiting, // so we won't use it anymore // // // Set the length to notify debuggee // ExitInstructionLength = CheckAddressMaximumInstructionLength((PVOID)LastVmexitRip); } // // Set the reading length of bytes (for instruction disassembling) // PausePacket.ReadInstructionLen = (UINT16)ExitInstructionLength; // // Find the current instruction // MemoryMapperReadMemorySafeOnTargetProcess(LastVmexitRip, &PausePacket.InstructionBytesOnRip, ExitInstructionLength); // // Send the pause packet, along with RIP and an indication // to pause to the debugger // KdResponsePacketToDebugger(DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGEE_TO_DEBUGGER, DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_PAUSED_AND_CURRENT_INSTRUCTION, (CHAR *)&PausePacket, sizeof(DEBUGGEE_KD_PAUSED_PACKET)); // // Perform Commands from the debugger // KdDispatchAndPerformCommandsFromDebugger(DbgState); // // Check if it's a change core event or not, otherwise finish the execution // and continue debuggee // if (!DbgState->MainDebuggingCore) { // // It's a core switch, start again // goto StartAgain; } } else { // // All cores except operating core // // // Lock and unlock the lock so all core can get the lock // and continue their normal execution // DbgState->NmiState.WaitingToBeLocked = FALSE; ScopedSpinlock( DbgState->Lock, // // Check if it's a change core event or not // if (DbgState->MainDebuggingCore) { // // It's a core change event // g_DebuggeeHaltReason = DEBUGGEE_PAUSING_REASON_DEBUGGEE_CORE_SWITCHED; goto StartAgain; } ); // // Check if any task needs to be executed on this core or not // if (DbgState->HaltedCoreTask.PerformHaltedTask) { // // Indicate that the halted core is no longer needed to execute a task // as the current task is executed once // DbgState->HaltedCoreTask.PerformHaltedTask = FALSE; // // Perform the target task // HaltedCorePerformTargetTask(DbgState, DbgState->HaltedCoreTask.TargetTask, DbgState->HaltedCoreTask.Context); // // Check if the core needs to be locked again // if (DbgState->HaltedCoreTask.LockAgainAfterTask) { // // Lock again // SpinlockLock(&DbgState->Lock); goto StartAgain; } } } // // Apply the basic task for the core before continue // KdApplyTasksPostContinueCore(DbgState); } /** * @brief routines for broadcast system halt * @return VOID */ VOID KdBroadcastHaltOnAllCores() { // // Broadcast to all cores // KeGenericCallDpc(DpcRoutineVmExitAndHaltSystemAllCores, NULL); } /** * @brief Halt the system * @param PausePacket * * @return VOID */ VOID KdHaltSystem(PDEBUGGER_PAUSE_PACKET_RECEIVED PausePacket) { // // Broadcast to halt everything // Instead of broadcasting we will just send one vmcall and // from that point, we halt all the other cores by NMIs, this // way we are sure that we get all the other cores at the middle // of their execution codes and not on HyperDbg routines // // KdBroadcastHaltOnAllCores(); // // // vm-exit and halt current core // VmFuncVmxVmcall(DEBUGGER_VMCALL_VM_EXIT_HALT_SYSTEM, 0, 0, 0); // // Set the status // PausePacket->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL; }