Fix adding missing IOCTL for Intel PT

This commit is contained in:
sina 2026-06-10 23:56:36 +02:00
parent ade6877a7d
commit df12e9fd79
6 changed files with 152 additions and 67 deletions

View file

@ -134,6 +134,74 @@ IoctlCheckIoctlAllowed(ULONG Ioctl)
}
}
/**
* @brief Resolve a process id to the CR3 the Intel PT engine should match.
*
* @details Intel PT's CR3 filter compares the live CR3, which differs between
* user and kernel mode when KVA shadowing (KPTI) is enabled:
* - kernel-mode runs under _KPROCESS.DirectoryTableBase
* - user-mode runs under _KPROCESS.UserDirectoryTableBase (shadow)
* So pick the kernel CR3 for kernel-only traces, otherwise the user
* (shadow) CR3 falling back to the kernel CR3 when KVA shadowing is
* off (UserDirectoryTableBase has a zero page base in that case, i.e.
* user-mode also runs under the kernel CR3).
*
* @param ProcessId Target process id
* @param TraceUser Whether CPL>0 will be traced
* @param TraceKernel Whether CPL==0 will be traced
*
* @return UINT64 CR3 page base (low 12 bits cleared), or 0 if the pid is gone
*/
static UINT64
DrvResolvePtTargetCr3(UINT32 ProcessId, BOOLEAN TraceUser, BOOLEAN TraceKernel)
{
//
// _KPROCESS.UserDirectoryTableBase offset (x64 Windows 10 1803+ / 11).
// Only this one constant is build-specific; adjust it if the user CR3
// ever reads wrong on a newer kernel.
//
const ULONG UserDirTableBaseOffset = 0x388;
PEPROCESS TargetProcess;
UINT64 KernelCr3;
UINT64 UserCr3;
UINT64 Chosen;
if (PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)ProcessId, &TargetProcess) != STATUS_SUCCESS)
{
return 0;
}
KernelCr3 = (UINT64)((NT_KPROCESS *)TargetProcess)->DirectoryTableBase;
UserCr3 = *(UINT64 *)((UCHAR *)TargetProcess + UserDirTableBaseOffset);
ObDereferenceObject(TargetProcess);
if (TraceKernel && !TraceUser)
{
//
// Kernel-only trace — match the kernel CR3
//
Chosen = KernelCr3;
}
else if ((UserCr3 & ~0xFFFULL) != 0)
{
//
// KVA shadowing on — user mode runs under the shadow CR3
//
Chosen = UserCr3;
}
else
{
//
// KVA shadowing off — user mode runs under the kernel CR3
//
Chosen = KernelCr3;
}
return Chosen & ~0xFFFULL;
}
/**
* @brief IOCTL Dispatcher for Basic IOCTLs (initialization and event registration)
*
@ -1265,27 +1333,6 @@ DrvDispatchVmmIoControl(PIRP Irp, PIO_STACK_LOCATION IrpStack, BOOLEAN * DoNotCh
//
DrvAdjustStatusAndSetOutputSize(SIZEOF_DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS, DoNotChangeInformation, Irp, &Status);
//
// If the caller asked to filter by a process id (and didn't
// already provide an explicit CR3), resolve the PID to the CR3
// the PT engine should match here — hyperkd owns the NT_KPROCESS
// layout, whereas the hypertrace engine only consumes a CR3. The
// kernel/user CR3 is chosen based on the requested trace mode so
// it works whether or not KVA shadowing (KPTI) is enabled.
//
if (HyperTracePtOperationRequest->TargetProcessId != 0 &&
HyperTracePtOperationRequest->TargetCr3 == 0)
{
HyperTracePtOperationRequest->TargetCr3 =
DrvResolvePtTargetCr3(HyperTracePtOperationRequest->TargetProcessId,
(BOOLEAN)(HyperTracePtOperationRequest->TraceUser != 0),
(BOOLEAN)(HyperTracePtOperationRequest->TraceKernel != 0));
}
//
// Perform the HyperTrace PT operation
//
HyperTracePtPerformOperation(HyperTracePtOperationRequest);
break;
case IOCTL_GET_LIST_OF_THREADS_AND_PROCESSES:
@ -1521,6 +1568,7 @@ DrvDispatchHyperTraceIoControl(PIRP Irp, PIO_STACK_LOCATION IrpStack, BOOLEAN *
PHYPERTRACE_LBR_OPERATION_PACKETS HyperTraceLbrOperationRequest;
PHYPERTRACE_LBR_DUMP_PACKETS HyperTraceLbrdumpRequest;
PHYPERTRACE_PT_OPERATION_PACKETS HyperTracePtOperationRequest;
PHYPERTRACE_PT_MMAP_PACKETS HyperTracePtMmapRequest;
ULONG InBuffLength;
ULONG OutBuffLength;
NTSTATUS Status = STATUS_SUCCESS;
@ -1614,6 +1662,23 @@ DrvDispatchHyperTraceIoControl(PIRP Irp, PIO_STACK_LOCATION IrpStack, BOOLEAN *
break;
}
//
// If the caller asked to filter by a process id (and didn't
// already provide an explicit CR3), resolve the PID to the CR3
// the PT engine should match here — hyperkd owns the NT_KPROCESS
// layout, whereas the hypertrace engine only consumes a CR3. The
// kernel/user CR3 is chosen based on the requested trace mode so
// it works whether or not KVA shadowing (KPTI) is enabled.
//
if (HyperTracePtOperationRequest->TargetProcessId != 0 &&
HyperTracePtOperationRequest->TargetCr3 == 0)
{
HyperTracePtOperationRequest->TargetCr3 =
DrvResolvePtTargetCr3(HyperTracePtOperationRequest->TargetProcessId,
(BOOLEAN)(HyperTracePtOperationRequest->TraceUser != 0),
(BOOLEAN)(HyperTracePtOperationRequest->TraceKernel != 0));
}
//
// Perform the HyperTrace PT operation
//
@ -1622,7 +1687,35 @@ DrvDispatchHyperTraceIoControl(PIRP Irp, PIO_STACK_LOCATION IrpStack, BOOLEAN *
//
// Adjust the status and output size
//
DrvAdjustStatusAndSetOutputSize(SIZEOF_HYPERTRACE_PT_OPERATION_PACKETS, DoNotChangeInformation, Irp, &Status);
DrvAdjustStatusAndSetOutputSize(SIZEOF_HYPERTRACE_LBR_DUMP_PACKETS, DoNotChangeInformation, Irp, &Status);
break;
case IOCTL_PERFORM_HYPERTRACE_PT_MMAP:
//
// Validate and adjust the parameters, and set the target buffer to the system buffer of the IRP
//
if (!DrvValidateAndAdjustIoctlParameter(SIZEOF_HYPERTRACE_PT_MMAP_PACKETS,
(PVOID *)&HyperTracePtMmapRequest,
Irp,
IrpStack,
&InBuffLength,
&OutBuffLength))
{
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// Map the per-CPU PT output buffers into the calling user process
//
HyperTracePtMmap(HyperTracePtMmapRequest);
//
// Adjust the status and output size
//
DrvAdjustStatusAndSetOutputSize(SIZEOF_HYPERTRACE_PT_MMAP_PACKETS, DoNotChangeInformation, Irp, &Status);
break;

View file

@ -67,12 +67,6 @@
#include "platform/kernel/header/PlatformIo.h"
#include "platform/kernel/header/PlatformEvent.h"
//
// DPC and broadcasting function headers
//
#include "broadcast/DpcRoutines.h"
#include "broadcast/Broadcast.h"
//
// Unload function (to be called when the driver is unloaded)
//
@ -113,6 +107,12 @@
#include "pt/Pt.h"
#include "api/PtApi.h"
//
// DPC and broadcasting function headers
//
#include "broadcast/DpcRoutines.h"
#include "broadcast/Broadcast.h"
//
// Export functions
//

View file

@ -30,51 +30,49 @@
# error "This code cannot compile on non-Windows, non-Linux, and non-BSD platforms"
#endif
#ifdef HYPERDBG_ENV_LINUX
// SAL annotations
# define _In_
# define _Out_
# define _Inout_
# define _In_opt_
# define _Out_opt_
# define _In_z_
# define _Outptr_
# define _In_reads_bytes_(x)
# define _Out_writes_bytes_(x)
# define _Inout_updates_bytes_all_(x)
# define _In_
# define _Out_
# define _Inout_
# define _In_opt_
# define _Out_opt_
# define _In_z_
# define _Outptr_
# define _In_reads_bytes_(x)
# define _Out_writes_bytes_(x)
# define _Inout_updates_bytes_all_(x)
// wchar_t is a C++ built-in but needs this header in C
# include <wchar.h>
# include <wchar.h>
// Windows string/char types
typedef char TCHAR;
typedef char * LPTSTR;
typedef const char * LPCTSTR;
typedef const char * LPCSTR;
typedef char * LPSTR;
typedef const char * PCSTR;
typedef char * PSTR;
typedef wchar_t * PWCHAR;
typedef char TCHAR;
typedef char * LPTSTR;
typedef const char * LPCTSTR;
typedef const char * LPCSTR;
typedef char * LPSTR;
typedef const char * PCSTR;
typedef char * PSTR;
typedef short * PWCHAR;
// Windows socket type (Linux sockets are plain int)
typedef int SOCKET;
# define INVALID_SOCKET ((SOCKET)(-1))
# define SOCKET_ERROR (-1)
typedef int SOCKET;
# define INVALID_SOCKET ((SOCKET)(-1))
# define SOCKET_ERROR (-1)
// Windows calling convention (no-op on Linux)
# define WINAPI
# define WINAPI
// Windows module handle (equivalent to dlopen's void * on Linux)
typedef void * HMODULE;
// Misc Windows macros
# define UNREFERENCED_PARAMETER(P) ((void)(P))
# define UNREFERENCED_PARAMETER(P) ((void)(P))
// Win32 wait/event constants (used by the cross-platform sync wrappers)
# define INFINITE 0xFFFFFFFF
# define WAIT_OBJECT_0 0x00000000
# define INFINITE 0xFFFFFFFF
# define WAIT_OBJECT_0 0x00000000
#endif // HYPERDBG_ENV_LINUX

View file

@ -218,13 +218,12 @@ PlatformGetCurrentProcessName(VOID)
//
// Return the basename (strip the directory part)
//
char * LastSeparator = strrchr(ProcessNameBuf, '\\');
if (LastSeparator)
{
char * LastSeparator = strrchr(ProcessNameBuf, '\\');
if (LastSeparator)
{
return LastSeparator + 1;
}
return LastSeparator + 1;
}
return ProcessNameBuf;
#elif defined(__linux__)

View file

@ -82,11 +82,6 @@ CommandCore(vector<CommandToken> CommandTokens, string Command)
//
// Send the changing core packet
//
#ifdef _WIN32
KdSendSwitchCorePacketToDebuggee(TargetCore);
#else
// TODO: kernel debugger communication is not yet implemented on Linux
ShowMessages("err, switching cores is not supported on Linux yet\n\n");
#endif
}
}

View file

@ -163,7 +163,7 @@ HyperDbgPtMmapSendRequest(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest)
return FALSE;
}
AssertShowMessageReturnStmt(g_DeviceHandle, ASSERT_MESSAGE_DRIVER_NOT_LOADED, AssertReturnFalse);
AssertShowMessageReturnStmt(g_IsHyperTraceModuleLoaded, g_DeviceHandle, ASSERT_MESSAGE_HYPERTRACE_NOT_LOADED, ASSERT_MESSAGE_DRIVER_NOT_LOADED, AssertReturnFalse);
Status = DeviceIoControl(
g_DeviceHandle, // Handle to device