added tracing.h and tracing.c

This commit is contained in:
Hari Mishal 2026-02-03 17:29:37 +01:00
parent 19437cd694
commit ef705fccc7
2 changed files with 648 additions and 13 deletions

View file

@ -2,11 +2,522 @@
* @file Tracing.c
* @author Hari Mishal (harimishal6@gmail.com)
* @brief Message logging and tracing implementation
* @details
* @details Modified from LIBIHT project (Thomasaon Zhao et al) with Windows style updates.
* @version 0.18
* @date 2025-12-02
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
#include "Tracing.h"
//////////////////////////////////////////////////
// Global Definitions //
//////////////////////////////////////////////////
ULONGLONG lbr_capacity = 0;
LIST_ENTRY lbr_state_head;
KSPIN_LOCK lbr_state_lock;
// Typical Intel LBR capacities based on CPU model
// This is a subset; you can expand this as needed
struct cpu_lbr_map cpu_lbr_maps[] = {
{0x3E, 16}, // Ivy Bridge
{0x3C, 16}, // Haswell
{0x45, 16}, // Haswell
{0x46, 16}, // Haswell
{0x3F, 16}, // Haswell-E
{0x3D, 32}, // Broadwell
{0x47, 32}, // Broadwell
{0x4E, 32}, // Skylake
{0x5E, 32}, // Skylake
{0x8E, 32}, // Kaby/Coffee/Whiskey Lake
{0x9E, 32} // Kaby/Coffee Lake
};
// Note: MAX_IRQL_LEN is removed in favor of native KIRQL.
void
XWriteMsr(
ULONG Msr,
ULONGLONG Val)
{
__writemsr(Msr, Val);
}
void
LbrGetLbr(struct lbr_state * State)
{
ULONG i;
ULONGLONG DbgCtlMsr;
KIRQL OldIrql;
xrdmsr(MSR_IA32_DEBUGCTLMSR, &DbgCtlMsr);
DbgCtlMsr &= ~DEBUGCTLMSR_LBR;
xwrmsr(MSR_IA32_DEBUGCTLMSR, DbgCtlMsr);
xacquire_lock(lbr_state_lock, &OldIrql);
xrdmsr(MSR_LBR_SELECT, &State->config.lbr_select);
xrdmsr(MSR_LBR_TOS, &State->data->lbr_tos);
for (i = 0; i < (ULONG)lbr_capacity; i++)
{
xrdmsr(MSR_LBR_NHM_FROM + i, &State->data->entries[i].from);
xrdmsr(MSR_LBR_NHM_TO + i, &State->data->entries[i].to);
}
xrelease_lock(&lbr_state_lock, &OldIrql);
}
void
LbrPutLbr(struct lbr_state * State)
{
ULONG i;
ULONGLONG DbgCtlMsr;
KIRQL OldIrql;
xacquire_lock(&lbr_state_lock, &OldIrql);
xwrmsr(MSR_LBR_SELECT, State->config.lbr_select);
xwrmsr(MSR_LBR_TOS, State->data->lbr_tos);
for (i = 0; i < (ULONG)lbr_capacity; i++)
{
xwrmsr(MSR_LBR_NHM_FROM + i, State->data->entries[i].from);
xwrmsr(MSR_LBR_NHM_TO + i, State->data->entries[i].to);
}
xrelease_lock(&lbr_state_lock, &OldIrql);
xrdmsr(MSR_IA32_DEBUGCTLMSR, &DbgCtlMsr);
DbgCtlMsr |= DEBUGCTLMSR_LBR;
xwrmsr(MSR_IA32_DEBUGCTLMSR, DbgCtlMsr);
}
void
LbrFlushLbr(
VOID)
{
ULONG i;
ULONGLONG DbgCtlMsr;
KIRQL OldIrql;
xlock_core(&OldIrql);
// Disable LBR
xprintdbg("LIBIHT-COM: Flush LBR on cpu core: %d\n", xcoreid());
xrdmsr(MSR_IA32_DEBUGCTLMSR, &DbgCtlMsr);
DbgCtlMsr &= ~DEBUGCTLMSR_LBR;
xwrmsr(MSR_IA32_DEBUGCTLMSR, DbgCtlMsr);
// Flush LBR registers
xwrmsr(MSR_LBR_SELECT, 0);
xwrmsr(MSR_LBR_TOS, 0);
for (i = 0; i < lbr_capacity; i++)
{
xwrmsr(MSR_LBR_NHM_FROM + i, 0);
xwrmsr(MSR_LBR_NHM_TO + i, 0);
}
xrelease_core(&OldIrql);
}
NTSTATUS
LbrEnableLbr(
struct lbr_ioctl_request * Request)
{
struct lbr_state * State;
State = LbrFindLbrState(Request->lbr_config.pid);
if (State)
{
xprintdbg("LIBIHT-COM: LBR already enabled for pid %d\n",
Request->lbr_config.pid);
return STATUS_ALREADY_REGISTERED;
}
State = LbrCreateLbrState();
if (State == NULL)
{
xprintdbg("LIBIHT-COM: Create LBR state failed\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
// Setup config fields for LBR state
State->parent = NULL;
State->config.pid = Request->lbr_config.pid ? Request->lbr_config.pid : xgetcurrent_pid();
State->config.lbr_select = Request->lbr_config.lbr_select ? Request->lbr_config.lbr_select : LBR_SELECT;
LbrInsertLbrState(State);
// If the requesting process is the current process, trace it right away
if (State->config.pid == xgetcurrent_pid())
LbrPutLbr(State);
return STATUS_SUCCESS;
}
NTSTATUS
LbrDisableLbr(
struct lbr_ioctl_request * Request)
{
struct lbr_state * State;
State = LbrFindLbrState(Request->lbr_config.pid);
if (State == NULL)
{
xprintdbg("LIBIHT-COM: LBR not enabled for pid %d\n",
Request->lbr_config.pid);
return STATUS_NOT_FOUND;
}
if (State->config.pid == xgetcurrent_pid())
LbrGetLbr(State);
LbrRemoveLbrState(State);
return STATUS_SUCCESS;
}
NTSTATUS
LbrDumpLbr(
struct lbr_ioctl_request * Request)
{
ULONGLONG i, BytesLeft;
struct lbr_state * State;
struct lbr_data ReqBuf;
KIRQL OldIrql;
State = LbrFindLbrState(Request->lbr_config.pid);
if (State == NULL)
{
xprintdbg("LIBIHT-COM: LBR not enabled for pid %d\n",
Request->lbr_config.pid);
return STATUS_NOT_FOUND;
}
// Examine if the current process is the owner of the LBR state
if (State->config.pid == xgetcurrent_pid())
{
xprintdbg("LIBIHT-COM: Dump LBR for current process\n");
// Get fresh LBR info
LbrGetLbr(State);
LbrPutLbr(State);
}
xacquire_lock(lbr_state_lock, &OldIrql);
// Dump the LBR state to debug logs
xprintdbg("PROC_PID: %d\n", State->config.pid);
xprintdbg("MSR_LBR_SELECT: 0x%llx\n", State->config.lbr_select);
xprintdbg("MSR_LBR_TOS: %lld\n", State->data->lbr_tos);
for (i = 0; i < lbr_capacity; i++)
{
xprintdbg("MSR_LBR_NHM_FROM[%2d]: 0x%llx\n", (ULONG)i, State->data->entries[i].from);
xprintdbg("MSR_LBR_NHM_TO [%2d]: 0x%llx\n", (ULONG)i, State->data->entries[i].to);
}
xprintdbg("LIBIHT-COM: LBR info for cpuid: %d\n", xcoreid());
// Dump the LBR data to userspace buffer
if (Request->buffer)
{
BytesLeft = xcopy_from_user(&ReqBuf, Request->buffer, sizeof(struct lbr_data));
if (BytesLeft)
{
xprintdbg("LIBIHT-COM: Copy LBR data from user failed\n");
xrelease_lock(lbr_state_lock, &OldIrql);
return STATUS_UNSUCCESSFUL;
}
ReqBuf.lbr_tos = State->data->lbr_tos;
if (ReqBuf.entries)
{
BytesLeft = xcopy_to_user(ReqBuf.entries,
State->data->entries,
lbr_capacity * sizeof(struct lbr_stack_entry));
if (BytesLeft)
{
xprintdbg("LIBIHT-COM: Copy LBR data to user failed\n");
xrelease_lock(lbr_state_lock, &OldIrql);
return STATUS_UNSUCCESSFUL;
}
}
BytesLeft = xcopy_to_user(Request->buffer, &ReqBuf, sizeof(struct lbr_data));
if (BytesLeft)
{
xprintdbg("LIBIHT-COM: Copy LBR data to user failed\n");
xrelease_lock(lbr_state_lock, &OldIrql);
return STATUS_UNSUCCESSFUL;
}
}
xrelease_lock(lbr_state_lock, &OldIrql);
return STATUS_SUCCESS;
}
NTSTATUS
LbrConfigLbr(
struct lbr_ioctl_request * Request)
{
struct lbr_state * State;
State = LbrFindLbrState(Request->lbr_config.pid);
if (State == NULL)
{
xprintdbg("LIBIHT-COM: LBR not enabled for pid %d\n",
Request->lbr_config.pid);
return STATUS_NOT_FOUND;
}
if (State->config.pid == xgetcurrent_pid())
{
LbrGetLbr(State);
State->config.lbr_select = Request->lbr_config.lbr_select;
LbrPutLbr(State);
}
else
{
State->config.lbr_select = Request->lbr_config.lbr_select;
}
return STATUS_SUCCESS;
}
struct lbr_state *
LbrCreateLbrState(
VOID)
{
struct lbr_state * State;
struct lbr_data * Data;
struct lbr_stack_entry * Entries;
State = xmalloc(sizeof(struct lbr_state));
if (State == NULL)
return NULL;
Data = xmalloc(sizeof(struct lbr_data));
if (Data == NULL)
{
xfree(State);
return NULL;
}
SIZE_T TotalEntrySize = (SIZE_T)sizeof(struct lbr_stack_entry) * lbr_capacity;
Entries = xmalloc(TotalEntrySize);
if (Entries == NULL)
{
xfree(Data);
xfree(State);
return NULL;
}
xmemset(State, sizeof(struct lbr_state));
xmemset(Data, sizeof(struct lbr_data));
xmemset(Entries, TotalEntrySize);
State->data = Data;
Data->entries = Entries;
return State;
}
struct lbr_state *
LbrFindLbrState(ULONG Pid)
{
KIRQL OldIrql;
struct lbr_state * RetState = NULL;
PLIST_ENTRY Link;
xacquire_lock(&lbr_state_lock, &OldIrql);
// Iterating through LIST_ENTRY correctly
for (Link = lbr_state_head.Flink; Link != &lbr_state_head; Link = Link->Flink)
{
struct lbr_state * Curr = CONTAINING_RECORD(Link, struct lbr_state, list);
if (Pid != 0 && Curr->config.pid == Pid)
{
RetState = Curr;
break;
}
}
xrelease_lock(&lbr_state_lock, &OldIrql);
return RetState;
}
void
LbrInsertLbrState(
struct lbr_state * NewState)
{
KIRQL OldIrql;
if (NewState == NULL)
return;
xacquire_lock(lbr_state_lock, &OldIrql);
xprintdbg("LIBIHT-COM: Insert LBR state for pid %d\n", NewState->config.pid);
xlist_add(NewState->list, lbr_state_head);
xrelease_lock(lbr_state_lock, &OldIrql);
}
void
LbrRemoveLbrState(
struct lbr_state * OldState)
{
KIRQL OldIrql;
if (OldState == NULL)
return;
xacquire_lock(lbr_state_lock, &OldIrql);
xprintdbg("LIBIHT-COM: Remove LBR state for pid %d\n", OldState->config.pid);
xlist_del(OldState->list);
xfree(OldState->data->entries);
xfree(OldState->data);
xfree(OldState);
xrelease_lock(lbr_state_lock, &OldIrql);
}
void
LbrFreeLbrStatList(VOID)
{
KIRQL OldIrql;
struct lbr_state * CurrState;
PLIST_ENTRY CurrLink;
xacquire_lock(&lbr_state_lock, &OldIrql);
CurrLink = lbr_state_head.Flink;
while (CurrLink != &lbr_state_head)
{
CurrState = CONTAINING_RECORD(CurrLink, struct lbr_state, list);
CurrLink = CurrLink->Flink; // Get next before deleting
xlist_del(CurrState->list);
xfree(CurrState->data->entries);
xfree(CurrState->data);
xfree(CurrState);
}
xrelease_lock(&lbr_state_lock, &OldIrql);
}
NTSTATUS
LbrIoctlHandler(
struct xioctl_request * Request)
{
NTSTATUS Status = STATUS_SUCCESS;
xprintdbg("LIBIHT-COM: LBR ioctl command %d.\n", Request->cmd);
switch (Request->cmd)
{
case LIBIHT_IOCTL_ENABLE_LBR:
Status = LbrEnableLbr(&Request->body.lbr);
break;
case LIBIHT_IOCTL_DISABLE_LBR:
Status = LbrDisableLbr(&Request->body.lbr);
break;
case LIBIHT_IOCTL_DUMP_LBR:
Status = LbrDumpLbr(&Request->body.lbr);
break;
case LIBIHT_IOCTL_CONFIG_LBR:
Status = LbrConfigLbr(&Request->body.lbr);
break;
default:
xprintdbg("LIBIHT-COM: Invalid LBR ioctl command\n");
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
return Status;
}
void
LbrCswitchHandler(
ULONG PrevPid,
ULONG NextPid)
{
struct lbr_state *PrevState, *NextState;
PrevState = LbrFindLbrState(PrevPid);
NextState = LbrFindLbrState(NextPid);
if (PrevState)
{
xprintdbg("LIBIHT-COM: LBR context switch from pid %d on cpu core %d\n",
PrevState->config.pid,
xcoreid());
LbrGetLbr(PrevState);
}
if (NextState)
{
xprintdbg("LIBIHT-COM: LBR context switch to pid %d on cpu core %d\n",
NextState->config.pid,
xcoreid());
LbrPutLbr(NextState);
}
}
void
LbrNewprocHandler(
ULONG ParentPid,
ULONG ChildPid)
{
struct lbr_state *ParentState, *ChildState;
KIRQL OldIrql;
ParentState = LbrFindLbrState(ParentPid);
if (ParentState == NULL)
return;
xprintdbg("LIBIHT-COM: LBR new child process pid %d, parent pid %d\n",
ChildPid,
ParentPid);
ChildState = LbrCreateLbrState();
if (ChildState == NULL)
return;
xacquire_lock(lbr_state_lock, &OldIrql);
ChildState->parent = ParentState;
ChildState->config.pid = ChildPid;
ChildState->config.lbr_select = ParentState->config.lbr_select;
xmemcpy(ChildState->data,
ParentState->data,
sizeof(struct lbr_data) + lbr_capacity * sizeof(struct lbr_stack_entry));
xrelease_lock(lbr_state_lock, &OldIrql);
LbrInsertLbrState(ChildState);
if (ChildPid == xgetcurrent_pid())
LbrPutLbr(ChildState);
}
NTSTATUS
LbrCheck(VOID)
{
ULONG a, b, c, d;
ULONG Family, Model;
ULONGLONG i;
xcpuid(1, &a, &b, &c, &d);
Family = ((a >> 8) & 0xF) + ((a >> 20) & 0xFF);
Model = ((a >> 4) & 0xF) | ((a >> 12) & 0xF0);
for (i = 0; i < sizeof(cpu_lbr_maps) / sizeof(cpu_lbr_maps[0]); ++i)
{
if (Model == cpu_lbr_maps[i].model)
{
lbr_capacity = cpu_lbr_maps[i].lbr_capacity;
break;
}
}
if (lbr_capacity == 0)
return STATUS_NOT_SUPPORTED;
return STATUS_SUCCESS;
}

View file

@ -1,25 +1,149 @@
/**
* @file Tracing.h
* @author Sina Karvandi (sina@hyperdbg.org)
* @brief Headers of Message logging and tracing
* @details
* @version 0.1
* @date 2020-04-11
* @author Hari Mishal (harimishal6@gmail.com)
* @brief Message logging and tracing implementation
* @details Modified from LIBIHT project (Thomasaon Zhao et al) with Windows style updates.
* @version 0.18
* @date 2025-12-02
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#pragma once
//////////////////////////////////////////////////
// Global Variables //
//////////////////////////////////////////////////
#include "pch.h"
// Intel MSR Constants
#define MSR_IA32_DEBUGCTLMSR 0x000001D9
#define DEBUGCTLMSR_LBR (1ULL << 0)
#define MSR_LBR_SELECT 0x000001C8
#define MSR_LBR_TOS 0x000001C9
#define MSR_LBR_NHM_FROM 0x00000680
#define MSR_LBR_NHM_TO 0x000006C0
#define LBR_SELECT 0x0
//////////////////////////////////////////////////
// Structures //
// Structures //
//////////////////////////////////////////////////
struct lbr_stack_entry
{
ULONGLONG from;
ULONGLONG to;
};
struct lbr_data
{
ULONGLONG lbr_tos;
struct lbr_stack_entry * entries;
};
struct lbr_config
{
ULONG pid;
ULONGLONG lbr_select;
};
struct lbr_state
{
struct lbr_config config;
struct lbr_data * data;
struct lbr_state * parent;
LIST_ENTRY list;
};
struct lbr_ioctl_request
{
struct lbr_config lbr_config;
struct lbr_data * buffer;
};
struct xioctl_request
{
ULONG cmd;
union
{
struct lbr_ioctl_request lbr;
} body;
};
// IOCTL Commands
#define LIBIHT_IOCTL_ENABLE_LBR 0x1
#define LIBIHT_IOCTL_DISABLE_LBR 0x2
#define LIBIHT_IOCTL_DUMP_LBR 0x3
#define LIBIHT_IOCTL_CONFIG_LBR 0x4
//////////////////////////////////////////////////
// Functions //
// Platform Wrappers //
//////////////////////////////////////////////////
#define xmalloc(sz) PlatformMemAllocateZeroedNonPagedPool(sz)
#define xfree(p) PlatformMemFreePool(p)
#define xmemset(ptr, sz) RtlZeroMemory(ptr, sz)
#define xmemcpy RtlCopyMemory
#define xrdmsr(msr, pval) (*(pval) = __readmsr(msr))
#define xwrmsr(msr, val) __writemsr(msr, val)
#define xprintdbg(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, __VA_ARGS__)
#define xcoreid() KeGetCurrentProcessorNumber()
#define xgetcurrent_pid() (ULONG)(ULONG_PTR) PsGetCurrentProcessId()
// List Handling
#define xlist_next(ptr) (ptr)->Flink
#define xlist_add(entry, head) InsertTailList(&(head), &(entry))
#define xlist_del(entry) RemoveEntryList(&(entry))
// Spinlock & IRQL (Using Windows Native to fix VCR001)
#define xacquire_lock(Lock, Irql) KeAcquireSpinLock((PKSPIN_LOCK)(Lock), (Irql))
#define xrelease_lock(Lock, Irql) KeReleaseSpinLock((PKSPIN_LOCK)(Lock), *(Irql))
#define xlock_core(irql) KeRaiseIrql(DISPATCH_LEVEL, irql)
#define xrelease_core(irql) KeLowerIrql(*(irql))
// Buffer Copy
#define xcopy_from_user(dest, src, sz) (RtlCopyMemory(dest, src, sz), 0)
#define xcopy_to_user(dest, src, sz) (RtlCopyMemory(dest, src, sz), 0)
// CPUID (Fixed C6001: initialized cpuInfo)
#define xcpuid(code, a, b, c, d) \
{ \
int cpuInfo[4] = {0}; \
__cpuid(cpuInfo, code); \
*a = cpuInfo[0]; \
*b = cpuInfo[1]; \
*c = cpuInfo[2]; \
*d = cpuInfo[3]; \
}
//////////////////////////////////////////////////
// Global Variables //
//////////////////////////////////////////////////
extern ULONGLONG lbr_capacity;
extern LIST_ENTRY lbr_state_head;
extern KSPIN_LOCK lbr_state_lock; // Standardized to KSPIN_LOCK
struct cpu_lbr_map
{
ULONG model;
ULONG lbr_capacity;
};
extern struct cpu_lbr_map cpu_lbr_maps[];
//////////////////////////////////////////////////
// Prototypes //
//////////////////////////////////////////////////
void
LbrGetLbr(struct lbr_state * State);
void
LbrPutLbr(struct lbr_state * State);
struct lbr_state * LbrCreateLbrState(VOID);
struct lbr_state *
LbrFindLbrState(ULONG Pid);
void
LbrInsertLbrState(struct lbr_state * NewState);
void
LbrRemoveLbrState(struct lbr_state * OldState);
void LbrFreeLbrStatList(VOID);
NTSTATUS LbrCheck(VOID);