fix IPT kernel structure based on the new model
Some checks are pending
vs2026-ci / win-amd64-build (debug, x64) (push) Waiting to run
vs2026-ci / win-amd64-build (release, x64) (push) Waiting to run
vs2026-ci / Deploy release (push) Blocked by required conditions

This commit is contained in:
sina 2026-07-01 19:44:15 +02:00
parent 72a8aa325f
commit 5bccbff58b
9 changed files with 86 additions and 95 deletions

View file

@ -155,23 +155,23 @@ PtFilter(UINT32 ProcessId, UINT64 Start, UINT64 End)
HYPERTRACE_PT_OPERATION_PACKETS Op = {};
Op.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER;
Op.TraceUser = 1;
Op.TargetProcessId = ProcessId;
Op.FilterOptions.TraceUser = 1;
Op.EnableOptions.Pid = ProcessId;
if (End > Start)
{
Op.NumAddrRanges = 1;
Op.AddrRanges[0].Start = Start;
Op.AddrRanges[0].End = End;
Op.FilterOptions.NumAddrRanges = 1;
Op.FilterOptions.AddrRanges[0].Start = Start;
Op.FilterOptions.AddrRanges[0].End = End;
}
if (!hyperdbg_u_pt_operation(&Op))
return FALSE;
printf("[+] PT filter: cr3=0x%llx traceuser=%u ranges=%u buffer=0x%llx\n",
(unsigned long long)Op.TargetCr3,
Op.TraceUser,
Op.NumAddrRanges,
(unsigned long long)Op.EnableOptions.Cr3,
Op.FilterOptions.TraceUser,
Op.FilterOptions.NumAddrRanges,
(unsigned long long)Op.BufferSize);
return TRUE;
}

View file

