mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-10 01:29:59 +00:00
1488 lines
47 KiB
C
1488 lines
47 KiB
C
/**
|
|
* @file DebuggerCommands.c
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @author Alee Amini (alee@hyperdbg.org)
|
|
* @brief Implementation of Debugger Commands
|
|
*
|
|
* @version 0.1
|
|
* @date 2020-04-23
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
/**
|
|
* @brief Read memory for different commands
|
|
*
|
|
* @param ReadMemRequest request structure for reading memory
|
|
* @param UserBuffer user buffer to copy the memory
|
|
* @param ReturnSize size that should be returned to user mode buffers
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
DebuggerCommandReadMemory(PDEBUGGER_READ_MEMORY ReadMemRequest, PVOID UserBuffer, PSIZE_T ReturnSize)
|
|
{
|
|
UINT32 Pid;
|
|
UINT32 Size;
|
|
UINT64 Address;
|
|
DEBUGGER_READ_MEMORY_TYPE MemType;
|
|
BOOLEAN Is32BitProcess = FALSE;
|
|
|
|
//
|
|
// Adjust the parameters
|
|
//
|
|
Pid = ReadMemRequest->Pid;
|
|
Size = ReadMemRequest->Size;
|
|
Address = ReadMemRequest->Address;
|
|
MemType = ReadMemRequest->MemoryType;
|
|
|
|
if (Size && Address != (UINT64)NULL)
|
|
{
|
|
if (MemoryManagerReadProcessMemoryNormal((HANDLE)Pid,
|
|
(PVOID)Address,
|
|
MemType,
|
|
(PVOID)UserBuffer,
|
|
Size,
|
|
ReturnSize))
|
|
{
|
|
//
|
|
// Reading memory was successful
|
|
//
|
|
|
|
//
|
|
// *** Now, we check whether this a disassembly request for a virtual address
|
|
// or not, if so, we'll detect whether the target process is 32-bit or 64-bit ***
|
|
//
|
|
|
|
//
|
|
// Check if the address is on a 32-bit mode process or not (just in case of disassembling)
|
|
//
|
|
if (ReadMemRequest->MemoryType == DEBUGGER_READ_VIRTUAL_ADDRESS && ReadMemRequest->GetAddressMode)
|
|
{
|
|
//
|
|
// Check if the address is in the canonical range for kernel space
|
|
//
|
|
if (ReadMemRequest->Address >= 0xFFFF800000000000 && ReadMemRequest->Address <= 0xFFFFFFFFFFFFFFFF)
|
|
{
|
|
//
|
|
// The address is in the range of canonical kernel space, so it's 64-bit process
|
|
//
|
|
ReadMemRequest->AddressMode = DEBUGGER_READ_ADDRESS_MODE_64_BIT;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The address is in the user-mode and the memory type is a virtual address
|
|
// for disassembly, so we have to query whether the target process is a
|
|
// 32-bit process or a 64-bit process
|
|
//
|
|
if (UserAccessIsWow64Process((HANDLE)ReadMemRequest->Pid, &Is32BitProcess))
|
|
{
|
|
if (Is32BitProcess)
|
|
{
|
|
ReadMemRequest->AddressMode = DEBUGGER_READ_ADDRESS_MODE_32_BIT;
|
|
}
|
|
else
|
|
{
|
|
ReadMemRequest->AddressMode = DEBUGGER_READ_ADDRESS_MODE_64_BIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We couldn't determine the type of process, let's assume that it's a
|
|
// 64-bit process by default
|
|
//
|
|
ReadMemRequest->AddressMode = DEBUGGER_READ_ADDRESS_MODE_64_BIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Anyway, the read was successful
|
|
//
|
|
ReadMemRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Reading memory was not successful
|
|
//
|
|
ReadMemRequest->KernelStatus = DEBUGGER_ERROR_READING_MEMORY_INVALID_PARAMETER;
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Parameters are invalid
|
|
//
|
|
ReadMemRequest->KernelStatus = DEBUGGER_ERROR_READING_MEMORY_INVALID_PARAMETER;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Read memory for different commands from vmxroot mode
|
|
*
|
|
* @param ReadMemRequest request structure for reading memory
|
|
* @param UserBuffer user buffer to copy the memory
|
|
* @param ReturnSize size that should be returned to user mode buffers
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
DebuggerCommandReadMemoryVmxRoot(PDEBUGGER_READ_MEMORY ReadMemRequest, UCHAR * UserBuffer, UINT32 * ReturnSize)
|
|
{
|
|
UINT32 Pid;
|
|
UINT32 Size;
|
|
UINT64 Address;
|
|
UINT64 OffsetInUserBuffer;
|
|
DEBUGGER_READ_MEMORY_TYPE MemType;
|
|
BOOLEAN Is32BitProcess = FALSE;
|
|
PLIST_ENTRY TempList = 0;
|
|
|
|
Pid = ReadMemRequest->Pid;
|
|
Size = ReadMemRequest->Size;
|
|
Address = ReadMemRequest->Address;
|
|
MemType = ReadMemRequest->MemoryType;
|
|
|
|
//
|
|
// read memory safe
|
|
//
|
|
if (MemType == DEBUGGER_READ_PHYSICAL_ADDRESS)
|
|
{
|
|
//
|
|
// Check whether the physical memory is valid or not
|
|
//
|
|
if (!CheckAddressPhysical(Address))
|
|
{
|
|
ReadMemRequest->KernelStatus = DEBUGGER_ERROR_INVALID_PHYSICAL_ADDRESS;
|
|
return FALSE;
|
|
}
|
|
|
|
MemoryMapperReadMemorySafeByPhysicalAddress(Address, (UINT64)UserBuffer, Size);
|
|
}
|
|
else if (MemType == DEBUGGER_READ_VIRTUAL_ADDRESS)
|
|
{
|
|
//
|
|
// Check whether the virtual memory is available in the current
|
|
// memory layout and also is present in the RAM
|
|
//
|
|
if (!CheckAccessValidityAndSafety(Address, Size))
|
|
{
|
|
ReadMemRequest->KernelStatus = DEBUGGER_ERROR_INVALID_ADDRESS;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Read memory safely
|
|
//
|
|
MemoryMapperReadMemorySafeOnTargetProcess(Address, UserBuffer, Size);
|
|
|
|
//
|
|
// Check if the target memory is filled with breakpoint of the 'bp' commands
|
|
// if the memory is changed due to this command, then we'll changes it to
|
|
// the previous byte
|
|
//
|
|
|
|
//
|
|
// Iterate through the breakpoint list
|
|
//
|
|
TempList = &g_BreakpointsListHead;
|
|
|
|
while (&g_BreakpointsListHead != TempList->Flink)
|
|
{
|
|
TempList = TempList->Flink;
|
|
PDEBUGGEE_BP_DESCRIPTOR CurrentBreakpointDesc = CONTAINING_RECORD(TempList, DEBUGGEE_BP_DESCRIPTOR, BreakpointsList);
|
|
|
|
if (CurrentBreakpointDesc->Address >= Address && CurrentBreakpointDesc->Address <= Address + Size)
|
|
{
|
|
//
|
|
// The address is found, we have to swap the byte if the target
|
|
// byte is 0xcc
|
|
//
|
|
|
|
//
|
|
// Find the address location at user buffer
|
|
//
|
|
OffsetInUserBuffer = CurrentBreakpointDesc->Address - Address;
|
|
|
|
if (UserBuffer[OffsetInUserBuffer] == 0xcc)
|
|
{
|
|
UserBuffer[OffsetInUserBuffer] = CurrentBreakpointDesc->PreviousByte;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReadMemRequest->KernelStatus = DEBUGGER_ERROR_MEMORY_TYPE_INVALID;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if the address is on a 32-bit mode process or not (just in case of disassembling)
|
|
//
|
|
if (ReadMemRequest->MemoryType == DEBUGGER_READ_VIRTUAL_ADDRESS && ReadMemRequest->GetAddressMode)
|
|
{
|
|
//
|
|
// Check if the address is in the canonical range for kernel space
|
|
//
|
|
if (ReadMemRequest->Address >= 0xFFFF800000000000 && ReadMemRequest->Address <= 0xFFFFFFFFFFFFFFFF)
|
|
{
|
|
//
|
|
// The address is in the range of canonical kernel space, so it's 64-bit process
|
|
//
|
|
ReadMemRequest->AddressMode = DEBUGGER_READ_ADDRESS_MODE_64_BIT;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The address is in the user-mode and the memory type is a virtual address
|
|
// for disassembly, so we have to query whether the target process is a
|
|
// 32-bit process or a 64-bit process
|
|
//
|
|
if (UserAccessIsWow64ProcessByEprocess(PsGetCurrentProcess(), &Is32BitProcess))
|
|
{
|
|
if (Is32BitProcess)
|
|
{
|
|
ReadMemRequest->AddressMode = DEBUGGER_READ_ADDRESS_MODE_32_BIT;
|
|
}
|
|
else
|
|
{
|
|
ReadMemRequest->AddressMode = DEBUGGER_READ_ADDRESS_MODE_64_BIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We couldn't determine the type of process, let's assume that it's a
|
|
// 64-bit process by default
|
|
//
|
|
ReadMemRequest->AddressMode = DEBUGGER_READ_ADDRESS_MODE_64_BIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the final status of memory read as it was successful
|
|
//
|
|
ReadMemRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
*ReturnSize = Size;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Perform rdmsr, wrmsr commands
|
|
*
|
|
* @param ReadOrWriteMsrRequest Msr read/write request
|
|
* @param UserBuffer user buffer to save the results
|
|
* @param ReturnSize return size to user-mode buffers
|
|
* @return NTSTATUS
|
|
*/
|
|
NTSTATUS
|
|
DebuggerReadOrWriteMsr(PDEBUGGER_READ_AND_WRITE_ON_MSR ReadOrWriteMsrRequest, UINT64 * UserBuffer, PSIZE_T ReturnSize)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ProcessorsCount;
|
|
|
|
ProcessorsCount = KeQueryActiveProcessorCount(0);
|
|
|
|
//
|
|
// We don't check whether the MSR is in valid range of hardware or not
|
|
// because the user might send a non-valid MSR which means sth to the
|
|
// Windows or VMM, e.g the range specified for VMMs in Hyper-v
|
|
//
|
|
|
|
if (ReadOrWriteMsrRequest->ActionType == DEBUGGER_MSR_WRITE)
|
|
{
|
|
//
|
|
// Set Msr to be applied on the target cores
|
|
//
|
|
if (ReadOrWriteMsrRequest->CoreNumber == DEBUGGER_READ_AND_WRITE_ON_MSR_APPLY_ALL_CORES)
|
|
{
|
|
//
|
|
// Means that we should apply it on all cores
|
|
//
|
|
for (size_t i = 0; i < ProcessorsCount; i++)
|
|
{
|
|
g_DbgState[i].MsrState.Msr = ReadOrWriteMsrRequest->Msr;
|
|
g_DbgState[i].MsrState.Value = ReadOrWriteMsrRequest->Value;
|
|
}
|
|
//
|
|
// Broadcast to all cores to change their Msrs
|
|
//
|
|
KeGenericCallDpc(DpcRoutineWriteMsrToAllCores, 0x0);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have to change a single core's msr
|
|
//
|
|
|
|
//
|
|
// Check if the core number is not invalid
|
|
//
|
|
if (ReadOrWriteMsrRequest->CoreNumber >= ProcessorsCount)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// Otherwise it's valid
|
|
//
|
|
g_DbgState[ReadOrWriteMsrRequest->CoreNumber].MsrState.Msr = ReadOrWriteMsrRequest->Msr;
|
|
g_DbgState[ReadOrWriteMsrRequest->CoreNumber].MsrState.Value = ReadOrWriteMsrRequest->Value;
|
|
|
|
//
|
|
// Execute it on a single core
|
|
//
|
|
Status = DpcRoutineRunTaskOnSingleCore(ReadOrWriteMsrRequest->CoreNumber, (PVOID)DpcRoutinePerformWriteMsr, NULL);
|
|
|
|
*ReturnSize = 0;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// It's an wrmsr, nothing to return
|
|
//
|
|
*ReturnSize = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (ReadOrWriteMsrRequest->ActionType == DEBUGGER_MSR_READ)
|
|
{
|
|
//
|
|
// Set Msr to be applied on the target cores
|
|
//
|
|
if (ReadOrWriteMsrRequest->CoreNumber == DEBUGGER_READ_AND_WRITE_ON_MSR_APPLY_ALL_CORES)
|
|
{
|
|
//
|
|
// Means that we should apply it on all cores
|
|
//
|
|
for (size_t i = 0; i < ProcessorsCount; i++)
|
|
{
|
|
g_DbgState[i].MsrState.Msr = ReadOrWriteMsrRequest->Msr;
|
|
}
|
|
|
|
//
|
|
// Broadcast to all cores to read their Msrs
|
|
//
|
|
KeGenericCallDpc(DpcRoutineReadMsrToAllCores, 0x0);
|
|
|
|
//
|
|
// When we reach here, all processors read their shits
|
|
// so we have to fill that fucking buffer for user mode
|
|
//
|
|
for (size_t i = 0; i < ProcessorsCount; i++)
|
|
{
|
|
UserBuffer[i] = g_DbgState[i].MsrState.Value;
|
|
}
|
|
|
|
//
|
|
// It's an rdmsr we have to return a value for all cores
|
|
//
|
|
|
|
*ReturnSize = sizeof(UINT64) * ProcessorsCount;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Apply to one core
|
|
//
|
|
|
|
//
|
|
// Check if the core number is not invalid
|
|
//
|
|
if (ReadOrWriteMsrRequest->CoreNumber >= ProcessorsCount)
|
|
{
|
|
*ReturnSize = 0;
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// Otherwise it's valid
|
|
//
|
|
g_DbgState[ReadOrWriteMsrRequest->CoreNumber].MsrState.Msr = ReadOrWriteMsrRequest->Msr;
|
|
|
|
//
|
|
// Execute it on a single core
|
|
//
|
|
Status = DpcRoutineRunTaskOnSingleCore(ReadOrWriteMsrRequest->CoreNumber, (PVOID)DpcRoutinePerformReadMsr, NULL);
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
*ReturnSize = 0;
|
|
return Status;
|
|
}
|
|
//
|
|
// Restore the result to the usermode
|
|
//
|
|
UserBuffer[0] = g_DbgState[ReadOrWriteMsrRequest->CoreNumber].MsrState.Value;
|
|
|
|
*ReturnSize = sizeof(UINT64);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
*ReturnSize = 0;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
/**
|
|
* @brief Edit physical and virtual memory
|
|
*
|
|
* @param EditMemRequest edit memory request
|
|
* @return NTSTATUS
|
|
*/
|
|
NTSTATUS
|
|
DebuggerCommandEditMemory(PDEBUGGER_EDIT_MEMORY EditMemRequest)
|
|
{
|
|
UINT32 LengthOfEachChunk = 0;
|
|
PVOID DestinationAddress = 0;
|
|
PVOID SourceAddress = 0;
|
|
|
|
//
|
|
// set chunk size in each modification
|
|
//
|
|
if (EditMemRequest->ByteSize == EDIT_BYTE)
|
|
{
|
|
LengthOfEachChunk = 1;
|
|
}
|
|
else if (EditMemRequest->ByteSize == EDIT_DWORD)
|
|
{
|
|
LengthOfEachChunk = 4;
|
|
}
|
|
else if (EditMemRequest->ByteSize == EDIT_QWORD)
|
|
{
|
|
LengthOfEachChunk = 8;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Invalid parameter
|
|
//
|
|
EditMemRequest->Result = DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_PARAMETER;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Check if address is valid or not valid (virtual address)
|
|
//
|
|
if (EditMemRequest->MemoryType == EDIT_VIRTUAL_MEMORY)
|
|
{
|
|
if (EditMemRequest->ProcessId == HANDLE_TO_UINT32(PsGetCurrentProcessId()) && VirtualAddressToPhysicalAddress((PVOID)EditMemRequest->Address) == 0)
|
|
{
|
|
//
|
|
// It's an invalid address in current process
|
|
//
|
|
EditMemRequest->Result = DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_ADDRESS_BASED_ON_CURRENT_PROCESS;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
else if (VirtualAddressToPhysicalAddressByProcessId((PVOID)EditMemRequest->Address, EditMemRequest->ProcessId) == 0)
|
|
{
|
|
//
|
|
// It's an invalid address in another process
|
|
//
|
|
EditMemRequest->Result = DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_ADDRESS_BASED_ON_OTHER_PROCESS;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Edit the memory
|
|
//
|
|
for (size_t i = 0; i < EditMemRequest->CountOf64Chunks; i++)
|
|
{
|
|
DestinationAddress = (PVOID)((UINT64)EditMemRequest->Address + (i * LengthOfEachChunk));
|
|
SourceAddress = (PVOID)((UINT64)EditMemRequest + SIZEOF_DEBUGGER_EDIT_MEMORY + (i * sizeof(UINT64)));
|
|
|
|
//
|
|
// Instead of directly accessing the memory we use the MemoryMapperWriteMemorySafe
|
|
// It is because the target page might be read-only so we can make it writable
|
|
//
|
|
// RtlCopyBytes(DestinationAddress, SourceAddress, LengthOfEachChunk);
|
|
MemoryMapperWriteMemoryUnsafe((UINT64)DestinationAddress, SourceAddress, LengthOfEachChunk, EditMemRequest->ProcessId);
|
|
}
|
|
}
|
|
else if (EditMemRequest->MemoryType == EDIT_PHYSICAL_MEMORY)
|
|
{
|
|
//
|
|
// Check whether the physical addres
|
|
//
|
|
if (!CheckAddressPhysical(EditMemRequest->Address))
|
|
{
|
|
EditMemRequest->Result = DEBUGGER_ERROR_INVALID_ADDRESS;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Edit the physical memory
|
|
//
|
|
for (size_t i = 0; i < EditMemRequest->CountOf64Chunks; i++)
|
|
{
|
|
DestinationAddress = (PVOID)((UINT64)EditMemRequest->Address + (i * LengthOfEachChunk));
|
|
SourceAddress = (PVOID)((UINT64)EditMemRequest + SIZEOF_DEBUGGER_EDIT_MEMORY + (i * sizeof(UINT64)));
|
|
|
|
MemoryMapperWriteMemorySafeByPhysicalAddress((UINT64)DestinationAddress, (UINT64)SourceAddress, LengthOfEachChunk);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Invalid parameter
|
|
//
|
|
EditMemRequest->Result = DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_PARAMETER;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Set the resutls
|
|
//
|
|
EditMemRequest->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Edit physical and virtual memory on vmxroot mode
|
|
*
|
|
* @param EditMemRequest edit memory request
|
|
* @return NTSTATUS
|
|
*/
|
|
BOOLEAN
|
|
DebuggerCommandEditMemoryVmxRoot(PDEBUGGER_EDIT_MEMORY EditMemRequest)
|
|
{
|
|
UINT32 LengthOfEachChunk = 0;
|
|
PVOID DestinationAddress = 0;
|
|
PVOID SourceAddress = 0;
|
|
|
|
//
|
|
// THIS FUNCTION IS SAFE TO BE CALLED FROM VMX-ROOT
|
|
//
|
|
|
|
//
|
|
// set chunk size in each modification
|
|
//
|
|
if (EditMemRequest->ByteSize == EDIT_BYTE)
|
|
{
|
|
LengthOfEachChunk = 1;
|
|
}
|
|
else if (EditMemRequest->ByteSize == EDIT_DWORD)
|
|
{
|
|
LengthOfEachChunk = 4;
|
|
}
|
|
else if (EditMemRequest->ByteSize == EDIT_QWORD)
|
|
{
|
|
LengthOfEachChunk = 8;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Invalid parameter
|
|
//
|
|
EditMemRequest->Result = DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_PARAMETER;
|
|
return FALSE;
|
|
}
|
|
|
|
if (EditMemRequest->MemoryType == EDIT_VIRTUAL_MEMORY)
|
|
{
|
|
//
|
|
// Check whether the virtual memory is available in the current
|
|
// memory layout and also is present in the RAM
|
|
//
|
|
if (!CheckAccessValidityAndSafety(EditMemRequest->Address,
|
|
EditMemRequest->ByteSize * EditMemRequest->CountOf64Chunks))
|
|
{
|
|
EditMemRequest->Result = DEBUGGER_ERROR_INVALID_ADDRESS;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Edit the memory
|
|
//
|
|
for (size_t i = 0; i < EditMemRequest->CountOf64Chunks; i++)
|
|
{
|
|
DestinationAddress = (PVOID)((UINT64)EditMemRequest->Address + (i * LengthOfEachChunk));
|
|
SourceAddress = (PVOID)((UINT64)EditMemRequest + SIZEOF_DEBUGGER_EDIT_MEMORY + (i * sizeof(UINT64)));
|
|
|
|
//
|
|
// Instead of directly accessing the memory we use the MemoryMapperWriteMemorySafeOnTargetProcess
|
|
// It is because the target page might be read-only so we can make it writable
|
|
//
|
|
|
|
// RtlCopyBytes(DestinationAddress, SourceAddress, LengthOfEachChunk);
|
|
MemoryMapperWriteMemorySafeOnTargetProcess((UINT64)DestinationAddress, SourceAddress, LengthOfEachChunk);
|
|
}
|
|
}
|
|
else if (EditMemRequest->MemoryType == EDIT_PHYSICAL_MEMORY)
|
|
{
|
|
//
|
|
// Check whether the physical addres
|
|
//
|
|
if (!CheckAddressPhysical(EditMemRequest->Address))
|
|
{
|
|
EditMemRequest->Result = DEBUGGER_ERROR_INVALID_ADDRESS;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Edit the physical memory
|
|
//
|
|
for (size_t i = 0; i < EditMemRequest->CountOf64Chunks; i++)
|
|
{
|
|
DestinationAddress = (PVOID)((UINT64)EditMemRequest->Address + (i * LengthOfEachChunk));
|
|
SourceAddress = (PVOID)((UINT64)EditMemRequest + SIZEOF_DEBUGGER_EDIT_MEMORY + (i * sizeof(UINT64)));
|
|
|
|
MemoryMapperWriteMemorySafeByPhysicalAddress((UINT64)DestinationAddress, (UINT64)SourceAddress, LengthOfEachChunk);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Invalid parameter
|
|
//
|
|
EditMemRequest->Result = DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_PARAMETER;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the resutls
|
|
//
|
|
EditMemRequest->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Search on virtual memory (not work on physical memory)
|
|
*
|
|
* @details This function can be called from vmx-root mode
|
|
* Do NOT directly call this function as the virtual addresses
|
|
* should be valid on the target process memory layout
|
|
* instead call : SearchAddressWrapper
|
|
* the address between StartAddress and EndAddress should be contiguous
|
|
*
|
|
* @param AddressToSaveResults Address to save the search results
|
|
* @param SearchMemRequest request structure of searching memory
|
|
* @param StartAddress valid start address based on target process
|
|
* @param EndAddress valid end address based on target process
|
|
* @param IsDebuggeePaused Set to true when the search is performed in
|
|
* the debugger mode
|
|
* @param CountOfMatchedCases Number of matched cases
|
|
* @return BOOLEAN Whether the search was successful or not
|
|
*/
|
|
BOOLEAN
|
|
PerformSearchAddress(UINT64 * AddressToSaveResults,
|
|
PDEBUGGER_SEARCH_MEMORY SearchMemRequest,
|
|
UINT64 StartAddress,
|
|
UINT64 EndAddress,
|
|
BOOLEAN IsDebuggeePaused,
|
|
PUINT32 CountOfMatchedCases)
|
|
{
|
|
UINT32 CountOfOccurance = 0;
|
|
UINT64 Cmp64 = 0;
|
|
UINT32 IndexToArrayOfResults = 0;
|
|
UINT32 LengthOfEachChunk = 0;
|
|
PVOID TempSourceAddress = 0;
|
|
PVOID SourceAddress = 0;
|
|
BOOLEAN StillMatch = FALSE;
|
|
UINT64 TempValue = (UINT64)NULL;
|
|
CR3_TYPE CurrentProcessCr3 = {0};
|
|
|
|
//
|
|
// set chunk size in each modification
|
|
//
|
|
if (SearchMemRequest->ByteSize == SEARCH_BYTE)
|
|
{
|
|
LengthOfEachChunk = 1;
|
|
}
|
|
else if (SearchMemRequest->ByteSize == SEARCH_DWORD)
|
|
{
|
|
LengthOfEachChunk = 4;
|
|
}
|
|
else if (SearchMemRequest->ByteSize == SEARCH_QWORD)
|
|
{
|
|
LengthOfEachChunk = 8;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Invalid parameter
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if address is virtual address or physical address
|
|
//
|
|
if (SearchMemRequest->MemoryType == SEARCH_VIRTUAL_MEMORY ||
|
|
SearchMemRequest->MemoryType == SEARCH_PHYSICAL_FROM_VIRTUAL_MEMORY)
|
|
{
|
|
//
|
|
// Search the memory
|
|
//
|
|
|
|
//
|
|
// Change the memory layout (cr3), if the user specified a
|
|
// special process
|
|
//
|
|
if (IsDebuggeePaused)
|
|
{
|
|
//
|
|
// Switch to target process memory layout
|
|
//
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayoutByCr3(LayoutGetCurrentProcessCr3());
|
|
}
|
|
else
|
|
{
|
|
if (SearchMemRequest->ProcessId != HANDLE_TO_UINT32(PsGetCurrentProcessId()))
|
|
{
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayout(SearchMemRequest->ProcessId);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Here we iterate through the buffer we received from
|
|
// user-mode
|
|
//
|
|
SourceAddress = (PVOID)((UINT64)SearchMemRequest + SIZEOF_DEBUGGER_SEARCH_MEMORY);
|
|
|
|
for (size_t BaseIterator = (size_t)StartAddress; BaseIterator < ((UINT64)EndAddress); BaseIterator += LengthOfEachChunk)
|
|
{
|
|
//
|
|
// *** Search the memory ***
|
|
//
|
|
|
|
//
|
|
// Copy 64bit, 32bit or one byte value into Cmp64 buffer and then compare it
|
|
// Check if we should access the memory directly, or through safe memory
|
|
// routine from vmx-root
|
|
//
|
|
if (IsDebuggeePaused)
|
|
{
|
|
MemoryMapperReadMemorySafe((UINT64)BaseIterator, &Cmp64, LengthOfEachChunk);
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(&Cmp64, (PVOID)BaseIterator, LengthOfEachChunk);
|
|
}
|
|
|
|
//
|
|
// Get the searching bytes
|
|
//
|
|
TempValue = *(UINT64 *)SourceAddress;
|
|
|
|
//
|
|
// Check whether the byte matches the source or not
|
|
//
|
|
if (Cmp64 == TempValue)
|
|
{
|
|
//
|
|
// Indicate that it matches until now
|
|
//
|
|
StillMatch = TRUE;
|
|
|
|
//
|
|
// Try to check each element (we don't start from the very first element as
|
|
// it checked before )
|
|
//
|
|
for (size_t i = LengthOfEachChunk; i < SearchMemRequest->CountOf64Chunks; i++)
|
|
{
|
|
//
|
|
// I know, we have a double check here ;)
|
|
//
|
|
TempSourceAddress = (PVOID)((UINT64)SearchMemRequest + SIZEOF_DEBUGGER_SEARCH_MEMORY + (i * sizeof(UINT64)));
|
|
|
|
//
|
|
// Add i to BaseIterator and recompute the Cmp64
|
|
// Check if we should access the memory directly, or through safe memory
|
|
// routine from vmx-root
|
|
//
|
|
if (IsDebuggeePaused)
|
|
{
|
|
MemoryMapperReadMemorySafe((UINT64)(BaseIterator + (LengthOfEachChunk * i)), &Cmp64, LengthOfEachChunk);
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(&Cmp64, (PVOID)(BaseIterator + (LengthOfEachChunk * i)), LengthOfEachChunk);
|
|
}
|
|
|
|
//
|
|
// Check if we should access the memory directly,
|
|
// or through safe memory routine from vmx-root
|
|
//
|
|
if (IsDebuggeePaused)
|
|
{
|
|
MemoryMapperReadMemorySafe((UINT64)TempSourceAddress, &TempValue, sizeof(UINT64));
|
|
}
|
|
else
|
|
{
|
|
TempValue = *(UINT64 *)TempSourceAddress;
|
|
}
|
|
|
|
if (!(Cmp64 == TempValue))
|
|
{
|
|
//
|
|
// One thing didn't match so this is not the pattern
|
|
//
|
|
StillMatch = FALSE;
|
|
|
|
//
|
|
// Break from the loop
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if we find the pattern or not
|
|
//
|
|
if (StillMatch)
|
|
{
|
|
//
|
|
// We found the a matching address, let's save the
|
|
// address for future use
|
|
//
|
|
CountOfOccurance++;
|
|
|
|
if (IsDebuggeePaused)
|
|
{
|
|
if (SearchMemRequest->MemoryType == SEARCH_PHYSICAL_FROM_VIRTUAL_MEMORY)
|
|
{
|
|
//
|
|
// It's a physical memory
|
|
//
|
|
Log("%llx\n", VirtualAddressToPhysicalAddress((PVOID)BaseIterator));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's a virtual memory
|
|
//
|
|
Log("%llx\n", BaseIterator);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SearchMemRequest->MemoryType == SEARCH_PHYSICAL_FROM_VIRTUAL_MEMORY)
|
|
{
|
|
//
|
|
// It's a physical memory
|
|
//
|
|
AddressToSaveResults[IndexToArrayOfResults] = VirtualAddressToPhysicalAddress((PVOID)BaseIterator);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's a virtual memory
|
|
//
|
|
AddressToSaveResults[IndexToArrayOfResults] = BaseIterator;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Increase the array pointer if it doesn't exceed the limitation
|
|
//
|
|
if (MaximumSearchResults > IndexToArrayOfResults)
|
|
{
|
|
IndexToArrayOfResults++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The result buffer is full!
|
|
//
|
|
*CountOfMatchedCases = CountOfOccurance;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not found in the place
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Restore the previous memory layout (cr3), if the user specified a
|
|
// special process
|
|
//
|
|
if (IsDebuggeePaused || SearchMemRequest->ProcessId != HANDLE_TO_UINT32(PsGetCurrentProcessId()))
|
|
{
|
|
SwitchToPreviousProcess(CurrentProcessCr3);
|
|
}
|
|
}
|
|
else if (SearchMemRequest->MemoryType == SEARCH_PHYSICAL_MEMORY)
|
|
{
|
|
//
|
|
// That's an error, the physical memory is handled like virtual memory and
|
|
// thus we should never reach here
|
|
//
|
|
LogError("Err, searching physical memory is not allowed without virtual address");
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Invalid parameter
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// As we're here the search is finished without error
|
|
//
|
|
*CountOfMatchedCases = CountOfOccurance;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief The wrapper to check for validity of addresses and call
|
|
* the search routines for both physical and virtual memory
|
|
*
|
|
* @details This function can be called from vmx-root mode
|
|
* The address between start address and end address will be checked
|
|
* to make a contiguous address
|
|
*
|
|
* @param AddressToSaveResults Address to save the search results
|
|
* @param SearchMemRequest request structure of searching memory
|
|
* @param StartAddress start address of searching based on target process
|
|
* @param EndAddress start address of searching based on target process
|
|
* @param IsDebuggeePaused Set to true when the search is performed in
|
|
* the debugger mode
|
|
* @param CountOfMatchedCases Number of matched cases
|
|
* @return BOOLEAN Whether there was any error or not
|
|
*/
|
|
BOOLEAN
|
|
SearchAddressWrapper(PUINT64 AddressToSaveResults,
|
|
PDEBUGGER_SEARCH_MEMORY SearchMemRequest,
|
|
UINT64 StartAddress,
|
|
UINT64 EndAddress,
|
|
BOOLEAN IsDebuggeePaused,
|
|
PUINT32 CountOfMatchedCases)
|
|
{
|
|
CR3_TYPE CurrentProcessCr3;
|
|
UINT64 BaseAddress = 0;
|
|
UINT64 RealPhysicalAddress = 0;
|
|
UINT64 TempValue = (UINT64)NULL;
|
|
UINT64 TempStartAddress = (UINT64)NULL;
|
|
BOOLEAN DoesBaseAddrSaved = FALSE;
|
|
BOOLEAN SearchResult = FALSE;
|
|
|
|
//
|
|
// Reset the count of matched cases
|
|
//
|
|
*CountOfMatchedCases = 0;
|
|
|
|
if (SearchMemRequest->MemoryType == SEARCH_VIRTUAL_MEMORY)
|
|
{
|
|
//
|
|
// It's a virtual address search
|
|
//
|
|
|
|
//
|
|
// Align the page and search with alignment
|
|
//
|
|
TempStartAddress = StartAddress;
|
|
StartAddress = (UINT64)PAGE_ALIGN(StartAddress);
|
|
|
|
if (IsDebuggeePaused)
|
|
{
|
|
//
|
|
// Switch to new process's memory layout
|
|
//
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayoutByCr3(LayoutGetCurrentProcessCr3());
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Switch to new process's memory layout
|
|
//
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayout(SearchMemRequest->ProcessId);
|
|
}
|
|
|
|
//
|
|
// We will try to find a contigues address
|
|
//
|
|
while (StartAddress < EndAddress)
|
|
{
|
|
//
|
|
// Check if address is valid or not
|
|
// Generally, we can use VirtualAddressToPhysicalAddressByProcessId
|
|
// but let's not change the cr3 multiple times
|
|
//
|
|
TempValue = VirtualAddressToPhysicalAddress((PVOID)StartAddress);
|
|
|
|
if (TempValue != 0)
|
|
{
|
|
//
|
|
// Address is valid, let's add a page size to it
|
|
// nothing to do
|
|
//
|
|
if (!DoesBaseAddrSaved)
|
|
{
|
|
BaseAddress = TempStartAddress;
|
|
DoesBaseAddrSaved = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Address is not valid anymore
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make the start address ready for next address
|
|
//
|
|
StartAddress += PAGE_SIZE;
|
|
}
|
|
|
|
//
|
|
// Restore the original process
|
|
//
|
|
SwitchToPreviousProcess(CurrentProcessCr3);
|
|
|
|
//
|
|
// All of the address chunk was valid
|
|
//
|
|
if (DoesBaseAddrSaved && StartAddress > BaseAddress)
|
|
{
|
|
SearchResult = PerformSearchAddress(AddressToSaveResults,
|
|
SearchMemRequest,
|
|
BaseAddress,
|
|
EndAddress,
|
|
IsDebuggeePaused,
|
|
CountOfMatchedCases);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There was an error, address was probably not contiguous
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (SearchMemRequest->MemoryType == SEARCH_PHYSICAL_MEMORY)
|
|
{
|
|
//
|
|
// when we reached here, we know that it's a valid physical memory,
|
|
// so we change the structure and pass it as a virtual address to
|
|
// the search function
|
|
//
|
|
RealPhysicalAddress = SearchMemRequest->Address;
|
|
|
|
//
|
|
// Change the start address
|
|
//
|
|
if (IsDebuggeePaused)
|
|
{
|
|
SearchMemRequest->Address = PhysicalAddressToVirtualAddressOnTargetProcess((PVOID)StartAddress);
|
|
EndAddress = PhysicalAddressToVirtualAddressOnTargetProcess((PVOID)EndAddress);
|
|
}
|
|
else if (SearchMemRequest->ProcessId == HANDLE_TO_UINT32(PsGetCurrentProcessId()))
|
|
{
|
|
SearchMemRequest->Address = PhysicalAddressToVirtualAddress(StartAddress);
|
|
EndAddress = PhysicalAddressToVirtualAddress(EndAddress);
|
|
}
|
|
else
|
|
{
|
|
SearchMemRequest->Address = PhysicalAddressToVirtualAddressByProcessId((PVOID)StartAddress,
|
|
SearchMemRequest->ProcessId);
|
|
EndAddress = PhysicalAddressToVirtualAddressByProcessId((PVOID)EndAddress,
|
|
SearchMemRequest->ProcessId);
|
|
}
|
|
|
|
//
|
|
// Change the type of memory
|
|
//
|
|
SearchMemRequest->MemoryType = SEARCH_PHYSICAL_FROM_VIRTUAL_MEMORY;
|
|
|
|
//
|
|
// Call the wrapper
|
|
//
|
|
SearchResult = PerformSearchAddress(AddressToSaveResults,
|
|
SearchMemRequest,
|
|
SearchMemRequest->Address,
|
|
EndAddress,
|
|
IsDebuggeePaused,
|
|
CountOfMatchedCases);
|
|
|
|
//
|
|
// Restore the previous state
|
|
//
|
|
SearchMemRequest->MemoryType = SEARCH_PHYSICAL_MEMORY;
|
|
SearchMemRequest->Address = RealPhysicalAddress;
|
|
}
|
|
|
|
return SearchResult;
|
|
}
|
|
|
|
/**
|
|
* @brief Start searching memory
|
|
*
|
|
* @param SearchMemRequest Request to search memory
|
|
* @return NTSTATUS
|
|
*/
|
|
NTSTATUS
|
|
DebuggerCommandSearchMemory(PDEBUGGER_SEARCH_MEMORY SearchMemRequest)
|
|
{
|
|
PUINT64 SearchResultsStorage = NULL;
|
|
PUINT64 UsermodeBuffer = NULL;
|
|
UINT64 AddressFrom = 0;
|
|
UINT64 AddressTo = 0;
|
|
UINT64 CurrentValue = 0;
|
|
UINT32 ResultsIndex = 0;
|
|
UINT32 CountOfResults = 0;
|
|
|
|
//
|
|
// Check if process id is valid or not
|
|
//
|
|
if (SearchMemRequest->ProcessId != HANDLE_TO_UINT32(PsGetCurrentProcessId()) && !CommonIsProcessExist(SearchMemRequest->ProcessId))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// User-mode buffer is same as SearchMemRequest
|
|
//
|
|
UsermodeBuffer = (UINT64 *)SearchMemRequest;
|
|
|
|
//
|
|
// We store the user-mode data in a separate variable because
|
|
// we will use them later when we Zeroed the SearchMemRequest
|
|
//
|
|
AddressFrom = SearchMemRequest->Address;
|
|
AddressTo = SearchMemRequest->Address + SearchMemRequest->Length;
|
|
|
|
//
|
|
// We support up to MaximumSearchResults search results
|
|
//
|
|
SearchResultsStorage = PlatformMemAllocateZeroedNonPagedPool(MaximumSearchResults * sizeof(UINT64));
|
|
|
|
if (SearchResultsStorage == NULL)
|
|
{
|
|
//
|
|
// Not enough memory
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Call the wrapper
|
|
//
|
|
SearchAddressWrapper(SearchResultsStorage, SearchMemRequest, AddressFrom, AddressTo, FALSE, &CountOfResults);
|
|
|
|
//
|
|
// In this point, we to store the results (if any) to the user-mode
|
|
// buffer SearchMemRequest itself is the user-mode buffer and we also
|
|
// checked from the previous function that the output buffer is at
|
|
// least SearchMemRequest bigger or equal to MaximumSearchResults * sizeof(UINT64)
|
|
// so we need to clear everything here, and also we should keep in mind that
|
|
// SearchMemRequest is no longer valid
|
|
//
|
|
RtlZeroMemory(SearchMemRequest, MaximumSearchResults * sizeof(UINT64));
|
|
|
|
//
|
|
// It's time to move the results from our temporary buffer to the user-mode
|
|
// buffer, also there is something that we should check and that's the fact
|
|
// that we used aligned page addresses so the results should be checked to
|
|
// see whether the results are between the user's entered addresses or not
|
|
//
|
|
for (size_t i = 0; i < MaximumSearchResults; i++)
|
|
{
|
|
CurrentValue = SearchResultsStorage[i];
|
|
|
|
if (CurrentValue == (UINT64)NULL)
|
|
{
|
|
//
|
|
// Nothing left to move
|
|
//
|
|
break;
|
|
}
|
|
|
|
if (CurrentValue >= AddressFrom && CurrentValue <= AddressTo)
|
|
{
|
|
//
|
|
// Move the variable
|
|
//
|
|
UsermodeBuffer[ResultsIndex] = CurrentValue;
|
|
ResultsIndex++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the results pool
|
|
//
|
|
PlatformMemFreePool(SearchResultsStorage);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Perform the flush requests to vmx-root and vmx non-root buffers
|
|
*
|
|
* @param DebuggerFlushBuffersRequest Request to flush the buffers
|
|
* @return NTSTATUS
|
|
*/
|
|
NTSTATUS
|
|
DebuggerCommandFlush(PDEBUGGER_FLUSH_LOGGING_BUFFERS DebuggerFlushBuffersRequest)
|
|
{
|
|
//
|
|
// We try to flush buffers for both vmx-root and regular kernel buffer
|
|
//
|
|
DebuggerFlushBuffersRequest->CountOfMessagesThatSetAsReadFromVmxRoot = LogMarkAllAsRead(TRUE);
|
|
DebuggerFlushBuffersRequest->CountOfMessagesThatSetAsReadFromVmxNonRoot = LogMarkAllAsRead(FALSE);
|
|
DebuggerFlushBuffersRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Perform the command finished signal
|
|
*
|
|
* @param DebuggerFinishedExecutionRequest Request to
|
|
* signal debuggee about execution state
|
|
* @return NTSTATUS
|
|
*/
|
|
NTSTATUS
|
|
DebuggerCommandSignalExecutionState(PDEBUGGER_SEND_COMMAND_EXECUTION_FINISHED_SIGNAL DebuggerFinishedExecutionRequest)
|
|
{
|
|
//
|
|
// It's better to send the signal from vmx-root mode
|
|
//
|
|
VmFuncVmxVmcall(DEBUGGER_VMCALL_SIGNAL_DEBUGGER_EXECUTION_FINISHED, 0, 0, 0);
|
|
|
|
DebuggerFinishedExecutionRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Send the user-mode buffer to debugger
|
|
*
|
|
* @param DebuggerSendUsermodeMessageRequest Request to send message to debugger
|
|
* @return NTSTATUS
|
|
*/
|
|
NTSTATUS
|
|
DebuggerCommandSendMessage(PDEBUGGER_SEND_USERMODE_MESSAGES_TO_DEBUGGER DebuggerSendUsermodeMessageRequest)
|
|
{
|
|
//
|
|
// It's better to send the signal from vmx-root mode to avoid deadlock
|
|
//
|
|
VmFuncVmxVmcall(DEBUGGER_VMCALL_SEND_MESSAGES_TO_DEBUGGER,
|
|
(UINT64)DebuggerSendUsermodeMessageRequest + (SIZEOF_DEBUGGER_SEND_USERMODE_MESSAGES_TO_DEBUGGER),
|
|
DebuggerSendUsermodeMessageRequest->Length,
|
|
0);
|
|
|
|
DebuggerSendUsermodeMessageRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Send general buffers from the debuggee to the debugger
|
|
*
|
|
* @param DebuggeeBufferRequest Request to buffer that will be sent to the debugger
|
|
* @return NTSTATUS
|
|
*/
|
|
NTSTATUS
|
|
DebuggerCommandSendGeneralBufferToDebugger(PDEBUGGEE_SEND_GENERAL_PACKET_FROM_DEBUGGEE_TO_DEBUGGER DebuggeeBufferRequest)
|
|
{
|
|
//
|
|
// It's better to send the signal from vmx-root mode to avoid deadlock
|
|
//
|
|
VmFuncVmxVmcall(DEBUGGER_VMCALL_SEND_GENERAL_BUFFER_TO_DEBUGGER,
|
|
(UINT64)DebuggeeBufferRequest,
|
|
0,
|
|
0);
|
|
|
|
DebuggeeBufferRequest->KernelResult = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Reserve and allocate pre-allocated buffers
|
|
*
|
|
* @param PreallocRequest Request details of needed buffers to be reserved
|
|
*
|
|
* @return NTSTATUS
|
|
*/
|
|
NTSTATUS
|
|
DebuggerCommandReservePreallocatedPools(PDEBUGGER_PREALLOC_COMMAND PreallocRequest)
|
|
{
|
|
switch (PreallocRequest->Type)
|
|
{
|
|
case DEBUGGER_PREALLOC_COMMAND_TYPE_THREAD_INTERCEPTION:
|
|
|
|
//
|
|
// Request pages to be allocated for thread interception mechanism
|
|
//
|
|
PoolManagerRequestAllocation(sizeof(USERMODE_DEBUGGING_THREAD_HOLDER),
|
|
PreallocRequest->Count,
|
|
PROCESS_THREAD_HOLDER);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_PREALLOC_COMMAND_TYPE_MONITOR:
|
|
|
|
//
|
|
// Perform the allocations for the '!monitor' command
|
|
//
|
|
ConfigureEptHookAllocateExtraHookingPagesForMemoryMonitorsAndExecEptHooks(PreallocRequest->Count);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_PREALLOC_COMMAND_TYPE_EPTHOOK:
|
|
|
|
//
|
|
// Perform the allocations for the '!epthook' command
|
|
//
|
|
ConfigureEptHookAllocateExtraHookingPagesForMemoryMonitorsAndExecEptHooks(PreallocRequest->Count);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_PREALLOC_COMMAND_TYPE_EPTHOOK2:
|
|
|
|
//
|
|
// All the prealloc requests of regular EPT hooks are needed for the '!epthook2'
|
|
//
|
|
ConfigureEptHookReservePreallocatedPoolsForEptHooks(PreallocRequest->Count);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_PREALLOC_COMMAND_TYPE_REGULAR_EVENT:
|
|
|
|
//
|
|
// Request pages to be allocated for regular instant events
|
|
//
|
|
PoolManagerRequestAllocation(REGULAR_INSTANT_EVENT_CONDITIONAL_BUFFER,
|
|
PreallocRequest->Count,
|
|
INSTANT_REGULAR_EVENT_BUFFER);
|
|
|
|
//
|
|
// Request pages to be allocated for regular instant events's actions
|
|
//
|
|
PoolManagerRequestAllocation(REGULAR_INSTANT_EVENT_ACTION_BUFFER,
|
|
PreallocRequest->Count,
|
|
INSTANT_REGULAR_EVENT_ACTION_BUFFER);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_PREALLOC_COMMAND_TYPE_BIG_EVENT:
|
|
|
|
//
|
|
// Request pages to be allocated for big instant events
|
|
//
|
|
PoolManagerRequestAllocation(BIG_INSTANT_EVENT_CONDITIONAL_BUFFER,
|
|
PreallocRequest->Count,
|
|
INSTANT_BIG_EVENT_BUFFER);
|
|
|
|
//
|
|
// Request pages to be allocated for big instant events's actions
|
|
//
|
|
PoolManagerRequestAllocation(BIG_INSTANT_EVENT_ACTION_BUFFER,
|
|
PreallocRequest->Count,
|
|
INSTANT_BIG_EVENT_ACTION_BUFFER);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_PREALLOC_COMMAND_TYPE_REGULAR_SAFE_BUFFER:
|
|
|
|
//
|
|
// Request pages to be allocated for regular safe buffer ($buffer) for events
|
|
//
|
|
PoolManagerRequestAllocation(REGULAR_INSTANT_EVENT_REQUESTED_SAFE_BUFFER,
|
|
PreallocRequest->Count,
|
|
INSTANT_REGULAR_SAFE_BUFFER_FOR_EVENTS);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_PREALLOC_COMMAND_TYPE_BIG_SAFE_BUFFER:
|
|
|
|
//
|
|
// Request pages to be allocated for big safe buffer ($buffer) for events
|
|
//
|
|
PoolManagerRequestAllocation(BIG_INSTANT_EVENT_REQUESTED_SAFE_BUFFER,
|
|
PreallocRequest->Count,
|
|
INSTANT_BIG_SAFE_BUFFER_FOR_EVENTS);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PreallocRequest->KernelStatus = DEBUGGER_ERROR_COULD_NOT_FIND_ALLOCATION_TYPE;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Invalidate and perform the allocations as we're in PASSIVE_LEVEL
|
|
//
|
|
PoolManagerCheckAndPerformAllocationAndDeallocation();
|
|
|
|
PreallocRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Preactivate a special functionality
|
|
*
|
|
* @param PreactivateRequest Request details of preactivation
|
|
*
|
|
* @return NTSTATUS
|
|
*/
|
|
NTSTATUS
|
|
DebuggerCommandPreactivateFunctionality(PDEBUGGER_PREACTIVATE_COMMAND PreactivateRequest)
|
|
{
|
|
switch (PreactivateRequest->Type)
|
|
{
|
|
case DEBUGGER_PREACTIVATE_COMMAND_TYPE_MODE:
|
|
|
|
//
|
|
// Request for allocating the mode mechanism
|
|
//
|
|
ConfigureInitializeExecTrapOnAllProcessors();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PreactivateRequest->KernelStatus = DEBUGGER_ERROR_COULD_NOT_FIND_PREACTIVATION_TYPE;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
PreactivateRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief routines for the .pagein command
|
|
*
|
|
* @param PageinRequest
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
DebuggerCommandBringPagein(PDEBUGGER_PAGE_IN_REQUEST PageinRequest)
|
|
{
|
|
//
|
|
// *** Perform the injection here ***
|
|
//
|
|
LogInfo("Page-request is received!");
|
|
|
|
//
|
|
// Adjust the flags for showing the successful #PF injection
|
|
//
|
|
PageinRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return TRUE;
|
|
}
|