@ -1670,13 +1670,13 @@ DrvDispatchHyperTraceIoControl(PIRP Irp, PIO_STACK_LOCATION IrpStack, BOOLEAN *
// 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)
if (HyperTracePtOperationRequest->EnableOptions.Pid != 0 &&
HyperTracePtOperationRequest->EnableOptions.Cr3 == 0)
{
HyperTracePtOperationRequest->TargetCr3 =
DrvResolvePtTargetCr3(HyperTracePtOperationRequest->TargetProcessId,
(BOOLEAN)(HyperTracePtOperationRequest->TraceUser != 0),
(BOOLEAN)(HyperTracePtOperationRequest->TraceKernel != 0));
HyperTracePtOperationRequest->EnableOptions.Cr3 =
DrvResolvePtTargetCr3(HyperTracePtOperationRequest->EnableOptions.Pid,
(BOOLEAN)(HyperTracePtOperationRequest->FilterOptions.TraceUser != 0),
(BOOLEAN)(HyperTracePtOperationRequest->FilterOptions.TraceKernel != 0));
}
//

View file

@ -36,26 +36,26 @@ HyperTracePtBuildConfig(const HYPERTRACE_PT_OPERATION_PACKETS * Req,
// If the request specifies neither user nor kernel, default to both
// (matches LBR behaviour when filter is empty).
//
if (Req->TraceUser || Req->TraceKernel)
if (Req->FilterOptions.TraceUser || Req->FilterOptions.TraceKernel)
{
OutCfg->TraceUser = (Req->TraceUser != 0) ? TRUE : FALSE;
OutCfg->TraceKernel = (Req->TraceKernel != 0) ? TRUE : FALSE;
OutCfg->TraceUser = (Req->FilterOptions.TraceUser != 0) ? TRUE : FALSE;
OutCfg->TraceKernel = (Req->FilterOptions.TraceKernel != 0) ? TRUE : FALSE;
}
OutCfg->TargetCr3 = Req->TargetCr3;
OutCfg->TargetCr3 = Req->EnableOptions.Cr3;
if (Req->BufferSize != 0)
{
OutCfg->BufferSize = Req->BufferSize;
}
OutCfg->NumAddrRanges = Req->NumAddrRanges;
OutCfg->NumAddrRanges = Req->FilterOptions.NumAddrRanges;
if (OutCfg->NumAddrRanges > PT_MAX_ADDR_RANGES)
OutCfg->NumAddrRanges = PT_MAX_ADDR_RANGES;
for (Copy = 0; Copy < OutCfg->NumAddrRanges; Copy++)
{
OutCfg->AddrRanges[Copy] = Req->AddrRanges[Copy];
OutCfg->AddrRanges[Copy] = Req->FilterOptions.AddrRanges[Copy];
}
}
@ -284,6 +284,7 @@ HyperTracePtSize(HYPERTRACE_PT_OPERATION_PACKETS * HyperTraceOperationRequest)
return FALSE;
ProcessorsCount = KeQueryActiveProcessorCount(0);
if (ProcessorsCount > PT_MAX_CPUS_FOR_MMAP)
ProcessorsCount = PT_MAX_CPUS_FOR_MMAP;
@ -405,37 +406,37 @@ HyperTracePtFlush(HYPERTRACE_PT_OPERATION_PACKETS * HyperTraceOperationRequest)
BOOLEAN
HyperTracePtFilter(HYPERTRACE_PT_OPERATION_PACKETS * Req)
{
PT_FILTER_OPTIONS FilterOptions = {0};
BOOLEAN WasEnabled = g_ProcessorTraceEnabled;
BOOLEAN BufferChanged = FALSE;
UINT64 ExistingSize = 0;
UINT32 Copy;
PT_APPLY_CORE_FILTER_REQUEST ApplyCoreFilterReq = {0};
BOOLEAN WasEnabled = g_ProcessorTraceEnabled;
BOOLEAN BufferChanged = FALSE;
UINT64 ExistingSize = 0;
//
// Translate the user-mode packet into PT_FILTER_OPTIONS — the narrow
// Copy the user-mode request into a kernel-mode PT_APPLY_CORE_FILTER_REQUEST
//
memcpy(&ApplyCoreFilterReq.FilterOptions, &Req->FilterOptions, sizeof(PT_FILTER_OPTIONS));
memcpy(&ApplyCoreFilterReq.EnableOptions, &Req->EnableOptions, sizeof(PT_ENABLE_OPTIONS));
ApplyCoreFilterReq.BufferSize = Req->BufferSize;
//
// Translate the user-mode packet into PT_FILTER_OPTIONS - the narrow
// surface PtFilter operates on. Default to user+kernel when the
// caller specified neither (matches LBR's empty-filter behaviour).
//
if (Req->TraceUser || Req->TraceKernel)
if (Req->FilterOptions.TraceUser || Req->FilterOptions.TraceKernel)
{
FilterOptions.TraceUser = (Req->TraceUser != 0) ? TRUE : FALSE;
FilterOptions.TraceKernel = (Req->TraceKernel != 0) ? TRUE : FALSE;
ApplyCoreFilterReq.FilterOptions.TraceUser = (Req->FilterOptions.TraceUser != 0) ? TRUE : FALSE;
ApplyCoreFilterReq.FilterOptions.TraceKernel = (Req->FilterOptions.TraceKernel != 0) ? TRUE : FALSE;
}
else
{
FilterOptions.TraceUser = TRUE;
FilterOptions.TraceKernel = TRUE;
}
FilterOptions.TargetCr3 = Req->TargetCr3;
FilterOptions.BufferSize = Req->BufferSize;
FilterOptions.NumAddrRanges = Req->NumAddrRanges;
if (FilterOptions.NumAddrRanges > PT_MAX_ADDR_RANGES)
FilterOptions.NumAddrRanges = PT_MAX_ADDR_RANGES;
for (Copy = 0; Copy < FilterOptions.NumAddrRanges; Copy++)
{
FilterOptions.AddrRanges[Copy] = Req->AddrRanges[Copy];
ApplyCoreFilterReq.FilterOptions.TraceUser = TRUE;
ApplyCoreFilterReq.FilterOptions.TraceKernel = TRUE;
}
if (Req->FilterOptions.NumAddrRanges > PT_MAX_ADDR_RANGES)
ApplyCoreFilterReq.FilterOptions.NumAddrRanges = PT_MAX_ADDR_RANGES;
//
// Decide between fast (filter-only) and slow (buffer-resize) paths.
//
@ -443,7 +444,8 @@ HyperTracePtFilter(HYPERTRACE_PT_OPERATION_PACKETS * Req)
{
ExistingSize = g_PtStateList[0].Config.BufferSize;
}
if (FilterOptions.BufferSize != 0 && FilterOptions.BufferSize != ExistingSize)
if (ApplyCoreFilterReq.BufferSize != 0 && ApplyCoreFilterReq.BufferSize != ExistingSize)
{
BufferChanged = TRUE;
}
@ -495,8 +497,8 @@ HyperTracePtFilter(HYPERTRACE_PT_OPERATION_PACKETS * Req)
// unchanged. Force FilterOptions.BufferSize=0 so PtFilter on each core
// keeps the buffer that's already allocated, then broadcast.
//
FilterOptions.BufferSize = 0;
BroadcastFilterPtOnAllCores(&FilterOptions);
ApplyCoreFilterReq.BufferSize = 0;
BroadcastFilterPtOnAllCores(&ApplyCoreFilterReq);
}
if (Req != NULL)

View file

@ -153,10 +153,12 @@ BroadcastFlushPtOnAllCores()
* pointer is passed to every per-core DPC; KeGenericCallDpc is
* synchronous so the caller's storage is valid throughout.
*
* @param FilterRequest Pointer to a PT_APPLY_CORE_FILTER_REQUEST structure containing the filter options to apply on all cores
*
* @return VOID
*/
VOID
BroadcastFilterPtOnAllCores(PT_FILTER_OPTIONS * FilterOptions)
BroadcastFilterPtOnAllCores(PT_APPLY_CORE_FILTER_REQUEST * FilterRequest)
{
KeGenericCallDpc(DpcRoutineFilterPt, (PVOID)FilterOptions);
KeGenericCallDpc(DpcRoutineFilterPt, (PVOID)FilterRequest);
}

View file

@ -330,7 +330,7 @@ DpcRoutineFlushPt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOI
/**
* @brief Broadcast applying a PT filter to all cores.
*
* DeferredContext carries the PT_FILTER_OPTIONS * supplied by the
* DeferredContext carries the PT_APPLY_CORE_FILTER_REQUEST * supplied by the
* broadcaster; PtFilter writes the user-tunable fields into the
* current CPU's per-CPU PT_TRACE_CONFIG and reprograms PT MSRs.
*/
@ -339,7 +339,7 @@ DpcRoutineFilterPt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVO
{
UNREFERENCED_PARAMETER(Dpc);
PtFilter((const PT_FILTER_OPTIONS *)DeferredContext);
PtFilter((const PT_APPLY_CORE_FILTER_REQUEST *)DeferredContext);
// ------------------------------------------------------------------------------
// Synchronize the end of this routine with the caller

View file

@ -1399,14 +1399,14 @@ PtDump()
* handles that case before broadcasting.
*/
VOID
PtFilter(const PT_FILTER_OPTIONS * FilterOptions)
PtFilter(const PT_APPLY_CORE_FILTER_REQUEST * FilterRequest)
{
UINT32 CurrentCore;
PT_PER_CPU * Cpu;
UINT32 i;
PT_CAPABILITIES Caps = {0};
if (g_PtStateList == NULL || FilterOptions == NULL)
if (g_PtStateList == NULL || FilterRequest == NULL)
return;
if (PtEngineQueryCapabilities(&Caps) != 0)
@ -1429,42 +1429,42 @@ PtFilter(const PT_FILTER_OPTIONS * FilterOptions)
//
// Apply only the user-tunable fields to this CPU's per-CPU config.
//
Cpu->Config.TraceUser = FilterOptions->TraceUser;
Cpu->Config.TraceKernel = FilterOptions->TraceKernel;
Cpu->Config.TraceUser = (BOOLEAN)FilterRequest->FilterOptions.TraceUser;
Cpu->Config.TraceKernel = (BOOLEAN)FilterRequest->FilterOptions.TraceKernel;
if (FilterOptions->TargetCr3 != 0 && !Caps.Cr3Filtering)
if (FilterRequest->EnableOptions.Cr3 != 0 && !Caps.Cr3Filtering)
{
LogInfo("PT: CR3 filtering requested but not supported by CPU\n");
Cpu->Config.TargetCr3 = 0;
}
else
{
Cpu->Config.TargetCr3 = FilterOptions->TargetCr3;
Cpu->Config.TargetCr3 = FilterRequest->EnableOptions.Cr3;
}
if (FilterOptions->NumAddrRanges > Caps.NumAddrRanges)
if (FilterRequest->FilterOptions.NumAddrRanges > Caps.NumAddrRanges)
{
LogInfo("PT: requested %u IP filter ranges, but CPU only supports %u\n", FilterOptions->NumAddrRanges, Caps.NumAddrRanges);
LogInfo("PT: requested %u IP filter ranges, but CPU only supports %u\n", FilterRequest->FilterOptions.NumAddrRanges, Caps.NumAddrRanges);
Cpu->Config.NumAddrRanges = Caps.NumAddrRanges;
}
else if (FilterOptions->NumAddrRanges > 0 && !Caps.IpFiltering)
else if (FilterRequest->FilterOptions.NumAddrRanges > 0 && !Caps.IpFiltering)
{
LogInfo("PT: IP filtering requested but not supported by CPU\n");
Cpu->Config.NumAddrRanges = 0;
}
else
{
Cpu->Config.NumAddrRanges = FilterOptions->NumAddrRanges;
Cpu->Config.NumAddrRanges = FilterRequest->FilterOptions.NumAddrRanges;
}
if (FilterOptions->BufferSize != 0)
if (FilterRequest->BufferSize != 0)
{
Cpu->Config.BufferSize = FilterOptions->BufferSize;
Cpu->Config.BufferSize = FilterRequest->BufferSize;
}
for (i = 0; i < PT_MAX_ADDR_RANGES; i++)
{
Cpu->Config.AddrRanges[i] = FilterOptions->AddrRanges[i];
Cpu->Config.AddrRanges[i] = FilterRequest->FilterOptions.AddrRanges[i];
}
//

View file

@ -50,4 +50,4 @@ VOID
BroadcastFlushPtOnAllCores();
VOID
BroadcastFilterPtOnAllCores(PT_FILTER_OPTIONS * FilterOptions);
BroadcastFilterPtOnAllCores(PT_APPLY_CORE_FILTER_REQUEST * FilterRequest);

View file

@ -12,42 +12,10 @@
*/
#pragma once
//////////////////////////////////////////////////
// Constants //
//////////////////////////////////////////////////
//
// Pool tag for PT contiguous allocations (ASCII "PtHd")
//
#define POOL_TAG_PT 'dHtP'
//////////////////////////////////////////////////
// Structures //
//////////////////////////////////////////////////
/**
* @brief Narrow input descriptor for PtFilter.
*
* These are the only fields a caller is allowed to set per-CPU
* when reconfiguring an active PT trace. Engine-internal options
* (BranchEn, TscEn, MtcEn, CycEn, RetCompression, *Freq, etc.)
* stay under the engine's control and are NOT exposed here.
*
* BufferSize == 0 means "keep whatever the per-CPU slot already
* has" — pure filter changes don't touch the ToPA / output /
* overflow buffers and can run from a DPC.
*/
typedef struct _PT_FILTER_OPTIONS
{
BOOLEAN TraceUser;
BOOLEAN TraceKernel;
UINT64 TargetCr3;
UINT64 BufferSize;
UINT32 NumAddrRanges;
PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES];
} PT_FILTER_OPTIONS, *PPT_FILTER_OPTIONS;
/**
* @brief Per-CPU bookkeeping for the user-mode mmap surface.
*
@ -63,6 +31,17 @@ typedef struct _PT_USER_MAPPING
} PT_USER_MAPPING, *PPT_USER_MAPPING;
/**
* @brief PT apply core filter requests.
*/
typedef struct _PT_APPLY_CORE_FILTER_REQUEST
{
PT_FILTER_OPTIONS FilterOptions;
PT_ENABLE_OPTIONS EnableOptions;
UINT64 BufferSize; /* Output buffer size (0 = default / PT_DEFAULT_BUFFER_SIZE) */
} PT_APPLY_CORE_FILTER_REQUEST, *PPT_APPLY_CORE_FILTER_REQUEST;
//////////////////////////////////////////////////
// Functions //
//////////////////////////////////////////////////
@ -97,14 +76,14 @@ PtFlush();
//
// LBR-style filter wrapper, one CPU at a time. Mirrors LbrFilter in shape:
// caller passes a PT_FILTER_OPTIONS describing only the user-tunable bits
// caller passes a PT_APPLY_CORE_FILTER_REQUEST describing only the user-tunable bits
// (TraceUser, TraceKernel, TargetCr3, BufferSize, NumAddrRanges, AddrRanges),
// and PtFilter handles the stop / config-update / start sequence on the
// CURRENT CPU. Engine-internal config (BranchEn, TscEn, etc.) is left
// untouched in the per-CPU PT_TRACE_CONFIG.
//
VOID
PtFilter(const PT_FILTER_OPTIONS * FilterOptions);
PtFilter(const PT_APPLY_CORE_FILTER_REQUEST * FilterRequest);
//
// PASSIVE_LEVEL helpers — call before / after the per-core DPC broadcasts.

View file

@ -1355,6 +1355,7 @@ typedef enum _HYPERTRACE_PT_OPERATION_REQUEST_TYPE
HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE,
HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE,
HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME,
HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE,
HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH,
HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DUMP,
HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER,
@ -1395,6 +1396,7 @@ typedef struct _PT_FILTER_OPTIONS
UINT32 TraceUser : 1; // Trace user mode
UINT32 TraceKernel : 1; // Trace kernel mode
UINT8 NumAddrRanges; /* Number of valid AddrRanges entries */
PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]; // Address ranges to filter by
} PT_FILTER_OPTIONS, *PPT_FILTER_OPTIONS;
@ -1442,6 +1444,12 @@ typedef struct _HYPERTRACE_PT_OPERATION_PACKETS
//
PT_PACKET_OPTIONS PacketOptions; /* Options for PT packets */
//
// Size
//
UINT32 NumCpus; /* CPUs populated in BytesPerCpu */
UINT64 BytesPerCpu[PT_MAX_CPUS_FOR_MMAP];
} HYPERTRACE_PT_OPERATION_PACKETS, *PHYPERTRACE_PT_OPERATION_PACKETS;
/**