From d6f56065d5f73d34cda558543545b21379dfdb65 Mon Sep 17 00:00:00 2001 From: Masoud Rahimi Jafari Date: Mon, 1 Jun 2026 11:21:00 +0200 Subject: [PATCH 01/57] Changed intel pt files author email --- hyperdbg/hypertrace/code/api/PtApi.c | 2 +- hyperdbg/hypertrace/code/pt/Pt.c | 2 +- hyperdbg/hypertrace/header/api/PtApi.h | 2 +- hyperdbg/hypertrace/header/pt/Pt.h | 2 +- hyperdbg/include/SDK/headers/PtDefinitions.h | 2 +- .../code/debugger/commands/extension-commands/pt.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hyperdbg/hypertrace/code/api/PtApi.c b/hyperdbg/hypertrace/code/api/PtApi.c index d1c2297a..c09b295b 100644 --- a/hyperdbg/hypertrace/code/api/PtApi.c +++ b/hyperdbg/hypertrace/code/api/PtApi.c @@ -1,6 +1,6 @@ /** * @file PtApi.c - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Tracing routines for HyperTrace module (Intel Processor Trace) * @details * @version 0.19 diff --git a/hyperdbg/hypertrace/code/pt/Pt.c b/hyperdbg/hypertrace/code/pt/Pt.c index 11bff50d..97c66007 100644 --- a/hyperdbg/hypertrace/code/pt/Pt.c +++ b/hyperdbg/hypertrace/code/pt/Pt.c @@ -1,6 +1,6 @@ /** * @file Pt.c - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Processor Trace (PT) tracing implementation for HyperTrace module * @details Programs Intel PT MSRs and manages per-CPU ToPA / output buffers. * The engine half (PtEngine*) deals with a single PT_PER_CPU at a diff --git a/hyperdbg/hypertrace/header/api/PtApi.h b/hyperdbg/hypertrace/header/api/PtApi.h index 10e4cba1..d75ea2b0 100644 --- a/hyperdbg/hypertrace/header/api/PtApi.h +++ b/hyperdbg/hypertrace/header/api/PtApi.h @@ -1,6 +1,6 @@ /** * @file PtApi.h - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Header for PT tracing routines for HyperTrace module (Intel Processor Trace) * @details * @version 0.19 diff --git a/hyperdbg/hypertrace/header/pt/Pt.h b/hyperdbg/hypertrace/header/pt/Pt.h index ceb5c98d..07a10506 100644 --- a/hyperdbg/hypertrace/header/pt/Pt.h +++ b/hyperdbg/hypertrace/header/pt/Pt.h @@ -1,6 +1,6 @@ /** * @file Pt.h - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Header for Processor Trace (PT) tracing routines for HyperTrace module * @details Engine that programs Intel PT MSRs from VMX root or kernel context. * Buffer / ToPA management is kept here; user-visible PT structures diff --git a/hyperdbg/include/SDK/headers/PtDefinitions.h b/hyperdbg/include/SDK/headers/PtDefinitions.h index ee02d613..a4751cd3 100644 --- a/hyperdbg/include/SDK/headers/PtDefinitions.h +++ b/hyperdbg/include/SDK/headers/PtDefinitions.h @@ -1,6 +1,6 @@ /** * @file PtDefinitions.h - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Intel Processor Trace (PT) related data structures and hardware * definitions shared between the kernel and user-mode components. * @details diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp index 5c5d6d9f..a4323e84 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp @@ -1,6 +1,6 @@ /** * @file pt.cpp - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief !pt command * @details * @version 0.19 From 2dc1a26f1c658865bd6115120204bbaf609a18c1 Mon Sep 17 00:00:00 2001 From: Masoud Rahimi Jafari Date: Thu, 4 Jun 2026 01:55:42 +0200 Subject: [PATCH 02/57] Added user app example for pt tracing --- .../user/hyperdbg_app/code/hyperdbg-app.cpp | 404 ++++++++++++++---- .../user/hyperdbg_app/hyperdbg_app.vcxproj | 19 +- hyperdbg/hyperkd/code/driver/Ioctl.c | 114 +++++ hyperdbg/include/SDK/headers/Ioctls.h | 8 + .../include/SDK/headers/RequestStructures.h | 6 +- .../SDK/imports/user/HyperDbgLibImports.h | 10 + .../commands/extension-commands/pt.cpp | 49 +++ hyperdbg/libhyperdbg/code/export/export.cpp | 28 ++ hyperdbg/libhyperdbg/header/debugger.h | 6 + 9 files changed, 558 insertions(+), 86 deletions(-) diff --git a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp index 2aedaa33..0a5d4e42 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp @@ -1,8 +1,18 @@ /** * @file hyperdbg-app.cpp * @author Sina Karvandi (sina@hyperdbg.org) - * @brief Controller of the reversing machine's module + * @brief Run a target executable under Intel PT (via HyperDbg's hypertrace + * engine) and decode the captured trace with libipt. * @details + * Flow: + * 1. load the HyperDbg VMM (local / VMI mode) + * 2. create the target process suspended + * 3. !pt filter for that process only (CR3 filter, resolved from PID) + * 4. !pt enable + pt_mmap (map per-CPU PT buffers into this process) + * 5. resume the target and wait for it to finish + * 6. !pt pause + !pt size (snapshot per-CPU valid byte counts) + * 7. decode every per-CPU buffer with libipt and print the packets + * 8. !pt disable (tears the mappings + buffers down) + unload VMM * * @version 0.2 * @date 2023-02-01 @@ -12,13 +22,20 @@ */ #include "pch.h" -PVOID g_SharedMessageBuffer = NULL; +#include + +// +// libipt (Intel Processor Trace decoder). +// +// The include / library directories come from the 'LibIptDir' MSBuild macro +// defined in hyperdbg_app.vcxproj — set it to your libipt install root (the +// folder that contains 'include\intel-pt.h' and 'lib\libipt.lib'), and make +// sure 'libipt.dll' is reachable at run time (next to the .exe or on PATH). +// +#include /** - * @brief Show messages - * - * @param Text - * @return int + * @brief Show messages coming from HyperDbg (driver + library) */ int hyperdbg_show_messages(const char * Text) @@ -28,87 +45,308 @@ hyperdbg_show_messages(const char * Text) } /** - * @brief Show messages (shared buffer) - * - * @param Text - * @return int + * @brief Human-readable name for a PT packet type */ -int -hyperdbg_show_messages_shared_buffer() +static const char * +PtPacketName(enum pt_packet_type Type) { - printf("%s", (char *)g_SharedMessageBuffer); + switch (Type) + { + case ppt_psb: return "PSB"; + case ppt_psbend: return "PSBEND"; + case ppt_pad: return "PAD"; + case ppt_fup: return "FUP"; + case ppt_tip: return "TIP"; + case ppt_tip_pge: return "TIP.PGE"; + case ppt_tip_pgd: return "TIP.PGD"; + case ppt_tnt_8: return "TNT-8"; + case ppt_tnt_64: return "TNT-64"; + case ppt_mode: return "MODE"; + case ppt_pip: return "PIP"; + case ppt_vmcs: return "VMCS"; + case ppt_cbr: return "CBR"; + case ppt_tsc: return "TSC"; + case ppt_tma: return "TMA"; + case ppt_mtc: return "MTC"; + case ppt_cyc: return "CYC"; + case ppt_stop: return "STOP"; + case ppt_ovf: return "OVF"; + case ppt_mnt: return "MNT"; + case ppt_exstop: return "EXSTOP"; + case ppt_ptw: return "PTW"; + default: return "UNKNOWN"; + } +} + +/** + * @brief Pretty-print a single decoded PT packet + */ +static void +PtPrintPacket(UINT32 Cpu, const struct pt_packet * Packet) +{ + switch (Packet->type) + { + case ppt_pad: + // + // Padding — skip to keep the dump readable + // + break; + case ppt_fup: + case ppt_tip: + case ppt_tip_pge: + case ppt_tip_pgd: + printf(" [cpu %u] %-8s ip=0x%016llx\n", Cpu, PtPacketName(Packet->type), (unsigned long long)Packet->payload.ip.ip); + break; + case ppt_tnt_8: + case ppt_tnt_64: + printf(" [cpu %u] %-8s taken/not-taken bits=%u\n", Cpu, PtPacketName(Packet->type), Packet->payload.tnt.bit_size); + break; + case ppt_pip: + printf(" [cpu %u] %-8s cr3=0x%016llx\n", Cpu, PtPacketName(Packet->type), (unsigned long long)Packet->payload.pip.cr3); + break; + case ppt_tsc: + printf(" [cpu %u] %-8s tsc=%llu\n", Cpu, PtPacketName(Packet->type), (unsigned long long)Packet->payload.tsc.tsc); + break; + case ppt_cbr: + printf(" [cpu %u] %-8s ratio=%u\n", Cpu, PtPacketName(Packet->type), Packet->payload.cbr.ratio); + break; + default: + printf(" [cpu %u] %-8s\n", Cpu, PtPacketName(Packet->type)); + break; + } +} + +/** + * @brief Decode one CPU's raw PT byte stream with libipt and print packets + * + * @param Cpu logical processor index (for labelling) + * @param Buf start of the mapped PT buffer (main + overflow) + * @param Size number of valid bytes to decode + */ +static void +PtDecodeBuffer(UINT32 Cpu, const UINT8 * Buf, UINT64 Size) +{ + struct pt_config Config; + struct pt_packet_decoder * Decoder; + UINT64 Count = 0; + int Status; + + printf("\n==== core %u : decoding %llu bytes ====\n", Cpu, (unsigned long long)Size); + + memset(&Config, 0, sizeof(Config)); + Config.size = sizeof(Config); + Config.begin = (uint8_t *)Buf; + Config.end = (uint8_t *)Buf + Size; + + Decoder = pt_pkt_alloc_decoder(&Config); + if (Decoder == NULL) + { + printf(" err, could not allocate libipt packet decoder\n"); + return; + } + + // + // Sync to the first PSB in the stream. The buffer is circular, so the + // very start may be mid-packet garbage — sync_forward skips to a real + // packet boundary. + // + Status = pt_pkt_sync_forward(Decoder); + if (Status < 0) + { + printf(" no synchronization point found (%s)\n", pt_errstr(pt_errcode(Status))); + pt_pkt_free_decoder(Decoder); + return; + } + + for (;;) + { + struct pt_packet Packet; + + Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); + if (Status < 0) + { + if (Status == -pte_eos) + { + // + // End of stream + // + break; + } + + // + // Decode error — try to resync at the next PSB + // + Status = pt_pkt_sync_forward(Decoder); + if (Status < 0) + { + break; + } + continue; + } + + PtPrintPacket(Cpu, &Packet); + Count++; + } + + printf(" ---- core %u : %llu packets decoded ----\n", Cpu, (unsigned long long)Count); + pt_pkt_free_decoder(Decoder); +} + +/** + * @brief Send a single PT operation and report failures + */ +static BOOLEAN +PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE Type, UINT32 TargetPid) +{ + HYPERTRACE_PT_OPERATION_PACKETS Op = {0}; + + Op.PtOperationType = Type; + + if (Type == HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER) + { + // + // Trace user-mode execution of the target process only. The kernel + // resolves TargetProcessId to the right CR3 for the requested mode + // (the KVA-shadow user CR3 here, since we trace user mode) and + // programs the PT CR3 filter, so other processes are not traced. + // + Op.TraceUser = 1; + Op.TraceKernel = 0; + Op.TargetProcessId = TargetPid; + } + + return hyperdbg_u_pt_operation(&Op); +} + +/** + * @brief Run the target under Intel PT and decode the result + */ +static int +RunAndTrace(const char * TargetPath) +{ + STARTUPINFOA Si = {0}; + PROCESS_INFORMATION Pi = {0}; + HYPERTRACE_PT_MMAP_PACKETS Mmap = {0}; + HYPERTRACE_PT_OPERATION_PACKETS Size = {0}; + + Si.cb = sizeof(Si); + + // + // Create the target suspended so PT is armed before its first instruction + // + if (!CreateProcessA(TargetPath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Si, &Pi)) + { + printf("err, could not start '%s' (0x%x)\n", TargetPath, GetLastError()); + return 1; + } + + printf("started '%s' (pid %u), arming Intel PT...\n", TargetPath, Pi.dwProcessId); + + // + // 1) Filter for this process, 2) enable tracing, 3) map the buffers + // + if (!PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER, Pi.dwProcessId) || + !PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE, 0)) + { + printf("err, could not enable Intel PT\n"); + TerminateProcess(Pi.hProcess, 1); + goto Cleanup; + } + + if (!hyperdbg_u_pt_mmap(&Mmap)) + { + printf("err, pt_mmap failed\n"); + PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE, 0); + TerminateProcess(Pi.hProcess, 1); + goto Cleanup; + } + + printf("Intel PT enabled, %u per-CPU buffers mapped:\n", Mmap.NumCpus); + for (UINT32 i = 0; i < Mmap.NumCpus; i++) + { + printf(" core %u : va=0x%016llx size=0x%llx\n", + Mmap.Cpus[i].CpuId, + (unsigned long long)Mmap.Cpus[i].UserVa, + (unsigned long long)Mmap.Cpus[i].Size); + } + + // + // Let the target run to completion + // + printf("resuming target and waiting for it to finish...\n"); + ResumeThread(Pi.hThread); + WaitForSingleObject(Pi.hProcess, INFINITE); + printf("target finished, pausing Intel PT\n"); + + // + // Stop tracing (flushes hardware buffers), then snapshot byte counts + // + PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE, 0); + + Size.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; + if (!hyperdbg_u_pt_operation(&Size)) + { + printf("err, could not query PT sizes\n"); + PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE, 0); + goto Cleanup; + } + + // + // Decode each CPU's trace (match the mmap descriptor by CpuId) + // + for (UINT32 i = 0; i < Mmap.NumCpus; i++) + { + UINT32 Cpu = Mmap.Cpus[i].CpuId; + UINT64 Bytes = (Cpu < Size.NumCpus) ? Size.BytesPerCpu[Cpu] : 0; + + if (Bytes == 0) + { + continue; + } + + if (Bytes > Mmap.Cpus[i].Size) + { + Bytes = Mmap.Cpus[i].Size; + } + + PtDecodeBuffer(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); + } + + // + // Disable PT (this also tears down the user mappings) + // + PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE, 0); + +Cleanup: + CloseHandle(Pi.hThread); + CloseHandle(Pi.hProcess); return 0; } /** - * @brief Load the driver + * @brief Load the HyperDbg VMM in local (VMI) mode * - * @return int return zero if it was successful or non-zero if there - * was error + * @return int zero on success */ -int +static int hyperdbg_load() { - char CpuId[13] = {0}; - - // - // Read the vendor string - // - hyperdbg_u_read_vendor_string(CpuId); - - printf("current processor vendor is : %s\n", CpuId); - - if (strcmp(CpuId, "GenuineIntel") == 0) - { - printf("virtualization technology is vt-x\n"); - } - else - { - printf("this program is not designed to run in a non-VT-x " - "environment !\n"); - return 1; - } - - // - // Detect if the processor supports vmx operation - // - if (hyperdbg_u_detect_vmx_support()) - { - printf("vmx operation is supported by your processor\n"); - } - else - { -#ifdef HYPERDBG_ENV_WINDOWS - printf("vmx operation is not supported by your processor " - "(if you are using an Intel processor, it might be because VBS is not disabled!)\n"); -#endif - return 1; - } - - // - // Set callback function for showing messages - // hyperdbg_u_set_text_message_callback(hyperdbg_show_messages); - // - // Test interpreter with shared buffer - // - // g_SharedMessageBuffer = hyperdbg_u_set_text_message_callback_using_shared_buffer(hyperdbg_show_messages_shared_buffer); + if (!hyperdbg_u_detect_vmx_support()) + { + printf("err, vmx operation is not supported on this processor\n"); + return 1; + } // - // Test interpreter + // Install + load the VMM driver (mirrors the CLI's 'load vmm') // - hyperdbg_u_connect_remote_debugger_using_named_pipe("\\\\.\\pipe\\HyperDbgPipe", TRUE); - Sleep(10000); - hyperdbg_u_run_command((CHAR *)"r"); - hyperdbg_u_run_command((CHAR *)".start path c:\\Windows\\system32\\calc.exe"); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); + if (hyperdbg_u_install_vmm_driver() != 0 || hyperdbg_u_load_vmm() != 0) + { + printf("err, could not load the HyperDbg VMM\n"); + return 1; + } return 0; } @@ -119,17 +357,25 @@ hyperdbg_load() * @return int */ int -main() +main(int argc, char ** argv) { - if (hyperdbg_load() == 0) + if (argc < 2) { - // - // HyperDbg driver loaded successfully - // - // hyperdbg_unload(); + printf("usage: %s \n", argv[0]); + return 1; } - else + + if (hyperdbg_load() != 0) { printf("err, in loading HyperDbg\n"); + return 1; } + + RunAndTrace(argv[1]); + + // + // Turn the hypervisor off + // + hyperdbg_u_unload_vmm(); + return 0; } diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj index eecf0a79..479232d7 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj @@ -49,7 +49,14 @@ - + + + C:\libipt + $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ @@ -67,13 +74,14 @@ MultiThreadedDebug Create pch.h - $(SolutionDir)include;$(ProjectDir)header;%(AdditionalIncludeDirectories) + $(LibIptDir)\include;$(SolutionDir)include;$(ProjectDir)header;%(AdditionalIncludeDirectories) true Console true - $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib + $(LibIptDir)\lib;%(AdditionalLibraryDirectories) + $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;libipt.lib true @@ -88,7 +96,7 @@ MultiThreaded Create pch.h - $(SolutionDir)include;$(ProjectDir)header;%(AdditionalIncludeDirectories) + $(LibIptDir)\include;$(SolutionDir)include;$(ProjectDir)header;%(AdditionalIncludeDirectories) true MaxSpeed @@ -97,7 +105,8 @@ true true true - $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib + $(LibIptDir)\lib;%(AdditionalLibraryDirectories) + $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;libipt.lib true diff --git a/hyperdbg/hyperkd/code/driver/Ioctl.c b/hyperdbg/hyperkd/code/driver/Ioctl.c index 3ba4008a..944a6d49 100644 --- a/hyperdbg/hyperkd/code/driver/Ioctl.c +++ b/hyperdbg/hyperkd/code/driver/Ioctl.c @@ -83,6 +83,74 @@ DrvAdjustStatusAndSetOutputSize(UINT32 ExpectedOutputBufferSize, *DoNotChangeInformation = TRUE; } +/** + * @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 Driver IOCTL Dispatcher * @@ -133,6 +201,7 @@ DrvDispatchIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) PHYPERTRACE_LBR_OPERATION_PACKETS HyperTraceLbrOperationRequest; PHYPERTRACE_LBR_DUMP_PACKETS HyperTraceLbrdumpRequest; PHYPERTRACE_PT_OPERATION_PACKETS HyperTracePtOperationRequest; + PHYPERTRACE_PT_MMAP_PACKETS HyperTracePtMmapRequest; PVOID BufferToStoreThreadsAndProcessesDetails; NTSTATUS Status; ULONG InBuffLength; // Input buffer length @@ -1072,6 +1141,23 @@ DrvDispatchIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) 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 // @@ -1084,6 +1170,34 @@ DrvDispatchIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) 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; + case IOCTL_SEND_USER_DEBUGGER_COMMANDS: // diff --git a/hyperdbg/include/SDK/headers/Ioctls.h b/hyperdbg/include/SDK/headers/Ioctls.h index ff4baae2..4abbaef4 100644 --- a/hyperdbg/include/SDK/headers/Ioctls.h +++ b/hyperdbg/include/SDK/headers/Ioctls.h @@ -351,3 +351,11 @@ */ #define IOCTL_PERFORM_HYPERTRACE_PT_OPERATION \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x829, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/** + * @brief ioctl, to map per-CPU HyperTrace PT output buffers into the + * calling user-mode process. See HYPERTRACE_PT_MMAP_PACKETS. + * + */ +#define IOCTL_PERFORM_HYPERTRACE_PT_MMAP \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82a, METHOD_BUFFERED, FILE_ANY_ACCESS) diff --git a/hyperdbg/include/SDK/headers/RequestStructures.h b/hyperdbg/include/SDK/headers/RequestStructures.h index f0a0b0a0..b20d224d 100644 --- a/hyperdbg/include/SDK/headers/RequestStructures.h +++ b/hyperdbg/include/SDK/headers/RequestStructures.h @@ -1357,8 +1357,10 @@ typedef struct _HYPERTRACE_PT_OPERATION_PACKETS UINT32 TraceKernel; /* Boolean: trace CPL == 0 */ UINT64 TargetCr3; /* CR3 to filter by (0 = no filter) */ UINT64 BufferSize; /* Output buffer size (0 = keep current) */ - UINT32 NumAddrRanges; /* Number of valid AddrRanges entries */ - UINT32 Reserved; /* Padding to keep the array 8-aligned */ + UINT32 NumAddrRanges; /* Number of valid AddrRanges entries */ + UINT32 TargetProcessId; /* Process to trace; kernel resolves it + to TargetCr3 when TargetCr3 == 0 + (0 = no PID-based CR3 filter) */ PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]; // diff --git a/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h b/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h index 75448bae..27c56729 100644 --- a/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h +++ b/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h @@ -263,6 +263,16 @@ hyperdbg_u_perform_smi_operation(SMI_OPERATION_PACKETS * SmiOperation); IMPORT_EXPORT_LIBHYPERDBG BOOLEAN hyperdbg_u_lbr_dump(HYPERTRACE_LBR_DUMP_PACKETS * LbrdumpRequest); +// +// Intel PT related commands +// Exported functionality of the '!pt' command + the PT mmap surface +// +IMPORT_EXPORT_LIBHYPERDBG BOOLEAN +hyperdbg_u_pt_operation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest); + +IMPORT_EXPORT_LIBHYPERDBG BOOLEAN +hyperdbg_u_pt_mmap(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest); + // // Transparent mode related command // Exported functionality of the '!hide', and '!unhide' commands diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp index a4323e84..a8cee676 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp @@ -135,6 +135,55 @@ HyperDbgPerformPtOperation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) return CommandPtSendRequest(PtRequest); } +/** + * @brief Map the per-CPU PT output buffers into the current process + * + * @details On success MmapRequest->Cpus[0..NumCpus) hold one { UserVa, Size } + * per CPU, valid in this process until PT is disabled / flushed. + * Only meaningful in local (VMI) mode. + * + * @param MmapRequest + * + * @return BOOLEAN + */ +BOOLEAN +HyperDbgPtMmapSendRequest(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest) +{ + BOOL Status; + ULONG ReturnedLength; + + if (g_IsSerialConnectedToRemoteDebuggee) + { + // + // The mmap surface maps into the caller's address space, which only + // makes sense in local mode (no remote-debuggee transport for it). + // + ShowMessages("err, PT mmap is only available in local (VMI) mode\n"); + return FALSE; + } + + AssertShowMessageReturnStmt(g_DeviceHandle, ASSERT_MESSAGE_DRIVER_NOT_LOADED, AssertReturnFalse); + + Status = DeviceIoControl( + g_DeviceHandle, // Handle to device + IOCTL_PERFORM_HYPERTRACE_PT_MMAP, // IO Control Code (IOCTL) + MmapRequest, // Input Buffer to driver. + SIZEOF_HYPERTRACE_PT_MMAP_PACKETS, // Input buffer length + MmapRequest, // Output Buffer from driver. + SIZEOF_HYPERTRACE_PT_MMAP_PACKETS, // Length of output buffer in bytes. + &ReturnedLength, // Bytes placed in buffer. + NULL // synchronous call + ); + + if (!Status) + { + ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + return FALSE; + } + + return MmapRequest->KernelStatus == DEBUGGER_OPERATION_WAS_SUCCESSFUL; +} + /** * @brief Parse a `!pt filter ...` clause into a HYPERTRACE_PT_OPERATION_PACKETS. * diff --git a/hyperdbg/libhyperdbg/code/export/export.cpp b/hyperdbg/libhyperdbg/code/export/export.cpp index a8f3041e..86afa46e 100644 --- a/hyperdbg/libhyperdbg/code/export/export.cpp +++ b/hyperdbg/libhyperdbg/code/export/export.cpp @@ -841,3 +841,31 @@ hyperdbg_u_lbr_dump(HYPERTRACE_LBR_DUMP_PACKETS * LbrdumpRequest) { return HyperDbgLbrdumpSendRequest(LbrdumpRequest); } + +/** + * @brief Perform an Intel PT operation (enable / disable / pause / resume / + * size / dump / flush / filter) + * + * @param PtRequest The PT operation request packet + * + * @return BOOLEAN TRUE if the operation was successful, otherwise FALSE + */ +BOOLEAN +hyperdbg_u_pt_operation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + return HyperDbgPerformPtOperation(PtRequest); +} + +/** + * @brief Map the per-CPU Intel PT output buffers into the calling process + * + * @param MmapRequest The PT mmap request packet; filled with per-CPU + * { UserVa, Size } on success + * + * @return BOOLEAN TRUE if the operation was successful, otherwise FALSE + */ +BOOLEAN +hyperdbg_u_pt_mmap(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest) +{ + return HyperDbgPtMmapSendRequest(MmapRequest); +} diff --git a/hyperdbg/libhyperdbg/header/debugger.h b/hyperdbg/libhyperdbg/header/debugger.h index 1ecb7113..8e2a31e4 100644 --- a/hyperdbg/libhyperdbg/header/debugger.h +++ b/hyperdbg/libhyperdbg/header/debugger.h @@ -315,3 +315,9 @@ HyperDbgDisableTransparentMode(); BOOLEAN HyperDbgLbrdumpSendRequest(HYPERTRACE_LBR_DUMP_PACKETS * LbrdumpRequest); + +BOOLEAN +HyperDbgPerformPtOperation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest); + +BOOLEAN +HyperDbgPtMmapSendRequest(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest); From 81c8b88dc3cd7a9a401a19ee8600ade8d6a08436 Mon Sep 17 00:00:00 2001 From: Masoud Rahimi Jafari Date: Sat, 6 Jun 2026 15:41:48 +0200 Subject: [PATCH 03/57] Added disassembler to hyperdbg app to show decoded intel pt buffers --- .../user/hyperdbg_app/code/hyperdbg-app.cpp | 707 +++++++++++------- .../user/hyperdbg_app/hyperdbg_app.vcxproj | 24 +- hyperdbg/hypertrace/code/pt/Pt.c | 2 +- hyperdbg/hypertrace/header/broadcast/Dpc.h | 39 + hyperdbg/hypertrace/header/pch.h | 11 - hyperdbg/hypertrace/hypertrace.vcxproj | 1 + .../hypertrace/hypertrace.vcxproj.filters | 3 + 7 files changed, 503 insertions(+), 284 deletions(-) create mode 100644 hyperdbg/hypertrace/header/broadcast/Dpc.h diff --git a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp index 0a5d4e42..13385130 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp @@ -1,381 +1,560 @@ -/** - * @file hyperdbg-app.cpp - * @author Sina Karvandi (sina@hyperdbg.org) - * @brief Run a target executable under Intel PT (via HyperDbg's hypertrace - * engine) and decode the captured trace with libipt. - * @details - * Flow: - * 1. load the HyperDbg VMM (local / VMI mode) - * 2. create the target process suspended - * 3. !pt filter for that process only (CR3 filter, resolved from PID) - * 4. !pt enable + pt_mmap (map per-CPU PT buffers into this process) - * 5. resume the target and wait for it to finish - * 6. !pt pause + !pt size (snapshot per-CPU valid byte counts) - * 7. decode every per-CPU buffer with libipt and print the packets - * 8. !pt disable (tears the mappings + buffers down) + unload VMM - * - * @version 0.2 - * @date 2023-02-01 - * - * @copyright This project is released under the GNU Public License v3. - * - */ #include "pch.h" #include - -// -// libipt (Intel Processor Trace decoder). -// -// The include / library directories come from the 'LibIptDir' MSBuild macro -// defined in hyperdbg_app.vcxproj — set it to your libipt install root (the -// folder that contains 'include\intel-pt.h' and 'lib\libipt.lib'), and make -// sure 'libipt.dll' is reachable at run time (next to the .exe or on PATH). -// +#include +#include #include +#include -/** - * @brief Show messages coming from HyperDbg (driver + library) - */ -int -hyperdbg_show_messages(const char * Text) +#pragma comment(lib, "dbghelp.lib") + +static UINT64 g_ImageBase = 0; +static UINT64 g_CodeBase = 0; +static UINT64 g_CodeSize = 0; +static UINT8 * g_Code = NULL; + +static int +ShowMessages(const char * Text) { printf("%s", Text); return 0; } -/** - * @brief Human-readable name for a PT packet type - */ +static int +ReadImage(uint8_t * Buffer, size_t Size, const struct pt_asid * Asid, uint64_t Ip, void * Context) +{ + (void)Asid; + (void)Context; + + if (g_Code == NULL || Ip < g_CodeBase || Ip >= g_CodeBase + g_CodeSize) + return -pte_nomap; + + uint64_t Available = g_CodeBase + g_CodeSize - Ip; + size_t Count = (Size < Available) ? Size : (size_t)Available; + + memcpy(Buffer, g_Code + (Ip - g_CodeBase), Count); + return (int)Count; +} + +typedef struct _PROC_BASIC_INFO +{ + LONG ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR Reserved[4]; +} PROC_BASIC_INFO; + +typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); + +static BOOLEAN +CaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) +{ + HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); + PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; + PROC_BASIC_INFO Pbi = {0}; + ULONG Ret = 0; + SIZE_T Got = 0; + UINT64 Base = 0; + IMAGE_DOS_HEADER Dos; + IMAGE_NT_HEADERS64 Nt; + UINT64 SectionBase; + + if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) + return FALSE; + + g_ImageBase = Base; + SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; + + for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) + { + IMAGE_SECTION_HEADER Section; + + if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) + return FALSE; + + if (memcmp(Section.Name, ".text", 6) != 0) + continue; + + UINT64 Start = Base + Section.VirtualAddress; + UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; + + if (Size == 0) + return FALSE; + + g_Code = (UINT8 *)malloc((size_t)Size); + if (g_Code == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Start, g_Code, (SIZE_T)Size, &Got) || Got != Size) + { + free(g_Code); + g_Code = NULL; + return FALSE; + } + + g_CodeBase = Start; + g_CodeSize = Size; + *TextStart = Start; + *TextEnd = Start + Size - 1; + return TRUE; + } + + return FALSE; +} + +static BOOLEAN +ResolveFunction(HANDLE Process, const char * Path, const char * Name, UINT64 * Start, UINT64 * End) +{ + union + { + SYMBOL_INFO Info; + BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + } Symbol = {0}; + BOOLEAN Ok = FALSE; + + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + if (!SymInitialize(Process, NULL, FALSE)) + return FALSE; + + if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)g_ImageBase, 0, NULL, 0) != 0) + { + Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); + Symbol.Info.MaxNameLen = MAX_SYM_NAME; + + if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) + { + *Start = Symbol.Info.Address; + *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; + Ok = TRUE; + } + } + + SymCleanup(Process); + return Ok; +} + +static BOOLEAN +PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE Type) +{ + HYPERTRACE_PT_OPERATION_PACKETS Op = {}; + Op.PtOperationType = Type; + return hyperdbg_u_pt_operation(&Op); +} + +static BOOLEAN +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; + + if (End > Start) + { + Op.NumAddrRanges = 1; + Op.AddrRanges[0].Start = Start; + Op.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.BufferSize); + return TRUE; +} + static const char * -PtPacketName(enum pt_packet_type Type) +PacketName(enum pt_packet_type Type) { switch (Type) { - case ppt_psb: return "PSB"; - case ppt_psbend: return "PSBEND"; - case ppt_pad: return "PAD"; - case ppt_fup: return "FUP"; - case ppt_tip: return "TIP"; - case ppt_tip_pge: return "TIP.PGE"; - case ppt_tip_pgd: return "TIP.PGD"; - case ppt_tnt_8: return "TNT-8"; - case ppt_tnt_64: return "TNT-64"; - case ppt_mode: return "MODE"; - case ppt_pip: return "PIP"; - case ppt_vmcs: return "VMCS"; - case ppt_cbr: return "CBR"; - case ppt_tsc: return "TSC"; - case ppt_tma: return "TMA"; - case ppt_mtc: return "MTC"; - case ppt_cyc: return "CYC"; - case ppt_stop: return "STOP"; - case ppt_ovf: return "OVF"; - case ppt_mnt: return "MNT"; - case ppt_exstop: return "EXSTOP"; - case ppt_ptw: return "PTW"; - default: return "UNKNOWN"; + case ppt_psb: return "PSB"; case ppt_psbend: return "PSBEND"; case ppt_pad: return "PAD"; + case ppt_fup: return "FUP"; case ppt_tip: return "TIP"; case ppt_tip_pge: return "TIP.PGE"; + case ppt_tip_pgd: return "TIP.PGD"; case ppt_tnt_8: return "TNT8"; case ppt_tnt_64: return "TNT64"; + case ppt_mode: return "MODE"; case ppt_pip: return "PIP"; case ppt_vmcs: return "VMCS"; + case ppt_cbr: return "CBR"; case ppt_tsc: return "TSC"; case ppt_tma: return "TMA"; + case ppt_mtc: return "MTC"; case ppt_cyc: return "CYC"; case ppt_ovf: return "OVF"; + case ppt_stop: return "STOP"; case ppt_exstop: return "EXSTOP"; case ppt_mnt: return "MNT"; + case ppt_ptw: return "PTW"; default: return "?"; } } -/** - * @brief Pretty-print a single decoded PT packet - */ -static void -PtPrintPacket(UINT32 Cpu, const struct pt_packet * Packet) +static uint64_t +ReconstructIp(const struct pt_packet_ip * Packet, uint64_t * LastIp) { - switch (Packet->type) + uint64_t Value = *LastIp; + + switch (Packet->ipc) { - case ppt_pad: - // - // Padding — skip to keep the dump readable - // - break; - case ppt_fup: - case ppt_tip: - case ppt_tip_pge: - case ppt_tip_pgd: - printf(" [cpu %u] %-8s ip=0x%016llx\n", Cpu, PtPacketName(Packet->type), (unsigned long long)Packet->payload.ip.ip); - break; - case ppt_tnt_8: - case ppt_tnt_64: - printf(" [cpu %u] %-8s taken/not-taken bits=%u\n", Cpu, PtPacketName(Packet->type), Packet->payload.tnt.bit_size); - break; - case ppt_pip: - printf(" [cpu %u] %-8s cr3=0x%016llx\n", Cpu, PtPacketName(Packet->type), (unsigned long long)Packet->payload.pip.cr3); - break; - case ppt_tsc: - printf(" [cpu %u] %-8s tsc=%llu\n", Cpu, PtPacketName(Packet->type), (unsigned long long)Packet->payload.tsc.tsc); - break; - case ppt_cbr: - printf(" [cpu %u] %-8s ratio=%u\n", Cpu, PtPacketName(Packet->type), Packet->payload.cbr.ratio); - break; - default: - printf(" [cpu %u] %-8s\n", Cpu, PtPacketName(Packet->type)); + case pt_ipc_update_16: Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); break; + case pt_ipc_update_32: Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); break; + case pt_ipc_update_48: Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); break; + case pt_ipc_sext_48: + Value = Packet->ip & 0xffffffffffffull; + if (Value & 0x800000000000ull) + Value |= 0xffff000000000000ull; break; + default: Value = Packet->ip; break; } + + *LastIp = Value; + return Value; } -/** - * @brief Decode one CPU's raw PT byte stream with libipt and print packets - * - * @param Cpu logical processor index (for labelling) - * @param Buf start of the mapped PT buffer (main + overflow) - * @param Size number of valid bytes to decode - */ -static void -PtDecodeBuffer(UINT32 Cpu, const UINT8 * Buf, UINT64 Size) +static UINT64 +DecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) { - struct pt_config Config; + struct pt_config Config; struct pt_packet_decoder * Decoder; - UINT64 Count = 0; + UINT64 Count = 0; + uint64_t LastIp = 0; int Status; - printf("\n==== core %u : decoding %llu bytes ====\n", Cpu, (unsigned long long)Size); - - memset(&Config, 0, sizeof(Config)); - Config.size = sizeof(Config); - Config.begin = (uint8_t *)Buf; - Config.end = (uint8_t *)Buf + Size; + pt_config_init(&Config); + Config.begin = (uint8_t *)Buffer; + Config.end = (uint8_t *)Buffer + Size; Decoder = pt_pkt_alloc_decoder(&Config); if (Decoder == NULL) { - printf(" err, could not allocate libipt packet decoder\n"); - return; - } - - // - // Sync to the first PSB in the stream. The buffer is circular, so the - // very start may be mid-packet garbage — sync_forward skips to a real - // packet boundary. - // - Status = pt_pkt_sync_forward(Decoder); - if (Status < 0) - { - printf(" no synchronization point found (%s)\n", pt_errstr(pt_errcode(Status))); - pt_pkt_free_decoder(Decoder); - return; + printf("[-] core %u: cannot allocate packet decoder\n", Cpu); + return 0; } for (;;) { - struct pt_packet Packet; - - Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); + Status = pt_pkt_sync_forward(Decoder); if (Status < 0) + break; + + for (;;) { - if (Status == -pte_eos) - { - // - // End of stream - // + struct pt_packet Packet; + + Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); + if (Status < 0) break; + + Count++; + + switch (Packet.type) + { + case ppt_tnt_8: + case ppt_tnt_64: + printf(" %-8s %2u ", PacketName(Packet.type), Packet.payload.tnt.bit_size); + for (uint8_t Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) + putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); + putchar('\n'); + break; + + case ppt_tip: + case ppt_fup: + case ppt_tip_pge: + case ppt_tip_pgd: + if (Packet.payload.ip.ipc == pt_ipc_suppressed) + printf(" %-8s (ip suppressed)\n", PacketName(Packet.type)); + else + { + uint64_t Ip = ReconstructIp(&Packet.payload.ip, &LastIp); + printf(" %-8s 0x%016llx exe+0x%llx\n", + PacketName(Packet.type), (unsigned long long)Ip, (unsigned long long)(Ip - g_ImageBase)); + } + break; + + case ppt_pip: + printf(" %-8s cr3=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.pip.cr3); + break; + + case ppt_cbr: + //printf(" %-8s ratio=%u\n", PacketName(Packet.type), Packet.payload.cbr.ratio); + break; + + case ppt_tsc: + printf(" %-8s tsc=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.tsc.tsc); + break; + + default: + //printf(" %-8s\n", PacketName(Packet.type)); + break; + } + } + } + + pt_pkt_free_decoder(Decoder); + return Count; +} + +static UINT64 +DecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +{ + struct pt_config Config; + struct pt_insn_decoder * Decoder; + struct pt_image * Image; + UINT64 Count = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (uint8_t *)Buffer; + Config.end = (uint8_t *)Buffer + Size; + + Decoder = pt_insn_alloc_decoder(&Config); + if (Decoder == NULL) + { + printf("[-] core %u: cannot allocate instruction decoder\n", Cpu); + return 0; + } + + Image = pt_insn_get_image(Decoder); + pt_image_set_callback(Image, ReadImage, NULL); + + for (;;) + { + Status = pt_insn_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_insn Insn; + + while (Status & pts_event_pending) + { + struct pt_event Event; + Status = pt_insn_event(Decoder, &Event, sizeof(Event)); + if (Status < 0) + break; } - // - // Decode error — try to resync at the next PSB - // - Status = pt_pkt_sync_forward(Decoder); - if (Status < 0) - { + if (Status < 0 || (Status & pts_eos)) break; - } - continue; + + Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); + if (Status < 0) + break; + + ZydisDisassembledInstruction Disasm; + ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; + + if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) + printf(" 0x%016llx exe+0x%-6llx %s\n", + (unsigned long long)Insn.ip, + (unsigned long long)(Insn.ip - g_ImageBase), + Disasm.text); + else + printf(" 0x%016llx (undecodable)\n", (unsigned long long)Insn.ip); + + Count++; } - PtPrintPacket(Cpu, &Packet); - Count++; + if (Status >= 0 && (Status & pts_eos)) + break; } - printf(" ---- core %u : %llu packets decoded ----\n", Cpu, (unsigned long long)Count); - pt_pkt_free_decoder(Decoder); + pt_insn_free_decoder(Decoder); + return Count; } -/** - * @brief Send a single PT operation and report failures - */ -static BOOLEAN -PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE Type, UINT32 TargetPid) +static void +RunAndTrace(const char * Path, const char * Function, BOOLEAN Packets, int PinCore) { - HYPERTRACE_PT_OPERATION_PACKETS Op = {0}; + STARTUPINFOA Startup = {0}; + PROCESS_INFORMATION Process = {0}; + HYPERTRACE_PT_MMAP_PACKETS Mmap = {0}; + HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; + UINT64 TextStart = 0; + UINT64 TextEnd = 0; + UINT64 FilterStart = 0; + UINT64 FilterEnd = 0; + UINT64 Total = 0; - Op.PtOperationType = Type; + Startup.cb = sizeof(Startup); - if (Type == HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER) + if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) { - // - // Trace user-mode execution of the target process only. The kernel - // resolves TargetProcessId to the right CR3 for the requested mode - // (the KVA-shadow user CR3 here, since we trace user mode) and - // programs the PT CR3 filter, so other processes are not traced. - // - Op.TraceUser = 1; - Op.TraceKernel = 0; - Op.TargetProcessId = TargetPid; + printf("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); + return; } - return hyperdbg_u_pt_operation(&Op); -} + printf("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); -/** - * @brief Run the target under Intel PT and decode the result - */ -static int -RunAndTrace(const char * TargetPath) -{ - STARTUPINFOA Si = {0}; - PROCESS_INFORMATION Pi = {0}; - HYPERTRACE_PT_MMAP_PACKETS Mmap = {0}; - HYPERTRACE_PT_OPERATION_PACKETS Size = {0}; - - Si.cb = sizeof(Si); - - // - // Create the target suspended so PT is armed before its first instruction - // - if (!CreateProcessA(TargetPath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Si, &Pi)) + if (PinCore >= 0) { - printf("err, could not start '%s' (0x%x)\n", TargetPath, GetLastError()); - return 1; + DWORD_PTR Mask = (DWORD_PTR)1 << PinCore; + if (SetProcessAffinityMask(Process.hProcess, Mask)) + printf("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); + else + printf("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); + } + else + { + printf("[*] target unpinned (scheduler may migrate it across cores)\n"); } - printf("started '%s' (pid %u), arming Intel PT...\n", TargetPath, Pi.dwProcessId); - - // - // 1) Filter for this process, 2) enable tracing, 3) map the buffers - // - if (!PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER, Pi.dwProcessId) || - !PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE, 0)) + if (!CaptureImage(Process.hProcess, &TextStart, &TextEnd)) { - printf("err, could not enable Intel PT\n"); - TerminateProcess(Pi.hProcess, 1); + printf("[-] cannot read target image / .text section\n"); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + printf("[+] image base 0x%llx, .text 0x%llx-0x%llx (%llu bytes)\n", + (unsigned long long)g_ImageBase, + (unsigned long long)TextStart, + (unsigned long long)TextEnd, + (unsigned long long)g_CodeSize); + + FilterStart = TextStart; + FilterEnd = TextEnd; + + if (Function != NULL && ResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) + printf("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", + Function, + (unsigned long long)FilterStart, + (unsigned long long)FilterEnd, + (unsigned long long)(FilterEnd - FilterStart + 1)); + else + printf("[!] IP filter: whole .text (symbol '%s' not found - build the target with a PDB)\n", + Function ? Function : "(none)"); + + if (!PtFilter(Process.dwProcessId, FilterStart, FilterEnd) || + !PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE)) + { + printf("[-] cannot enable Intel PT\n"); + TerminateProcess(Process.hProcess, 1); goto Cleanup; } if (!hyperdbg_u_pt_mmap(&Mmap)) { - printf("err, pt_mmap failed\n"); - PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE, 0); - TerminateProcess(Pi.hProcess, 1); + printf("[-] pt_mmap failed\n"); + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + TerminateProcess(Process.hProcess, 1); goto Cleanup; } - printf("Intel PT enabled, %u per-CPU buffers mapped:\n", Mmap.NumCpus); - for (UINT32 i = 0; i < Mmap.NumCpus; i++) + printf("[+] PT enabled, %u per-core buffers mapped\n", Mmap.NumCpus); + printf("[*] resuming target and waiting for it to exit...\n"); + + ResumeThread(Process.hThread); + WaitForSingleObject(Process.hProcess, INFINITE); + printf("[+] target exited, decoding trace\n"); + + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE); + + Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; + if (!hyperdbg_u_pt_operation(&Sizes)) { - printf(" core %u : va=0x%016llx size=0x%llx\n", - Mmap.Cpus[i].CpuId, - (unsigned long long)Mmap.Cpus[i].UserVa, - (unsigned long long)Mmap.Cpus[i].Size); - } - - // - // Let the target run to completion - // - printf("resuming target and waiting for it to finish...\n"); - ResumeThread(Pi.hThread); - WaitForSingleObject(Pi.hProcess, INFINITE); - printf("target finished, pausing Intel PT\n"); - - // - // Stop tracing (flushes hardware buffers), then snapshot byte counts - // - PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE, 0); - - Size.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; - if (!hyperdbg_u_pt_operation(&Size)) - { - printf("err, could not query PT sizes\n"); - PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE, 0); + printf("[-] cannot query PT sizes\n"); + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); goto Cleanup; } - // - // Decode each CPU's trace (match the mmap descriptor by CpuId) - // for (UINT32 i = 0; i < Mmap.NumCpus; i++) { UINT32 Cpu = Mmap.Cpus[i].CpuId; - UINT64 Bytes = (Cpu < Size.NumCpus) ? Size.BytesPerCpu[Cpu] : 0; + UINT64 Bytes = (Cpu < Sizes.NumCpus) ? Sizes.BytesPerCpu[Cpu] : 0; if (Bytes == 0) - { continue; - } if (Bytes > Mmap.Cpus[i].Size) - { Bytes = Mmap.Cpus[i].Size; - } - PtDecodeBuffer(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); + printf("\n[*] core %u: %llu bytes of trace\n", Cpu, (unsigned long long)Bytes); + Total += Packets + ? DecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes) + : DecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); } - // - // Disable PT (this also tears down the user mappings) - // - PtDo(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE, 0); + printf("\n[+] decoded %llu %s total\n", (unsigned long long)Total, Packets ? "packet(s)" : "instruction(s)"); + + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); Cleanup: - CloseHandle(Pi.hThread); - CloseHandle(Pi.hProcess); - return 0; + if (g_Code != NULL) + { + free(g_Code); + g_Code = NULL; + } + if (Process.hThread != NULL) + CloseHandle(Process.hThread); + if (Process.hProcess != NULL) + CloseHandle(Process.hProcess); } -/** - * @brief Load the HyperDbg VMM in local (VMI) mode - * - * @return int zero on success - */ static int -hyperdbg_load() +LoadVmm() { - hyperdbg_u_set_text_message_callback(hyperdbg_show_messages); + hyperdbg_u_set_text_message_callback((PVOID)ShowMessages); if (!hyperdbg_u_detect_vmx_support()) { - printf("err, vmx operation is not supported on this processor\n"); + printf("[-] VT-x (VMX) is not supported / enabled on this processor\n"); return 1; } - // - // Install + load the VMM driver (mirrors the CLI's 'load vmm') - // + printf("[*] loading HyperDbg VMM...\n"); if (hyperdbg_u_install_vmm_driver() != 0 || hyperdbg_u_load_vmm() != 0) { - printf("err, could not load the HyperDbg VMM\n"); + printf("[-] cannot load the HyperDbg VMM\n"); return 1; } + printf("[+] HyperDbg VMM is running\n"); return 0; } -/** - * @brief main function - * - * @return int - */ int main(int argc, char ** argv) { + const char * function = "main"; + BOOLEAN packets = FALSE; + int pinCore = 0; + if (argc < 2) { - printf("usage: %s \n", argv[0]); + printf("HyperDbg Intel PT tracer\n"); + printf("usage: %s [function] [-p] [-c core]\n", argv[0]); + printf(" [function] symbol to IP-filter (default 'main'; pass '*' for whole .text)\n"); + printf(" -p dump raw PT packets (TNT/TIP/FUP/PSB/...) instead of instructions\n"); + printf(" -c core pin the target to this logical core (default 0; -1 = unpinned)\n"); return 1; } - if (hyperdbg_load() != 0) + for (int i = 2; i < argc; i++) { - printf("err, in loading HyperDbg\n"); - return 1; + if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--packets") == 0) + packets = TRUE; + else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) + pinCore = atoi(argv[++i]); + else if (strcmp(argv[i], "*") == 0) + function = NULL; + else + function = argv[i]; } - RunAndTrace(argv[1]); + if (LoadVmm() != 0) + return 1; - // - // Turn the hypervisor off - // + RunAndTrace(argv[1], function, packets, pinCore); + + printf("[*] unloading HyperDbg VMM...\n"); hyperdbg_u_unload_vmm(); + hyperdbg_u_stop_vmm_driver(); + hyperdbg_u_uninstall_vmm_driver(); + printf("[+] done\n"); return 0; } diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj index 479232d7..2ba4224e 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj @@ -55,7 +55,7 @@ Set this to the folder that contains 'include\intel-pt.h' and 'lib\libipt.lib'. 'libipt.dll' must be reachable at run time. --> - C:\libipt + C:\Users\masra\Desktop\libipt $(SolutionDir)build\bin\$(Configuration)\ @@ -69,21 +69,25 @@ Level3 true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _DEBUG;_CONSOLE;ZYCORE_STATIC_BUILD;ZYDIS_STATIC_BUILD;%(PreprocessorDefinitions) true MultiThreadedDebug Create pch.h - $(LibIptDir)\include;$(SolutionDir)include;$(ProjectDir)header;%(AdditionalIncludeDirectories) + $(LibIptDir)\include;$(SolutionDir)include;$(SolutionDir)dependencies\zydis\include;$(SolutionDir)dependencies\zydis\dependencies\zycore\include;$(ProjectDir)header;%(AdditionalIncludeDirectories) true Console true $(LibIptDir)\lib;%(AdditionalLibraryDirectories) - $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;libipt.lib - true + $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)libraries\zydis\user\Zycore.lib;libipt.lib + false + + copy /Y "$(LibIptDir)\bin\libipt.dll" "$(OutDir)libipt.dll" + Copying libipt.dll (Intel PT decoder) to output directory + @@ -91,12 +95,12 @@ true true true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + NDEBUG;_CONSOLE;ZYCORE_STATIC_BUILD;ZYDIS_STATIC_BUILD;%(PreprocessorDefinitions) true MultiThreaded Create pch.h - $(LibIptDir)\include;$(SolutionDir)include;$(ProjectDir)header;%(AdditionalIncludeDirectories) + $(LibIptDir)\include;$(SolutionDir)include;$(SolutionDir)dependencies\zydis\include;$(SolutionDir)dependencies\zydis\dependencies\zycore\include;$(ProjectDir)header;%(AdditionalIncludeDirectories) true MaxSpeed @@ -106,9 +110,13 @@ true true $(LibIptDir)\lib;%(AdditionalLibraryDirectories) - $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;libipt.lib + $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)libraries\zydis\user\Zycore.lib;libipt.lib true + + copy /Y "$(LibIptDir)\bin\libipt.dll" "$(OutDir)libipt.dll" + Copying libipt.dll (Intel PT decoder) to output directory + diff --git a/hyperdbg/hypertrace/code/pt/Pt.c b/hyperdbg/hypertrace/code/pt/Pt.c index 97c66007..2c8c03bb 100644 --- a/hyperdbg/hypertrace/code/pt/Pt.c +++ b/hyperdbg/hypertrace/code/pt/Pt.c @@ -348,7 +348,7 @@ PtEngineInitDefaultConfig(PT_TRACE_CONFIG * Config) Config->TargetCr3 = 0; Config->NumAddrRanges = 0; Config->EnableBranch = TRUE; - Config->EnableTsc = TRUE; + Config->EnableTsc = FALSE; Config->EnableMtc = FALSE; Config->EnableCyc = FALSE; Config->EnableRetCompression = FALSE; diff --git a/hyperdbg/hypertrace/header/broadcast/Dpc.h b/hyperdbg/hypertrace/header/broadcast/Dpc.h new file mode 100644 index 00000000..f2fc16e7 --- /dev/null +++ b/hyperdbg/hypertrace/header/broadcast/Dpc.h @@ -0,0 +1,39 @@ +/** + * @file Dpc.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Definition for Windows DPC functions + * @details + * @version 0.19 + * @date 2026-04-19 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +NTKERNELAPI +_IRQL_requires_max_(APC_LEVEL) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID +KeGenericCallDpc( + _In_ PKDEFERRED_ROUTINE Routine, + _In_opt_ PVOID Context); + +NTKERNELAPI +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID +KeSignalCallDpcDone( + _In_ PVOID SystemArgument1); + +NTKERNELAPI +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +LOGICAL +KeSignalCallDpcSynchronize( + _In_ PVOID SystemArgument2); diff --git a/hyperdbg/hypertrace/header/pch.h b/hyperdbg/hypertrace/header/pch.h index 871e2aed..8bdd85e7 100644 --- a/hyperdbg/hypertrace/header/pch.h +++ b/hyperdbg/hypertrace/header/pch.h @@ -67,17 +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) -// -#include "common/UnloadDll.h" - // // Hyperlog headers // diff --git a/hyperdbg/hypertrace/hypertrace.vcxproj b/hyperdbg/hypertrace/hypertrace.vcxproj index 804c776e..57b7c97e 100644 --- a/hyperdbg/hypertrace/hypertrace.vcxproj +++ b/hyperdbg/hypertrace/hypertrace.vcxproj @@ -124,6 +124,7 @@ + diff --git a/hyperdbg/hypertrace/hypertrace.vcxproj.filters b/hyperdbg/hypertrace/hypertrace.vcxproj.filters index c81ca949..445d6430 100644 --- a/hyperdbg/hypertrace/hypertrace.vcxproj.filters +++ b/hyperdbg/hypertrace/hypertrace.vcxproj.filters @@ -112,6 +112,9 @@ header\broadcast + + header\broadcast + header\broadcast From 25871f4e4873c48d47e3f4a78f0fe76a8fa9395c Mon Sep 17 00:00:00 2001 From: sina Date: Wed, 10 Jun 2026 16:03:38 +0200 Subject: [PATCH 04/57] change version --- CHANGELOG.md | 11 ++++++++++- hyperdbg/include/SDK/headers/Constants.h | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b5d5059..b36051a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.20.0.0] - 2026-XX-XX +New release of the HyperDbg Debugger. + +### Added +- + +### Changed +- + ## [0.19.0.0-beta] - 2026-06-10 New release of the HyperDbg Debugger. @@ -13,7 +22,7 @@ New release of the HyperDbg Debugger. - Added Legacy LBR support to the HyperTrace module - Added Architectural LBR support to the HyperTrace module - Added the '!lbr' command for performing different Last Branch Record (LBR) operations ([link](https://docs.hyperdbg.org/commands/extension-commands/lbr)) -- Added the '!lbrdump' command for dumping saved Last Branch Record (LBR) entries ([link](https://docs.hyperdbg.org/commands/extension-commands/lbrdmp)) +- Added the '!lbrdump' command for dumping saved Last Branch Record (LBR) entries ([link](https://docs.hyperdbg.org/commands/extension-commands/lbrdump)) - Added **lbr_save()** and **lbr_print()** functions in the script engine ([link](https://docs.hyperdbg.org/commands/scripting-language/functions/tracing/lbr/lbr_save))([link](https://docs.hyperdbg.org/commands/scripting-language/functions/tracing/lbr/lbr_print)) - Added mock application for compiling SDK for Linux - Added '!help' alias for the '.help' command diff --git a/hyperdbg/include/SDK/headers/Constants.h b/hyperdbg/include/SDK/headers/Constants.h index 0c893b51..2f3c508d 100644 --- a/hyperdbg/include/SDK/headers/Constants.h +++ b/hyperdbg/include/SDK/headers/Constants.h @@ -18,10 +18,10 @@ ////////////////////////////////////////////////// #define VERSION_MAJOR 0 -#define VERSION_MINOR 19 +#define VERSION_MINOR 20 #define VERSION_PATCH 0 -#define BETA_VERSION 1 +#define BETA_VERSION 0 // // Example of __DATE__ string: "Jul 27 2012" From 0e39c24f28c5741390f7574a177a76e5fce5a596 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 17:08:21 +0200 Subject: [PATCH 05/57] updates to cmake file for linux --- hyperdbg/CMakeLists.txt | 28 ++++++++++------------------ hyperdbg/libhyperdbg/CMakeLists.txt | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/hyperdbg/CMakeLists.txt b/hyperdbg/CMakeLists.txt index dce26678..bf2267a7 100644 --- a/hyperdbg/CMakeLists.txt +++ b/hyperdbg/CMakeLists.txt @@ -7,7 +7,6 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) if(LINUX) - add_subdirectory(script-engine) message(STATUS "Building on Linux") elseif(WIN32) message(STATUS "Building on Windows") @@ -53,36 +52,29 @@ target_link_libraries(hyperhv Zycore Zydis) add_subdirectory(hyperkd) target_link_libraries(hyperkd hyperlog hyperhv kdserial) +endif() -add_subdirectory(dependencies/pdbex/Source) +#add_subdirectory(dependencies/pdbex/Source) -add_subdirectory(symbol-parser) -target_link_libraries(symbol-parser pdbex) +#add_subdirectory(symbol-parser) +#target_link_libraries(symbol-parser pdbex) +#target_link_libraries(script-engine symbol-parser) -target_link_libraries(script-engine symbol-parser) - -link_directories(libraries/zydis/user libraries/keystone/release-lib) -add_subdirectory(libhyperdbg) -target_link_libraries(libhyperdbg Zycore Zydis script-engine keystone) - - -add_subdirectory(hyperdbg-test) +#add_subdirectory(hyperdbg-test) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") -add_subdirectory(symbol-parser) -target_link_libraries(symbol-parser pdbex) +#add_subdirectory(symbol-parser) +#target_link_libraries(symbol-parser pdbex) +#add_subdirectory(script-engine) +#target_link_libraries(script-engine symbol-parser) add_subdirectory(script-engine) -target_link_libraries(script-engine symbol-parser) - link_directories(libraries/zydis/user libraries/keystone/release-lib) add_subdirectory(libhyperdbg) target_link_libraries(libhyperdbg Zycore Zydis script-engine keystone) -endif() - add_subdirectory(hyperdbg-cli) target_link_libraries(hyperdbg-cli libhyperdbg) diff --git a/hyperdbg/libhyperdbg/CMakeLists.txt b/hyperdbg/libhyperdbg/CMakeLists.txt index 91d5ce95..29324cff 100644 --- a/hyperdbg/libhyperdbg/CMakeLists.txt +++ b/hyperdbg/libhyperdbg/CMakeLists.txt @@ -1,6 +1,6 @@ # Code generated by Visual Studio kit, DO NOT EDIT. set(SourceFiles - "../include/platform/user/header/Environment.h" + "../include/platform/general/header/Environment.h" "../include/platform/user/header/Windows.h" "header/assembler.h" "header/commands.h" @@ -27,6 +27,8 @@ set(SourceFiles "header/transparency.h" "header/ud.h" "pch.h" + "../include/platform/user/code/platform-intrinsics.c" + "../include/platform/user/code/platform-lib-calls.c" "../script-eval/code/Functions.c" "../script-eval/code/Keywords.c" "../script-eval/code/PseudoRegisters.c" @@ -166,4 +168,15 @@ include_directories( "../script-eval" "../dependencies/keystone/include" ) +set_source_files_properties( + "../include/platform/user/code/platform-intrinsics.c" + "../include/platform/user/code/platform-lib-calls.c" + "../script-eval/code/Functions.c" + "../script-eval/code/Keywords.c" + "../script-eval/code/PseudoRegisters.c" + "../script-eval/code/Regs.c" + "../script-eval/code/ScriptEngineEval.c" + PROPERTIES LANGUAGE CXX +) + add_library(libhyperdbg SHARED ${SourceFiles}) From 9c3e0ed23b5f4bb64bd305e546dd0e86ee1910d9 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 17:15:14 +0200 Subject: [PATCH 06/57] added Linux distinction and pragmas together with new platform functions --- hyperdbg/include/SDK/headers/BasicTypes.h | 10 +- .../components/pe/header/pe-image-reader.h | 8 +- .../platform/general/header/Environment.h | 40 ++++- .../platform/user/code/platform-intrinsics.c | 104 ++++++++++- .../platform/user/code/platform-lib-calls.c | 169 ++++++++++++++++++ .../user/header/platform-intrinsics.h | 30 +++- .../platform/user/header/platform-lib-calls.h | 30 ++++ hyperdbg/libhyperdbg/code/app/messaging.cpp | 2 +- hyperdbg/libhyperdbg/code/common/spinlock.cpp | 2 +- hyperdbg/libhyperdbg/pch.h | 76 +++++--- hyperdbg/script-eval/code/Functions.c | 28 +-- hyperdbg/script-eval/code/PseudoRegisters.c | 57 +++--- 12 files changed, 470 insertions(+), 86 deletions(-) diff --git a/hyperdbg/include/SDK/headers/BasicTypes.h b/hyperdbg/include/SDK/headers/BasicTypes.h index 0b0d1963..91b8a546 100644 --- a/hyperdbg/include/SDK/headers/BasicTypes.h +++ b/hyperdbg/include/SDK/headers/BasicTypes.h @@ -72,7 +72,8 @@ typedef wchar_t WCHAR; typedef void VOID; -typedef size_t SIZE_T; +typedef size_t SIZE_T; +typedef SIZE_T * PSIZE_T; typedef signed long long INT64, *PINT64; typedef unsigned long long UINT64, *PUINT64; @@ -83,6 +84,13 @@ typedef unsigned long long DWORD64, *PDWORD64; typedef unsigned long long ULONGLONG; typedef unsigned long long ULONG_PTR, *PULONG_PTR; +typedef INT64 LONGLONG; + +typedef union _LARGE_INTEGER { + struct { DWORD LowPart; LONG HighPart; }; + LONGLONG QuadPart; +} LARGE_INTEGER, *PLARGE_INTEGER; + // // To be fixed later, linux wchar_t is 4 bytes, but windows wchar_t is 2 bytes // diff --git a/hyperdbg/include/components/pe/header/pe-image-reader.h b/hyperdbg/include/components/pe/header/pe-image-reader.h index bc4e0852..61fd23a5 100644 --- a/hyperdbg/include/components/pe/header/pe-image-reader.h +++ b/hyperdbg/include/components/pe/header/pe-image-reader.h @@ -11,7 +11,9 @@ */ #pragma once -/* +#ifndef _WIN32 +// On Windows these are provided by winnt.h; define them here for other platforms. + typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number @@ -46,7 +48,7 @@ typedef struct _IMAGE_FILE_HEADER WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; -#define IMAGE_SIZEOF_SHORT_NAME 8 +# define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { @@ -66,7 +68,7 @@ typedef struct _IMAGE_SECTION_HEADER DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; -*/ +#endif // _WIN32 typedef struct _PE_IMAGE_READER { diff --git a/hyperdbg/include/platform/general/header/Environment.h b/hyperdbg/include/platform/general/header/Environment.h index 55c5f29a..a5920d71 100644 --- a/hyperdbg/include/platform/general/header/Environment.h +++ b/hyperdbg/include/platform/general/header/Environment.h @@ -32,9 +32,45 @@ -// Windows source annotation language (SAL) not present in Linux, so defining them here for the compiler #ifdef HYPERDBG_ENV_LINUX + +// SAL annotations # define _In_ # define _Out_ # define _Inout_ -#endif +# 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 + +// 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; + +// Windows socket type (Linux sockets are plain int) +typedef int SOCKET; +# define INVALID_SOCKET ((SOCKET)(-1)) +# define SOCKET_ERROR (-1) + +// Windows calling convention (no-op on Linux) +# define WINAPI + +// Windows module handle (equivalent to dlopen's void * on Linux) +typedef void * HMODULE; + +// Misc Windows macros +# define UNREFERENCED_PARAMETER(P) ((void)(P)) + +#endif // HYPERDBG_ENV_LINUX diff --git a/hyperdbg/include/platform/user/code/platform-intrinsics.c b/hyperdbg/include/platform/user/code/platform-intrinsics.c index 7340662f..805c78e9 100644 --- a/hyperdbg/include/platform/user/code/platform-intrinsics.c +++ b/hyperdbg/include/platform/user/code/platform-intrinsics.c @@ -83,10 +83,32 @@ CpuReadTsc(VOID) // Misc Instructions // ////////////////////////////////////////////////// +/** + * @brief Read Time-Stamp Counter (serializing) + * + * @param Aux processor ID output (may be NULL) + * @return UINT64 + */ +UINT64 +CpuReadTscp(UINT32 * Aux) +{ +#if defined(_WIN32) || defined(_WIN64) + return __rdtscp(Aux); +#elif defined(__linux__) + UINT32 __lo, __hi, __aux; + __asm__ __volatile__("rdtscp" : "=a"(__lo), "=d"(__hi), "=c"(__aux)); + if (Aux) + *Aux = __aux; + return ((UINT64)__hi << 32) | __lo; +#else +# error "Unsupported platform" +#endif +} + /** * @brief Execute PAUSE (spin-wait hint) */ - VOID +VOID CpuPause(VOID) { #if defined(_WIN32) || defined(_WIN64) @@ -97,3 +119,83 @@ CpuPause(VOID) # error "Unsupported platform" #endif } + +////////////////////////////////////////////////// +// Interlocked (Atomic) Operations // +////////////////////////////////////////////////// + +INT64 +CpuInterlockedExchange64(INT64 volatile * Target, INT64 Value) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedExchange64(Target, Value); +#elif defined(__linux__) + return __atomic_exchange_n(Target, Value, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +INT64 +CpuInterlockedExchangeAdd64(INT64 volatile * Addend, INT64 Value) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedExchangeAdd64(Addend, Value); +#elif defined(__linux__) + return __atomic_fetch_add(Addend, Value, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +INT64 +CpuInterlockedIncrement64(INT64 volatile * Addend) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedIncrement64(Addend); +#elif defined(__linux__) + return __atomic_add_fetch(Addend, 1LL, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +INT64 +CpuInterlockedDecrement64(INT64 volatile * Addend) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedDecrement64(Addend); +#elif defined(__linux__) + return __atomic_sub_fetch(Addend, 1LL, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +INT64 +CpuInterlockedCompareExchange64(INT64 volatile * Destination, INT64 ExChange, INT64 Comparand) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedCompareExchange64(Destination, ExChange, Comparand); +#elif defined(__linux__) + INT64 Expected = Comparand; + __atomic_compare_exchange_n(Destination, &Expected, ExChange, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return Expected; +#else +# error "Unsupported platform" +#endif +} + +UCHAR +CpuInterlockedBitTestAndSet(volatile LONG * Base, LONG Bit) +{ +#if defined(_WIN32) || defined(_WIN64) + return _interlockedbittestandset(Base, Bit); +#elif defined(__linux__) + LONG Mask = (1L << Bit); + LONG Old = __atomic_fetch_or(Base, Mask, __ATOMIC_SEQ_CST); + return (UCHAR)((Old >> Bit) & 1); +#else +# error "Unsupported platform" +#endif +} diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index 803a253a..37d922b0 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -13,6 +13,9 @@ #if defined(__linux__) # include "../header/platform-lib-calls.h" +# include +# include +# include #endif // defined(__linux__) /** @@ -71,3 +74,169 @@ PlatformZeroMemory(PVOID Buffer, SIZE_T Size) # error "Unsupported platform" #endif } + +/** + * @brief Platform independent wrapper for QueryPerformanceFrequency + * + * @param Frequency output — ticks per second + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformQueryPerformanceFrequency(LARGE_INTEGER * Frequency) +{ +#if defined(_WIN32) + return (BOOLEAN)QueryPerformanceFrequency((LARGE_INTEGER *)Frequency); +#elif defined(__linux__) + Frequency->QuadPart = 1000000000LL; // clock_gettime gives nanosecond resolution + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for QueryPerformanceCounter + * + * @param Count output — current tick count + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformQueryPerformanceCounter(LARGE_INTEGER * Count) +{ +#if defined(_WIN32) + return (BOOLEAN)QueryPerformanceCounter((LARGE_INTEGER *)Count); +#elif defined(__linux__) + struct timespec Ts; + clock_gettime(CLOCK_MONOTONIC, &Ts); + Count->QuadPart = (INT64)Ts.tv_sec * 1000000000LL + Ts.tv_nsec; + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for sprintf_s / snprintf + * + * @param Buffer output buffer + * @param BufferSize size of the output buffer + * @param Format format string + * @return INT number of characters written, or -1 on error + */ +INT +PlatformSprintf(char * Buffer, SIZE_T BufferSize, const char * Format, ...) +{ + va_list Args; + va_start(Args, Format); + INT Result; +#if defined(_WIN32) + Result = vsprintf_s(Buffer, BufferSize, Format, Args); +#elif defined(__linux__) + Result = vsnprintf(Buffer, BufferSize, Format, Args); +#else +# error "Unsupported platform" +#endif + va_end(Args); + return Result; +} + +/** + * @brief Platform independent wrapper for GetCurrentThreadId / gettid + * + * @return UINT32 thread ID of the calling thread + */ +UINT32 +PlatformGetCurrentThreadId(VOID) +{ +#if defined(_WIN32) + return (UINT32)GetCurrentThreadId(); +#elif defined(__linux__) + return (UINT32)syscall(SYS_gettid); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for GetCurrentProcessorNumber / sched_getcpu + * + * @return UINT32 logical processor index the calling thread is running on + */ +UINT32 +PlatformGetCurrentProcessorNumber(VOID) +{ +#if defined(_WIN32) + return (UINT32)GetCurrentProcessorNumber(); +#elif defined(__linux__) + return (UINT32)sched_getcpu(); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for GetCurrentProcessId / getpid + * + * @return UINT32 PID of the calling process + */ +UINT32 +PlatformGetCurrentProcessId(VOID) +{ +#if defined(_WIN32) + return (UINT32)GetCurrentProcessId(); +#elif defined(__linux__) + return (UINT32)getpid(); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to get the current process name + * + * @return CHAR* pointer to a static buffer holding the process name, or NULL on failure + */ +CHAR * +PlatformGetCurrentProcessName(VOID) +{ + static CHAR ProcessNameBuf[MAX_PATH] = {0}; + +#if defined(_WIN32) + HANDLE Handle = OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, + GetCurrentProcessId()); + + if (Handle) + { + CHAR ModulePath[MAX_PATH] = {0}; + if (GetModuleFileNameEx(Handle, 0, ModulePath, MAX_PATH)) + { + CloseHandle(Handle); + strncpy(ProcessNameBuf, PathFindFileNameA(ModulePath), MAX_PATH - 1); + ProcessNameBuf[MAX_PATH - 1] = '\0'; + return ProcessNameBuf; + } + CloseHandle(Handle); + } + return NULL; + +#elif defined(__linux__) + FILE * f = fopen("/proc/self/comm", "r"); + if (f) + { + if (fgets(ProcessNameBuf, sizeof(ProcessNameBuf), f)) + { + size_t Len = strlen(ProcessNameBuf); + if (Len > 0 && ProcessNameBuf[Len - 1] == '\n') + ProcessNameBuf[Len - 1] = '\0'; + } + fclose(f); + return ProcessNameBuf; + } + return NULL; + +#else +# error "Unsupported platform" +#endif +} diff --git a/hyperdbg/include/platform/user/header/platform-intrinsics.h b/hyperdbg/include/platform/user/header/platform-intrinsics.h index c2334a1d..f90b7c90 100644 --- a/hyperdbg/include/platform/user/header/platform-intrinsics.h +++ b/hyperdbg/include/platform/user/header/platform-intrinsics.h @@ -41,6 +41,12 @@ CpuCpuIdEx(INT32 * CpuInfo, INT32 FunctionId, INT32 SubFunctionId); UINT64 CpuReadTsc(VOID); +// +// RDTSCP +// +UINT64 +CpuReadTscp(UINT32 * Aux); + ////////////////////////////////////////////////// // Misc Instructions // ////////////////////////////////////////////////// @@ -49,4 +55,26 @@ CpuReadTsc(VOID); // PAUSE // VOID - CpuPause(VOID); +CpuPause(VOID); + +////////////////////////////////////////////////// +// Interlocked (Atomic) Operations // +////////////////////////////////////////////////// + +INT64 +CpuInterlockedExchange64(INT64 volatile * Target, INT64 Value); + +INT64 +CpuInterlockedExchangeAdd64(INT64 volatile * Addend, INT64 Value); + +INT64 +CpuInterlockedIncrement64(INT64 volatile * Addend); + +INT64 +CpuInterlockedDecrement64(INT64 volatile * Addend); + +INT64 +CpuInterlockedCompareExchange64(INT64 volatile * Destination, INT64 ExChange, INT64 Comparand); + +UCHAR +CpuInterlockedBitTestAndSet(volatile LONG * Base, LONG Bit); diff --git a/hyperdbg/include/platform/user/header/platform-lib-calls.h b/hyperdbg/include/platform/user/header/platform-lib-calls.h index a449758c..a19d2b3d 100644 --- a/hyperdbg/include/platform/user/header/platform-lib-calls.h +++ b/hyperdbg/include/platform/user/header/platform-lib-calls.h @@ -35,3 +35,33 @@ PlatformStrDup(const char * Str); // VOID PlatformZeroMemory(PVOID Buffer, SIZE_T Size); + +// +// SPRINTF +// +INT +PlatformSprintf(char * Buffer, SIZE_T BufferSize, const char * Format, ...); + +// +// HIGH-RESOLUTION PERFORMANCE COUNTER +// +BOOLEAN +PlatformQueryPerformanceFrequency(LARGE_INTEGER * Frequency); + +BOOLEAN +PlatformQueryPerformanceCounter(LARGE_INTEGER * Count); + +// +// PROCESS / THREAD IDENTITY +// +UINT32 +PlatformGetCurrentThreadId(VOID); + +UINT32 +PlatformGetCurrentProcessorNumber(VOID); + +UINT32 +PlatformGetCurrentProcessId(VOID); + +CHAR * +PlatformGetCurrentProcessName(VOID); diff --git a/hyperdbg/libhyperdbg/code/app/messaging.cpp b/hyperdbg/libhyperdbg/code/app/messaging.cpp index c8d903f3..ca0f7264 100644 --- a/hyperdbg/libhyperdbg/code/app/messaging.cpp +++ b/hyperdbg/libhyperdbg/code/app/messaging.cpp @@ -103,7 +103,7 @@ ShowMessages(const CHAR * Fmt, ...) va_start(ArgList, Fmt); - INT SprintfResult = vsprintf_s(TempMessage, Fmt, ArgList); + INT SprintfResult = PlatformVsnprintf(TempMessage, sizeof(TempMessage), Fmt, ArgList); va_end(ArgList); diff --git a/hyperdbg/libhyperdbg/code/common/spinlock.cpp b/hyperdbg/libhyperdbg/code/common/spinlock.cpp index 39ebb82c..96cc5e0c 100644 --- a/hyperdbg/libhyperdbg/code/common/spinlock.cpp +++ b/hyperdbg/libhyperdbg/code/common/spinlock.cpp @@ -40,7 +40,7 @@ static UINT32 MaxWait = 65536; BOOLEAN SpinlockTryLock(volatile LONG * Lock) { - return (!(*Lock) && !_interlockedbittestandset(Lock, 0)); + return (!(*Lock) && !CpuInterlockedBitTestAndSet(Lock, 0)); } /** diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index dffab754..a2be9aac 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -41,6 +41,8 @@ typedef RFLAGS * PRFLAGS; #define USE_NATIVE_SDK_HEADERS #define _AMD64_ + +#ifdef _WIN32 #if defined(USE__NATIVE_PHNT_HEADERS) // @@ -64,18 +66,22 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include +#endif //_WIN32 + +#ifdef _WIN32 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif #include -#include -#include #include #include #include @@ -117,11 +123,6 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; # define NDEBUG #endif // !NDEBUG -// -// Keystone -// -#include "keystone/keystone.h" - // // HyperDbg defined headers // @@ -129,6 +130,11 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "config/Definition.h" #include "SDK/HyperDbgSdk.h" +// +// Keystone +// +#include "keystone/keystone.h" + // // Script-engine // @@ -141,10 +147,21 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "SDK/imports/user/HyperDbgLibImports.h" // -// Platform-specific intrinsics +// Platform lib calls (cross-platform wrappers) +// +#include "platform/user/header/platform-lib-calls.h" + +// +// Platform intrinsics (cross-platform CPU instructions and atomic ops) // #include "platform/user/header/platform-intrinsics.h" -#include "platform/user/header/windows-only/windows-privilege.h" + +// +// Platform-specific intrinsics +// +#ifdef _WIN32 +# include "platform/user/header/windows-only/windows-privilege.h" +#endif // // PCI IDs @@ -159,11 +176,15 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "header/inipp.h" #include "header/commands.h" #include "header/common.h" -#include "header/symbol.h" +#ifdef _WIN32 +# include "header/symbol.h" +#endif #include "header/debugger.h" #include "header/script-engine.h" #include "header/help.h" -#include "header/install.h" +#ifdef _WIN32 +# include "header/install.h" +#endif #include "header/list.h" #include "header/tests.h" #include "header/messaging.h" @@ -172,7 +193,15 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "header/communication.h" #include "header/namedpipe.h" #include "header/forwarding.h" -#include "header/kd.h" +#ifdef _WIN32 +# include "header/kd.h" +#endif + +// +// Components +// +#include "../include/components/pe/header/pe-image-reader.h" + #include "header/pe-parser.h" #include "header/ud.h" #include "header/objects.h" @@ -180,11 +209,6 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "header/rev-ctrl.h" #include "header/assembler.h" -// -// Components -// -#include "../include/components/pe/header/pe-image-reader.h" - // // hwdbg // diff --git a/hyperdbg/script-eval/code/Functions.c b/hyperdbg/script-eval/code/Functions.c index 46562663..17b0fc0e 100644 --- a/hyperdbg/script-eval/code/Functions.c +++ b/hyperdbg/script-eval/code/Functions.c @@ -797,16 +797,16 @@ VOID UserModeMicroSleep(UINT64 Us) { LARGE_INTEGER Start, End, Frequency; - QueryPerformanceFrequency(&Frequency); + PlatformQueryPerformanceFrequency(&Frequency); LONGLONG TickPerUs = Frequency.QuadPart / 1000000; LONGLONG Ticks = TickPerUs * Us; - QueryPerformanceCounter(&Start); + PlatformQueryPerformanceCounter(&Start); while (TRUE) { - QueryPerformanceCounter(&End); + PlatformQueryPerformanceCounter(&End); if (End.QuadPart - Start.QuadPart > Ticks) { @@ -840,7 +840,7 @@ ScriptEngineFunctionMicroSleep(UINT64 Us) UINT64 ScriptEngineFunctionRdtsc() { - return __rdtsc(); + return CpuReadTsc(); } /** @@ -850,8 +850,8 @@ ScriptEngineFunctionRdtsc() UINT64 ScriptEngineFunctionRdtscp() { - unsigned int Aux; - return __rdtscp(&Aux); + UINT32 Aux; + return CpuReadTscp(&Aux); } /** @@ -879,7 +879,7 @@ ScriptEngineFunctionInterlockedExchange(long long volatile * Target, #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedExchange64(Target, Value); + Result = CpuInterlockedExchange64(Target, Value); return Result; } @@ -909,7 +909,7 @@ ScriptEngineFunctionInterlockedExchangeAdd(long long volatile * Addend, #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedExchangeAdd64(Addend, Value); + Result = CpuInterlockedExchangeAdd64(Addend, Value); return Result; } @@ -937,7 +937,7 @@ ScriptEngineFunctionInterlockedIncrement(long long volatile * Addend, #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedIncrement64(Addend); + Result = CpuInterlockedIncrement64(Addend); return Result; } @@ -965,7 +965,7 @@ ScriptEngineFunctionInterlockedDecrement(long long volatile * Addend, #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedDecrement64(Addend); + Result = CpuInterlockedDecrement64(Addend); return Result; } @@ -998,7 +998,7 @@ ScriptEngineFunctionInterlockedCompareExchange( #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedCompareExchange64(Destination, ExChange, Comperand); + Result = CpuInterlockedCompareExchange64(Destination, ExChange, Comperand); return Result; } @@ -1338,7 +1338,7 @@ ApplyFormatSpecifier(const CHAR * CurrentSpecifier, CHAR * FinalBuffer, PUINT32 *CurrentProcessedPositionFromStartOfFormat = *CurrentProcessedPositionFromStartOfFormat + (UINT32)strlen(CurrentSpecifier); - sprintf_s(TempBuffer, sizeof(TempBuffer), CurrentSpecifier, Val); + PlatformSprintf(TempBuffer, sizeof(TempBuffer), CurrentSpecifier, Val); TempBufferLen = (UINT32)strlen(TempBuffer); // @@ -1473,8 +1473,8 @@ ApplyStringFormatSpecifier(const CHAR * CurrentSpecifier, CHAR * FinalBuffer, PU // // Zero the buffers // - RtlZeroMemory(WstrBuffer, sizeof(WstrBuffer)); - RtlZeroMemory(AsciiBuffer, sizeof(AsciiBuffer)); + PlatformZeroMemory(WstrBuffer, sizeof(WstrBuffer)); + PlatformZeroMemory(AsciiBuffer, sizeof(AsciiBuffer)); // // Check for the last block diff --git a/hyperdbg/script-eval/code/PseudoRegisters.c b/hyperdbg/script-eval/code/PseudoRegisters.c index a3a46178..493c8a6f 100644 --- a/hyperdbg/script-eval/code/PseudoRegisters.c +++ b/hyperdbg/script-eval/code/PseudoRegisters.c @@ -25,7 +25,7 @@ UINT64 ScriptEnginePseudoRegGetTid() { #ifdef SCRIPT_ENGINE_USER_MODE - return (UINT64)GetCurrentThreadId(); + return (UINT64)PlatformGetCurrentThreadId(); #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE @@ -42,7 +42,7 @@ UINT64 ScriptEnginePseudoRegGetCore() { #ifdef SCRIPT_ENGINE_USER_MODE - return (UINT64)GetCurrentProcessorNumber(); + return (UINT64)PlatformGetCurrentProcessorNumber(); #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE @@ -59,7 +59,7 @@ UINT64 ScriptEnginePseudoRegGetPid() { #ifdef SCRIPT_ENGINE_USER_MODE - return (UINT64)GetCurrentProcessId(); + return (UINT64)PlatformGetCurrentProcessId(); #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE @@ -76,39 +76,7 @@ CHAR * ScriptEnginePseudoRegGetPname() { #ifdef SCRIPT_ENGINE_USER_MODE - - HANDLE Handle = OpenProcess( - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - FALSE, - GetCurrentProcessId() /* Current process */ - ); - - if (Handle) - { - CHAR CurrentModulePath[MAX_PATH] = {0}; - if (GetModuleFileNameEx(Handle, 0, CurrentModulePath, MAX_PATH)) - { - // - // At this point, buffer contains the full path to the executable - // - CloseHandle(Handle); - return PathFindFileNameA(CurrentModulePath); - } - else - { - // - // error might be shown by GetLastError() - // - CloseHandle(Handle); - return NULL; - } - } - - // - // unable to get handle - // - return NULL; - + return PlatformGetCurrentProcessName(); #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE @@ -159,6 +127,7 @@ UINT64 ScriptEnginePseudoRegGetPeb() { #ifdef SCRIPT_ENGINE_USER_MODE +# ifdef _WIN32 // // Hand-rolled structs ( may cause conflict depending on your dev env ) // @@ -269,6 +238,22 @@ ScriptEnginePseudoRegGetPeb() return (UINT64)PebPtr; +# else // !_WIN32 + + // + // The PEB (Process Environment Block) is a Windows NT-specific structure + // maintained by the kernel for every user-mode process. It holds loader + // data, heap pointers, the image path, and other process-wide state that + // has no direct equivalent in the Linux process model. + // + // TODO: investigate whether a meaningful substitute exists on Linux + // (e.g. /proc/self/maps, dl_iterate_phdr, or a custom auxv walk) + // and implement it here if HyperDbg user-mode on Linux ever needs $peb. + // + return 0; + +# endif // _WIN32 + #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE From e048f5b822e8a30b637d2a82bac75e4595889c5e Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 17:17:01 +0200 Subject: [PATCH 07/57] added call to platform api and new TODO --- .../debugger/commands/debugging-commands/a.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp index 05f2c882..81cccb93 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp @@ -238,6 +238,7 @@ CommandAssemble(vector CommandTokens, string Command) // if (!CMD.AddressStr.empty()) // was any address provided to assemble to? { +#ifdef _WIN32 if (!SymbolConvertNameOrExprToAddress(CMD.AddressStr, &Address)) { ShowMessages("err, couldn't resolve Address at '%s'\n\n", @@ -245,9 +246,15 @@ CommandAssemble(vector CommandTokens, string Command) CommandAssembleHelp(); return; } +#else + // TODO: symbol resolution is not yet implemented on Linux + ShowMessages("err, symbol resolution is not supported on Linux yet\n\n"); + return; +#endif } else if (!CMD.StartAddressStr.empty()) // was a custom start_address provided? { +#ifdef _WIN32 if (!SymbolConvertNameOrExprToAddress(CMD.StartAddressStr, &StartAddress)) { ShowMessages("err, couldn't resolve Address at '%s'\n\n", @@ -256,6 +263,11 @@ CommandAssemble(vector CommandTokens, string Command) return; } Address = StartAddress; +#else + // TODO: symbol resolution is not yet implemented on Linux + ShowMessages("err, symbol resolution is not supported on Linux yet\n\n"); + return; +#endif } else { @@ -275,7 +287,7 @@ CommandAssemble(vector CommandTokens, string Command) if (ProcId == 0) { - ProcId = GetCurrentProcessId(); + ProcId = PlatformGetCurrentProcessId(); } if (Address) // was the user only trying to get the bytes? From c2c0030f1bf0d97a9e372d299f66307e2f9e6633 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 17:19:35 +0200 Subject: [PATCH 08/57] Switching cores --- .../code/debugger/commands/debugging-commands/core.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/core.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/core.cpp index 2a1852db..de8cd321 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/core.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/core.cpp @@ -82,6 +82,11 @@ CommandCore(vector 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 } } From 5a02c43cc19d0b6bd0cbfd55c2bbb58af31071bb Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 20:04:18 +0200 Subject: [PATCH 09/57] New platform lib calls --- .../platform/user/code/platform-lib-calls.c | 114 ++++++++++++++++++ .../platform/user/header/platform-lib-calls.h | 24 ++++ 2 files changed, 138 insertions(+) diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index 37d922b0..61281bb8 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -16,6 +16,8 @@ # include # include # include +# include +# include #endif // defined(__linux__) /** @@ -240,3 +242,115 @@ PlatformGetCurrentProcessName(VOID) # error "Unsupported platform" #endif } + +/** + * @brief Platform independent wrapper for CreateEvent + * + * @param ManualReset TRUE for a manual-reset event, FALSE for auto-reset + * @param InitialState TRUE if the event starts signaled + * @return HANDLE to the event, or NULL on failure + */ +HANDLE +PlatformCreateEvent(BOOLEAN ManualReset, BOOLEAN InitialState) +{ +#if defined(_WIN32) + return CreateEvent(NULL, ManualReset, InitialState, NULL); +#elif defined(__linux__) + // + // TODO: back this with a pthread mutex+cond (or eventfd) when the Linux + // kernel-debugger transport is implemented. For now return a dummy + // non-NULL handle so existing NULL-checks treat creation as success. + // + (void)ManualReset; + (void)InitialState; + return (HANDLE)(uintptr_t)1; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for SetEvent + */ +BOOLEAN +PlatformSetEvent(HANDLE EventHandle) +{ +#if defined(_WIN32) + return (BOOLEAN)SetEvent(EventHandle); +#elif defined(__linux__) + (void)EventHandle; // TODO: signal the underlying cond/eventfd + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for ResetEvent + */ +BOOLEAN +PlatformResetEvent(HANDLE EventHandle) +{ +#if defined(_WIN32) + return (BOOLEAN)ResetEvent(EventHandle); +#elif defined(__linux__) + (void)EventHandle; // TODO: clear the underlying cond/eventfd + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for WaitForSingleObject + * + * @return 0 (WAIT_OBJECT_0) on success + */ +DWORD +PlatformWaitForSingleObject(HANDLE Handle, DWORD TimeoutMilliseconds) +{ +#if defined(_WIN32) + return WaitForSingleObject(Handle, TimeoutMilliseconds); +#elif defined(__linux__) + // + // TODO: wait on the underlying cond/eventfd. For now return immediately as + // success — no real transport exists yet to wait on. + // + (void)Handle; + (void)TimeoutMilliseconds; + return 0; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for CloseHandle + */ +BOOLEAN +PlatformCloseHandle(HANDLE Handle) +{ +#if defined(_WIN32) + return (BOOLEAN)CloseHandle(Handle); +#elif defined(__linux__) + (void)Handle; // TODO: free the underlying cond/eventfd or close the fd + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for GetLastError + */ +DWORD +PlatformGetLastError(VOID) +{ +#if defined(_WIN32) + return GetLastError(); +#elif defined(__linux__) + return (DWORD)errno; +#else +# error "Unsupported platform" +#endif +} diff --git a/hyperdbg/include/platform/user/header/platform-lib-calls.h b/hyperdbg/include/platform/user/header/platform-lib-calls.h index a19d2b3d..bf74c760 100644 --- a/hyperdbg/include/platform/user/header/platform-lib-calls.h +++ b/hyperdbg/include/platform/user/header/platform-lib-calls.h @@ -51,6 +51,30 @@ PlatformQueryPerformanceFrequency(LARGE_INTEGER * Frequency); BOOLEAN PlatformQueryPerformanceCounter(LARGE_INTEGER * Count); +// +// EVENT / SYNCHRONIZATION OBJECTS +// +HANDLE +PlatformCreateEvent(BOOLEAN ManualReset, BOOLEAN InitialState); + +BOOLEAN +PlatformSetEvent(HANDLE EventHandle); + +BOOLEAN +PlatformResetEvent(HANDLE EventHandle); + +DWORD +PlatformWaitForSingleObject(HANDLE Handle, DWORD TimeoutMilliseconds); + +BOOLEAN +PlatformCloseHandle(HANDLE Handle); + +// +// LAST OS ERROR +// +DWORD +PlatformGetLastError(VOID); + // // PROCESS / THREAD IDENTITY // From 853ae9d5fe26390bbce941db746d33fe8aaea5e3 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 20:04:51 +0200 Subject: [PATCH 10/57] added defs to linux environment --- hyperdbg/include/platform/general/header/Environment.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hyperdbg/include/platform/general/header/Environment.h b/hyperdbg/include/platform/general/header/Environment.h index a5920d71..652f8e3b 100644 --- a/hyperdbg/include/platform/general/header/Environment.h +++ b/hyperdbg/include/platform/general/header/Environment.h @@ -73,4 +73,8 @@ typedef void * HMODULE; // Misc Windows macros # 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 + #endif // HYPERDBG_ENV_LINUX From 31b514f8df40d81d44ae07a2328c02360ce488c3 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 20:05:38 +0200 Subject: [PATCH 11/57] Interface Linux for the serial port --- .../platform/user/code/platform-serial.c | 270 ++++++++++++++++++ .../platform/user/header/platform-serial.h | 70 +++++ hyperdbg/libhyperdbg/CMakeLists.txt | 7 + .../commands/debugging-commands/a.cpp | 12 - .../commands/debugging-commands/dt-struct.cpp | 2 +- .../code/debugger/kernel-level/kd.cpp | 46 +++ .../debugger/script-engine/symbol-linux.cpp | 107 +++++++ hyperdbg/libhyperdbg/header/common.h | 4 +- hyperdbg/libhyperdbg/header/globals.h | 2 + hyperdbg/libhyperdbg/header/kd.h | 3 + hyperdbg/libhyperdbg/header/symbol.h | 3 + hyperdbg/libhyperdbg/pch.h | 8 +- 12 files changed, 513 insertions(+), 21 deletions(-) create mode 100644 hyperdbg/include/platform/user/code/platform-serial.c create mode 100644 hyperdbg/include/platform/user/header/platform-serial.h create mode 100644 hyperdbg/libhyperdbg/code/debugger/script-engine/symbol-linux.cpp diff --git a/hyperdbg/include/platform/user/code/platform-serial.c b/hyperdbg/include/platform/user/code/platform-serial.c new file mode 100644 index 00000000..29224214 --- /dev/null +++ b/hyperdbg/include/platform/user/code/platform-serial.c @@ -0,0 +1,270 @@ +/** + * @file platform-serial.c + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform implementation of the kernel-debugger serial transport + * @details See platform-serial.h. The Windows branch wraps the Win32 serial primitives + * (CreateFile / Comm* / overlapped ReadFile/WriteFile) and owns the per-direction + * OVERLAPPED state internally so the protocol layer never sees it. The Linux + * branch is currently stubbed (returns FALSE/NULL) and is the home where the + * termios-based implementation over /dev/tty* will live. + * + * @version 0.1 + * @date 2026-06-08 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +#if defined(__linux__) +# include "../header/platform-serial.h" +#endif // defined(__linux__) + +#if defined(_WIN32) + +// +// Per-direction overlapped I/O state, owned by the transport layer. +// +static OVERLAPPED g_PlatformOverlappedReadDebugger = {0}; +static OVERLAPPED g_PlatformOverlappedReadDebuggee = {0}; +static OVERLAPPED g_PlatformOverlappedWriteDebugger = {0}; + +HANDLE +PlatformSerialOpen(const char * PortName, PLATFORM_SERIAL_IO_ROLE Role) +{ + HANDLE Comm; + char PortNo[24] = {0}; + + // + // Append name to make a Windows-understandable format + // + sprintf_s(PortNo, sizeof(PortNo), "\\\\.\\%s", PortName); + + if (Role == PLATFORM_SERIAL_IO_DEBUGGEE) + { + // + // Debuggee uses non-overlapped (blocking) I/O + // + Comm = CreateFile(PortNo, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + g_PlatformOverlappedReadDebuggee.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + } + else + { + // + // Debugger uses overlapped (async) I/O + // + Comm = CreateFile(PortNo, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + g_PlatformOverlappedReadDebugger.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + g_PlatformOverlappedWriteDebugger.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + } + + if (Comm == INVALID_HANDLE_VALUE) + { + return NULL; + } + + // + // Purge the serial port + // + PurgeComm(Comm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); + + return Comm; +} + +BOOLEAN +PlatformSerialConfigure(HANDLE Handle, DWORD BaudRate) +{ + DCB SerialParams = {0}; + SerialParams.DCBlength = sizeof(SerialParams); + + if (GetCommState(Handle, &SerialParams) == FALSE) + { + return FALSE; + } + + SerialParams.BaudRate = BaudRate; + SerialParams.ByteSize = 8; + SerialParams.StopBits = ONESTOPBIT; + SerialParams.Parity = NOPARITY; + + if (SetCommState(Handle, &SerialParams) == FALSE) + { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +PlatformSerialReadByte(HANDLE Handle, + CHAR * OutByte, + DWORD * BytesRead, + PLATFORM_SERIAL_IO_ROLE Role) +{ + OVERLAPPED * Ovl = (Role == PLATFORM_SERIAL_IO_DEBUGGEE) + ? &g_PlatformOverlappedReadDebuggee + : &g_PlatformOverlappedReadDebugger; + + if (Role == PLATFORM_SERIAL_IO_DEBUGGEE) + { + // + // Apply a read timeout for the debuggee side + // + COMMTIMEOUTS Timeouts; + GetCommTimeouts(Handle, &Timeouts); + Timeouts.ReadIntervalTimeout = MAXDWORD; + Timeouts.ReadTotalTimeoutConstant = 5000; + Timeouts.ReadTotalTimeoutMultiplier = 0; + Timeouts.WriteTotalTimeoutConstant = 0; + Timeouts.WriteTotalTimeoutMultiplier = 0; + SetCommTimeouts(Handle, &Timeouts); + } + + if (!ReadFile(Handle, OutByte, sizeof(CHAR), NULL, Ovl)) + { + if (GetLastError() != ERROR_IO_PENDING) + { + return FALSE; + } + } + + WaitForSingleObject(Ovl->hEvent, INFINITE); + GetOverlappedResult(Handle, Ovl, BytesRead, FALSE); + ResetEvent(Ovl->hEvent); + + return TRUE; +} + +BOOLEAN +PlatformSerialWrite(HANDLE Handle, const void * Buffer, UINT32 Length, BOOLEAN Synchronous) +{ + if (Synchronous) + { + DWORD BytesWritten = 0; + if (WriteFile(Handle, Buffer, Length, &BytesWritten, NULL) == FALSE) + { + return FALSE; + } + return (BytesWritten == Length); + } + else + { + if (WriteFile(Handle, Buffer, Length, NULL, &g_PlatformOverlappedWriteDebugger)) + { + return TRUE; + } + + if (GetLastError() != ERROR_IO_PENDING) + { + return FALSE; + } + + if (WaitForSingleObject(g_PlatformOverlappedWriteDebugger.hEvent, INFINITE) != WAIT_OBJECT_0) + { + return FALSE; + } + + ResetEvent(g_PlatformOverlappedWriteDebugger.hEvent); + return TRUE; + } +} + +BOOLEAN +PlatformSerialClose(HANDLE Handle) +{ + if (g_PlatformOverlappedReadDebugger.hEvent) + CloseHandle(g_PlatformOverlappedReadDebugger.hEvent); + if (g_PlatformOverlappedReadDebuggee.hEvent) + CloseHandle(g_PlatformOverlappedReadDebuggee.hEvent); + if (g_PlatformOverlappedWriteDebugger.hEvent) + CloseHandle(g_PlatformOverlappedWriteDebugger.hEvent); + + g_PlatformOverlappedReadDebugger.hEvent = NULL; + g_PlatformOverlappedReadDebuggee.hEvent = NULL; + g_PlatformOverlappedWriteDebugger.hEvent = NULL; + + if (Handle) + return (BOOLEAN)CloseHandle(Handle); + + return TRUE; +} + +#elif defined(__linux__) + +// +// TODO: implement the serial transport on Linux using termios over /dev/tty*: +// - PlatformSerialOpen -> open(PortName, O_RDWR | O_NOCTTY) +// - PlatformSerialConfigure -> tcgetattr/cfsetspeed/tcsetattr (raw, 8-N-1) +// - PlatformSerialReadByte -> read() (with VTIME/VMIN or poll() for the timeout role) +// - PlatformSerialWrite -> write() +// - PlatformSerialClose -> close() +// Named-pipe transport would map onto a UNIX domain socket / FIFO. +// +// Until then these return failure so the kernel-debugger connection simply +// reports "not supported on Linux yet" rather than crashing. +// + +HANDLE +PlatformSerialOpen(const char * PortName, PLATFORM_SERIAL_IO_ROLE Role) +{ + (void)PortName; + (void)Role; + return NULL; +} + +BOOLEAN +PlatformSerialConfigure(HANDLE Handle, DWORD BaudRate) +{ + (void)Handle; + (void)BaudRate; + return FALSE; +} + +BOOLEAN +PlatformSerialReadByte(HANDLE Handle, + CHAR * OutByte, + DWORD * BytesRead, + PLATFORM_SERIAL_IO_ROLE Role) +{ + (void)Handle; + (void)OutByte; + (void)Role; + if (BytesRead) + *BytesRead = 0; + return FALSE; +} + +BOOLEAN +PlatformSerialWrite(HANDLE Handle, const void * Buffer, UINT32 Length, BOOLEAN Synchronous) +{ + (void)Handle; + (void)Buffer; + (void)Length; + (void)Synchronous; + return FALSE; +} + +BOOLEAN +PlatformSerialClose(HANDLE Handle) +{ + (void)Handle; + return TRUE; +} + +#else +# error "Unsupported platform" +#endif diff --git a/hyperdbg/include/platform/user/header/platform-serial.h b/hyperdbg/include/platform/user/header/platform-serial.h new file mode 100644 index 00000000..8e49d5ba --- /dev/null +++ b/hyperdbg/include/platform/user/header/platform-serial.h @@ -0,0 +1,70 @@ +/** + * @file platform-serial.h + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform interface for the kernel-debugger serial transport + * @details The kernel-debugging *protocol* in kd.cpp is platform independent; only + * the byte transport underneath it (serial COM port / named pipe) is OS + * specific. This interface isolates those primitives so the protocol layer + * can stay shared. Windows maps onto Win32 (CreateFile / Comm* / overlapped + * ReadFile/WriteFile); Linux will map onto termios over /dev/tty* (TODO). + * + * @version 0.1 + * @date 2026-06-08 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#if defined(__linux__) +# include "../../../../include/SDK/HyperDbgSdk.h" +#endif // defined(__linux__) + +// +// Distinguishes the two I/O modes the transport uses. On Windows the debugger +// side opens the port for overlapped (async) I/O while the debuggee side uses +// blocking I/O; this enum lets the platform layer pick the right mechanism +// without leaking OVERLAPPED into the protocol code. +// +typedef enum _PLATFORM_SERIAL_IO_ROLE +{ + PLATFORM_SERIAL_IO_DEBUGGER, // overlapped / async reads + PLATFORM_SERIAL_IO_DEBUGGEE, // blocking reads (with comm timeout) + +} PLATFORM_SERIAL_IO_ROLE; + +// +// OPEN a serial COM port by name for the given role. +// Returns a transport handle, or NULL on failure. +// +HANDLE +PlatformSerialOpen(const char * PortName, PLATFORM_SERIAL_IO_ROLE Role); + +// +// CONFIGURE baud rate and the fixed 8-N-1 framing the protocol expects. +// +BOOLEAN +PlatformSerialConfigure(HANDLE Handle, DWORD BaudRate); + +// +// READ a single byte. *BytesRead is set to the number of bytes actually read. +// The caller (protocol layer) owns the packet-assembly loop. +// +BOOLEAN +PlatformSerialReadByte(HANDLE Handle, + CHAR * OutByte, + DWORD * BytesRead, + PLATFORM_SERIAL_IO_ROLE Role); + +// +// WRITE a buffer. Synchronous selects blocking write (debuggee/handshaking) +// versus overlapped write (debugger). +// +BOOLEAN +PlatformSerialWrite(HANDLE Handle, const void * Buffer, UINT32 Length, BOOLEAN Synchronous); + +// +// CLOSE the transport handle and release any associated OS resources. +// +BOOLEAN +PlatformSerialClose(HANDLE Handle); diff --git a/hyperdbg/libhyperdbg/CMakeLists.txt b/hyperdbg/libhyperdbg/CMakeLists.txt index 29324cff..dc41e929 100644 --- a/hyperdbg/libhyperdbg/CMakeLists.txt +++ b/hyperdbg/libhyperdbg/CMakeLists.txt @@ -29,6 +29,7 @@ set(SourceFiles "pch.h" "../include/platform/user/code/platform-intrinsics.c" "../include/platform/user/code/platform-lib-calls.c" + "../include/platform/user/code/platform-serial.c" "../script-eval/code/Functions.c" "../script-eval/code/Keywords.c" "../script-eval/code/PseudoRegisters.c" @@ -171,6 +172,7 @@ include_directories( set_source_files_properties( "../include/platform/user/code/platform-intrinsics.c" "../include/platform/user/code/platform-lib-calls.c" + "../include/platform/user/code/platform-serial.c" "../script-eval/code/Functions.c" "../script-eval/code/Keywords.c" "../script-eval/code/PseudoRegisters.c" @@ -179,4 +181,9 @@ set_source_files_properties( PROPERTIES LANGUAGE CXX ) +if(UNIX) + list(REMOVE_ITEM SourceFiles "code/debugger/script-engine/symbol.cpp") + list(APPEND SourceFiles "code/debugger/script-engine/symbol-linux.cpp") +endif() + add_library(libhyperdbg SHARED ${SourceFiles}) diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp index 81cccb93..c7aca424 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp @@ -238,7 +238,6 @@ CommandAssemble(vector CommandTokens, string Command) // if (!CMD.AddressStr.empty()) // was any address provided to assemble to? { -#ifdef _WIN32 if (!SymbolConvertNameOrExprToAddress(CMD.AddressStr, &Address)) { ShowMessages("err, couldn't resolve Address at '%s'\n\n", @@ -246,15 +245,9 @@ CommandAssemble(vector CommandTokens, string Command) CommandAssembleHelp(); return; } -#else - // TODO: symbol resolution is not yet implemented on Linux - ShowMessages("err, symbol resolution is not supported on Linux yet\n\n"); - return; -#endif } else if (!CMD.StartAddressStr.empty()) // was a custom start_address provided? { -#ifdef _WIN32 if (!SymbolConvertNameOrExprToAddress(CMD.StartAddressStr, &StartAddress)) { ShowMessages("err, couldn't resolve Address at '%s'\n\n", @@ -263,11 +256,6 @@ CommandAssemble(vector CommandTokens, string Command) return; } Address = StartAddress; -#else - // TODO: symbol resolution is not yet implemented on Linux - ShowMessages("err, symbol resolution is not supported on Linux yet\n\n"); - return; -#endif } else { diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/dt-struct.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/dt-struct.cpp index 6233e00a..c3c3db68 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/dt-struct.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/dt-struct.cpp @@ -353,7 +353,7 @@ CommandDtShowDataBasedOnSymbolTypes( // // Use the current process for the pid // - TargetPid = GetCurrentProcessId(); + TargetPid = PlatformGetCurrentProcessId(); } } diff --git a/hyperdbg/libhyperdbg/code/debugger/kernel-level/kd.cpp b/hyperdbg/libhyperdbg/code/debugger/kernel-level/kd.cpp index f229ff5d..4020c620 100644 --- a/hyperdbg/libhyperdbg/code/debugger/kernel-level/kd.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/kernel-level/kd.cpp @@ -21,9 +21,11 @@ extern DEBUGGER_SYNCRONIZATION_EVENTS_STATE g_KernelSyncronizationObjectsHandleTable[DEBUGGER_MAXIMUM_SYNCRONIZATION_KERNEL_DEBUGGER_OBJECTS]; extern BYTE g_CurrentRunningInstruction[MAXIMUM_INSTR_SIZE]; extern BOOLEAN g_IsConnectedToHyperDbgLocally; +#ifdef _WIN32 extern OVERLAPPED g_OverlappedIoStructureForReadDebugger; extern OVERLAPPED g_OverlappedIoStructureForWriteDebugger; extern OVERLAPPED g_OverlappedIoStructureForReadDebuggee; +#endif // _WIN32 extern DEBUGGER_EVENT_AND_ACTION_RESULT g_DebuggeeResultOfRegisteringEvent; extern DEBUGGER_EVENT_AND_ACTION_RESULT g_DebuggeeResultOfAddingActionsToEvent; @@ -1565,6 +1567,7 @@ KdReceivePacketFromDebuggee(CHAR * BufferToSave, // do { +#ifdef _WIN32 // // It's in the debugger // @@ -1600,6 +1603,18 @@ KdReceivePacketFromDebuggee(CHAR * BufferToSave, // Reset event for next try // ResetEvent(g_OverlappedIoStructureForReadDebugger.hEvent); +#else + // + // Linux: read one byte through the cross-platform serial transport + // + if (!PlatformSerialReadByte(g_SerialRemoteComPortHandle, + &ReadData, + &NoBytesRead, + PLATFORM_SERIAL_IO_DEBUGGER)) + { + return FALSE; + } +#endif // // We already now that the maximum packet size is MaxSerialPacketSize @@ -1650,6 +1665,7 @@ KdReceivePacketFromDebugger(CHAR * BufferToSave, DWORD NoBytesRead = 0; /* Bytes read by ReadFile() */ UINT32 Loop = 0; +#ifdef _WIN32 // // Set the timeout in milliseconds (e.g., 5000 ms = 5 seconds) // @@ -1666,12 +1682,14 @@ KdReceivePacketFromDebugger(CHAR * BufferToSave, Timeouts.WriteTotalTimeoutConstant = 0; Timeouts.WriteTotalTimeoutMultiplier = 0; SetCommTimeouts(g_SerialRemoteComPortHandle, &Timeouts); +#endif // // Read data and store in a buffer // do { +#ifdef _WIN32 // // It's in the debuggee // @@ -1707,6 +1725,19 @@ KdReceivePacketFromDebugger(CHAR * BufferToSave, // Reset event for next try // ResetEvent(g_OverlappedIoStructureForReadDebuggee.hEvent); +#else + // + // Linux: read one byte through the cross-platform serial transport + // (the 5s read timeout is applied inside the platform layer) + // + if (!PlatformSerialReadByte(g_SerialRemoteComPortHandle, + &ReadData, + &NoBytesRead, + PLATFORM_SERIAL_IO_DEBUGGEE)) + { + return FALSE; + } +#endif // // We already now that the maximum packet size is MaxSerialPacketSize @@ -1781,6 +1812,7 @@ KdSendPacketToDebuggee(const CHAR * Buffer, UINT32 Length, BOOLEAN SendEndOfBuff return FALSE; } +#ifdef _WIN32 if (g_IsSerialConnectedToRemoteDebugger || g_IsDebuggeeInHandshakingPhase) { // @@ -1846,6 +1878,20 @@ KdSendPacketToDebuggee(const CHAR * Buffer, UINT32 Length, BOOLEAN SendEndOfBuff // ResetEvent(g_OverlappedIoStructureForWriteDebugger.hEvent); } +#else + // + // Linux: write through the cross-platform serial transport. The debuggee / + // handshaking path is synchronous; the debugger path is overlapped (handled + // inside the platform layer). + // + { + BOOLEAN Synchronous = (g_IsSerialConnectedToRemoteDebugger || g_IsDebuggeeInHandshakingPhase); + if (!PlatformSerialWrite(g_SerialRemoteComPortHandle, Buffer, Length, Synchronous)) + { + return FALSE; + } + } +#endif Out: if (SendEndOfBuffer) diff --git a/hyperdbg/libhyperdbg/code/debugger/script-engine/symbol-linux.cpp b/hyperdbg/libhyperdbg/code/debugger/script-engine/symbol-linux.cpp new file mode 100644 index 00000000..2f7f65a4 --- /dev/null +++ b/hyperdbg/libhyperdbg/code/debugger/script-engine/symbol-linux.cpp @@ -0,0 +1,107 @@ +/** + * @file symbol-linux.cpp + * @author Max Raulea (max.raulea@gmail.com) + * @brief Linux stub implementations of the symbol subsystem + * @details The Windows implementation uses DbgHelp + PDB files (symbol-parser/). + * Linux uses ELF/DWARF which requires a separate implementation. + * These stubs allow the library to compile and link on Linux while + * keeping all call sites intact. + * + * TODO: implement a real ELF/DWARF symbol parser for Linux + * (libdw / libelf / libbfd) and replace these stubs. + * + * @version 0.1 + * @date 2026-06-08 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +#ifdef __linux__ + +VOID +SymbolBuildAndShowSymbolTable() +{ + ShowMessages("err, symbol table is not supported on Linux yet\n"); +} + +BOOLEAN +SymbolShowFunctionNameBasedOnAddress(UINT64 Address, PUINT64 UsedBaseAddress) +{ + return FALSE; +} + +BOOLEAN +SymbolLoadOrDownloadSymbols(BOOLEAN IsDownload, BOOLEAN SilentLoad) +{ + if (!SilentLoad) + ShowMessages("err, symbol loading is not supported on Linux yet\n"); + return FALSE; +} + +/** + * @brief Attempt to resolve a name or expression to an address. + * + * On Linux, full symbol resolution (ELF/DWARF) is not yet implemented. + * This stub handles the common case of a plain hex/decimal literal so that + * numeric addresses still work everywhere in the debugger. + */ +BOOLEAN +SymbolConvertNameOrExprToAddress(const string & TextToConvert, PUINT64 Result) +{ + try + { + *Result = std::stoull(TextToConvert, nullptr, 0); + return TRUE; + } + catch (...) + { + return FALSE; + } +} + +BOOLEAN +SymbolDeleteSymTable() +{ + return TRUE; +} + +BOOLEAN +SymbolBuildSymbolTable(PMODULE_SYMBOL_DETAIL * BufferToStoreDetails, + PUINT32 StoredLength, + UINT32 UserProcessId, + BOOLEAN SendOverSerial) +{ + return FALSE; +} + +BOOLEAN +SymbolBuildAndUpdateSymbolTable(PMODULE_SYMBOL_DETAIL SymbolDetail) +{ + return FALSE; +} + +VOID +SymbolInitialReload() +{ +} + +BOOLEAN +SymbolLocalReload(UINT32 UserProcessId) +{ + return FALSE; +} + +VOID +SymbolPrepareDebuggerWithSymbolInfo(UINT32 UserProcessId) +{ +} + +BOOLEAN +SymbolReloadSymbolTableInDebuggerMode(UINT32 ProcessId) +{ + return FALSE; +} + +#endif // __linux__ diff --git a/hyperdbg/libhyperdbg/header/common.h b/hyperdbg/libhyperdbg/header/common.h index 70281a36..bdf2a82b 100644 --- a/hyperdbg/libhyperdbg/header/common.h +++ b/hyperdbg/libhyperdbg/header/common.h @@ -102,7 +102,7 @@ &g_KernelSyncronizationObjectsHandleTable[KernelSyncObjectId]; \ \ SyncronizationObject->IsOnWaitingState = TRUE; \ - WaitForSingleObject(SyncronizationObject->EventHandle, INFINITE); \ + PlatformWaitForSingleObject(SyncronizationObject->EventHandle, INFINITE); \ \ } while (FALSE); @@ -113,7 +113,7 @@ &g_UserSyncronizationObjectsHandleTable[UserSyncObjectId]; \ \ SyncronizationObject->IsOnWaitingState = TRUE; \ - WaitForSingleObject(SyncronizationObject->EventHandle, INFINITE); \ + PlatformWaitForSingleObject(SyncronizationObject->EventHandle, INFINITE); \ \ } while (FALSE); diff --git a/hyperdbg/libhyperdbg/header/globals.h b/hyperdbg/libhyperdbg/header/globals.h index 7838e15b..bf8dfd11 100644 --- a/hyperdbg/libhyperdbg/header/globals.h +++ b/hyperdbg/libhyperdbg/header/globals.h @@ -317,10 +317,12 @@ DEBUGGER_EVENT_AND_ACTION_RESULT g_DebuggeeResultOfAddingActionsToEvent = { * to write simultaneously but it's needed for write) * */ +#ifdef _WIN32 OVERLAPPED g_OverlappedIoStructureForReadDebugger = {0}; OVERLAPPED g_OverlappedIoStructureForWriteDebugger = {0}; OVERLAPPED g_OverlappedIoStructureForReadDebuggee = {0}; +#endif // _WIN32 /** * @brief Shows whether the queried event is enabled or disabled diff --git a/hyperdbg/libhyperdbg/header/kd.h b/hyperdbg/libhyperdbg/header/kd.h index da50ff2b..9a7e2798 100644 --- a/hyperdbg/libhyperdbg/header/kd.h +++ b/hyperdbg/libhyperdbg/header/kd.h @@ -15,6 +15,8 @@ // Display Windows Details // ////////////////////////////////////////////////// +#ifdef _WIN32 +// HKEY / RegCloseKey are Windows registry APIs; this RAII helper is Windows-only struct HKeyHolder { private: @@ -37,6 +39,7 @@ public: HKEY * operator&() { return &m_Key; } }; +#endif // _WIN32 ////////////////////////////////////////////////// // Functions // diff --git a/hyperdbg/libhyperdbg/header/symbol.h b/hyperdbg/libhyperdbg/header/symbol.h index 6fab7ff4..ab6a177d 100644 --- a/hyperdbg/libhyperdbg/header/symbol.h +++ b/hyperdbg/libhyperdbg/header/symbol.h @@ -72,5 +72,8 @@ SymbolPrepareDebuggerWithSymbolInfo(UINT32 UserProcessId); BOOLEAN SymbolReloadSymbolTableInDebuggerMode(UINT32 ProcessId); +#ifdef _WIN32 +// PRTL_PROCESS_MODULES is from winternl.h — Windows-only BOOLEAN SymbolCheckAndAllocateModuleInformation(PRTL_PROCESS_MODULES * Modules); +#endif // _WIN32 diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index a2be9aac..82add933 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -176,9 +176,7 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "header/inipp.h" #include "header/commands.h" #include "header/common.h" -#ifdef _WIN32 -# include "header/symbol.h" -#endif +#include "header/symbol.h" #include "header/debugger.h" #include "header/script-engine.h" #include "header/help.h" @@ -193,9 +191,7 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "header/communication.h" #include "header/namedpipe.h" #include "header/forwarding.h" -#ifdef _WIN32 -# include "header/kd.h" -#endif +#include "header/kd.h" // // Components From 62991619807a5f164027a7f70cfd0ce60b882a76 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 20:25:23 +0200 Subject: [PATCH 12/57] Hopefully fix build errors --- .../platform/user/code/platform-lib-calls.c | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index 61281bb8..d3ebbcae 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -204,24 +204,28 @@ PlatformGetCurrentProcessName(VOID) static CHAR ProcessNameBuf[MAX_PATH] = {0}; #if defined(_WIN32) - HANDLE Handle = OpenProcess( - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - FALSE, - GetCurrentProcessId()); - - if (Handle) + // + // Use base kernel32 only (no psapi/shlwapi) so this compiles in every + // project that builds platform-lib-calls.c (e.g. script-engine, which has + // a minimal include set). GetModuleFileNameA(NULL, ...) returns the full + // path of the current process image. + // + if (GetModuleFileNameA(NULL, ProcessNameBuf, MAX_PATH) == 0) { - CHAR ModulePath[MAX_PATH] = {0}; - if (GetModuleFileNameEx(Handle, 0, ModulePath, MAX_PATH)) - { - CloseHandle(Handle); - strncpy(ProcessNameBuf, PathFindFileNameA(ModulePath), MAX_PATH - 1); - ProcessNameBuf[MAX_PATH - 1] = '\0'; - return ProcessNameBuf; - } - CloseHandle(Handle); + return NULL; } - return NULL; + + // + // Return the basename (strip the directory part) + // + { + char * LastSeparator = strrchr(ProcessNameBuf, '\\'); + if (LastSeparator) + { + return LastSeparator + 1; + } + } + return ProcessNameBuf; #elif defined(__linux__) FILE * f = fopen("/proc/self/comm", "r"); From 6c97d86610a9199eeaa0c8bb329c1dcab0eff403 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 20:47:10 +0200 Subject: [PATCH 13/57] Hopefully fix build errors V2 --- .../platform/kernel/code/PlatformIntrinsics.c | 81 +++++++++++++++++++ .../platform/kernel/code/PlatformMem.c | 25 ++++++ .../kernel/header/PlatformIntrinsics.h | 19 +++++ .../platform/kernel/header/PlatformMem.h | 3 + 4 files changed, 128 insertions(+) diff --git a/hyperdbg/include/platform/kernel/code/PlatformIntrinsics.c b/hyperdbg/include/platform/kernel/code/PlatformIntrinsics.c index caf93924..3233d67e 100644 --- a/hyperdbg/include/platform/kernel/code/PlatformIntrinsics.c +++ b/hyperdbg/include/platform/kernel/code/PlatformIntrinsics.c @@ -325,6 +325,87 @@ CpuReadTscp(UINT32 * Aux) #endif } +////////////////////////////////////////////////// +// Interlocked (Atomic) Operations // +////////////////////////////////////////////////// + +/** + * @brief Atomic 64-bit exchange + */ +inline INT64 +CpuInterlockedExchange64(INT64 volatile * Target, INT64 Value) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedExchange64(Target, Value); +#elif defined(__linux__) + return __atomic_exchange_n(Target, Value, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Atomic 64-bit exchange-add + */ +inline INT64 +CpuInterlockedExchangeAdd64(INT64 volatile * Addend, INT64 Value) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedExchangeAdd64(Addend, Value); +#elif defined(__linux__) + return __atomic_fetch_add(Addend, Value, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Atomic 64-bit increment + */ +inline INT64 +CpuInterlockedIncrement64(INT64 volatile * Addend) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedIncrement64(Addend); +#elif defined(__linux__) + return __atomic_add_fetch(Addend, 1LL, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Atomic 64-bit decrement + */ +inline INT64 +CpuInterlockedDecrement64(INT64 volatile * Addend) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedDecrement64(Addend); +#elif defined(__linux__) + return __atomic_sub_fetch(Addend, 1LL, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Atomic 64-bit compare-exchange + */ +inline INT64 +CpuInterlockedCompareExchange64(INT64 volatile * Destination, INT64 ExChange, INT64 Comparand) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedCompareExchange64(Destination, ExChange, Comparand); +#elif defined(__linux__) + INT64 Expected = Comparand; + __atomic_compare_exchange_n(Destination, &Expected, ExChange, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return Expected; +#else +# error "Unsupported platform" +#endif +} + ////////////////////////////////////////////////// // Descriptor Table Instructions // ////////////////////////////////////////////////// diff --git a/hyperdbg/include/platform/kernel/code/PlatformMem.c b/hyperdbg/include/platform/kernel/code/PlatformMem.c index f93164ae..27e8112d 100644 --- a/hyperdbg/include/platform/kernel/code/PlatformMem.c +++ b/hyperdbg/include/platform/kernel/code/PlatformMem.c @@ -17,6 +17,31 @@ # include "../header/PlatformMem.h" #endif // defined(__linux__) +/** + * @brief Platform independent wrapper for sprintf_s / snprintf + * + * @param Buffer output buffer + * @param BufferSize size of the output buffer + * @param Format format string + * @return INT number of characters written, or -1 on error + */ +INT +PlatformSprintf(char * Buffer, SIZE_T BufferSize, const char * Format, ...) +{ + va_list Args; + va_start(Args, Format); + INT Result; +#if defined(_WIN32) || defined(_WIN64) + Result = vsprintf_s(Buffer, BufferSize, Format, Args); +#elif defined(__linux__) + Result = vsnprintf(Buffer, BufferSize, Format, Args); +#else +# error "Unsupported platform" +#endif + va_end(Args); + return Result; +} + ///////////////////////////////////////////////// /// ... New Unified API ... ///////////////////////////////////////////////// diff --git a/hyperdbg/include/platform/kernel/header/PlatformIntrinsics.h b/hyperdbg/include/platform/kernel/header/PlatformIntrinsics.h index 318b878b..618fc71b 100644 --- a/hyperdbg/include/platform/kernel/header/PlatformIntrinsics.h +++ b/hyperdbg/include/platform/kernel/header/PlatformIntrinsics.h @@ -207,6 +207,25 @@ extern inline UINT64 extern inline UINT64 CpuReadTscp(UINT32 * Aux); +////////////////////////////////////////////////// +// Interlocked (Atomic) Operations // +////////////////////////////////////////////////// + +extern inline INT64 +CpuInterlockedExchange64(INT64 volatile * Target, INT64 Value); + +extern inline INT64 +CpuInterlockedExchangeAdd64(INT64 volatile * Addend, INT64 Value); + +extern inline INT64 +CpuInterlockedIncrement64(INT64 volatile * Addend); + +extern inline INT64 +CpuInterlockedDecrement64(INT64 volatile * Addend); + +extern inline INT64 +CpuInterlockedCompareExchange64(INT64 volatile * Destination, INT64 ExChange, INT64 Comparand); + ////////////////////////////////////////////////// // Descriptor Table Instructions // ////////////////////////////////////////////////// diff --git a/hyperdbg/include/platform/kernel/header/PlatformMem.h b/hyperdbg/include/platform/kernel/header/PlatformMem.h index c7398722..b031fee1 100644 --- a/hyperdbg/include/platform/kernel/header/PlatformMem.h +++ b/hyperdbg/include/platform/kernel/header/PlatformMem.h @@ -22,6 +22,9 @@ // Functions // ////////////////////////////////////////////////// +INT +PlatformSprintf(char * Buffer, SIZE_T BufferSize, const char * Format, ...); + VOID PlatformFreeMemory(PVOID Memory); From f9b912d365b3b28c8c1b11b00d8488444341ba8f Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 8 Jun 2026 20:58:05 +0200 Subject: [PATCH 14/57] Fix Build files: Added new platform API filesa --- hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 4 ++++ hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters | 12 ++++++++++++ hyperdbg/libhyperdbg/pch.h | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index ec6a41f3..5f1ceeee 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -129,6 +129,8 @@ + + @@ -165,6 +167,8 @@ + + diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters index 99d0aa4a..7185ea94 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters @@ -194,6 +194,12 @@ header\platform + + header\platform + + + header\platform + header @@ -640,6 +646,12 @@ code\platform + + code\platform + + + code\platform + code\app diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index 82add933..b6591f83 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -156,6 +156,11 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; // #include "platform/user/header/platform-intrinsics.h" +// +// Platform serial transport (cross-platform kernel-debugger serial I/O) +// +#include "platform/user/header/platform-serial.h" + // // Platform-specific intrinsics // From df12e9fd79851c8f378ec82a45066da510da507a Mon Sep 17 00:00:00 2001 From: sina Date: Wed, 10 Jun 2026 23:56:36 +0200 Subject: [PATCH 15/57] Fix adding missing IOCTL for Intel PT --- hyperdbg/hyperkd/code/driver/Ioctl.c | 137 +++++++++++++++--- hyperdbg/hypertrace/header/pch.h | 12 +- .../platform/general/header/Environment.h | 54 ++++--- .../platform/user/code/platform-lib-calls.c | 9 +- .../commands/debugging-commands/core.cpp | 5 - .../commands/extension-commands/pt.cpp | 2 +- 6 files changed, 152 insertions(+), 67 deletions(-) diff --git a/hyperdbg/hyperkd/code/driver/Ioctl.c b/hyperdbg/hyperkd/code/driver/Ioctl.c index 5ae146e7..d3e22af3 100644 --- a/hyperdbg/hyperkd/code/driver/Ioctl.c +++ b/hyperdbg/hyperkd/code/driver/Ioctl.c @@ -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; diff --git a/hyperdbg/hypertrace/header/pch.h b/hyperdbg/hypertrace/header/pch.h index a6f7b465..4e591600 100644 --- a/hyperdbg/hypertrace/header/pch.h +++ b/hyperdbg/hypertrace/header/pch.h @@ -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 // diff --git a/hyperdbg/include/platform/general/header/Environment.h b/hyperdbg/include/platform/general/header/Environment.h index 652f8e3b..caaa22e7 100644 --- a/hyperdbg/include/platform/general/header/Environment.h +++ b/hyperdbg/include/platform/general/header/Environment.h @@ -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 +# include // 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 diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index d3ebbcae..c52759ec 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -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__) diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/core.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/core.cpp index de8cd321..2a1852db 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/core.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/core.cpp @@ -82,11 +82,6 @@ CommandCore(vector 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 } } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp index 79f4fc6f..e97e4a35 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp @@ -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 From 8803e6f4951ba42c1151e434d5754d16eb3649ae Mon Sep 17 00:00:00 2001 From: sina Date: Thu, 11 Jun 2026 00:13:25 +0200 Subject: [PATCH 16/57] remove unused DPC definitions --- hyperdbg/hypertrace/header/broadcast/Dpc.h | 39 ---------------------- hyperdbg/hypertrace/hypertrace.vcxproj | 1 - 2 files changed, 40 deletions(-) delete mode 100644 hyperdbg/hypertrace/header/broadcast/Dpc.h diff --git a/hyperdbg/hypertrace/header/broadcast/Dpc.h b/hyperdbg/hypertrace/header/broadcast/Dpc.h deleted file mode 100644 index f2fc16e7..00000000 --- a/hyperdbg/hypertrace/header/broadcast/Dpc.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file Dpc.h - * @author Sina Karvandi (sina@hyperdbg.org) - * @brief Definition for Windows DPC functions - * @details - * @version 0.19 - * @date 2026-04-19 - * - * @copyright This project is released under the GNU Public License v3. - * - */ -#pragma once - -////////////////////////////////////////////////// -// Functions // -////////////////////////////////////////////////// - -NTKERNELAPI -_IRQL_requires_max_(APC_LEVEL) -_IRQL_requires_min_(PASSIVE_LEVEL) -_IRQL_requires_same_ -VOID -KeGenericCallDpc( - _In_ PKDEFERRED_ROUTINE Routine, - _In_opt_ PVOID Context); - -NTKERNELAPI -_IRQL_requires_(DISPATCH_LEVEL) -_IRQL_requires_same_ -VOID -KeSignalCallDpcDone( - _In_ PVOID SystemArgument1); - -NTKERNELAPI -_IRQL_requires_(DISPATCH_LEVEL) -_IRQL_requires_same_ -LOGICAL -KeSignalCallDpcSynchronize( - _In_ PVOID SystemArgument2); diff --git a/hyperdbg/hypertrace/hypertrace.vcxproj b/hyperdbg/hypertrace/hypertrace.vcxproj index 3b021d4d..794cfc67 100644 --- a/hyperdbg/hypertrace/hypertrace.vcxproj +++ b/hyperdbg/hypertrace/hypertrace.vcxproj @@ -124,7 +124,6 @@ - From 9ad48d30dcf6b409ae86b2d08262584cd06f606e Mon Sep 17 00:00:00 2001 From: sina Date: Thu, 11 Jun 2026 14:31:10 +0200 Subject: [PATCH 17/57] fix new form of loading and unloading in the example app --- examples/user/hyperdbg_app/code/hyperdbg-app.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp index 40f3adfc..96908b27 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp @@ -507,7 +507,7 @@ LoadVmm() } printf("[*] loading HyperDbg VMM...\n"); - if (hyperdbg_u_install_vmm_driver() != 0 || hyperdbg_u_load_vmm() != 0) + if (hyperdbg_u_install_kd_driver() == 1 || hyperdbg_u_load_vmm() == 1) { printf("[-] cannot load the HyperDbg VMM\n"); return 1; @@ -552,9 +552,16 @@ main(int argc, char ** argv) RunAndTrace(argv[1], function, packets, pinCore); printf("[*] unloading HyperDbg VMM...\n"); + + // + // Unload the driver + // hyperdbg_u_unload_vmm(); - hyperdbg_u_stop_vmm_driver(); - hyperdbg_u_uninstall_vmm_driver(); + hyperdbg_u_unload_kd(); + hyperdbg_u_stop_kd_driver(); + hyperdbg_u_uninstall_kd_driver(); + printf("[+] done\n"); + return 0; } From e04ee4dcc3e2202f082cb726d771115e99c4dec8 Mon Sep 17 00:00:00 2001 From: sina Date: Sat, 13 Jun 2026 14:20:32 +0200 Subject: [PATCH 18/57] add Intel PT libraries --- .../user/hyperdbg_app/code/hyperdbg-app.cpp | 19 +- .../user/hyperdbg_app/code/hyperdbg-ipt.cpp | 56 + .../user/hyperdbg_app/header/example-ipt.h | 16 + examples/user/hyperdbg_app/header/pch.h | 5 + .../user/hyperdbg_app/hyperdbg_app.vcxproj | 20 +- .../hyperdbg_app/hyperdbg_app.vcxproj.filters | 6 + hyperdbg/dependencies/libipt/intel-pt.h | 3177 +++++++++++++++++ hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj | 38 +- hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 8 +- hyperdbg/libraries/libipt/libipt.dll | Bin 0 -> 111616 bytes hyperdbg/libraries/libipt/libipt.lib | Bin 0 -> 19768 bytes 11 files changed, 3315 insertions(+), 30 deletions(-) create mode 100644 examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp create mode 100644 examples/user/hyperdbg_app/header/example-ipt.h create mode 100644 hyperdbg/dependencies/libipt/intel-pt.h create mode 100644 hyperdbg/libraries/libipt/libipt.dll create mode 100644 hyperdbg/libraries/libipt/libipt.lib diff --git a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp index 96908b27..cdb219e9 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include "../dependencies/libipt/intel-pt.h" #include #pragma comment(lib, "dbghelp.lib") @@ -496,7 +496,7 @@ Cleanup: } static int -LoadVmm() +LoadVmmAndTrace() { hyperdbg_u_set_text_message_callback((PVOID)ShowMessages); @@ -514,11 +514,22 @@ LoadVmm() } printf("[+] HyperDbg VMM is running\n"); + + printf("[*] loading HyperTrace...\n"); + + if ( hyperdbg_u_load_hypertrace_module() == 1) + { + printf("[-] cannot load the HyperDbg HyperTrace\n"); + return 1; + } + + printf("[+] HyperDbg HyperTrace is running\n"); + return 0; } int -main(int argc, char ** argv) +main2(int argc, char ** argv) { const char * function = "main"; BOOLEAN packets = FALSE; @@ -546,7 +557,7 @@ main(int argc, char ** argv) function = argv[i]; } - if (LoadVmm() != 0) + if (LoadVmmAndTrace() != 0) return 1; RunAndTrace(argv[1], function, packets, pinCore); diff --git a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp new file mode 100644 index 00000000..c82097b8 --- /dev/null +++ b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp @@ -0,0 +1,56 @@ +#include "pch.h" + +static int +ShowMessages(const char * Text) +{ + printf("%s", Text); + return 0; +} + +static int +LoadVmm() +{ + hyperdbg_u_set_text_message_callback((PVOID)ShowMessages); + + if (!hyperdbg_u_detect_vmx_support()) + { + printf("[-] VT-x (VMX) is not supported / enabled on this processor\n"); + return 1; + } + + printf("[*] loading HyperDbg VMM...\n"); + if (hyperdbg_u_install_kd_driver() == 1 || hyperdbg_u_load_vmm() == 1) + { + printf("[-] cannot load the HyperDbg VMM\n"); + return 1; + } + + printf("[+] HyperDbg VMM is running\n"); + + return 0; +} + +int +main(int argc, char ** argv) +{ + if (LoadVmm() != 0) + { + return 1; + } + + hyperdbg_u_run_command((CHAR*)"lm"); + + printf("[*] unloading HyperDbg VMM...\n"); + + // + // Unload the driver + // + hyperdbg_u_unload_vmm(); + hyperdbg_u_unload_kd(); + hyperdbg_u_stop_kd_driver(); + hyperdbg_u_uninstall_kd_driver(); + + printf("[+] done\n"); + + return 0; +} diff --git a/examples/user/hyperdbg_app/header/example-ipt.h b/examples/user/hyperdbg_app/header/example-ipt.h new file mode 100644 index 00000000..90f00b87 --- /dev/null +++ b/examples/user/hyperdbg_app/header/example-ipt.h @@ -0,0 +1,16 @@ +/** + * @file example-ipt.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers for Intel PT example + * @details + * + * @version 0.20 + * @date 2026-06-13 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +int +main2(int argc, char** argv); \ No newline at end of file diff --git a/examples/user/hyperdbg_app/header/pch.h b/examples/user/hyperdbg_app/header/pch.h index f62bfc90..d4bbf789 100644 --- a/examples/user/hyperdbg_app/header/pch.h +++ b/examples/user/hyperdbg_app/header/pch.h @@ -38,3 +38,8 @@ // #include "SDK/HyperDbgSdk.h" #include "SDK/imports/user/HyperDbgLibImports.h" + +// +// Other internal headers +// +#include "example-ipt.h" diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj index 2ba4224e..0f4ae87e 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj @@ -11,10 +11,12 @@ + + 16.0 @@ -60,10 +62,12 @@ $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + true @@ -81,12 +85,14 @@ Console true $(LibIptDir)\lib;%(AdditionalLibraryDirectories) - $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)libraries\zydis\user\Zycore.lib;libipt.lib + $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\libipt\libipt.lib false - copy /Y "$(LibIptDir)\bin\libipt.dll" "$(OutDir)libipt.dll" - Copying libipt.dll (Intel PT decoder) to output directory + + + + @@ -110,12 +116,14 @@ true true $(LibIptDir)\lib;%(AdditionalLibraryDirectories) - $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)libraries\zydis\user\Zycore.lib;libipt.lib + $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\libipt\libipt.lib true - copy /Y "$(LibIptDir)\bin\libipt.dll" "$(OutDir)libipt.dll" - Copying libipt.dll (Intel PT decoder) to output directory + + + + diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters index 8a2081a1..221a1596 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters @@ -15,10 +15,16 @@ header + + header + code + + code + \ No newline at end of file diff --git a/hyperdbg/dependencies/libipt/intel-pt.h b/hyperdbg/dependencies/libipt/intel-pt.h new file mode 100644 index 00000000..4b90482c --- /dev/null +++ b/hyperdbg/dependencies/libipt/intel-pt.h @@ -0,0 +1,3177 @@ +/* + * Copyright (C) 2013-2026 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef INTEL_PT_H +#define INTEL_PT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Intel(R) Processor Trace (Intel PT) decoder library. + * + * This file is logically structured into the following sections: + * + * - Version + * - Errors + * - Configuration + * - Packet encoder / decoder + * - Event decoder + * - Query decoder + * - Traced image + * - Instruction flow decoder + * - Block decoder + */ + + + +struct pt_encoder; +struct pt_packet_decoder; +struct pt_event_decoder; +struct pt_query_decoder; +struct pt_insn_decoder; +struct pt_block_decoder; + + + +/* A macro to mark functions as exported. */ +#ifndef pt_export +# if defined(__GNUC__) +# define pt_export __attribute__((visibility("default"))) +# elif defined(_MSC_VER) +# define pt_export __declspec(dllimport) +# else +# error "unknown compiler" +# endif +#endif + + + +/* Version. */ + + +/** The header version. */ +#define LIBIPT_VERSION_MAJOR 2 +#define LIBIPT_VERSION_MINOR 3 +#define LIBIPT_VERSION_PATCH 0 + +#define LIBIPT_VERSION ((LIBIPT_VERSION_MAJOR << 8) + LIBIPT_VERSION_MINOR) + + +/** The library version. */ +struct pt_version { + /** Major version number. */ + uint8_t major; + + /** Minor version number. */ + uint8_t minor; + + /** Patch level. */ + uint16_t patch; + + /** Build number. */ + uint32_t build; + + /** Version extension. */ + const char *ext; +}; + + +/** Return the library version. */ +extern pt_export struct pt_version pt_library_version(void); + + + +/* Errors. */ + + + +/** Error codes. */ +enum pt_error_code { + /* No error. Everything is OK. */ + pte_ok, + + /* Internal decoder error. */ + pte_internal, + + /* Invalid argument. */ + pte_invalid, + + /* Decoder out of sync. */ + pte_nosync, + + /* Unknown opcode. */ + pte_bad_opc, + + /* Unknown payload. */ + pte_bad_packet, + + /* Unexpected packet context. */ + pte_bad_context, + + /* Decoder reached end of trace stream. */ + pte_eos, + + /* No packet matching the query to be found. */ + pte_bad_query, + + /* Decoder out of memory. */ + pte_nomem, + + /* Bad configuration. */ + pte_bad_config, + + /* There is no IP. */ + pte_noip, + + /* The IP has been suppressed. */ + pte_ip_suppressed, + + /* There is no memory mapped at the requested address. */ + pte_nomap, + + /* An instruction could not be decoded. */ + pte_bad_insn, + + /* No wall-clock time is available. */ + pte_no_time, + + /* No core:bus ratio available. */ + pte_no_cbr, + + /* Bad traced image. */ + pte_bad_image, + + /* A locking error. */ + pte_bad_lock, + + /* The requested feature is not supported. */ + pte_not_supported, + + /* The return address stack is empty. */ + pte_retstack_empty, + + /* A compressed return is not indicated correctly by a taken branch. */ + pte_bad_retcomp, + + /* The current decoder state does not match the state in the trace. */ + pte_bad_status_update, + + /* The trace did not contain an expected enabled event. */ + pte_no_enable, + + /* An event was ignored. */ + pte_event_ignored, + + /* Something overflowed. */ + pte_overflow, + + /* A file handling error. */ + pte_bad_file, + + /* Unknown cpu. */ + pte_bad_cpu +}; + + +/** Decode a function return value into an pt_error_code. */ +static inline enum pt_error_code pt_errcode(int status) +{ + return (status >= 0) ? pte_ok : (enum pt_error_code) -status; +} + +/** Return a human readable error string. */ +extern pt_export const char *pt_errstr(enum pt_error_code); + + + +/* Configuration. */ + + + +/** A cpu vendor. */ +enum pt_cpu_vendor { + pcv_unknown, + pcv_intel +}; + +/** A cpu identifier. */ +struct pt_cpu { + /** The cpu vendor. */ + enum pt_cpu_vendor vendor; + + /** The cpu family. */ + uint16_t family; + + /** The cpu model. */ + uint8_t model; + + /** The stepping. */ + uint8_t stepping; +}; + +/** A collection of Intel PT errata. */ +struct pt_errata { + /** BDM70: Intel(R) Processor Trace PSB+ Packets May Contain + * Unexpected Packets. + * + * Same as: SKD024, SKL021, KBL021. + * + * Some Intel Processor Trace packets should be issued only between + * TIP.PGE and TIP.PGD packets. Due to this erratum, when a TIP.PGE + * packet is generated it may be preceded by a PSB+ that incorrectly + * includes FUP and MODE.Exec packets. + */ + uint32_t bdm70:1; + + /** BDM64: An Incorrect LBR or Intel(R) Processor Trace Packet May Be + * Recorded Following a Transactional Abort. + * + * Use of Intel(R) Transactional Synchronization Extensions (Intel(R) + * TSX) may result in a transactional abort. If an abort occurs + * immediately following a branch instruction, an incorrect branch + * target may be logged in an LBR (Last Branch Record) or in an Intel(R) + * Processor Trace (Intel(R) PT) packet before the LBR or Intel PT + * packet produced by the abort. + */ + uint32_t bdm64:1; + + /** SKD007: Intel(R) PT Buffer Overflow May Result in Incorrect Packets. + * + * Same as: SKL049, KBL041. + * + * Under complex micro-architectural conditions, an Intel PT (Processor + * Trace) OVF (Overflow) packet may be issued after the first byte of a + * multi-byte CYC (Cycle Count) packet, instead of any remaining bytes + * of the CYC. + */ + uint32_t skd007:1; + + /** SKD022: VM Entry That Clears TraceEn May Generate a FUP. + * + * Same as: SKL024, KBL023. + * + * If VM entry clears Intel(R) PT (Intel Processor Trace) + * IA32_RTIT_CTL.TraceEn (MSR 570H, bit 0) while PacketEn is 1 then a + * FUP (Flow Update Packet) will precede the TIP.PGD (Target IP Packet, + * Packet Generation Disable). VM entry can clear TraceEn if the + * VM-entry MSR-load area includes an entry for the IA32_RTIT_CTL MSR. + */ + uint32_t skd022:1; + + /** SKD010: Intel(R) PT FUP May be Dropped After OVF. + * + * Same as: SKD014, SKL033, KBL030. + * + * Some Intel PT (Intel Processor Trace) OVF (Overflow) packets may not + * be followed by a FUP (Flow Update Packet) or TIP.PGE (Target IP + * Packet, Packet Generation Enable). + */ + uint32_t skd010:1; + + /** SKL014: Intel(R) PT TIP.PGD May Not Have Target IP Payload. + * + * Same as: KBL014. + * + * When Intel PT (Intel Processor Trace) is enabled and a direct + * unconditional branch clears IA32_RTIT_STATUS.FilterEn (MSR 571H, bit + * 0), due to this erratum, the resulting TIP.PGD (Target IP Packet, + * Packet Generation Disable) may not have an IP payload with the target + * IP. + */ + uint32_t skl014:1; + + /** APL12: Intel(R) PT OVF May Be Followed By An Unexpected FUP Packet. + * + * Certain Intel PT (Processor Trace) packets including FUPs (Flow + * Update Packets), should be issued only between TIP.PGE (Target IP + * Packet - Packet Generation Enable) and TIP.PGD (Target IP Packet - + * Packet Generation Disable) packets. When outside a TIP.PGE/TIP.PGD + * pair, as a result of IA32_RTIT_STATUS.FilterEn[0] (MSR 571H) being + * cleared, an OVF (Overflow) packet may be unexpectedly followed by a + * FUP. + */ + uint32_t apl12:1; + + /** APL11: Intel(R) PT OVF Packet May Be Followed by TIP.PGD Packet + * + * If Intel PT (Processor Trace) encounters an internal buffer overflow + * and generates an OVF (Overflow) packet just as IA32_RTIT_CTL (MSR + * 570H) bit 0 (TraceEn) is cleared, or during a far transfer that + * causes IA32_RTIT_STATUS.ContextEn[1] (MSR 571H) to be cleared, the + * OVF may be followed by a TIP.PGD (Target Instruction Pointer - Packet + * Generation Disable) packet. + */ + uint32_t apl11:1; + + /** SKL168: Intel(R) PT CYC Packets Can be Dropped When Immediately + * Preceding PSB + * + * Due to a rare microarchitectural condition, generation of an Intel + * PT (Processor Trace) PSB (Packet Stream Boundary) packet can cause a + * single CYC (Cycle Count) packet, possibly along with an associated + * MTC (Mini Time Counter) packet, to be dropped. + */ + uint32_t skl168:1; + + /** SKZ84: Use of VMX TSC Scaling or TSC Offsetting Will Result in + * Corrupted Intel PT Packets + * + * When Intel(R) PT (Processor Trace) is enabled within a VMX (Virtual + * Machine Extensions) guest, and TSC (Time Stamp Counter) offsetting + * or TSC scaling is enabled for that guest, by setting primary + * processor-based execution control bit 3 or secondary processor-based + * execution control bit 25, respectively, in the VMCS (Virtual Machine + * Control Structure) for that guest, any TMA (TSC(MTC Alignment) + * packet generated will have corrupted values in the CTC (Core Timer + * Copy) and FastCounter fields. Additionally, the corrupted TMA + * packet will be followed by a bogus data byte. + */ + uint32_t skz84:1; + + /* Reserve a few bytes for the future. */ + uint32_t reserved[15]; +}; + +/** A collection of decoder-specific configuration flags. */ +struct pt_conf_flags { + /** The decoder variant. */ + union { + /** Flags for the block decoder. */ + struct { + /** End a block after a call instruction. */ + uint32_t end_on_call:1; + + /** Enable tick events for timing updates. */ + uint32_t enable_tick_events:1; + + /** End a block after a jump instruction. */ + uint32_t end_on_jump:1; + +#if (LIBIPT_VERSION >= 0x201) + /** Preserve timing calibration on overflow. */ + uint32_t keep_tcal_on_ovf:1; + + /** Enable iflags events. + * + * Use this only when Event Tracing was enabled via + * IA32_RTIT_CTL[31]. + */ + uint32_t enable_iflags_events:1; +#endif + } block; + + /** Flags for the instruction flow decoder. */ + struct { + /** Enable tick events for timing updates. */ + uint32_t enable_tick_events:1; + +#if (LIBIPT_VERSION >= 0x201) + /** Preserve timing calibration on overflow. */ + uint32_t keep_tcal_on_ovf:1; + + /** Enable iflags events. + * + * Use this only when Event Tracing was enabled via + * IA32_RTIT_CTL[31]. + */ + uint32_t enable_iflags_events:1; +#endif + } insn; + +#if (LIBIPT_VERSION >= 0x201) + /** Flags for the query decoder. */ + struct { + /** Preserve timing calibration on overflow. */ + uint32_t keep_tcal_on_ovf:1; + + /** Enable iflags events. + * + * Use this only when Event Tracing was enabled via + * IA32_RTIT_CTL[31]. + */ + uint32_t enable_iflags_events:1; + } query; + + /** Flags for the event decoder. */ + struct { + /** Preserve timing calibration on overflow. */ + uint32_t keep_tcal_on_ovf:1; + + /** Enable iflags events. + * + * Use this only when Event Tracing was enabled via + * IA32_RTIT_CTL[31]. + */ + uint32_t enable_iflags_events:1; + } event; +#endif /* (LIBIPT_VERSION >= 0x201) */ + + /* Reserve a few bytes for future extensions. */ + uint32_t reserved[4]; + } variant; +}; + +/** The address filter configuration. */ +struct pt_conf_addr_filter { + /** The address filter configuration. + * + * This corresponds to the respective fields in IA32_RTIT_CTL MSR. + */ + union { + uint64_t addr_cfg; + + struct { + uint32_t addr0_cfg:4; + uint32_t addr1_cfg:4; + uint32_t addr2_cfg:4; + uint32_t addr3_cfg:4; + } ctl; + } config; + + /** The address ranges configuration. + * + * This corresponds to the IA32_RTIT_ADDRn_A/B MSRs. + */ + uint64_t addr0_a; + uint64_t addr0_b; + uint64_t addr1_a; + uint64_t addr1_b; + uint64_t addr2_a; + uint64_t addr2_b; + uint64_t addr3_a; + uint64_t addr3_b; + + /* Reserve some space. */ + uint64_t reserved[8]; +}; + +/** An unknown packet. */ +struct pt_packet_unknown; + +/** An Intel PT decoder configuration. + */ +struct pt_config { + /** The size of the config structure in bytes. */ + size_t size; + + /** The trace buffer begin address. */ + uint8_t *begin; + + /** The trace buffer end address. */ + uint8_t *end; + + /** An optional callback for handling unknown packets. + * + * If \@callback is not NULL, it is called for any unknown opcode. + */ + struct { + /** The callback function. + * + * It shall decode the packet at \@pos into \@unknown. + * It shall return the number of bytes read upon success. + * It shall return a negative pt_error_code otherwise. + * The below context is passed as \@context. + */ + int (*callback)(struct pt_packet_unknown *unknown, + const struct pt_config *config, + const uint8_t *pos, void *context); + + /** The user-defined context for this configuration. */ + void *context; + } decode; + + /** The cpu on which Intel PT has been recorded. */ + struct pt_cpu cpu; + + /** The errata to apply when encoding or decoding Intel PT. */ + struct pt_errata errata; + + /* The CTC frequency. + * + * This is only required if MTC packets have been enabled in + * IA32_RTIT_CTRL.MTCEn. + */ + uint32_t cpuid_0x15_eax, cpuid_0x15_ebx; + + /* The MTC frequency as defined in IA32_RTIT_CTL.MTCFreq. + * + * This is only required if MTC packets have been enabled in + * IA32_RTIT_CTRL.MTCEn. + */ + uint8_t mtc_freq; + + /* The nominal frequency as defined in MSR_PLATFORM_INFO[15:8]. + * + * This is only required if CYC packets have been enabled in + * IA32_RTIT_CTRL.CYCEn. + * + * If zero, timing calibration will only be able to use MTC and CYC + * packets. + * + * If not zero, timing calibration will also be able to use CBR + * packets. + */ + uint8_t nom_freq; + + /** A collection of decoder-specific flags. */ + struct pt_conf_flags flags; + + /** The address filter configuration. */ + struct pt_conf_addr_filter addr_filter; +}; + + +/** Zero-initialize an Intel PT configuration. */ +static inline void pt_config_init(struct pt_config *config) +{ + memset(config, 0, sizeof(*config)); + + config->size = sizeof(*config); +} + +/** Determine errata for a given cpu. + * + * Updates \@errata based on \@cpu. + * + * Returns 0 on success, a negative error code otherwise. + * Returns -pte_invalid if \@errata or \@cpu is NULL. + * Returns -pte_bad_cpu if \@cpu is not known. + */ +extern pt_export int pt_cpu_errata(struct pt_errata *errata, + const struct pt_cpu *cpu); + + + +/* Packet encoder / decoder. */ + + + +/** Intel PT packet types. */ +enum pt_packet_type { + /* An invalid packet. */ + ppt_invalid, + + /* A packet decodable by the optional decoder callback. */ + ppt_unknown, + + /* Actual packets supported by this library. */ + ppt_pad, + ppt_psb, + ppt_psbend, + ppt_fup, + ppt_tip, + ppt_tip_pge, + ppt_tip_pgd, + ppt_tnt_8, + ppt_tnt_64, + ppt_mode, + ppt_pip, + ppt_vmcs, + ppt_cbr, + ppt_tsc, + ppt_tma, + ppt_mtc, + ppt_cyc, + ppt_stop, + ppt_ovf, + ppt_mnt, + ppt_exstop, + ppt_mwait, + ppt_pwre, + ppt_pwrx, + ppt_ptw, +#if (LIBIPT_VERSION >= 0x201) + ppt_cfe, + ppt_evd, +#endif +#if (LIBIPT_VERSION >= 0x202) + ppt_trig, +#endif +}; + +/** The IP compression. */ +enum pt_ip_compression { + /* The bits encode the payload size and the encoding scheme. + * + * No payload. The IP has been suppressed. + */ + pt_ipc_suppressed = 0x0, + + /* Payload: 16 bits. Update last IP. */ + pt_ipc_update_16 = 0x01, + + /* Payload: 32 bits. Update last IP. */ + pt_ipc_update_32 = 0x02, + + /* Payload: 48 bits. Sign extend to full address. */ + pt_ipc_sext_48 = 0x03, + + /* Payload: 48 bits. Update last IP. */ + pt_ipc_update_48 = 0x04, + + /* Payload: 64 bits. Full address. */ + pt_ipc_full = 0x06 +}; + +/** An execution mode. */ +enum pt_exec_mode { + ptem_unknown, + ptem_16bit, + ptem_32bit, + ptem_64bit +}; + +/** Mode packet leaves. */ +enum pt_mode_leaf { + pt_mol_exec = 0x00, + pt_mol_tsx = 0x20 +}; + +/** A TNT-8 or TNT-64 packet. */ +struct pt_packet_tnt { + /** TNT payload bit size. */ + uint8_t bit_size; + + /** TNT payload excluding stop bit. */ + uint64_t payload; +}; + +/** A packet with IP payload. */ +struct pt_packet_ip { + /** IP compression. */ + enum pt_ip_compression ipc; + + /** Zero-extended payload ip. */ + uint64_t ip; +}; + +/** A mode.exec packet. */ +struct pt_packet_mode_exec { + /** The mode.exec csl bit. */ + uint32_t csl:1; + + /** The mode.exec csd bit. */ + uint32_t csd:1; + +#if (LIBIPT_VERSION >= 0x201) + /** The mode.exec if bit. + * + * Enable Event Tracing to get updates when IF changes. + */ + uint32_t iflag:1; +#endif +}; + +static inline enum pt_exec_mode +pt_get_exec_mode(const struct pt_packet_mode_exec *packet) +{ + if (packet->csl) + return packet->csd ? ptem_unknown : ptem_64bit; + else + return packet->csd ? ptem_32bit : ptem_16bit; +} + +static inline struct pt_packet_mode_exec +pt_set_exec_mode(enum pt_exec_mode mode) +{ + struct pt_packet_mode_exec packet; + + memset(&packet, 0, sizeof(packet)); + switch (mode) { + default: + packet.csl = 1; + packet.csd = 1; + break; + + case ptem_64bit: + packet.csl = 1; + break; + + case ptem_32bit: + packet.csd = 1; + break; + + case ptem_16bit: + break; + } + + return packet; +} + +/** A mode.tsx packet. */ +struct pt_packet_mode_tsx { + /** The mode.tsx intx bit. */ + uint32_t intx:1; + + /** The mode.tsx abrt bit. */ + uint32_t abrt:1; +}; + +/** A mode packet. */ +struct pt_packet_mode { + /** Mode leaf. */ + enum pt_mode_leaf leaf; + + /** Mode bits. */ + union { + /** Packet: mode.exec. */ + struct pt_packet_mode_exec exec; + + /** Packet: mode.tsx. */ + struct pt_packet_mode_tsx tsx; + } bits; +}; + +/** A PIP packet. */ +struct pt_packet_pip { + /** The CR3 value. */ + uint64_t cr3; + + /** The non-root bit. */ + uint32_t nr:1; +}; + +/** A TSC packet. */ +struct pt_packet_tsc { + /** The TSC value. */ + uint64_t tsc; +}; + +/** A CBR packet. */ +struct pt_packet_cbr { + /** The core/bus cycle ratio. */ + uint8_t ratio; +}; + +/** A TMA packet. */ +struct pt_packet_tma { + /** The crystal clock tick counter value. */ + uint16_t ctc; + + /** The fast counter value. */ + uint16_t fc; +}; + +/** A MTC packet. */ +struct pt_packet_mtc { + /** The crystal clock tick counter value. */ + uint8_t ctc; +}; + +/** A CYC packet. */ +struct pt_packet_cyc { + /** The cycle counter value. */ + uint64_t value; +}; + +/** A VMCS packet. */ +struct pt_packet_vmcs { + /* The VMCS Base Address (i.e. the shifted payload). */ + uint64_t base; +}; + +/** A MNT packet. */ +struct pt_packet_mnt { + /** The raw payload. */ + uint64_t payload; +}; + +/** A EXSTOP packet. */ +struct pt_packet_exstop { + /** A flag specifying the binding of the packet: + * + * set: binds to the next FUP. + * clear: standalone. + */ + uint32_t ip:1; +}; + +/** A MWAIT packet. */ +struct pt_packet_mwait { + /** The MWAIT hints (EAX). */ + uint32_t hints; + + /** The MWAIT extensions (ECX). */ + uint32_t ext; +}; + +/** A PWRE packet. */ +struct pt_packet_pwre { + /** The resolved thread C-state. */ + uint8_t state; + + /** The resolved thread sub C-state. */ + uint8_t sub_state; + + /** A flag indicating whether the C-state entry was initiated by h/w. */ + uint32_t hw:1; +}; + +/** A PWRX packet. */ +struct pt_packet_pwrx { + /** The core C-state at the time of the wake. */ + uint8_t last; + + /** The deepest core C-state achieved during sleep. */ + uint8_t deepest; + + /** The wake reason: + * + * - due to external interrupt received. + */ + uint32_t interrupt:1; + + /** - due to store to monitored address. */ + uint32_t store:1; + + /** - due to h/w autonomous condition such as HDC. */ + uint32_t autonomous:1; +}; + +/** A PTW packet. */ +struct pt_packet_ptw { + /** The raw payload. */ + uint64_t payload; + + /** The payload size as encoded in the packet. */ + uint8_t plc; + + /** A flag saying whether a FUP is following PTW that provides + * the IP of the corresponding PTWRITE instruction. + */ + uint32_t ip:1; +}; + +static inline int pt_ptw_size(uint8_t plc) +{ + switch (plc) { + case 0: + return 4; + + case 1: + return 8; + + case 2: + case 3: + return -pte_bad_packet; + } + + return -pte_internal; +} + +#if (LIBIPT_VERSION >= 0x201) +/* Control-flow event types. */ +enum pt_cfe_type { + pt_cfe_intr = 0x01, + pt_cfe_iret = 0x02, + pt_cfe_smi = 0x03, + pt_cfe_rsm = 0x04, + pt_cfe_sipi = 0x05, + pt_cfe_init = 0x06, + pt_cfe_vmentry = 0x07, + pt_cfe_vmexit = 0x08, + pt_cfe_vmexit_intr = 0x09, + pt_cfe_shutdown = 0x0a, + pt_cfe_uintr = 0x0c, + pt_cfe_uiret = 0x0d, +#if (LIBIPT_VERSION >= 0x202) + pt_cfe_swintr = 0x0e, + pt_cfe_syscall = 0x0f, +#endif +}; + +/* Interrupt vectors defined in Table 6-1 in §6.5 of Vol.1 of the SDM. */ +enum pt_cfe_intr { + pt_cfe_intr_de = 0, /* Divide Error. */ + pt_cfe_intr_db = 1, /* Debug. */ + pt_cfe_intr_nmi = 2, /* Non Maskable Interrupt. */ + pt_cfe_intr_bp = 3, /* Breakpoint. */ + pt_cfe_intr_of = 4, /* Overflow. */ + pt_cfe_intr_br = 5, /* Bound Range Exceeded. */ + pt_cfe_intr_ud = 6, /* Undefined Opcode. */ + pt_cfe_intr_nm = 7, /* Device Not Available. */ + pt_cfe_intr_df = 8, /* Double Fault. */ + pt_cfe_intr_ts = 10, /* Invalid TSS. */ + pt_cfe_intr_np = 11, /* Segment Not Present. */ + pt_cfe_intr_ss = 12, /* Stack Segment Fault. */ + pt_cfe_intr_gp = 13, /* General Protection Fault. */ + pt_cfe_intr_pf = 14, /* Page Fault. */ + pt_cfe_intr_mf = 16, /* Math Fault. */ + pt_cfe_intr_ac = 17, /* Alignment Check. */ + pt_cfe_intr_mc = 18, /* Machine Check. */ + pt_cfe_intr_xm = 19, /* SIMD Floating Point Exception. */ + pt_cfe_intr_ve = 20, /* Virtualization Exception. */ + pt_cfe_intr_cp = 21, /* Control Protection Exception. */ +}; + +/** A CFE packet. */ +struct pt_packet_cfe { + /** The type of control-flow event. */ + enum pt_cfe_type type; + + /** The vector depending on the type: + * + * pt_cfe_intr: the event vector. + * pt_cfe_vmexit_intr: the event vector. + * pt_cfe_sipi: the SIPI vector. + * pt_cfe_uintr: the user interrupt vector. + * pt_cfe_swintr: the event vector. + */ + uint8_t vector; + + /** A flag specifying the binding of the packet: + * + * set: binds to the next FUP. + * clear: binds to the next FUP + TIP, if tracing enabled. + * clear: standalone, if tracing disabled. + */ + uint32_t ip:1; +}; + +/* Event data types. */ +enum pt_evd_type { + pt_evd_cr2 = 0x00, /* Page fault linear address (cr2). */ + pt_evd_vmxq = 0x01, /* VMX exit qualification. */ + pt_evd_vmxr = 0x02, /* VMX exit reason. */ +}; + +/** A EVD packet. */ +struct pt_packet_evd { + /** The type of control-flow event. */ + enum pt_evd_type type; + + /** The payload depending on the type: + * + * 0x00: page fault linear address (cr2). + * 0x01: vmx exit qualification. + * 0x02: vmx exit reason. + */ + uint64_t payload; +}; +#endif /* (LIBIPT_VERSION >= 0x201) */ + +#if (LIBIPT_VERSION >= 0x202) +/** A TRIG packet. */ +struct pt_packet_trig { + /** A bit vector of triggers that are represented by this packet. */ + uint8_t trbv; + + /** An instruction count from the last IP packet (FUP, TIP*, or TNT) + * indicating the instruction to which this packet is attributed. + * + * This field is only valid if \@icntv is set. + */ + uint16_t icnt; + + /** A flag saying whether a FUP is following TRIG that provides a + * reference IP from which to start counting. + */ + uint32_t ip:1; + + /** A flag saying whether the \@icnt field is valid.*/ + uint32_t icntv:1; + + /** A flag saying whether there were other triggers firing that are not + * reported. + */ + uint32_t mult:1; +}; +#endif /* (LIBIPT_VERSION >= 0x202) */ + +/** An unknown packet decodable by the optional decoder callback. */ +struct pt_packet_unknown { + /** Pointer to the raw packet bytes. */ + const uint8_t *packet; + + /** Optional pointer to a user-defined structure. */ + void *priv; +}; + +/** An Intel PT packet. */ +struct pt_packet { + /** The type of the packet. + * + * This also determines the \@payload field. + */ + enum pt_packet_type type; + + /** The size of the packet including opcode and payload. */ + uint8_t size; + + /** Packet specific data. */ + union { + /** Packets: pad, ovf, psb, psbend, stop - no payload. */ + + /** Packet: tnt-8, tnt-64. */ + struct pt_packet_tnt tnt; + + /** Packet: tip, fup, tip.pge, tip.pgd. */ + struct pt_packet_ip ip; + + /** Packet: mode. */ + struct pt_packet_mode mode; + + /** Packet: pip. */ + struct pt_packet_pip pip; + + /** Packet: tsc. */ + struct pt_packet_tsc tsc; + + /** Packet: cbr. */ + struct pt_packet_cbr cbr; + + /** Packet: tma. */ + struct pt_packet_tma tma; + + /** Packet: mtc. */ + struct pt_packet_mtc mtc; + + /** Packet: cyc. */ + struct pt_packet_cyc cyc; + + /** Packet: vmcs. */ + struct pt_packet_vmcs vmcs; + + /** Packet: mnt. */ + struct pt_packet_mnt mnt; + + /** Packet: exstop. */ + struct pt_packet_exstop exstop; + + /** Packet: mwait. */ + struct pt_packet_mwait mwait; + + /** Packet: pwre. */ + struct pt_packet_pwre pwre; + + /** Packet: pwrx. */ + struct pt_packet_pwrx pwrx; + + /** Packet: ptw. */ + struct pt_packet_ptw ptw; + +#if (LIBIPT_VERSION >= 0x201) + /** Packet: cfe. */ + struct pt_packet_cfe cfe; + + /** Packet: evd. */ + struct pt_packet_evd evd; +#endif +#if (LIBIPT_VERSION >= 0x202) + /** Packet: trig. */ + struct pt_packet_trig trig; +#endif + /** Packet: unknown. */ + struct pt_packet_unknown unknown; + } payload; +}; + + + +/* Packet encoder. */ + + + +/** Allocate an Intel PT packet encoder. + * + * The encoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the encoder. + * + * The encoder starts at the beginning of the trace buffer. + */ +extern pt_export struct pt_encoder * +pt_alloc_encoder(const struct pt_config *config); + +/** Free an Intel PT packet encoder. + * + * The \@encoder must not be used after a successful return. + */ +extern pt_export void pt_free_encoder(struct pt_encoder *encoder); + +/** Hard set synchronization point of an Intel PT packet encoder. + * + * Synchronize \@encoder to \@offset within the trace buffer. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_eos if the given offset is behind the end of the trace buffer. + * Returns -pte_invalid if \@encoder is NULL. + */ +extern pt_export int pt_enc_sync_set(struct pt_encoder *encoder, + uint64_t offset); + +/** Get the current packet encoder position. + * + * Fills the current \@encoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@encoder or \@offset is NULL. + */ +extern pt_export int pt_enc_get_offset(const struct pt_encoder *encoder, + uint64_t *offset); + +/* Return a pointer to \@encoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@encoder is NULL. + */ +extern pt_export const struct pt_config * +pt_enc_get_config(const struct pt_encoder *encoder); + +/** Encode an Intel PT packet. + * + * Writes \@packet at \@encoder's current position in the Intel PT buffer and + * advances the \@encoder beyond the written packet. + * + * The \@packet.size field is ignored. + * + * In case of errors, the \@encoder is not advanced and nothing is written + * into the Intel PT buffer. + * + * Returns the number of bytes written on success, a negative error code + * otherwise. + * + * Returns -pte_bad_opc if \@packet.type is not known. + * Returns -pte_bad_packet if \@packet's payload is invalid. + * Returns -pte_eos if \@encoder reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@encoder or \@packet is NULL. + */ +extern pt_export int pt_enc_next(struct pt_encoder *encoder, + const struct pt_packet *packet); + + + +/* Packet decoder. */ + + + +/** Allocate an Intel PT packet decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_packet_decoder * +pt_pkt_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT packet decoder. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_pkt_free_decoder(struct pt_packet_decoder *decoder); + +/** Synchronize an Intel PT packet decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_pkt_sync_forward(struct pt_packet_decoder *decoder); +extern pt_export int pt_pkt_sync_backward(struct pt_packet_decoder *decoder); + +/** Hard set synchronization point of an Intel PT decoder. + * + * Synchronize \@decoder to \@offset within the trace buffer. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_eos if the given offset is behind the end of the trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_pkt_sync_set(struct pt_packet_decoder *decoder, + uint64_t offset); + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_pkt_get_offset(const struct pt_packet_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * This is useful when splitting a trace stream for parallel decoding. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_pkt_get_sync_offset(const struct pt_packet_decoder *decoder, + uint64_t *offset); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_pkt_get_config(const struct pt_packet_decoder *decoder); + +/** Decode the next packet and advance the decoder. + * + * Decodes the packet at \@decoder's current position into \@packet and + * adjusts the \@decoder's position by the number of bytes the packet had + * consumed. + * + * The \@size argument must be set to sizeof(struct pt_packet). + * + * Returns the number of bytes consumed on success, a negative error code + * otherwise. + * + * Returns -pte_bad_opc if the packet is unknown. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@decoder reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@packet is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_pkt_next(struct pt_packet_decoder *decoder, + struct pt_packet *packet, size_t size); + + + +/* Event decoder. */ + + + +/** Event types. */ +enum pt_event_type { + /* Tracing has been enabled/disabled. */ + ptev_enabled, + ptev_disabled, + + /* Tracing has been disabled asynchronously. */ + ptev_async_disabled, + + /* An asynchronous branch, e.g. interrupt. */ + ptev_async_branch, + + /* A synchronous paging event. */ + ptev_paging, + + /* An asynchronous paging event. */ + ptev_async_paging, + + /* Trace overflow. */ + ptev_overflow, + + /* An execution mode change. */ + ptev_exec_mode, + + /* A transactional execution state change. */ + ptev_tsx, + + /* Trace Stop. */ + ptev_stop, + + /* A synchronous vmcs event. */ + ptev_vmcs, + + /* An asynchronous vmcs event. */ + ptev_async_vmcs, + + /* Execution has stopped. */ + ptev_exstop, + + /* An MWAIT operation completed. */ + ptev_mwait, + + /* A power state was entered. */ + ptev_pwre, + + /* A power state was exited. */ + ptev_pwrx, + + /* A PTWRITE event. */ + ptev_ptwrite, + + /* A timing event. */ + ptev_tick, + + /* A core:bus ratio event. */ + ptev_cbr, + + /* A maintenance event. */ + ptev_mnt, + +#if (LIBIPT_VERSION >= 0x201) + /* An indirect branch event. */ + ptev_tip, + + /* A conditional branch indicator event. */ + ptev_tnt, + + /* An interrupt status event. */ + ptev_iflags, + + /* An interrupt or exception event. + * + * This event extends the async_branch event generated for the + * control-flow change; it does not replace that event. + */ + ptev_interrupt, + + /* A return from interrupt event. */ + ptev_iret, + + /* A system management interrupt. */ + ptev_smi, + + /* A return from system management mode. */ + ptev_rsm, + + /* A SIPI message. */ + ptev_sipi, + + /* An INIT reset. */ + ptev_init, + + /* A Virtual Machine entry. */ + ptev_vmentry, + + /* A Virtual Machine exit. */ + ptev_vmexit, + + /* A shutdown event. */ + ptev_shutdown, + + /* A user interrupt. */ + ptev_uintr, + + /* A return from user interrupt. */ + ptev_uiret, +#endif +#if (LIBIPT_VERSION >= 0x202) + /* A trigger event. */ + ptev_trig, + + /* A software interrupt. */ + ptev_swintr, + + /* A system call. */ + ptev_syscall, +#endif +}; + +/** An event. */ +struct pt_event { + /** The type of the event. */ + enum pt_event_type type; + + /** A flag indicating that the event IP has been suppressed. */ + uint32_t ip_suppressed:1; + + /** A flag indicating that the event is for status update. */ + uint32_t status_update:1; + + /** A flag indicating that the event has timing information. */ + uint32_t has_tsc:1; + + /** The time stamp count of the event. + * + * This field is only valid if \@has_tsc is set. + */ + uint64_t tsc; + + /** The number of lost mtc and cyc packets. + * + * This gives an idea about the quality of the \@tsc. The more packets + * were dropped, the less precise timing is. + */ + uint32_t lost_mtc; + uint32_t lost_cyc; + + /* Reserved space for future extensions. */ + uint64_t reserved[2]; + + /** Event specific data. */ + union { + /** Event: enabled. */ + struct { + /** The address at which tracing resumes. */ + uint64_t ip; + + /** A flag indicating that tracing resumes from the IP + * at which tracing had been disabled before. + */ + uint32_t resumed:1; + } enabled; + + /** Event: disabled. */ + struct { + /** The destination of the first branch inside a + * filtered area. + * + * This field is not valid if \@ip_suppressed is set. + */ + uint64_t ip; + + /* The exact source ip needs to be determined using + * disassembly and the filter configuration. + */ + } disabled; + + /** Event: async disabled. */ + struct { + /** The source address of the asynchronous branch that + * disabled tracing. + */ + uint64_t at; + + /** The destination of the first branch inside a + * filtered area. + * + * This field is not valid if \@ip_suppressed is set. + */ + uint64_t ip; + } async_disabled; + + /** Event: async branch. */ + struct { + /** The branch source address. */ + uint64_t from; + + /** The branch destination address. + * + * This field is not valid if \@ip_suppressed is set. + */ + uint64_t to; + } async_branch; + + /** Event: paging. */ + struct { + /** The updated CR3 value. + * + * The lower 5 bit have been zeroed out. + * The upper bits have been zeroed out depending on the + * maximum possible address. + */ + uint64_t cr3; + + /** A flag indicating whether the cpu is operating in + * vmx non-root (guest) mode. + */ + uint32_t non_root:1; + + /* The address at which the event is effective is + * obvious from the disassembly. + */ + } paging; + + /** Event: async paging. */ + struct { + /** The updated CR3 value. + * + * The lower 5 bit have been zeroed out. + * The upper bits have been zeroed out depending on the + * maximum possible address. + */ + uint64_t cr3; + + /** A flag indicating whether the cpu is operating in + * vmx non-root (guest) mode. + */ + uint32_t non_root:1; + + /** The address at which the event is effective. */ + uint64_t ip; + } async_paging; + + /** Event: overflow. */ + struct { + /** The address at which tracing resumes after overflow. + * + * This field is not valid, if ip_suppressed is set. + * In this case, the overflow resolved while tracing + * was disabled. + */ + uint64_t ip; + } overflow; + + /** Event: exec mode. */ + struct { + /** The execution mode. */ + enum pt_exec_mode mode; + + /** The address at which the event is effective. */ + uint64_t ip; + } exec_mode; + + /** Event: tsx. */ + struct { + /** The address at which the event is effective. + * + * This field is not valid if \@ip_suppressed is set. + */ + uint64_t ip; + + /** A flag indicating speculative execution mode. */ + uint32_t speculative:1; + + /** A flag indicating speculative execution aborts. */ + uint32_t aborted:1; + } tsx; + + /** Event: vmcs. */ + struct { + /** The VMCS base address. + * + * The address is zero-extended with the lower 12 bits + * all zero. + */ + uint64_t base; + + /* The new VMCS base address should be stored and + * applied on subsequent VM entries. + */ + } vmcs; + + /** Event: async vmcs. */ + struct { + /** The VMCS base address. + * + * The address is zero-extended with the lower 12 bits + * all zero. + */ + uint64_t base; + + /** The address at which the event is effective. */ + uint64_t ip; + + /* An async paging event that binds to the same IP + * will always succeed this async vmcs event. + */ + } async_vmcs; + + /** Event: execution stopped. */ + struct { + /** The address at which execution has stopped. This is + * the last instruction that did not complete. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } exstop; + + /** Event: mwait. */ + struct { + /** The address of the instruction causing the mwait. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + + /** The mwait hints (eax). + * + * Reserved bits are undefined. + */ + uint32_t hints; + + /** The mwait extensions (ecx). + * + * Reserved bits are undefined. + */ + uint32_t ext; + } mwait; + + /** Event: power state entry. */ + struct { + /** The resolved thread C-state. */ + uint8_t state; + + /** The resolved thread sub C-state. */ + uint8_t sub_state; + + /** A flag indicating whether the C-state entry was + * initiated by h/w. + */ + uint32_t hw:1; + } pwre; + + /** Event: power state exit. */ + struct { + /** The core C-state at the time of the wake. */ + uint8_t last; + + /** The deepest core C-state achieved during sleep. */ + uint8_t deepest; + + /** The wake reason: + * + * - due to external interrupt received. + */ + uint32_t interrupt:1; + + /** - due to store to monitored address. */ + uint32_t store:1; + + /** - due to h/w autonomous condition such as HDC. */ + uint32_t autonomous:1; + } pwrx; + + /** Event: ptwrite. */ + struct { + /** The address of the ptwrite instruction. + * + * This field is not valid, if \@ip_suppressed is set. + * + * In this case, the address is obvious from the + * disassembly. + */ + uint64_t ip; + + /** The size of the below \@payload in bytes. */ + uint8_t size; + + /** The ptwrite payload. */ + uint64_t payload; + } ptwrite; + + /** Event: tick. */ + struct { + /** The instruction address near which the tick + * occurred. + * + * A timestamp can sometimes be attributed directly to + * an instruction (e.g. to an indirect branch that + * receives CYC + TIP) and sometimes not (e.g. MTC). + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } tick; + + /** Event: cbr. */ + struct { + /** The core:bus ratio. */ + uint16_t ratio; + } cbr; + + /** Event: mnt. */ + struct { + /** The raw payload. */ + uint64_t payload; + } mnt; + +#if (LIBIPT_VERSION >= 0x201) + /** Event: tip. */ + struct { + /** The target instruction address. */ + uint64_t ip; + } tip; + + /** Event: tnt. */ + struct { + /** A sequence of conditional branch indicator bits. + * + * Indicators are ordered from oldest (bits[size-1]) to + * youngest (bits[0]) branch: + * + * 0...branch not taken + * 1...branch taken + */ + uint64_t bits; + + /** The number of valid bits in @bits. */ + uint8_t size; + } tnt; + + /** Event: iflags. */ + struct { + /** The EFLAGS.IF status. */ + uint32_t iflag:1; + + /** The address of the instruction at which the new + * status applies. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } iflags; + + /** Event: interrupt/exception. */ + struct { + /** A flag saying whether the \@cr2 field is valid. */ + uint32_t has_cr2:1; + + /** The interrupt/exception vector. */ + uint8_t vector; + + /** The page fault linear address in cr2. + * + * This field is only valid if \@has_cr2 is set. + */ + uint64_t cr2; + + /** The address of the instruction at which the + * interrupt or exception occurred. + * + * For faults, this will be the faulting instruction. + * For traps, this will be the next instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } interrupt; + + /** Event: iret. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } iret; + + /** Event: smi. */ + struct { + /** The address of the instruction at/before which the + * system management interrupt occurred. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } smi; + + /** Event: rsm. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } rsm; + + /** Event: sipi. */ + struct { + /** The SIPI vector. */ + uint8_t vector; + } sipi; + + /** Event: init. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } init; + + /** Event: vmentry. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } vmentry; + + /** Event: vmexit. */ + struct { + /** A flag saying whether the \@vector field is valid. + * + * When set, the vmexit occurred due to an interrupt or + * exception. + */ + uint32_t has_vector:1; + + /** A flag saying whether the \@vmxr field is valid. */ + uint32_t has_vmxr:1; + + /** A flag saying whether the \@vmxq field is valid. */ + uint32_t has_vmxq:1; + + /** The interrupt/exception vector. + * + * This field is only valid if \@has_vector is set. + */ + uint8_t vector; + + /** The vmexit reason. + * + * This field is only valid if \@has_vmxr is set. + */ + uint64_t vmxr; + + /** The vmexit qualification. + * + * This field is only valid if \@has_vmxq is set. + */ + uint64_t vmxq; + + /** The address of the instruction at which the + * interrupt or exception occurred. + * + * For faults, this will be the faulting instruction. + * For traps, this will be the next instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } vmexit; + + /** Event: shutdown. */ + struct { + /** The address of the first instruction that did not + * complete due to the shutdown. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } shutdown; + + /** Event: user interrupt. */ + struct { + /** The user interrupt vector. */ + uint8_t vector; + + /** The address of the instruction before which the + * user interrupt was delivered. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } uintr; + + /** Event: uiret. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } uiret; +#endif /* (LIBIPT_VERSION >= 0x201) */ + +#if (LIBIPT_VERSION >= 0x202) + /** Event: trigger. */ + struct { + /** A bit vector of triggers represented by this event. + * + * Triggers are configured via IA32_RTIT_TRIGGERx_CFG. + */ + uint8_t trbv; + + /** The number of instructions after the anchor. + * + * The insn and block decoders will attempty to reduce + * \@icount to zero and update \@ip. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint16_t icnt; + + /** The address of the anchor instruction. + * + * If this field is zero, use the address of the last + * trig, tip, tip.pge, or async event, or the address + * after applying the final bit in the last tnt event. + * + * The insn and block decoders will supply a non-zero + * address or set \@ip_suppressed. + * + * The event is reported when reaching the instruction + * address before the instruction itself to correctly + * handle cases where the instruction faults. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + + /** A flag saying whether there were other triggers + * firing that are not reported. + */ + uint32_t mult:1; + } trig; + + /** Event: software interrupt. */ + struct { + /** The interrupt vector. */ + uint8_t vector; + + /** The address of the instruction that generated the + * software interrupt. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } swintr; + + /** Event: system call. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } syscall; +#endif /* (LIBIPT_VERSION >= 0x202) */ + } variant; +}; + + +#if (LIBIPT_VERSION >= 0x201) +/** Allocate an Intel PT event decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_event_decoder * +pt_evt_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT event decoder. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_evt_free_decoder(struct pt_event_decoder *decoder); + +/** Synchronize an Intel PT event decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_evt_sync_forward(struct pt_event_decoder *decoder); +extern pt_export int pt_evt_sync_backward(struct pt_event_decoder *decoder); + +/** Manually synchronize an Intel PT event decoder. + * + * Synchronize \@decoder on the syncpoint at \@offset. There must be a PSB + * packet at \@offset. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@offset lies outside of \@decoder's trace buffer. + * Returns -pte_eos if \@decoder reaches the end of its trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + * Returns -pte_nosync if there is no syncpoint at \@offset. + */ +extern pt_export int pt_evt_sync_set(struct pt_event_decoder *decoder, + uint64_t offset); + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_evt_get_offset(const struct pt_event_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * This is useful for splitting a trace stream for parallel decoding. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_evt_get_sync_offset(const struct pt_event_decoder *decoder, + uint64_t *offset); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_evt_get_config(const struct pt_event_decoder *decoder); + +/** Determine the next event. + * + * On success, provides the next event in \@event. + * + * The \@size argument must be set to sizeof(struct pt_event). + * + * Returns zero or a positive value on success, a negative error code + * otherwise. + * + * Returns -pte_bad_opc if the packet is unknown. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@decoder reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@event is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_evt_next(struct pt_event_decoder *decoder, + struct pt_event *event, size_t size); +#endif /* (LIBIPT_VERSION >= 0x201) */ + + + +/* Query decoder. */ + + + +/** Decoder status flags. */ +enum pt_status_flag { + /** There is an event pending. */ + pts_event_pending = 1 << 0, + + /** The address has been suppressed. */ + pts_ip_suppressed = 1 << 1, + + /** There is no more trace data available. */ + pts_eos = 1 << 2 +}; + +/** Allocate an Intel PT query decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_query_decoder * +pt_qry_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT query decoder. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_qry_free_decoder(struct pt_query_decoder *decoder); + +/** Synchronize an Intel PT query decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * If \@ip is not NULL, set it to last ip. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_qry_sync_forward(struct pt_query_decoder *decoder, + uint64_t *ip); +extern pt_export int pt_qry_sync_backward(struct pt_query_decoder *decoder, + uint64_t *ip); + +/** Manually synchronize an Intel PT query decoder. + * + * Synchronize \@decoder on the syncpoint at \@offset. There must be a PSB + * packet at \@offset. + * + * If \@ip is not NULL, set it to last ip. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@offset lies outside of \@decoder's trace buffer. + * Returns -pte_eos if \@decoder reaches the end of its trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + * Returns -pte_nosync if there is no syncpoint at \@offset. + */ +extern pt_export int pt_qry_sync_set(struct pt_query_decoder *decoder, + uint64_t *ip, uint64_t offset); + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_qry_get_offset(const struct pt_query_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * This is useful for splitting a trace stream for parallel decoding. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_qry_get_sync_offset(const struct pt_query_decoder *decoder, + uint64_t *offset); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_qry_get_config(const struct pt_query_decoder *decoder); + +/** Query whether the next unconditional branch has been taken. + * + * On success, provides 1 (taken) or 0 (not taken) in \@taken for the next + * conditional branch and updates \@decoder. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_bad_query if no conditional branch is found. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@taken is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_qry_cond_branch(struct pt_query_decoder *decoder, + int *taken); + +/** Get the next indirect branch destination. + * + * On success, provides the linear destination address of the next indirect + * branch in \@ip and updates \@decoder. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_bad_query if no indirect branch is found. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@ip is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_qry_indirect_branch(struct pt_query_decoder *decoder, + uint64_t *ip); + +/** Query the next pending event. + * + * On success, provides the next event \@event and updates \@decoder. + * + * The \@size argument must be set to sizeof(struct pt_event). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_bad_query if no event is found. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@event is NULL. + * Returns -pte_invalid if \@size is too small. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_qry_event(struct pt_query_decoder *decoder, + struct pt_event *event, size_t size); + +/** Query the current time. + * + * On success, provides the time at the last query in \@time. + * + * The time is similar to what a rdtsc instruction would return. Depending + * on the configuration, the time may not be fully accurate. If TSC is not + * enabled, the time is relative to the last synchronization and can't be used + * to correlate with other TSC-based time sources. In this case, -pte_no_time + * is returned and the relative time is provided in \@time. + * + * Some timing-related packets may need to be dropped (mostly due to missing + * calibration or incomplete configuration). To get an idea about the quality + * of the estimated time, we record the number of dropped MTC and CYC packets. + * + * If \@lost_mtc is not NULL, set it to the number of lost MTC packets. + * If \@lost_cyc is not NULL, set it to the number of lost CYC packets. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@time is NULL. + * Returns -pte_no_time if there has not been a TSC packet. + */ +extern pt_export int pt_qry_time(struct pt_query_decoder *decoder, + uint64_t *time, uint32_t *lost_mtc, + uint32_t *lost_cyc); + +/** Return the current core bus ratio. + * + * On success, provides the current core:bus ratio in \@cbr. The ratio is + * defined as core cycles per bus clock cycle. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@cbr is NULL. + * Returns -pte_no_cbr if there has not been a CBR packet. + */ +extern pt_export int pt_qry_core_bus_ratio(struct pt_query_decoder *decoder, + uint32_t *cbr); + + + +/* Traced image. */ + + + +/** An Intel PT address space identifier. + * + * This identifies a particular address space when adding file sections or + * when reading memory. + */ +struct pt_asid { + /** The size of this object - set to sizeof(struct pt_asid). */ + size_t size; + + /** The CR3 value. */ + uint64_t cr3; + + /** The VMCS Base address. */ + uint64_t vmcs; +}; + +/** An unknown CR3 value to be used for pt_asid objects. */ +static const uint64_t pt_asid_no_cr3 = 0xffffffffffffffffull; + +/** An unknown VMCS Base value to be used for pt_asid objects. */ +static const uint64_t pt_asid_no_vmcs = 0xffffffffffffffffull; + +/** Initialize an address space identifier. */ +static inline void pt_asid_init(struct pt_asid *asid) +{ + asid->size = sizeof(*asid); + asid->cr3 = pt_asid_no_cr3; + asid->vmcs = pt_asid_no_vmcs; +} + + +/** A cache of traced image sections. */ +struct pt_image_section_cache; + +/** Allocate a traced memory image section cache. + * + * An optional \@name may be given to the cache. The name string is copied. + * + * Returns a new traced memory image section cache on success, NULL otherwise. + */ +extern pt_export struct pt_image_section_cache * +pt_iscache_alloc(const char *name); + +/** Free a traced memory image section cache. + * + * The \@iscache must have been allocated with pt_iscache_alloc(). + * The \@iscache must not be used after a successful return. + */ +extern pt_export void pt_iscache_free(struct pt_image_section_cache *iscache); + +/** Set the image section cache limit. + * + * Set the limit for a section cache in bytes. A non-zero limit will keep the + * least recently used sections mapped until the limit is reached. A limit of + * zero disables caching. + * + * Returns zero on success, a negative pt_error_code otherwise. + * Returns -pte_invalid if \@iscache is NULL. + */ +extern pt_export int +pt_iscache_set_limit(struct pt_image_section_cache *iscache, uint64_t limit); + +/** Get the image section cache name. + * + * Returns a pointer to \@iscache's name or NULL if there is no name. + */ +extern pt_export const char * +pt_iscache_name(const struct pt_image_section_cache *iscache); + +/** Add a new file section to the traced memory image section cache. + * + * Adds a new section consisting of \@size bytes starting at \@offset in + * \@filename loaded at the virtual address \@vaddr if \@iscache does not + * already contain such a section. + * + * Returns an image section identifier (isid) uniquely identifying that section + * in \@iscache. + * + * The section is silently truncated to match the size of \@filename. + * + * Returns a positive isid on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@iscache or \@filename is NULL. + * Returns -pte_invalid if \@offset is too big. + */ +extern pt_export int pt_iscache_add_file(struct pt_image_section_cache *iscache, + const char *filename, uint64_t offset, + uint64_t size, uint64_t vaddr); + +/** Read memory from a cached file section + * + * Reads \@size bytes of memory starting at virtual address \@vaddr in the + * section identified by \@isid in \@iscache into \@buffer. + * + * The caller is responsible for allocating a \@buffer of at least \@size bytes. + * + * The read request may be truncated if it crosses section boundaries or if + * \@size is getting too big. We support reading at least 4Kbyte in one chunk + * unless the read would cross a section boundary. + * + * Returns the number of bytes read on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@iscache or \@buffer is NULL. + * Returns -pte_invalid if \@size is zero. + * Returns -pte_nomap if \@vaddr is not contained in section \@isid. + * Returns -pte_bad_image if \@iscache does not contain \@isid. + */ +extern pt_export int pt_iscache_read(struct pt_image_section_cache *iscache, + uint8_t *buffer, uint64_t size, int isid, + uint64_t vaddr); + +/** The traced memory image. */ +struct pt_image; + + +/** Allocate a traced memory image. + * + * An optional \@name may be given to the image. The name string is copied. + * + * Returns a new traced memory image on success, NULL otherwise. + */ +extern pt_export struct pt_image *pt_image_alloc(const char *name); + +/** Free a traced memory image. + * + * The \@image must have been allocated with pt_image_alloc(). + * The \@image must not be used after a successful return. + */ +extern pt_export void pt_image_free(struct pt_image *image); + +/** Get the image name. + * + * Returns a pointer to \@image's name or NULL if there is no name. + */ +extern pt_export const char *pt_image_name(const struct pt_image *image); + +/** Add a new file section to the traced memory image. + * + * Adds \@size bytes starting at \@offset in \@filename. The section is + * loaded at the virtual address \@vaddr in the address space \@asid. + * + * The \@asid may be NULL or (partially) invalid. In that case only the valid + * fields are considered when comparing with other address-spaces. Use this + * when tracing a single process or when adding sections to all processes. + * + * The section is silently truncated to match the size of \@filename. + * + * Existing sections that would overlap with the new section will be shrunk + * or split. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@image or \@filename is NULL. + * Returns -pte_invalid if \@offset is too big. + */ +extern pt_export int pt_image_add_file(struct pt_image *image, + const char *filename, uint64_t offset, + uint64_t size, + const struct pt_asid *asid, + uint64_t vaddr); + +/** Add a section from an image section cache. + * + * Add the section from \@iscache identified by \@isid in address space \@asid. + * + * Existing sections that would overlap with the new section will be shrunk + * or split. + * + * Returns zero on success, a negative error code otherwise. + * Returns -pte_invalid if \@image or \@iscache is NULL. + * Returns -pte_bad_image if \@iscache does not contain \@isid. + */ +extern pt_export int pt_image_add_cached(struct pt_image *image, + struct pt_image_section_cache *iscache, + int isid, const struct pt_asid *asid); + +/** Copy an image. + * + * Adds all sections from \@src to \@image. Sections that could not be added + * will be ignored. + * + * Returns the number of ignored sections on success, a negative error code + * otherwise. + * + * Returns -pte_invalid if \@image or \@src is NULL. + */ +extern pt_export int pt_image_copy(struct pt_image *image, + const struct pt_image *src); + +/** Remove all sections loaded from a file. + * + * Removes all sections loaded from \@filename from the address space \@asid. + * Specify the same \@asid that was used for adding sections from \@filename. + * + * Returns the number of removed sections on success, a negative error code + * otherwise. + * + * Returns -pte_invalid if \@image or \@filename is NULL. + */ +extern pt_export int pt_image_remove_by_filename(struct pt_image *image, + const char *filename, + const struct pt_asid *asid); + +/** Remove all sections loaded into an address space. + * + * Removes all sections loaded into \@asid. Specify the same \@asid that was + * used for adding sections. + * + * Returns the number of removed sections on success, a negative error code + * otherwise. + * + * Returns -pte_invalid if \@image is NULL. + */ +extern pt_export int pt_image_remove_by_asid(struct pt_image *image, + const struct pt_asid *asid); + +/** A read memory callback function. + * + * It shall read \@size bytes of memory from address space \@asid starting + * at \@ip into \@buffer. + * + * It shall return the number of bytes read on success. + * It shall return a negative pt_error_code otherwise. + */ +typedef int (read_memory_callback_t)(uint8_t *buffer, size_t size, + const struct pt_asid *asid, + uint64_t ip, void *context); + +/** Set the memory callback for the traced memory image. + * + * Sets \@callback for reading memory. The callback is used for addresses + * that are not found in file sections. The \@context argument is passed + * to \@callback on each use. + * + * There can only be one callback at any time. A subsequent call will replace + * the previous callback. If \@callback is NULL, the callback is removed. + * + * Returns -pte_invalid if \@image is NULL. + */ +extern pt_export int pt_image_set_callback(struct pt_image *image, + read_memory_callback_t *callback, + void *context); + + + +/* Instruction flow decoder. */ + + + +/** The instruction class. + * + * We provide only a very coarse classification suitable for reconstructing + * the execution flow. + */ +enum pt_insn_class { +#if (LIBIPT_VERSION >= 0x201) + /* The instruction has not been classified. */ + ptic_unknown, + + /* For backwards compatibility. */ + ptic_error = ptic_unknown, +#else + /* The instruction has not been classified. */ + ptic_error, +#endif + + /* The instruction is something not listed below. */ + ptic_other, + + /* The instruction is a near (function) call. */ + ptic_call, + + /* The instruction is a near (function) return. */ + ptic_return, + + /* The instruction is a near unconditional jump. */ + ptic_jump, + + /* The instruction is a near conditional jump. */ + ptic_cond_jump, + + /* The instruction is a call-like far transfer. + * E.g. SYSCALL, SYSENTER, or FAR CALL. + */ + ptic_far_call, + + /* The instruction is a return-like far transfer. + * E.g. SYSRET, SYSEXIT, IRET, or FAR RET. + */ + ptic_far_return, + + /* The instruction is a jump-like far transfer. + * E.g. FAR JMP. + */ + ptic_far_jump, + + /* The instruction is a PTWRITE. */ + ptic_ptwrite, + +#if (LIBIPT_VERSION >= 0x201) + /* The instruction is an indirect jump or a far transfer. */ + ptic_indirect, +#endif +}; + +/** The maximal size of an instruction. */ +enum { + pt_max_insn_size = 15 +}; + +/** A single traced instruction. */ +struct pt_insn { + /** The virtual address in its process. */ + uint64_t ip; + + /** The image section identifier for the section containing this + * instruction. + * + * A value of zero means that the section did not have an identifier. + * The section was not added via an image section cache or the memory + * was read via the read memory callback. + */ + int isid; + + /** The execution mode. */ + enum pt_exec_mode mode; + + /** A coarse classification. */ + enum pt_insn_class iclass; + + /** The raw bytes. */ + uint8_t raw[pt_max_insn_size]; + + /** The size in bytes. */ + uint8_t size; + + /** A collection of flags giving additional information: + * + * - the instruction was executed speculatively. + */ + uint32_t speculative:1; + + /** - this instruction is truncated in its image section. + * + * It starts in the image section identified by \@isid and continues + * in one or more other sections. + */ + uint32_t truncated:1; +}; + + +/** Allocate an Intel PT instruction flow decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_insn_decoder * +pt_insn_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT instruction flow decoder. + * + * This will destroy the decoder's default image. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_insn_free_decoder(struct pt_insn_decoder *decoder); + +/** Synchronize an Intel PT instruction flow decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_insn_sync_forward(struct pt_insn_decoder *decoder); +extern pt_export int pt_insn_sync_backward(struct pt_insn_decoder *decoder); + +/** Manually synchronize an Intel PT instruction flow decoder. + * + * Synchronize \@decoder on the syncpoint at \@offset. There must be a PSB + * packet at \@offset. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@offset lies outside of \@decoder's trace buffer. + * Returns -pte_eos if \@decoder reaches the end of its trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + * Returns -pte_nosync if there is no syncpoint at \@offset. + */ +extern pt_export int pt_insn_sync_set(struct pt_insn_decoder *decoder, + uint64_t offset); + +#if (LIBIPT_VERSION >= 0x202) +/** Re-synchronize an Intel PT instruction flow decoder. + * + * Scan ahead for the next IP providing packet. Apply state changing packets + * along the way, including timing packets, but ignore everything else. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative + * error code otherwise. + * + * Returns pts_ip_suppressed if re-synchronization succeeded with tracing + * disabled. + * + * Returns -pte_event_ignored if an event cannot safely be skipped + * (e.g. ptwrite). Use pt_insn_event() to read the event, then continue with + * pt_insn_resync() until re-synchronization completes or fails with an error. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_insn_resync(struct pt_insn_decoder *decoder); +#endif + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_insn_get_offset(const struct pt_insn_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_insn_get_sync_offset(const struct pt_insn_decoder *decoder, + uint64_t *offset); + +/** Get the traced image. + * + * The returned image may be modified as long as no decoder that uses this + * image is running. + * + * Returns a pointer to the traced image the decoder uses for reading memory. + * Returns NULL if \@decoder is NULL. + */ +extern pt_export struct pt_image * +pt_insn_get_image(struct pt_insn_decoder *decoder); + +/** Set the traced image. + * + * Sets the image that \@decoder uses for reading memory to \@image. If \@image + * is NULL, sets the image to \@decoder's default image. + * + * Only one image can be active at any time. + * + * Returns zero on success, a negative error code otherwise. + * Return -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_insn_set_image(struct pt_insn_decoder *decoder, + struct pt_image *image); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_insn_get_config(const struct pt_insn_decoder *decoder); + +/** Return the current time. + * + * On success, provides the time at the last preceding timing packet in \@time. + * + * The time is similar to what a rdtsc instruction would return. Depending + * on the configuration, the time may not be fully accurate. If TSC is not + * enabled, the time is relative to the last synchronization and can't be used + * to correlate with other TSC-based time sources. In this case, -pte_no_time + * is returned and the relative time is provided in \@time. + * + * Some timing-related packets may need to be dropped (mostly due to missing + * calibration or incomplete configuration). To get an idea about the quality + * of the estimated time, we record the number of dropped MTC and CYC packets. + * + * If \@lost_mtc is not NULL, set it to the number of lost MTC packets. + * If \@lost_cyc is not NULL, set it to the number of lost CYC packets. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@time is NULL. + * Returns -pte_no_time if there has not been a TSC packet. + */ +extern pt_export int pt_insn_time(struct pt_insn_decoder *decoder, + uint64_t *time, uint32_t *lost_mtc, + uint32_t *lost_cyc); + +/** Return the current core bus ratio. + * + * On success, provides the current core:bus ratio in \@cbr. The ratio is + * defined as core cycles per bus clock cycle. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@cbr is NULL. + * Returns -pte_no_cbr if there has not been a CBR packet. + */ +extern pt_export int pt_insn_core_bus_ratio(struct pt_insn_decoder *decoder, + uint32_t *cbr); + +/** Return the current address space identifier. + * + * On success, provides the current address space identifier in \@asid. + * + * The \@size argument must be set to sizeof(struct pt_asid). At most \@size + * bytes will be copied and \@asid->size will be set to the actual size of the + * provided address space identifier. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@asid is NULL. + */ +extern pt_export int pt_insn_asid(const struct pt_insn_decoder *decoder, + struct pt_asid *asid, size_t size); + +/** Determine the next instruction. + * + * On success, provides the next instruction in execution order in \@insn. + * + * The \@size argument must be set to sizeof(struct pt_insn). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns pts_eos to indicate the end of the trace stream. Subsequent calls + * to pt_insn_next() will continue to return pts_eos until trace is required + * to determine the next instruction. + * + * Returns -pte_event_ignored to indicate that the user needs to drain events + * using pt_insn_event() before continuing. + * + * Returns -pte_bad_context if the decoder encountered an unexpected packet. + * Returns -pte_bad_opc if the decoder encountered unknown packets. + * Returns -pte_bad_packet if the decoder encountered unknown packet payloads. + * Returns -pte_bad_query if the decoder got out of sync. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@insn is NULL. + * Returns -pte_nomap if the memory at the instruction address can't be read. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_insn_next(struct pt_insn_decoder *decoder, + struct pt_insn *insn, size_t size); + +/** Get the next pending event. + * + * On success, provides the next event in \@event and updates \@decoder. + * + * The \@size argument must be set to sizeof(struct pt_event). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_query if there is no event. + * Returns -pte_invalid if \@decoder or \@event is NULL. + * Returns -pte_invalid if \@size is too small. + */ +extern pt_export int pt_insn_event(struct pt_insn_decoder *decoder, + struct pt_event *event, size_t size); + + + +/* Block decoder. */ + + + +/** A block of instructions. + * + * Instructions in this block are executed sequentially but are not necessarily + * contiguous in memory. Users are expected to follow direct branches. + */ +struct pt_block { + /** The IP of the first instruction in this block. */ + uint64_t ip; + + /** The IP of the last instruction in this block. + * + * This can be used for error-detection. + */ + uint64_t end_ip; + + /** The image section that contains the instructions in this block. + * + * A value of zero means that the section did not have an identifier. + * The section was not added via an image section cache or the memory + * was read via the read memory callback. + */ + int isid; + + /** The execution mode for all instructions in this block. */ + enum pt_exec_mode mode; + + /** The instruction class for the last instruction in this block. + * + * This field may be set to ptic_unknown (ptic_error prior to v2.1) to + * indicate that the instruction class is not available. The block + * decoder may choose to not provide the instruction class in some + * cases for performance reasons. + */ + enum pt_insn_class iclass; + + /** The number of instructions in this block. */ + uint16_t ninsn; + + /** The raw bytes of the last instruction in this block in case the + * instruction does not fit entirely into this block's section. + * + * This field is only valid if \@truncated is set. + */ + uint8_t raw[pt_max_insn_size]; + + /** The size of the last instruction in this block in bytes. + * + * This field is only valid if \@truncated is set. + */ + uint8_t size; + + /** A collection of flags giving additional information about the + * instructions in this block. + * + * - all instructions in this block were executed speculatively. + */ + uint32_t speculative:1; + + /** - the last instruction in this block is truncated. + * + * It starts in this block's section but continues in one or more + * other sections depending on how fragmented the memory image is. + * + * The raw bytes for the last instruction are provided in \@raw and + * its size in \@size in this case. + */ + uint32_t truncated:1; +}; + +/** Allocate an Intel PT block decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_block_decoder * +pt_blk_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT block decoder. + * + * This will destroy the decoder's default image. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_blk_free_decoder(struct pt_block_decoder *decoder); + +/** Synchronize an Intel PT block decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_blk_sync_forward(struct pt_block_decoder *decoder); +extern pt_export int pt_blk_sync_backward(struct pt_block_decoder *decoder); + +/** Manually synchronize an Intel PT block decoder. + * + * Synchronize \@decoder on the syncpoint at \@offset. There must be a PSB + * packet at \@offset. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@offset lies outside of \@decoder's trace buffer. + * Returns -pte_eos if \@decoder reaches the end of its trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + * Returns -pte_nosync if there is no syncpoint at \@offset. + */ +extern pt_export int pt_blk_sync_set(struct pt_block_decoder *decoder, + uint64_t offset); + +#if (LIBIPT_VERSION >= 0x202) +/** Re-synchronize an Intel PT block decoder. + * + * Scan ahead for the next IP providing packet. Apply state changing packets + * along the way, including timing packets, but ignore everything else. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative + * error code otherwise. + * + * Returns pts_ip_suppressed if re-synchronization succeeded with tracing + * disabled. + * + * Returns -pte_event_ignored if an event cannot safely be skipped + * (e.g. ptwrite). Use pt_blk_event() to read the event, then continue with + * pt_blk_resync() until re-synchronization completes or fails with an error. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_blk_resync(struct pt_block_decoder *decoder); +#endif + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_blk_get_offset(const struct pt_block_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_blk_get_sync_offset(const struct pt_block_decoder *decoder, + uint64_t *offset); + +/** Get the traced image. + * + * The returned image may be modified as long as \@decoder is not running. + * + * Returns a pointer to the traced image \@decoder uses for reading memory. + * Returns NULL if \@decoder is NULL. + */ +extern pt_export struct pt_image * +pt_blk_get_image(struct pt_block_decoder *decoder); + +/** Set the traced image. + * + * Sets the image that \@decoder uses for reading memory to \@image. If \@image + * is NULL, sets the image to \@decoder's default image. + * + * Only one image can be active at any time. + * + * Returns zero on success, a negative error code otherwise. + * Return -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_blk_set_image(struct pt_block_decoder *decoder, + struct pt_image *image); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_blk_get_config(const struct pt_block_decoder *decoder); + +/** Return the current time. + * + * On success, provides the time at the last preceding timing packet in \@time. + * + * The time is similar to what a rdtsc instruction would return. Depending + * on the configuration, the time may not be fully accurate. If TSC is not + * enabled, the time is relative to the last synchronization and can't be used + * to correlate with other TSC-based time sources. In this case, -pte_no_time + * is returned and the relative time is provided in \@time. + * + * Some timing-related packets may need to be dropped (mostly due to missing + * calibration or incomplete configuration). To get an idea about the quality + * of the estimated time, we record the number of dropped MTC and CYC packets. + * + * If \@lost_mtc is not NULL, set it to the number of lost MTC packets. + * If \@lost_cyc is not NULL, set it to the number of lost CYC packets. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@time is NULL. + * Returns -pte_no_time if there has not been a TSC packet. + */ +extern pt_export int pt_blk_time(struct pt_block_decoder *decoder, + uint64_t *time, uint32_t *lost_mtc, + uint32_t *lost_cyc); + +/** Return the current core bus ratio. + * + * On success, provides the current core:bus ratio in \@cbr. The ratio is + * defined as core cycles per bus clock cycle. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@cbr is NULL. + * Returns -pte_no_cbr if there has not been a CBR packet. + */ +extern pt_export int pt_blk_core_bus_ratio(struct pt_block_decoder *decoder, + uint32_t *cbr); + +/** Return the current address space identifier. + * + * On success, provides the current address space identifier in \@asid. + * + * The \@size argument must be set to sizeof(struct pt_asid). At most \@size + * bytes will be copied and \@asid->size will be set to the actual size of the + * provided address space identifier. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@asid is NULL. + */ +extern pt_export int pt_blk_asid(const struct pt_block_decoder *decoder, + struct pt_asid *asid, size_t size); + +/** Determine the next block of instructions. + * + * On success, provides the next block of instructions in execution order in + * \@block. + * + * The \@size argument must be set to sizeof(struct pt_block). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns pts_eos to indicate the end of the trace stream. Subsequent calls + * to pt_block_next() will continue to return pts_eos until trace is required + * to determine the next instruction. + * + * Returns -pte_bad_context if the decoder encountered an unexpected packet. + * Returns -pte_bad_opc if the decoder encountered unknown packets. + * Returns -pte_bad_packet if the decoder encountered unknown packet payloads. + * Returns -pte_bad_query if the decoder got out of sync. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@block is NULL. + * Returns -pte_nomap if the memory at the instruction address can't be read. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_blk_next(struct pt_block_decoder *decoder, + struct pt_block *block, size_t size); + +/** Get the next pending event. + * + * On success, provides the next event in \@event and updates \@decoder. + * + * The \@size argument must be set to sizeof(struct pt_event). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_query if there is no event. + * Returns -pte_invalid if \@decoder or \@event is NULL. + * Returns -pte_invalid if \@size is too small. + */ +extern pt_export int pt_blk_event(struct pt_block_decoder *decoder, + struct pt_event *event, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* INTEL_PT_H */ diff --git a/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj b/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj index b40055ce..29d0f676 100644 --- a/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj +++ b/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj @@ -82,15 +82,16 @@ if exist "$(OutDir)SDK\" rd /q /s "$(OutDir)SDK\" xcopy /E /I /Y "$(SolutionDir)include\SDK" "$(OutDir)SDK" -xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\Examples" -mkdir "$(OutDir)SDK\Libraries" -copy "$(OutDir)pdbex.lib" "$(OutDir)SDK\Libraries\pdbex.lib" -copy "$(OutDir)kdserial.lib" "$(OutDir)SDK\Libraries\kdserial.lib" -copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\Libraries\libhyperdbg.dll" -copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\Libraries\script-engine.dll" -copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\Libraries\symbol-parser.dll" -copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\Libraries\hyperlog.dll" -copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\Libraries\hyperhv.dll" +xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\examples" +mkdir "$(OutDir)SDK\libraries" +copy "$(OutDir)pdbex.lib" "$(OutDir)SDK\libraries\pdbex.lib" +copy "$(OutDir)kdserial.lib" "$(OutDir)SDK\libraries\kdserial.lib" +copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\libraries\libhyperdbg.dll" +copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\libraries\script-engine.dll" +copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\libraries\symbol-parser.dll" +copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\libraries\hyperlog.dll" +copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\libraries\hyperhv.dll" +copy "$(OutDir)libipt.dll" "$(OutDir)SDK\libraries\libipt.dll" if exist "$(OutDir)constants\" rd /q /s "$(OutDir)constants\" mkdir "$(OutDir)constants" copy "$(SolutionDir)miscellaneous\constants\pciid\pci.ids" "$(OutDir)constants\pci.ids" @@ -126,15 +127,16 @@ copy "$(SolutionDir)miscellaneous\constants\pciid\pci.ids" "$(OutDir)constants\p if exist "$(OutDir)SDK\" rd /q /s "$(OutDir)SDK\" xcopy /E /I /Y "$(SolutionDir)include\SDK" "$(OutDir)SDK" -xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\Examples" -mkdir "$(OutDir)SDK\Libraries" -copy "$(OutDir)pdbex.lib" "$(OutDir)SDK\Libraries\pdbex.lib" -copy "$(OutDir)kdserial.lib" "$(OutDir)SDK\Libraries\kdserial.lib" -copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\Libraries\libhyperdbg.dll" -copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\Libraries\script-engine.dll" -copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\Libraries\symbol-parser.dll" -copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\Libraries\hyperlog.dll" -copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\Libraries\hyperhv.dll" +xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\examples" +mkdir "$(OutDir)SDK\libraries" +copy "$(OutDir)pdbex.lib" "$(OutDir)SDK\libraries\pdbex.lib" +copy "$(OutDir)kdserial.lib" "$(OutDir)SDK\libraries\kdserial.lib" +copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\libraries\libhyperdbg.dll" +copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\libraries\script-engine.dll" +copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\libraries\symbol-parser.dll" +copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\libraries\hyperlog.dll" +copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\libraries\hyperhv.dll" +copy "$(OutDir)libipt.dll" "$(OutDir)SDK\libraries\libipt.dll" if exist "$(OutDir)constants\" rd /q /s "$(OutDir)constants\" mkdir "$(OutDir)constants" copy "$(SolutionDir)miscellaneous\constants\pciid\pci.ids" "$(OutDir)constants\pci.ids" diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index 5f1ceeee..2cc81985 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -87,7 +87,9 @@ - msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="Release MT" /p:Platform=$(Platform) /target:zydis /target:zycore /p:OutDir="$(SolutionDir)libraries\zydis\user" + copy "$(SolutionDir)libraries\libipt\libipt.lib" "$(OutDir)" +copy "$(SolutionDir)libraries\libipt\libipt.dll" "$(OutDir)" +msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="Release MT" /p:Platform=$(Platform) /target:zydis /target:zycore /p:OutDir="$(SolutionDir)libraries\zydis\user" @@ -123,7 +125,9 @@ - msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="Release MT" /p:Platform=$(Platform) /target:zydis /target:zycore /p:OutDir="$(SolutionDir)libraries\zydis\user" + copy "$(SolutionDir)libraries\libipt\libipt.lib" "$(OutDir)" +copy "$(SolutionDir)libraries\libipt\libipt.dll" "$(OutDir)" +msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="Release MT" /p:Platform=$(Platform) /target:zydis /target:zycore /p:OutDir="$(SolutionDir)libraries\zydis\user" diff --git a/hyperdbg/libraries/libipt/libipt.dll b/hyperdbg/libraries/libipt/libipt.dll new file mode 100644 index 0000000000000000000000000000000000000000..7c873442a0a36804f0f8233ad43033829be889ba GIT binary patch literal 111616 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 literal 0 HcmV?d00001 From b11da036d10fa1c9054e823f5b10c6434cffe128 Mon Sep 17 00:00:00 2001 From: sina Date: Sat, 13 Jun 2026 15:22:34 +0200 Subject: [PATCH 19/57] fix name of example files --- .../user/hyperdbg_app/code/hyperdbg-app.cpp | 530 +----------------- .../user/hyperdbg_app/code/hyperdbg-ipt.cpp | 530 +++++++++++++++++- .../user/hyperdbg_app/hyperdbg_app.vcxproj | 2 +- .../hyperdbg_app/hyperdbg_app.vcxproj.filters | 4 +- 4 files changed, 533 insertions(+), 533 deletions(-) diff --git a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp index cdb219e9..c82097b8 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp @@ -1,18 +1,5 @@ #include "pch.h" -#include -#include -#include -#include "../dependencies/libipt/intel-pt.h" -#include - -#pragma comment(lib, "dbghelp.lib") - -static UINT64 g_ImageBase = 0; -static UINT64 g_CodeBase = 0; -static UINT64 g_CodeSize = 0; -static UINT8 * g_Code = NULL; - static int ShowMessages(const char * Text) { @@ -21,482 +8,7 @@ ShowMessages(const char * Text) } static int -ReadImage(uint8_t * Buffer, size_t Size, const struct pt_asid * Asid, uint64_t Ip, void * Context) -{ - (void)Asid; - (void)Context; - - if (g_Code == NULL || Ip < g_CodeBase || Ip >= g_CodeBase + g_CodeSize) - return -pte_nomap; - - uint64_t Available = g_CodeBase + g_CodeSize - Ip; - size_t Count = (Size < Available) ? Size : (size_t)Available; - - memcpy(Buffer, g_Code + (Ip - g_CodeBase), Count); - return (int)Count; -} - -typedef struct _PROC_BASIC_INFO -{ - LONG ExitStatus; - PVOID PebBaseAddress; - ULONG_PTR Reserved[4]; -} PROC_BASIC_INFO; - -typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); - -static BOOLEAN -CaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) -{ - HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); - PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; - PROC_BASIC_INFO Pbi = {0}; - ULONG Ret = 0; - SIZE_T Got = 0; - UINT64 Base = 0; - IMAGE_DOS_HEADER Dos; - IMAGE_NT_HEADERS64 Nt; - UINT64 SectionBase; - - if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) - return FALSE; - - if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) - return FALSE; - - if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) - return FALSE; - - if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) - return FALSE; - - g_ImageBase = Base; - SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; - - for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) - { - IMAGE_SECTION_HEADER Section; - - if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) - return FALSE; - - if (memcmp(Section.Name, ".text", 6) != 0) - continue; - - UINT64 Start = Base + Section.VirtualAddress; - UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; - - if (Size == 0) - return FALSE; - - g_Code = (UINT8 *)malloc((size_t)Size); - if (g_Code == NULL) - return FALSE; - - if (!ReadProcessMemory(Process, (PVOID)Start, g_Code, (SIZE_T)Size, &Got) || Got != Size) - { - free(g_Code); - g_Code = NULL; - return FALSE; - } - - g_CodeBase = Start; - g_CodeSize = Size; - *TextStart = Start; - *TextEnd = Start + Size - 1; - return TRUE; - } - - return FALSE; -} - -static BOOLEAN -ResolveFunction(HANDLE Process, const char * Path, const char * Name, UINT64 * Start, UINT64 * End) -{ - union - { - SYMBOL_INFO Info; - BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; - } Symbol = {0}; - BOOLEAN Ok = FALSE; - - SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); - if (!SymInitialize(Process, NULL, FALSE)) - return FALSE; - - if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)g_ImageBase, 0, NULL, 0) != 0) - { - Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); - Symbol.Info.MaxNameLen = MAX_SYM_NAME; - - if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) - { - *Start = Symbol.Info.Address; - *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; - Ok = TRUE; - } - } - - SymCleanup(Process); - return Ok; -} - -static BOOLEAN -PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE Type) -{ - HYPERTRACE_PT_OPERATION_PACKETS Op = {}; - Op.PtOperationType = Type; - return hyperdbg_u_pt_operation(&Op); -} - -static BOOLEAN -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; - - if (End > Start) - { - Op.NumAddrRanges = 1; - Op.AddrRanges[0].Start = Start; - Op.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.BufferSize); - return TRUE; -} - -static const char * -PacketName(enum pt_packet_type Type) -{ - switch (Type) - { - case ppt_psb: return "PSB"; case ppt_psbend: return "PSBEND"; case ppt_pad: return "PAD"; - case ppt_fup: return "FUP"; case ppt_tip: return "TIP"; case ppt_tip_pge: return "TIP.PGE"; - case ppt_tip_pgd: return "TIP.PGD"; case ppt_tnt_8: return "TNT8"; case ppt_tnt_64: return "TNT64"; - case ppt_mode: return "MODE"; case ppt_pip: return "PIP"; case ppt_vmcs: return "VMCS"; - case ppt_cbr: return "CBR"; case ppt_tsc: return "TSC"; case ppt_tma: return "TMA"; - case ppt_mtc: return "MTC"; case ppt_cyc: return "CYC"; case ppt_ovf: return "OVF"; - case ppt_stop: return "STOP"; case ppt_exstop: return "EXSTOP"; case ppt_mnt: return "MNT"; - case ppt_ptw: return "PTW"; default: return "?"; - } -} - -static uint64_t -ReconstructIp(const struct pt_packet_ip * Packet, uint64_t * LastIp) -{ - uint64_t Value = *LastIp; - - switch (Packet->ipc) - { - case pt_ipc_update_16: Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); break; - case pt_ipc_update_32: Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); break; - case pt_ipc_update_48: Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); break; - case pt_ipc_sext_48: - Value = Packet->ip & 0xffffffffffffull; - if (Value & 0x800000000000ull) - Value |= 0xffff000000000000ull; - break; - default: Value = Packet->ip; break; - } - - *LastIp = Value; - return Value; -} - -static UINT64 -DecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) -{ - struct pt_config Config; - struct pt_packet_decoder * Decoder; - UINT64 Count = 0; - uint64_t LastIp = 0; - int Status; - - pt_config_init(&Config); - Config.begin = (uint8_t *)Buffer; - Config.end = (uint8_t *)Buffer + Size; - - Decoder = pt_pkt_alloc_decoder(&Config); - if (Decoder == NULL) - { - printf("[-] core %u: cannot allocate packet decoder\n", Cpu); - return 0; - } - - for (;;) - { - Status = pt_pkt_sync_forward(Decoder); - if (Status < 0) - break; - - for (;;) - { - struct pt_packet Packet; - - Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); - if (Status < 0) - break; - - Count++; - - switch (Packet.type) - { - case ppt_tnt_8: - case ppt_tnt_64: - printf(" %-8s %2u ", PacketName(Packet.type), Packet.payload.tnt.bit_size); - for (uint8_t Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) - putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); - putchar('\n'); - break; - - case ppt_tip: - case ppt_fup: - case ppt_tip_pge: - case ppt_tip_pgd: - if (Packet.payload.ip.ipc == pt_ipc_suppressed) - printf(" %-8s (ip suppressed)\n", PacketName(Packet.type)); - else - { - uint64_t Ip = ReconstructIp(&Packet.payload.ip, &LastIp); - printf(" %-8s 0x%016llx exe+0x%llx\n", - PacketName(Packet.type), (unsigned long long)Ip, (unsigned long long)(Ip - g_ImageBase)); - } - break; - - case ppt_pip: - printf(" %-8s cr3=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.pip.cr3); - break; - - case ppt_cbr: - //printf(" %-8s ratio=%u\n", PacketName(Packet.type), Packet.payload.cbr.ratio); - break; - - case ppt_tsc: - printf(" %-8s tsc=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.tsc.tsc); - break; - - default: - //printf(" %-8s\n", PacketName(Packet.type)); - break; - } - } - } - - pt_pkt_free_decoder(Decoder); - return Count; -} - -static UINT64 -DecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) -{ - struct pt_config Config; - struct pt_insn_decoder * Decoder; - struct pt_image * Image; - UINT64 Count = 0; - int Status; - - pt_config_init(&Config); - Config.begin = (uint8_t *)Buffer; - Config.end = (uint8_t *)Buffer + Size; - - Decoder = pt_insn_alloc_decoder(&Config); - if (Decoder == NULL) - { - printf("[-] core %u: cannot allocate instruction decoder\n", Cpu); - return 0; - } - - Image = pt_insn_get_image(Decoder); - pt_image_set_callback(Image, ReadImage, NULL); - - for (;;) - { - Status = pt_insn_sync_forward(Decoder); - if (Status < 0) - break; - - for (;;) - { - struct pt_insn Insn; - - while (Status & pts_event_pending) - { - struct pt_event Event; - Status = pt_insn_event(Decoder, &Event, sizeof(Event)); - if (Status < 0) - break; - } - - if (Status < 0 || (Status & pts_eos)) - break; - - Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); - if (Status < 0) - break; - - ZydisDisassembledInstruction Disasm; - ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; - - if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) - printf(" 0x%016llx exe+0x%-6llx %s\n", - (unsigned long long)Insn.ip, - (unsigned long long)(Insn.ip - g_ImageBase), - Disasm.text); - else - printf(" 0x%016llx (undecodable)\n", (unsigned long long)Insn.ip); - - Count++; - } - - if (Status >= 0 && (Status & pts_eos)) - break; - } - - pt_insn_free_decoder(Decoder); - return Count; -} - -static void -RunAndTrace(const char * Path, const char * Function, BOOLEAN Packets, int PinCore) -{ - STARTUPINFOA Startup = {0}; - PROCESS_INFORMATION Process = {0}; - HYPERTRACE_PT_MMAP_PACKETS Mmap = {0}; - HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; - UINT64 TextStart = 0; - UINT64 TextEnd = 0; - UINT64 FilterStart = 0; - UINT64 FilterEnd = 0; - UINT64 Total = 0; - - Startup.cb = sizeof(Startup); - - if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) - { - printf("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); - return; - } - - printf("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); - - if (PinCore >= 0) - { - DWORD_PTR Mask = (DWORD_PTR)1 << PinCore; - if (SetProcessAffinityMask(Process.hProcess, Mask)) - printf("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); - else - printf("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); - } - else - { - printf("[*] target unpinned (scheduler may migrate it across cores)\n"); - } - - if (!CaptureImage(Process.hProcess, &TextStart, &TextEnd)) - { - printf("[-] cannot read target image / .text section\n"); - TerminateProcess(Process.hProcess, 1); - goto Cleanup; - } - - printf("[+] image base 0x%llx, .text 0x%llx-0x%llx (%llu bytes)\n", - (unsigned long long)g_ImageBase, - (unsigned long long)TextStart, - (unsigned long long)TextEnd, - (unsigned long long)g_CodeSize); - - FilterStart = TextStart; - FilterEnd = TextEnd; - - if (Function != NULL && ResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) - printf("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", - Function, - (unsigned long long)FilterStart, - (unsigned long long)FilterEnd, - (unsigned long long)(FilterEnd - FilterStart + 1)); - else - printf("[!] IP filter: whole .text (symbol '%s' not found - build the target with a PDB)\n", - Function ? Function : "(none)"); - - if (!PtFilter(Process.dwProcessId, FilterStart, FilterEnd) || - !PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE)) - { - printf("[-] cannot enable Intel PT\n"); - TerminateProcess(Process.hProcess, 1); - goto Cleanup; - } - - if (!hyperdbg_u_pt_mmap(&Mmap)) - { - printf("[-] pt_mmap failed\n"); - PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); - TerminateProcess(Process.hProcess, 1); - goto Cleanup; - } - - printf("[+] PT enabled, %u per-core buffers mapped\n", Mmap.NumCpus); - printf("[*] resuming target and waiting for it to exit...\n"); - - ResumeThread(Process.hThread); - WaitForSingleObject(Process.hProcess, INFINITE); - printf("[+] target exited, decoding trace\n"); - - PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE); - - Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; - if (!hyperdbg_u_pt_operation(&Sizes)) - { - printf("[-] cannot query PT sizes\n"); - PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); - goto Cleanup; - } - - for (UINT32 i = 0; i < Mmap.NumCpus; i++) - { - UINT32 Cpu = Mmap.Cpus[i].CpuId; - UINT64 Bytes = (Cpu < Sizes.NumCpus) ? Sizes.BytesPerCpu[Cpu] : 0; - - if (Bytes == 0) - continue; - - if (Bytes > Mmap.Cpus[i].Size) - Bytes = Mmap.Cpus[i].Size; - - printf("\n[*] core %u: %llu bytes of trace\n", Cpu, (unsigned long long)Bytes); - Total += Packets - ? DecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes) - : DecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); - } - - printf("\n[+] decoded %llu %s total\n", (unsigned long long)Total, Packets ? "packet(s)" : "instruction(s)"); - - PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); - -Cleanup: - if (g_Code != NULL) - { - free(g_Code); - g_Code = NULL; - } - if (Process.hThread != NULL) - CloseHandle(Process.hThread); - if (Process.hProcess != NULL) - CloseHandle(Process.hProcess); -} - -static int -LoadVmmAndTrace() +LoadVmm() { hyperdbg_u_set_text_message_callback((PVOID)ShowMessages); @@ -515,52 +27,18 @@ LoadVmmAndTrace() printf("[+] HyperDbg VMM is running\n"); - printf("[*] loading HyperTrace...\n"); - - if ( hyperdbg_u_load_hypertrace_module() == 1) - { - printf("[-] cannot load the HyperDbg HyperTrace\n"); - return 1; - } - - printf("[+] HyperDbg HyperTrace is running\n"); - return 0; } int -main2(int argc, char ** argv) +main(int argc, char ** argv) { - const char * function = "main"; - BOOLEAN packets = FALSE; - int pinCore = 0; - - if (argc < 2) + if (LoadVmm() != 0) { - printf("HyperDbg Intel PT tracer\n"); - printf("usage: %s [function] [-p] [-c core]\n", argv[0]); - printf(" [function] symbol to IP-filter (default 'main'; pass '*' for whole .text)\n"); - printf(" -p dump raw PT packets (TNT/TIP/FUP/PSB/...) instead of instructions\n"); - printf(" -c core pin the target to this logical core (default 0; -1 = unpinned)\n"); return 1; } - for (int i = 2; i < argc; i++) - { - if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--packets") == 0) - packets = TRUE; - else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) - pinCore = atoi(argv[++i]); - else if (strcmp(argv[i], "*") == 0) - function = NULL; - else - function = argv[i]; - } - - if (LoadVmmAndTrace() != 0) - return 1; - - RunAndTrace(argv[1], function, packets, pinCore); + hyperdbg_u_run_command((CHAR*)"lm"); printf("[*] unloading HyperDbg VMM...\n"); diff --git a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp index c82097b8..cdb219e9 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp @@ -1,5 +1,18 @@ #include "pch.h" +#include +#include +#include +#include "../dependencies/libipt/intel-pt.h" +#include + +#pragma comment(lib, "dbghelp.lib") + +static UINT64 g_ImageBase = 0; +static UINT64 g_CodeBase = 0; +static UINT64 g_CodeSize = 0; +static UINT8 * g_Code = NULL; + static int ShowMessages(const char * Text) { @@ -8,7 +21,482 @@ ShowMessages(const char * Text) } static int -LoadVmm() +ReadImage(uint8_t * Buffer, size_t Size, const struct pt_asid * Asid, uint64_t Ip, void * Context) +{ + (void)Asid; + (void)Context; + + if (g_Code == NULL || Ip < g_CodeBase || Ip >= g_CodeBase + g_CodeSize) + return -pte_nomap; + + uint64_t Available = g_CodeBase + g_CodeSize - Ip; + size_t Count = (Size < Available) ? Size : (size_t)Available; + + memcpy(Buffer, g_Code + (Ip - g_CodeBase), Count); + return (int)Count; +} + +typedef struct _PROC_BASIC_INFO +{ + LONG ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR Reserved[4]; +} PROC_BASIC_INFO; + +typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); + +static BOOLEAN +CaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) +{ + HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); + PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; + PROC_BASIC_INFO Pbi = {0}; + ULONG Ret = 0; + SIZE_T Got = 0; + UINT64 Base = 0; + IMAGE_DOS_HEADER Dos; + IMAGE_NT_HEADERS64 Nt; + UINT64 SectionBase; + + if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) + return FALSE; + + g_ImageBase = Base; + SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; + + for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) + { + IMAGE_SECTION_HEADER Section; + + if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) + return FALSE; + + if (memcmp(Section.Name, ".text", 6) != 0) + continue; + + UINT64 Start = Base + Section.VirtualAddress; + UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; + + if (Size == 0) + return FALSE; + + g_Code = (UINT8 *)malloc((size_t)Size); + if (g_Code == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Start, g_Code, (SIZE_T)Size, &Got) || Got != Size) + { + free(g_Code); + g_Code = NULL; + return FALSE; + } + + g_CodeBase = Start; + g_CodeSize = Size; + *TextStart = Start; + *TextEnd = Start + Size - 1; + return TRUE; + } + + return FALSE; +} + +static BOOLEAN +ResolveFunction(HANDLE Process, const char * Path, const char * Name, UINT64 * Start, UINT64 * End) +{ + union + { + SYMBOL_INFO Info; + BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + } Symbol = {0}; + BOOLEAN Ok = FALSE; + + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + if (!SymInitialize(Process, NULL, FALSE)) + return FALSE; + + if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)g_ImageBase, 0, NULL, 0) != 0) + { + Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); + Symbol.Info.MaxNameLen = MAX_SYM_NAME; + + if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) + { + *Start = Symbol.Info.Address; + *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; + Ok = TRUE; + } + } + + SymCleanup(Process); + return Ok; +} + +static BOOLEAN +PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE Type) +{ + HYPERTRACE_PT_OPERATION_PACKETS Op = {}; + Op.PtOperationType = Type; + return hyperdbg_u_pt_operation(&Op); +} + +static BOOLEAN +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; + + if (End > Start) + { + Op.NumAddrRanges = 1; + Op.AddrRanges[0].Start = Start; + Op.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.BufferSize); + return TRUE; +} + +static const char * +PacketName(enum pt_packet_type Type) +{ + switch (Type) + { + case ppt_psb: return "PSB"; case ppt_psbend: return "PSBEND"; case ppt_pad: return "PAD"; + case ppt_fup: return "FUP"; case ppt_tip: return "TIP"; case ppt_tip_pge: return "TIP.PGE"; + case ppt_tip_pgd: return "TIP.PGD"; case ppt_tnt_8: return "TNT8"; case ppt_tnt_64: return "TNT64"; + case ppt_mode: return "MODE"; case ppt_pip: return "PIP"; case ppt_vmcs: return "VMCS"; + case ppt_cbr: return "CBR"; case ppt_tsc: return "TSC"; case ppt_tma: return "TMA"; + case ppt_mtc: return "MTC"; case ppt_cyc: return "CYC"; case ppt_ovf: return "OVF"; + case ppt_stop: return "STOP"; case ppt_exstop: return "EXSTOP"; case ppt_mnt: return "MNT"; + case ppt_ptw: return "PTW"; default: return "?"; + } +} + +static uint64_t +ReconstructIp(const struct pt_packet_ip * Packet, uint64_t * LastIp) +{ + uint64_t Value = *LastIp; + + switch (Packet->ipc) + { + case pt_ipc_update_16: Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); break; + case pt_ipc_update_32: Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); break; + case pt_ipc_update_48: Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); break; + case pt_ipc_sext_48: + Value = Packet->ip & 0xffffffffffffull; + if (Value & 0x800000000000ull) + Value |= 0xffff000000000000ull; + break; + default: Value = Packet->ip; break; + } + + *LastIp = Value; + return Value; +} + +static UINT64 +DecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +{ + struct pt_config Config; + struct pt_packet_decoder * Decoder; + UINT64 Count = 0; + uint64_t LastIp = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (uint8_t *)Buffer; + Config.end = (uint8_t *)Buffer + Size; + + Decoder = pt_pkt_alloc_decoder(&Config); + if (Decoder == NULL) + { + printf("[-] core %u: cannot allocate packet decoder\n", Cpu); + return 0; + } + + for (;;) + { + Status = pt_pkt_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_packet Packet; + + Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); + if (Status < 0) + break; + + Count++; + + switch (Packet.type) + { + case ppt_tnt_8: + case ppt_tnt_64: + printf(" %-8s %2u ", PacketName(Packet.type), Packet.payload.tnt.bit_size); + for (uint8_t Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) + putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); + putchar('\n'); + break; + + case ppt_tip: + case ppt_fup: + case ppt_tip_pge: + case ppt_tip_pgd: + if (Packet.payload.ip.ipc == pt_ipc_suppressed) + printf(" %-8s (ip suppressed)\n", PacketName(Packet.type)); + else + { + uint64_t Ip = ReconstructIp(&Packet.payload.ip, &LastIp); + printf(" %-8s 0x%016llx exe+0x%llx\n", + PacketName(Packet.type), (unsigned long long)Ip, (unsigned long long)(Ip - g_ImageBase)); + } + break; + + case ppt_pip: + printf(" %-8s cr3=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.pip.cr3); + break; + + case ppt_cbr: + //printf(" %-8s ratio=%u\n", PacketName(Packet.type), Packet.payload.cbr.ratio); + break; + + case ppt_tsc: + printf(" %-8s tsc=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.tsc.tsc); + break; + + default: + //printf(" %-8s\n", PacketName(Packet.type)); + break; + } + } + } + + pt_pkt_free_decoder(Decoder); + return Count; +} + +static UINT64 +DecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +{ + struct pt_config Config; + struct pt_insn_decoder * Decoder; + struct pt_image * Image; + UINT64 Count = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (uint8_t *)Buffer; + Config.end = (uint8_t *)Buffer + Size; + + Decoder = pt_insn_alloc_decoder(&Config); + if (Decoder == NULL) + { + printf("[-] core %u: cannot allocate instruction decoder\n", Cpu); + return 0; + } + + Image = pt_insn_get_image(Decoder); + pt_image_set_callback(Image, ReadImage, NULL); + + for (;;) + { + Status = pt_insn_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_insn Insn; + + while (Status & pts_event_pending) + { + struct pt_event Event; + Status = pt_insn_event(Decoder, &Event, sizeof(Event)); + if (Status < 0) + break; + } + + if (Status < 0 || (Status & pts_eos)) + break; + + Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); + if (Status < 0) + break; + + ZydisDisassembledInstruction Disasm; + ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; + + if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) + printf(" 0x%016llx exe+0x%-6llx %s\n", + (unsigned long long)Insn.ip, + (unsigned long long)(Insn.ip - g_ImageBase), + Disasm.text); + else + printf(" 0x%016llx (undecodable)\n", (unsigned long long)Insn.ip); + + Count++; + } + + if (Status >= 0 && (Status & pts_eos)) + break; + } + + pt_insn_free_decoder(Decoder); + return Count; +} + +static void +RunAndTrace(const char * Path, const char * Function, BOOLEAN Packets, int PinCore) +{ + STARTUPINFOA Startup = {0}; + PROCESS_INFORMATION Process = {0}; + HYPERTRACE_PT_MMAP_PACKETS Mmap = {0}; + HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; + UINT64 TextStart = 0; + UINT64 TextEnd = 0; + UINT64 FilterStart = 0; + UINT64 FilterEnd = 0; + UINT64 Total = 0; + + Startup.cb = sizeof(Startup); + + if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) + { + printf("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); + return; + } + + printf("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); + + if (PinCore >= 0) + { + DWORD_PTR Mask = (DWORD_PTR)1 << PinCore; + if (SetProcessAffinityMask(Process.hProcess, Mask)) + printf("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); + else + printf("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); + } + else + { + printf("[*] target unpinned (scheduler may migrate it across cores)\n"); + } + + if (!CaptureImage(Process.hProcess, &TextStart, &TextEnd)) + { + printf("[-] cannot read target image / .text section\n"); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + printf("[+] image base 0x%llx, .text 0x%llx-0x%llx (%llu bytes)\n", + (unsigned long long)g_ImageBase, + (unsigned long long)TextStart, + (unsigned long long)TextEnd, + (unsigned long long)g_CodeSize); + + FilterStart = TextStart; + FilterEnd = TextEnd; + + if (Function != NULL && ResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) + printf("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", + Function, + (unsigned long long)FilterStart, + (unsigned long long)FilterEnd, + (unsigned long long)(FilterEnd - FilterStart + 1)); + else + printf("[!] IP filter: whole .text (symbol '%s' not found - build the target with a PDB)\n", + Function ? Function : "(none)"); + + if (!PtFilter(Process.dwProcessId, FilterStart, FilterEnd) || + !PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE)) + { + printf("[-] cannot enable Intel PT\n"); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + if (!hyperdbg_u_pt_mmap(&Mmap)) + { + printf("[-] pt_mmap failed\n"); + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + printf("[+] PT enabled, %u per-core buffers mapped\n", Mmap.NumCpus); + printf("[*] resuming target and waiting for it to exit...\n"); + + ResumeThread(Process.hThread); + WaitForSingleObject(Process.hProcess, INFINITE); + printf("[+] target exited, decoding trace\n"); + + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE); + + Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; + if (!hyperdbg_u_pt_operation(&Sizes)) + { + printf("[-] cannot query PT sizes\n"); + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + goto Cleanup; + } + + for (UINT32 i = 0; i < Mmap.NumCpus; i++) + { + UINT32 Cpu = Mmap.Cpus[i].CpuId; + UINT64 Bytes = (Cpu < Sizes.NumCpus) ? Sizes.BytesPerCpu[Cpu] : 0; + + if (Bytes == 0) + continue; + + if (Bytes > Mmap.Cpus[i].Size) + Bytes = Mmap.Cpus[i].Size; + + printf("\n[*] core %u: %llu bytes of trace\n", Cpu, (unsigned long long)Bytes); + Total += Packets + ? DecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes) + : DecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); + } + + printf("\n[+] decoded %llu %s total\n", (unsigned long long)Total, Packets ? "packet(s)" : "instruction(s)"); + + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + +Cleanup: + if (g_Code != NULL) + { + free(g_Code); + g_Code = NULL; + } + if (Process.hThread != NULL) + CloseHandle(Process.hThread); + if (Process.hProcess != NULL) + CloseHandle(Process.hProcess); +} + +static int +LoadVmmAndTrace() { hyperdbg_u_set_text_message_callback((PVOID)ShowMessages); @@ -27,18 +515,52 @@ LoadVmm() printf("[+] HyperDbg VMM is running\n"); + printf("[*] loading HyperTrace...\n"); + + if ( hyperdbg_u_load_hypertrace_module() == 1) + { + printf("[-] cannot load the HyperDbg HyperTrace\n"); + return 1; + } + + printf("[+] HyperDbg HyperTrace is running\n"); + return 0; } int -main(int argc, char ** argv) +main2(int argc, char ** argv) { - if (LoadVmm() != 0) + const char * function = "main"; + BOOLEAN packets = FALSE; + int pinCore = 0; + + if (argc < 2) { + printf("HyperDbg Intel PT tracer\n"); + printf("usage: %s [function] [-p] [-c core]\n", argv[0]); + printf(" [function] symbol to IP-filter (default 'main'; pass '*' for whole .text)\n"); + printf(" -p dump raw PT packets (TNT/TIP/FUP/PSB/...) instead of instructions\n"); + printf(" -c core pin the target to this logical core (default 0; -1 = unpinned)\n"); return 1; } - hyperdbg_u_run_command((CHAR*)"lm"); + for (int i = 2; i < argc; i++) + { + if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--packets") == 0) + packets = TRUE; + else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) + pinCore = atoi(argv[++i]); + else if (strcmp(argv[i], "*") == 0) + function = NULL; + else + function = argv[i]; + } + + if (LoadVmmAndTrace() != 0) + return 1; + + RunAndTrace(argv[1], function, packets, pinCore); printf("[*] unloading HyperDbg VMM...\n"); diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj index 0f4ae87e..305d9cc1 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj @@ -15,8 +15,8 @@ - + 16.0 diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters index 221a1596..d7dd0c26 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters @@ -20,10 +20,10 @@ - + code - + code From 122a99a6fe032729a668c32b675048f17886f989 Mon Sep 17 00:00:00 2001 From: sina Date: Sat, 13 Jun 2026 15:42:40 +0200 Subject: [PATCH 20/57] commenting synthetic MSRs --- .../user/hyperdbg_app/code/hyperdbg-app.cpp | 2 ++ hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c | 28 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp index c82097b8..095876bf 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp @@ -33,6 +33,8 @@ LoadVmm() int main(int argc, char ** argv) { + return main2(argc, argv); + if (LoadVmm() != 0) { return 1; diff --git a/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c b/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c index 8998e8e1..36971745 100644 --- a/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c +++ b/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c @@ -98,15 +98,15 @@ MsrHandleRdmsrVmexit(VIRTUAL_MACHINE_STATE * VCpu) // VCpu->LastVmexitRip); // // Checking whether it is a synthetic MSR for Hyper-V. - if (MsrHandleIsHypervSyntheticMsr(TargetMsr)) - { - Msr.Flags = CpuReadMsr(TargetMsr); - - GuestRegs->rax = Msr.Fields.Low; - GuestRegs->rdx = Msr.Fields.High; - - return; - } + // if (MsrHandleIsHypervSyntheticMsr(TargetMsr)) + // { + // Msr.Flags = CpuReadMsr(TargetMsr); + // + // GuestRegs->rax = Msr.Fields.Low; + // GuestRegs->rdx = Msr.Fields.High; + // + // return; + // } // // Check for sanity of MSR if they're valid or they're for reserved range for WRMSR and RDMSR @@ -259,11 +259,11 @@ MsrHandleWrmsrVmexit(VIRTUAL_MACHINE_STATE * VCpu) // VCpu->LastVmexitRip); // // Checking whether it is a synthetic MSR for Hyper-V. - if (MsrHandleIsHypervSyntheticMsr(TargetMsr)) - { - CpuWriteMsr(TargetMsr, Msr.Flags); - return; - } + // if (MsrHandleIsHypervSyntheticMsr(TargetMsr)) + // { + // CpuWriteMsr(TargetMsr, Msr.Flags); + // return; + // } // // Check for sanity of MSR if they're valid or they're for reserved range for WRMSR and RDMSR From 60ab87875ccb1a9414d6ae339a3e20fad655429f Mon Sep 17 00:00:00 2001 From: Sina Karvandi Date: Sun, 14 Jun 2026 01:27:28 +0200 Subject: [PATCH 21/57] Update CONTRIBUTING.md --- CONTRIBUTING.md | 44 +++++--------------------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc90e82f..4b0d2c53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,47 +8,13 @@ HyperDbg is a large-scale project that requires a lot of time and effort from th Please make sure to create a [discussion](https://github.com/orgs/HyperDbg/discussions) or an [issue](https://github.com/HyperDbg/HyperDbg/issues), or even better, join the HyperDbg groups ([Telegram](https://t.me/HyperDbg), [Discord](https://discord.gg/anSPsGUtzN), [Matrix](https://matrix.to/#/#hyperdbg-discussion:matrix.org)) on social media. Discuss the way you want to implement your changes and inform developers, because we often see people simultaneously working on the same issue. To avoid this collision, make sure to inform us before you start developing. +### Working on an Idea or a Task +- We have a [wide range of ideas and tasks](https://github.com/orgs/HyperDbg/projects/2) that you might be interested in working on. You can choose a task and start working on it, and if you have any questions or need clarification, feel free to ask. This list will be updated frequently. + +### Other Things to Work on - Writing blog posts and creating videos about use-cases of HyperDbg (make sure to add it to the [awesome](https://github.com/HyperDbg/awesome) repository). - Fixing unresolved GitHub [issues](https://github.com/HyperDbg/HyperDbg/issues). -- Troubleshooting problems with running on Hyper-V's nested virtualization. -- Troubleshooting problems with running on VirtualBox's nested virtualization. -- Supporting KDNET (sending data over the network). -- Enhancing HyperDbg's [Transparent Mode](https://docs.hyperdbg.org/using-hyperdbg/prerequisites/operation-modes#transparent-mode). These features should be added as an extension to the [HyperEvade](https://www.vusec.net/projects/hyperevade/) project (e.g., by bypassing [al-khaser](https://github.com/LordNoteworthy/al-khaser) and similar anti-debugging and anti-hypervisor projects). -- Adding HyperDbg to the system startup using UEFI. -- Creating a QT-based GUI. -- Creating a SoftICE-style GUI. -- Supporting nested-virtualization on HyperDbg itself. -- Protecting HyperDbg code and memory from modification using VT-x capabilities. -- Adding support for the Intel Processor Trace (PT) and event command for detecting coverage | (In progress). -- Creating a wrapper that automatically interprets the [HyperDbg SDK](https://github.com/HyperDbg/HyperDbg/tree/master/hyperdbg/include/SDK) to GO, RUST, C#, Python, etc. -- Creating syntax highlighting for dslang for different IDEs (VSCode, VIM, etc.). -- Building HyperDbg using LLVM clang. -- Helping us start supporting HyperDbg on Linux (discussion needed) | (In progress). -- Helping us start supporting HyperDbg on AMD processors (discussion needed). -- Adding digital (FPGA) modules to the hwdbg hardware debugger. -- Creating a [ret-sync](https://github.com/bootleg/ret-sync) module for HyperDbg. -- Adding fuzzing capabilities to HyperDbg (maybe integrating [AFL++](https://github.com/AFLplusplus/AFLplusplus) into HyperDbg). -- Working on live memory migration and adding support for kernel-mode time travel debugging. -- Integrating the [z3 project](https://github.com/Z3Prover/z3) into HyperDbg and adding commands based on the z3 solver. -- Adding the [Bochs emulator](https://github.com/bochs-emu/Bochs) to HyperDbg. -- Creating different examples of how to use the SDK (using different programming languages). -- Debugging and fixing bugs related to HyperDbg's physical serial communication. -- Reading symbol information from modules in memory (currently, HyperDbg opens a file which continues the debugger). -- Adding APIC virtualization. -- Reading the list of modules for the '[lm](https://docs.hyperdbg.org/commands/debugging-commands/lm)' command directly from kernel-mode. -- Detecting and fixing anti-hypervisor methods described [here](https://github.com/Ahora57/MAJESTY-technologies). -- Investigating why the symbols parser (DIA SDK) could not read symbols of the 'kernel32!*'. -- Creating the 'alias' command that converts or registers scripts as a command, for example: "alias !list .script list.dbg" (discussion needed). -- Adding support for [Hardware Performance Counters (HPC)](https://en.wikipedia.org/wiki/Hardware_performance_counter). -- Any other interesting tasks you might find! - -- ~~Enhancing and adding more features to the ['.pe'](https://docs.hyperdbg.org/commands/meta-commands/.pe) command.~~ Added: [link][link] -- ~~Adding routines to activate and use Last Branch Record (LBR) and Branch Trace Store (BTS).~~ Added: [link][link] -- ~~Creating commands to inspect and read details of PCIe devices.~~ Added: [link][link] -- ~~Mitigating the anti-hypervisor method described [here](https://howtohypervise.blogspot.com/2019/01/a-common-missight-in-most-hypervisors.html).~~ [[Fixed](https://github.com/HyperDbg/HyperDbg/pull/497)] -- ~~Fixing the problem with [XSETBV instruction freezing](https://github.com/HyperDbg/HyperDbg/issues/429).~~ [[Fixed](https://github.com/HyperDbg/HyperDbg/pull/491)] - -This list will be updated frequently. +- Any other interesting ideas you might find! (Let's have a discussion before working on them.) ## Fixing Bugs From 54b5b5a42feb5ec3edb0ff33efa7cb599bb42462 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Fri, 12 Jun 2026 14:27:51 +0200 Subject: [PATCH 22/57] Platform ioctl interface --- hyperdbg/include/SDK/headers/BasicTypes.h | 4 + .../platform/user/code/platform-ioctl.c | 83 +++++++++++++++++++ .../platform/user/header/platform-ioctl.h | 40 +++++++++ hyperdbg/libhyperdbg/CMakeLists.txt | 2 + .../debugging-commands/preactivate.cpp | 6 +- .../commands/debugging-commands/prealloc.cpp | 6 +- hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 2 + .../libhyperdbg/libhyperdbg.vcxproj.filters | 6 ++ hyperdbg/libhyperdbg/pch.h | 5 ++ 9 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 hyperdbg/include/platform/user/code/platform-ioctl.c create mode 100644 hyperdbg/include/platform/user/header/platform-ioctl.h diff --git a/hyperdbg/include/SDK/headers/BasicTypes.h b/hyperdbg/include/SDK/headers/BasicTypes.h index 91b8a546..17e5c84a 100644 --- a/hyperdbg/include/SDK/headers/BasicTypes.h +++ b/hyperdbg/include/SDK/headers/BasicTypes.h @@ -101,6 +101,10 @@ typedef UINT16 WCHAR; typedef PVOID HANDLE; typedef HANDLE * PHANDLE; +// Windows pointer-style aliases (used by the cross-platform driver/IOCTL wrappers) +typedef PVOID LPVOID; +typedef DWORD * LPDWORD; + #endif #define NULL_ZERO 0 diff --git a/hyperdbg/include/platform/user/code/platform-ioctl.c b/hyperdbg/include/platform/user/code/platform-ioctl.c new file mode 100644 index 00000000..0c78ef42 --- /dev/null +++ b/hyperdbg/include/platform/user/code/platform-ioctl.c @@ -0,0 +1,83 @@ +/** + * @file platform-ioctl.c + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform implementation of the local kernel-driver IOCTL transport + * @details See platform-ioctl.h. The Windows branch forwards directly to Win32 + * DeviceIoControl. The Linux branch is currently stubbed (returns FALSE) and is + * the home where the ioctl()-based implementation against the /dev/HyperDbg + * character device will live once the kernel module exists. + * + * @version 0.1 + * @date 2026-06-09 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +#if defined(__linux__) +# include "../header/platform-ioctl.h" +#endif // defined(__linux__) + +#if defined(_WIN32) + +BOOL +PlatformDeviceIoControl(HANDLE Device, + DWORD IoControlCode, + LPVOID InBuffer, + DWORD InBufferSize, + LPVOID OutBuffer, + DWORD OutBufferSize, + LPDWORD BytesReturned, + LPVOID Overlapped) +{ + return DeviceIoControl(Device, + IoControlCode, + InBuffer, + InBufferSize, + OutBuffer, + OutBufferSize, + BytesReturned, + (LPOVERLAPPED)Overlapped); +} + +#elif defined(__linux__) + +// +// TODO: implement the local driver transport on Linux using ioctl() against a +// /dev/HyperDbg character device exposed by the kernel module: +// - open the device once (in the library init path) -> file descriptor stored +// in g_DeviceHandle +// - PlatformDeviceIoControl -> ioctl(fd, IoControlCode, ...) with the in/out +// buffer marshalling the driver expects (likely a single in-out buffer) +// - close on teardown +// The kernel module does not exist yet, so this returns failure for now: callers +// that have already asserted g_DeviceHandle will simply report the IOCTL failed +// rather than crashing. +// + +BOOL +PlatformDeviceIoControl(HANDLE Device, + DWORD IoControlCode, + LPVOID InBuffer, + DWORD InBufferSize, + LPVOID OutBuffer, + DWORD OutBufferSize, + LPDWORD BytesReturned, + LPVOID Overlapped) +{ + (void)Device; + (void)IoControlCode; + (void)InBuffer; + (void)InBufferSize; + (void)OutBuffer; + (void)OutBufferSize; + (void)Overlapped; + if (BytesReturned) + *BytesReturned = 0; + return FALSE; +} + +#else +# error "Unsupported platform" +#endif diff --git a/hyperdbg/include/platform/user/header/platform-ioctl.h b/hyperdbg/include/platform/user/header/platform-ioctl.h new file mode 100644 index 00000000..e300342a --- /dev/null +++ b/hyperdbg/include/platform/user/header/platform-ioctl.h @@ -0,0 +1,40 @@ +/** + * @file platform-ioctl.h + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform interface for the local kernel-driver IOCTL transport + * @details Distinct from the serial transport (platform-serial), which talks to a remote + * debuggee. This interface is the LOCAL control channel: the userspace library + * drives the HyperDbg kernel driver through device I/O control codes. The command + * and packet layers are platform independent; only the device-control primitive + * underneath them is OS specific. Windows maps onto Win32 DeviceIoControl over a + * \\.\HyperDbgDebuggerDevice handle; Linux will map onto ioctl() against a + * /dev/HyperDbg character device exposed by the (future) kernel module (TODO). + * + * @version 0.1 + * @date 2026-06-09 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#if defined(__linux__) +# include "../../../../include/SDK/HyperDbgSdk.h" +#endif // defined(__linux__) + +// +// SEND an I/O control code to the local kernel driver. Mirrors the Win32 +// DeviceIoControl signature so the call sites stay verbatim apart from the name. +// The Overlapped parameter is kept as a void * so the shared signature does not +// leak OVERLAPPED into the protocol code; synchronous callers pass NULL. +// Returns TRUE on success; on failure use PlatformGetLastError for the reason. +// +BOOL +PlatformDeviceIoControl(HANDLE Device, + DWORD IoControlCode, + LPVOID InBuffer, + DWORD InBufferSize, + LPVOID OutBuffer, + DWORD OutBufferSize, + LPDWORD BytesReturned, + LPVOID Overlapped); diff --git a/hyperdbg/libhyperdbg/CMakeLists.txt b/hyperdbg/libhyperdbg/CMakeLists.txt index dc41e929..a0bfa2df 100644 --- a/hyperdbg/libhyperdbg/CMakeLists.txt +++ b/hyperdbg/libhyperdbg/CMakeLists.txt @@ -30,6 +30,7 @@ set(SourceFiles "../include/platform/user/code/platform-intrinsics.c" "../include/platform/user/code/platform-lib-calls.c" "../include/platform/user/code/platform-serial.c" + "../include/platform/user/code/platform-ioctl.c" "../script-eval/code/Functions.c" "../script-eval/code/Keywords.c" "../script-eval/code/PseudoRegisters.c" @@ -173,6 +174,7 @@ set_source_files_properties( "../include/platform/user/code/platform-intrinsics.c" "../include/platform/user/code/platform-lib-calls.c" "../include/platform/user/code/platform-serial.c" + "../include/platform/user/code/platform-ioctl.c" "../script-eval/code/Functions.c" "../script-eval/code/Keywords.c" "../script-eval/code/PseudoRegisters.c" diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/preactivate.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/preactivate.cpp index a64f908d..ee88936d 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/preactivate.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/preactivate.cpp @@ -50,7 +50,7 @@ CommandPreactivate(vector CommandTokens, string Command) { BOOL Status; ULONG ReturnedLength; - DEBUGGER_PREACTIVATE_COMMAND PreactivateRequest = {0}; + DEBUGGER_PREACTIVATE_COMMAND PreactivateRequest = {}; if (CommandTokens.size() != 2) { @@ -82,7 +82,7 @@ CommandPreactivate(vector CommandTokens, string Command) // // Send IOCTL // - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_PREACTIVATE_FUNCTIONALITY, // IO Control Code (IOCTL) &PreactivateRequest, // Input Buffer to driver. @@ -96,7 +96,7 @@ CommandPreactivate(vector CommandTokens, string Command) if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return; } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/prealloc.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/prealloc.cpp index 9015c420..0bf9545b 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/prealloc.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/prealloc.cpp @@ -62,7 +62,7 @@ CommandPrealloc(vector CommandTokens, string Command) BOOL Status; ULONG ReturnedLength; UINT64 Count; - DEBUGGER_PREALLOC_COMMAND PreallocRequest = {0}; + DEBUGGER_PREALLOC_COMMAND PreallocRequest = {}; string SecondParam; if (CommandTokens.size() != 3) @@ -143,7 +143,7 @@ CommandPrealloc(vector CommandTokens, string Command) // // Send IOCTL // - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_RESERVE_PRE_ALLOCATED_POOLS, // IO Control Code (IOCTL) &PreallocRequest, // Input Buffer to driver. @@ -157,7 +157,7 @@ CommandPrealloc(vector CommandTokens, string Command) if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return; } diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index 5f1ceeee..dbbff03f 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -131,6 +131,7 @@ + @@ -169,6 +170,7 @@ + diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters index 7185ea94..ad369cec 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters @@ -200,6 +200,9 @@ header\platform + + header\platform + header @@ -652,6 +655,9 @@ code\platform + + code\platform + code\app diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index b6591f83..5b9d0c1c 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -161,6 +161,11 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; // #include "platform/user/header/platform-serial.h" +// +// Platform IOCTL transport (cross-platform local kernel-driver device I/O) +// +#include "platform/user/header/platform-ioctl.h" + // // Platform-specific intrinsics // From dabf132d31503843f90949f35f8dce4601d43126 Mon Sep 17 00:00:00 2001 From: Masoud Rahimi Jafari Date: Sun, 14 Jun 2026 16:29:26 +0200 Subject: [PATCH 23/57] Fixed ioctl pt operation size bug --- hyperdbg/hyperkd/code/driver/Ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperdbg/hyperkd/code/driver/Ioctl.c b/hyperdbg/hyperkd/code/driver/Ioctl.c index d3e22af3..b7bb7d16 100644 --- a/hyperdbg/hyperkd/code/driver/Ioctl.c +++ b/hyperdbg/hyperkd/code/driver/Ioctl.c @@ -1687,7 +1687,7 @@ DrvDispatchHyperTraceIoControl(PIRP Irp, PIO_STACK_LOCATION IrpStack, BOOLEAN * // // Adjust the status and output size // - DrvAdjustStatusAndSetOutputSize(SIZEOF_HYPERTRACE_LBR_DUMP_PACKETS, DoNotChangeInformation, Irp, &Status); + DrvAdjustStatusAndSetOutputSize(SIZEOF_HYPERTRACE_PT_OPERATION_PACKETS, DoNotChangeInformation, Irp, &Status); break; From 4db59dc013fae951ff89d3504d684d29a1a5a686 Mon Sep 17 00:00:00 2001 From: sina Date: Mon, 15 Jun 2026 00:04:28 +0200 Subject: [PATCH 24/57] fix changelog and pt app issues --- CHANGELOG.md | 10 ++++++-- .../user/hyperdbg_app/code/hyperdbg-ipt.cpp | 23 +++++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b36051a7..8639a2a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 New release of the HyperDbg Debugger. ### Added -- +- Added the libipt library to the project and the SDK +- Added Intel Processor Trace (PT) example app +- Added Linux distinction and pragmas together with new platform functions ([link](https://github.com/HyperDbg/HyperDbg/commit/9c3e0ed23b5f4bb64bd305e546dd0e86ee1910d9)) +- Added interfaces to Linux port for the serial devices ([link](https://github.com/HyperDbg/HyperDbg/commit/31b514f8df40d81d44ae07a2328c02360ce488c3)) +- Added platform IOCTL interface ([link](https://github.com/HyperDbg/HyperDbg/pull/619)) ### Changed -- +- Synthetic MSRs are commented for further tests +- Fix adding missing IOCTL for Intel PT ([link](https://github.com/HyperDbg/HyperDbg/commit/df12e9fd79851c8f378ec82a45066da510da507a)) +- Fix the new form of loading and unloading in the example app and PT app ([link](https://github.com/HyperDbg/HyperDbg/commit/9ad48d30dcf6b409ae86b2d08262584cd06f606e)) ## [0.19.0.0-beta] - 2026-06-10 New release of the HyperDbg Debugger. diff --git a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp index cdb219e9..280fde5e 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp @@ -373,7 +373,7 @@ RunAndTrace(const char * Path, const char * Function, BOOLEAN Packets, int PinCo STARTUPINFOA Startup = {0}; PROCESS_INFORMATION Process = {0}; HYPERTRACE_PT_MMAP_PACKETS Mmap = {0}; - HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; + HYPERTRACE_PT_OPERATION_PACKETS Sizes = {0}; UINT64 TextStart = 0; UINT64 TextEnd = 0; UINT64 FilterStart = 0; @@ -419,15 +419,18 @@ RunAndTrace(const char * Path, const char * Function, BOOLEAN Packets, int PinCo FilterStart = TextStart; FilterEnd = TextEnd; - if (Function != NULL && ResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) + if (Function != NULL && ResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) { printf("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", - Function, - (unsigned long long)FilterStart, - (unsigned long long)FilterEnd, - (unsigned long long)(FilterEnd - FilterStart + 1)); - else + Function, + (unsigned long long)FilterStart, + (unsigned long long)FilterEnd, + (unsigned long long)(FilterEnd - FilterStart + 1)); + } + else + { printf("[!] IP filter: whole .text (symbol '%s' not found - build the target with a PDB)\n", - Function ? Function : "(none)"); + Function ? Function : "(none)"); + } if (!PtFilter(Process.dwProcessId, FilterStart, FilterEnd) || !PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE)) @@ -558,8 +561,10 @@ main2(int argc, char ** argv) } if (LoadVmmAndTrace() != 0) + { return 1; - + } + RunAndTrace(argv[1], function, packets, pinCore); printf("[*] unloading HyperDbg VMM...\n"); From ced1ee94a418c841725b6f918f67c6ecf5405fce Mon Sep 17 00:00:00 2001 From: maxraulea <79024173+maxraulea@users.noreply.github.com> Date: Fri, 12 Jun 2026 13:39:54 +0200 Subject: [PATCH 25/57] Update platform-lib-calls.c --- hyperdbg/include/platform/user/code/platform-lib-calls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index c52759ec..ac012f70 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -1,6 +1,6 @@ /** * @file platform-lib-calls.c - * @author Max Raulea (max.raulea@gmail.com) + * @author Max Raulea (max.raulea@gmail.com) * @brief User mode Cross platform APIs for platofrm dependend library calls * @details * @version 0.19 From 0b598769196fd6bb6e2c719a705ffa94c018d55d Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 15 Jun 2026 15:18:56 +0200 Subject: [PATCH 26/57] compiling dump.cpp and track.cpp --- .../platform/general/header/Environment.h | 3 + .../platform/user/code/platform-lib-calls.c | 90 +++++++++++++++++++ .../platform/user/header/platform-lib-calls.h | 30 +++++++ .../commands/extension-commands/track.cpp | 8 +- .../debugger/commands/meta-commands/dump.cpp | 19 ++-- 5 files changed, 132 insertions(+), 18 deletions(-) diff --git a/hyperdbg/include/platform/general/header/Environment.h b/hyperdbg/include/platform/general/header/Environment.h index caaa22e7..59619c1c 100644 --- a/hyperdbg/include/platform/general/header/Environment.h +++ b/hyperdbg/include/platform/general/header/Environment.h @@ -75,4 +75,7 @@ typedef void * HMODULE; # define INFINITE 0xFFFFFFFF # define WAIT_OBJECT_0 0x00000000 +// Win32 invalid handle sentinel (returned by the cross-platform file/serial wrappers) +# define INVALID_HANDLE_VALUE ((HANDLE)(SIZE_T)-1) + #endif // HYPERDBG_ENV_LINUX diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index ac012f70..d19b3725 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -357,3 +357,93 @@ PlatformGetLastError(VOID) # error "Unsupported platform" #endif } + +/** + * @brief Platform independent wrapper to write raw bytes to the console + * + * @details Used to emit pre-encoded UTF-8 byte sequences (e.g. box-drawing + * characters) directly to standard output. On Windows this goes + * through WriteConsoleA so the console code page is bypassed; on + * Linux the terminal is UTF-8 native so the bytes are written as-is. + * + * @param Buffer pointer to the bytes to write + * @param NumberOfBytes number of bytes to write + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformWriteConsole(const VOID * Buffer, DWORD NumberOfBytes) +{ +#if defined(_WIN32) + return (BOOLEAN)WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Buffer, NumberOfBytes, NULL, NULL); +#elif defined(__linux__) + return (BOOLEAN)(fwrite(Buffer, 1, NumberOfBytes, stdout) == NumberOfBytes); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to create/open a file for writing + * + * @param Path wide path of the file to create (truncated if it exists) + * @return HANDLE to the opened file, or INVALID_HANDLE_VALUE on failure + */ +HANDLE +PlatformOpenFileForWriting(const wchar_t * Path) +{ +#if defined(_WIN32) + return CreateFileW(Path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); +#elif defined(__linux__) + // + // TODO: handle this later. The path arrives as a std::wstring (4-byte + // wchar_t on Linux) and must be narrowed to a UTF-8 char* before it + // can be handed to fopen. Until that conversion is wired up, fail the + // open so callers (e.g. dump.cpp) bail out cleanly instead of writing + // to a bogus handle. + // + (void)Path; + return INVALID_HANDLE_VALUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to write a buffer to an open file + * + * @param FileHandle handle returned by PlatformOpenFileForWriting + * @param Buffer pointer to the bytes to write + * @param NumberOfBytes number of bytes to write + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformWriteFile(HANDLE FileHandle, const VOID * Buffer, DWORD NumberOfBytes) +{ +#if defined(_WIN32) + DWORD BytesWritten; + return (BOOLEAN)WriteFile(FileHandle, Buffer, NumberOfBytes, &BytesWritten, NULL); +#elif defined(__linux__) + return (BOOLEAN)(fwrite(Buffer, 1, NumberOfBytes, (FILE *)FileHandle) == NumberOfBytes); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to close a file opened by + * PlatformOpenFileForWriting + * + * @param FileHandle handle to close + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformCloseFile(HANDLE FileHandle) +{ +#if defined(_WIN32) + return (BOOLEAN)CloseHandle(FileHandle); +#elif defined(__linux__) + return (BOOLEAN)(fclose((FILE *)FileHandle) == 0); +#else +# error "Unsupported platform" +#endif +} diff --git a/hyperdbg/include/platform/user/header/platform-lib-calls.h b/hyperdbg/include/platform/user/header/platform-lib-calls.h index bf74c760..f7a603eb 100644 --- a/hyperdbg/include/platform/user/header/platform-lib-calls.h +++ b/hyperdbg/include/platform/user/header/platform-lib-calls.h @@ -72,9 +72,39 @@ PlatformCloseHandle(HANDLE Handle); // // LAST OS ERROR // +// TODO (linux, correctness): the wrappers below only unify the success/failure +// *boolean* convention. The actual error semantics still differ across platforms +// and must be reconciled later: +// - PlatformGetLastError returns raw Linux errno (EACCES=13, ...) which does NOT +// match the Win32 ERROR_* code space (ERROR_ACCESS_DENIED=5, ...). Code that +// merely logs/checks-nonzero is fine; code that compares against ERROR_* needs +// an errno -> ERROR_* mapping here. +// - Failure sentinels are not uniform across the Win32 calls we wrap: file opens +// fail with INVALID_HANDLE_VALUE (not NULL), most other handle calls fail with +// NULL. Callers must test the right one. +// For now: getting the project to compile is step 1; correctness comes next. +// DWORD PlatformGetLastError(VOID); +// +// CONSOLE OUTPUT (raw bytes to stdout) +// +BOOLEAN +PlatformWriteConsole(const VOID * Buffer, DWORD NumberOfBytes); + +// +// FILE I/O +// +HANDLE +PlatformOpenFileForWriting(const wchar_t * Path); + +BOOLEAN +PlatformWriteFile(HANDLE FileHandle, const VOID * Buffer, DWORD NumberOfBytes); + +BOOLEAN +PlatformCloseFile(HANDLE FileHandle); + // // PROCESS / THREAD IDENTITY // diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/track.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/track.cpp index 23c91fab..fee0841c 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/track.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/track.cpp @@ -244,7 +244,7 @@ CommandTrackHandleReceivedCallInstructions(const CHAR * NameOfFunctionFromSymbol for (SIZE_T i = 0; i < NumberOfCallsIdentation; i++) { - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Utf8String1, sizeof(Utf8String1) - 1, NULL, NULL); + PlatformWriteConsole(Utf8String1, sizeof(Utf8String1) - 1); } // @@ -252,7 +252,7 @@ CommandTrackHandleReceivedCallInstructions(const CHAR * NameOfFunctionFromSymbol // // CHAR Utf8String[] = "\xE2\x94\x9C\xE2\x94\x80\xE2\x94\x80"; CHAR Utf8String[] = "\xE2\x94\x8C\xE2\x94\x80\xE2\x94\x80"; - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Utf8String, sizeof(Utf8String) - 1, NULL, NULL); + PlatformWriteConsole(Utf8String, sizeof(Utf8String) - 1); if (NameOfFunctionFromSymbols != NULL) { @@ -296,14 +296,14 @@ CommandTrackHandleReceivedRetInstructions(UINT64 CurrentRip) for (SIZE_T i = 0; i < NumberOfCallsIdentation; i++) { - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Utf8String1, sizeof(Utf8String1) - 1, NULL, NULL); + PlatformWriteConsole(Utf8String1, sizeof(Utf8String1) - 1); } // // Write the UTF-8 encoded character sequence to the console // CHAR Utf8String[] = "\xE2\x94\x94\xE2\x94\x80\xE2\x94\x80\x20"; - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Utf8String, sizeof(Utf8String) - 1, NULL, NULL); + PlatformWriteConsole(Utf8String, sizeof(Utf8String) - 1); // // Apply addressconversion of settings here diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp index f5de3129..0f8d0e5e 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp @@ -212,7 +212,7 @@ CommandDump(vector CommandTokens, string Command) // // Default process we read from current process // - Pid = GetCurrentProcessId(); + Pid = PlatformGetCurrentProcessId(); } // @@ -226,14 +226,7 @@ CommandDump(vector CommandTokens, string Command) // // Create or open the file for writing the dump file // - DumpFileHandle = CreateFileW( - Filepath.c_str(), - GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); + DumpFileHandle = PlatformOpenFileForWriting(Filepath.c_str()); if (DumpFileHandle == INVALID_HANDLE_VALUE) { @@ -284,7 +277,7 @@ CommandDump(vector CommandTokens, string Command) // if (DumpFileHandle != NULL) { - CloseHandle(DumpFileHandle); + PlatformCloseFile(DumpFileHandle); DumpFileHandle = NULL; } @@ -302,8 +295,6 @@ CommandDump(vector CommandTokens, string Command) VOID CommandDumpSaveIntoFile(PVOID Buffer, UINT32 Length) { - DWORD BytesWritten; - // // Check if handle is valid // @@ -316,11 +307,11 @@ CommandDumpSaveIntoFile(PVOID Buffer, UINT32 Length) // // Write the buffer into the dump file // - if (!WriteFile(DumpFileHandle, Buffer, Length, &BytesWritten, NULL)) + if (!PlatformWriteFile(DumpFileHandle, Buffer, Length)) { ShowMessages("err, unable to write buffer into the dump\n"); - CloseHandle(DumpFileHandle); + PlatformCloseFile(DumpFileHandle); DumpFileHandle = NULL; return; From 5e074a5cbdae81c509c7e7035f1904fea8fca883 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 15 Jun 2026 15:52:50 +0200 Subject: [PATCH 27/57] PE parsing compiles now --- .../platform/user/code/platform-lib-calls.c | 164 ++++++++++++++++++ .../platform/user/header/platform-lib-calls.h | 16 ++ .../commands/meta-commands/pagein.cpp | 6 +- .../code/debugger/user-level/pe-parser.cpp | 122 ++++--------- hyperdbg/libhyperdbg/header/pe-parser.h | 8 +- 5 files changed, 218 insertions(+), 98 deletions(-) diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index d19b3725..d6241284 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -447,3 +447,167 @@ PlatformCloseFile(HANDLE FileHandle) # error "Unsupported platform" #endif } + +/** + * @brief Platform independent wrapper to map an entire file read-only into memory + * + * @details The returned pointer stays valid until released with PlatformUnmapFile; + * the underlying file/descriptor is closed before returning (the mapping + * outlives it on both platforms). + * + * @param Path wide path of the file to map + * @param OutFileSize output — size of the file in bytes (0 on failure) + * @return VOID* base address of the mapped file, or NULL on failure + */ +VOID * +PlatformMapFileReadOnly(const wchar_t * Path, PSIZE_T OutFileSize, PHANDLE OutFileHandle) +{ +#if defined(_WIN32) + HANDLE FileHandle; + HANDLE MapObjectHandle; + VOID * BaseAddr; + LARGE_INTEGER FileSize; + + *OutFileSize = 0; + *OutFileHandle = INVALID_HANDLE_VALUE; + + FileHandle = CreateFileW(Path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (FileHandle == INVALID_HANDLE_VALUE) + { + return NULL; + } + + if (!GetFileSizeEx(FileHandle, &FileSize)) + { + CloseHandle(FileHandle); + return NULL; + } + + MapObjectHandle = CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); + if (MapObjectHandle == NULL) + { + CloseHandle(FileHandle); + return NULL; + } + + BaseAddr = MapViewOfFile(MapObjectHandle, FILE_MAP_READ, 0, 0, 0); + + // + // The view stays valid after the mapping object handle is closed. The file + // handle is kept open and handed back so the caller can still issue raw + // reads (PlatformReadFileAtOffset); it is closed by PlatformUnmapFile. + // + CloseHandle(MapObjectHandle); + + if (BaseAddr == NULL) + { + CloseHandle(FileHandle); + return NULL; + } + + *OutFileSize = (SIZE_T)FileSize.QuadPart; + *OutFileHandle = FileHandle; + return BaseAddr; +#elif defined(__linux__) + // + // TODO (linux): implement the real mapping. Expected contract: + // 1. Narrow the 4-byte wchar_t 'Path' to a UTF-8 char* (the project still + // lacks a wchar_t->UTF-8 helper; the same one is needed by + // PlatformOpenFileForWriting for the dump.cpp write path). + // 2. fd = open(narrowed_path, O_RDONLY); // fail -> NULL + // 3. fstat(fd, &st) to get the file size. + // 4. base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + // 5. *OutFileHandle = (HANDLE)(intptr_t)fd; // keep fd open for raw reads + // 6. *OutFileSize = st.st_size; return base; (return NULL on any failure) + // PlatformUnmapFile must then munmap(base, size) and close the fd — which is + // why both the size and the handle are passed back in on unmap. The raw-read + // path (PlatformReadFileAtOffset) would pread() from that same fd. + // + // Until implemented, fail the map so PE-parser callers print "could not open + // the file" and bail out cleanly instead of dereferencing a bogus pointer. + // + (void)Path; + *OutFileSize = 0; + *OutFileHandle = INVALID_HANDLE_VALUE; + return NULL; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for a positioned (seek + read) file read + * + * @param FileHandle handle handed back by PlatformMapFileReadOnly + * @param Offset byte offset to read from (absolute, from start of file) + * @param Buffer destination buffer + * @param NumberOfBytes number of bytes to read + * @param BytesRead output — number of bytes actually read + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformReadFileAtOffset(HANDLE FileHandle, UINT64 Offset, VOID * Buffer, DWORD NumberOfBytes, LPDWORD BytesRead) +{ +#if defined(_WIN32) + LARGE_INTEGER Distance; + Distance.QuadPart = (LONGLONG)Offset; + + if (!SetFilePointerEx(FileHandle, Distance, NULL, FILE_BEGIN)) + { + return FALSE; + } + + return (BOOLEAN)ReadFile(FileHandle, Buffer, NumberOfBytes, BytesRead, NULL); +#elif defined(__linux__) + // + // TODO (linux): pread((int)(intptr_t)FileHandle, Buffer, NumberOfBytes, Offset) + // once PlatformMapFileReadOnly wraps a real fd. Unreached today + // because the map returns NULL on Linux, so callers bail first. + // + (void)FileHandle; + (void)Offset; + (void)Buffer; + (void)NumberOfBytes; + if (BytesRead != NULL) + { + *BytesRead = 0; + } + return FALSE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to release a mapping from PlatformMapFileReadOnly + * + * @param BaseAddress base address returned by PlatformMapFileReadOnly + * @param FileSize size that was reported by PlatformMapFileReadOnly (needed by munmap) + * @param FileHandle file handle handed back by PlatformMapFileReadOnly + */ +VOID +PlatformUnmapFile(VOID * BaseAddress, SIZE_T FileSize, HANDLE FileHandle) +{ +#if defined(_WIN32) + (void)FileSize; // not needed by UnmapViewOfFile + if (BaseAddress != NULL) + { + UnmapViewOfFile(BaseAddress); + } + if (FileHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(FileHandle); + } +#elif defined(__linux__) + // + // TODO (linux): munmap(BaseAddress, FileSize) and close the fd behind + // FileHandle once PlatformMapFileReadOnly is implemented. + // No-op for now since the map always returns NULL. + // + (void)BaseAddress; + (void)FileSize; + (void)FileHandle; +#else +# error "Unsupported platform" +#endif +} diff --git a/hyperdbg/include/platform/user/header/platform-lib-calls.h b/hyperdbg/include/platform/user/header/platform-lib-calls.h index f7a603eb..d24d2a91 100644 --- a/hyperdbg/include/platform/user/header/platform-lib-calls.h +++ b/hyperdbg/include/platform/user/header/platform-lib-calls.h @@ -105,6 +105,22 @@ PlatformWriteFile(HANDLE FileHandle, const VOID * Buffer, DWORD NumberOfBytes); BOOLEAN PlatformCloseFile(HANDLE FileHandle); +// +// READ-ONLY FILE MAPPING +// +// PlatformMapFileReadOnly also hands back the still-open file handle in +// *OutFileHandle, so callers can issue supplementary raw reads via +// PlatformReadFileAtOffset; release everything with PlatformUnmapFile. +// +VOID * +PlatformMapFileReadOnly(const wchar_t * Path, PSIZE_T OutFileSize, PHANDLE OutFileHandle); + +BOOLEAN +PlatformReadFileAtOffset(HANDLE FileHandle, UINT64 Offset, VOID * Buffer, DWORD NumberOfBytes, LPDWORD BytesRead); + +VOID +PlatformUnmapFile(VOID * BaseAddress, SIZE_T FileSize, HANDLE FileHandle); + // // PROCESS / THREAD IDENTITY // diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pagein.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pagein.cpp index 5d83424d..a01aec56 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pagein.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pagein.cpp @@ -216,14 +216,14 @@ CommandPageinRequest(UINT64 TargetVirtualAddrFrom, if (Pid == 0) { - Pid = GetCurrentProcessId(); + Pid = PlatformGetCurrentProcessId(); PageFaultRequest.ProcessId = Pid; } // // Send IOCTL // - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_BRING_PAGES_IN, // IO Control Code (IOCTL) &PageFaultRequest, // Input Buffer to driver. @@ -237,7 +237,7 @@ CommandPageinRequest(UINT64 TargetVirtualAddrFrom, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return; } diff --git a/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp b/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp index 3ce28f1e..a9c8078b 100644 --- a/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp @@ -3724,9 +3724,9 @@ PeHexDump(CHAR * Ptr, SIZE_T Size, ULONGLONG SecAddress) * @return BOOLEAN */ BOOLEAN -PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, - const CHAR * SectionToShow, - BOOLEAN Is32Bit) +PeShowSectionInformationAndDump(const wchar_t * AddressOfFile, + const CHAR * SectionToShow, + BOOLEAN Is32Bit) { RICH_HEADER_INFO PeFileRichHeaderInfo {0}; RICH_HEADER PeFileRichHeader {0}; @@ -3738,7 +3738,8 @@ PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, const ULONGLONG MaxTotalSectionScanBytes = 64 * 1024 * 1024; const SIZE_T MaxSectionDumpBytes = 1024 * 1024; const ULONGLONG MaxTotalSectionDumpBytes = 4 * 1024 * 1024; - HANDLE MapObjectHandle = NULL, FileHandle = INVALID_HANDLE_VALUE; // File Mapping Object + HANDLE FileHandle = INVALID_HANDLE_VALUE; // Open file handle (kept for the raw Rich-header read) + SIZE_T MappedSize = 0; // Size of the mapped file in bytes UINT32 NumberOfSections; // Number of sections LPVOID BaseAddr = NULL; // Pointer to the base memory of mapped file PIMAGE_DOS_HEADER DosHeader; // Pointer to DOS Header @@ -3760,39 +3761,22 @@ PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, time_t TimeDateStamp; // - // Open the EXE File + // Open and map the EXE file read-only. The wrapper also hands back the open + // file handle so the raw Rich-header read below can still seek/read it. // - FileHandle = CreateFileW(AddressOfFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + BaseAddr = PlatformMapFileReadOnly(AddressOfFile, &MappedSize, &FileHandle); - if (FileHandle == INVALID_HANDLE_VALUE) + if (BaseAddr == NULL) { ShowMessages("err, could not open the file specified\n"); return FALSE; } - if (!GetFileSizeEx(FileHandle, &FileSize) || FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER)) + FileSize.QuadPart = (LONGLONG)MappedSize; + + if (FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER)) { - CloseHandle(FileHandle); - return FALSE; - } - - // - // Mapping Given EXE file to Memory - // - MapObjectHandle = CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); - - if (MapObjectHandle == NULL) - { - CloseHandle(FileHandle); - return FALSE; - } - - BaseAddr = MapViewOfFile(MapObjectHandle, FILE_MAP_READ, 0, 0, 0); - - if (BaseAddr == NULL) - { - CloseHandle(MapObjectHandle); - CloseHandle(FileHandle); + PlatformUnmapFile(BaseAddr, MappedSize, FileHandle); return FALSE; } @@ -3843,15 +3827,7 @@ PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, goto SkipRichHeader; } - if (!SetFilePointerEx(FileHandle, FileStart, NULL, FILE_BEGIN)) - { - RichHeaderStatus = "read failed"; - delete[] DataPtr; - goto SkipRichHeader; - } - - BOOL Result = ReadFile(FileHandle, DataPtr, RichReadSize, &BytesRead, NULL); - if (!Result || BytesRead != RichReadSize) + if (!PlatformReadFileAtOffset(FileHandle, 0, DataPtr, RichReadSize, &BytesRead) || BytesRead != RichReadSize) { RichHeaderStatus = "read failed"; delete[] DataPtr; @@ -4491,20 +4467,7 @@ Finished: delete[] PeFileRichHeader.Entries; } - if (BaseAddr != NULL) - { - UnmapViewOfFile(BaseAddr); - } - - if (MapObjectHandle != NULL) - { - CloseHandle(MapObjectHandle); - } - - if (FileHandle != INVALID_HANDLE_VALUE) - { - CloseHandle(FileHandle); - } + PlatformUnmapFile(BaseAddr, MappedSize, FileHandle); return Result; } @@ -4517,54 +4480,33 @@ Finished: * @return BOOLEAN */ BOOLEAN -PeIsPE32BitOr64Bit(const WCHAR * AddressOfFile, PBOOLEAN Is32Bit) +PeIsPE32BitOr64Bit(const wchar_t * AddressOfFile, PBOOLEAN Is32Bit) { - BOOLEAN Result = FALSE; - HANDLE MapObjectHandle, FileHandle; // File Mapping Object - LPVOID BaseAddr; // Pointer to the base memory of mapped file + BOOLEAN Result = FALSE; + HANDLE FileHandle = INVALID_HANDLE_VALUE; // Open file handle + SIZE_T MappedSize = 0; // Size of the mapped file in bytes + LPVOID BaseAddr; // Pointer to the base memory of mapped file PIMAGE_DOS_HEADER DosHeader; // Pointer to DOS Header PIMAGE_NT_HEADERS32 NtHeader32 = NULL; // Pointer to NT Header 32 bit PE_IMAGE_READER Reader; LARGE_INTEGER FileSize; // - // Open the EXE File + // Open and map the EXE file read-only // - FileHandle = CreateFileW(AddressOfFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (FileHandle == INVALID_HANDLE_VALUE) - { - ShowMessages("err, unable to read the file (%x)\n", GetLastError()); - return FALSE; - }; - - if (!GetFileSizeEx(FileHandle, &FileSize) || FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER)) - { - CloseHandle(FileHandle); - return FALSE; - } - - // - // Mapping Given EXE file to Memory - // - MapObjectHandle = - CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); - - if (MapObjectHandle == NULL) - { - CloseHandle(FileHandle); - - ShowMessages("err, unable to create file mappings (%x)\n", GetLastError()); - return FALSE; - } - - BaseAddr = MapViewOfFile(MapObjectHandle, FILE_MAP_READ, 0, 0, 0); + BaseAddr = PlatformMapFileReadOnly(AddressOfFile, &MappedSize, &FileHandle); if (BaseAddr == NULL) { - CloseHandle(MapObjectHandle); - CloseHandle(FileHandle); + ShowMessages("err, unable to read or map the file (%x)\n", PlatformGetLastError()); + return FALSE; + } - ShowMessages("err, unable to create map view of file (%x)\n", GetLastError()); + FileSize.QuadPart = (LONGLONG)MappedSize; + + if (FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER)) + { + PlatformUnmapFile(BaseAddr, MappedSize, FileHandle); return FALSE; } @@ -4649,9 +4591,7 @@ Finished: // // Unmap and close the handles // - UnmapViewOfFile(BaseAddr); - CloseHandle(MapObjectHandle); - CloseHandle(FileHandle); + PlatformUnmapFile(BaseAddr, MappedSize, FileHandle); return Result; } diff --git a/hyperdbg/libhyperdbg/header/pe-parser.h b/hyperdbg/libhyperdbg/header/pe-parser.h index af5ca293..fb7d32c6 100644 --- a/hyperdbg/libhyperdbg/header/pe-parser.h +++ b/hyperdbg/libhyperdbg/header/pe-parser.h @@ -46,12 +46,12 @@ typedef struct _PE_RAW_SECTION_RANGE ////////////////////////////////////////////////// BOOLEAN -PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, - const CHAR * SectionToShow, - BOOLEAN Is32Bit); +PeShowSectionInformationAndDump(const wchar_t * AddressOfFile, + const CHAR * SectionToShow, + BOOLEAN Is32Bit); BOOLEAN -PeIsPE32BitOr64Bit(const WCHAR * AddressOfFile, PBOOLEAN Is32Bit); +PeIsPE32BitOr64Bit(const wchar_t * AddressOfFile, PBOOLEAN Is32Bit); UINT32 PeGetSyscallNumber(LPCSTR NtFunctionName); From 7586303ee86c1fd17b369e8e5e26caf0f3c1a0bd Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 15 Jun 2026 15:57:57 +0200 Subject: [PATCH 28/57] revert w_char types --- hyperdbg/include/platform/user/code/platform-lib-calls.c | 4 ++-- .../include/platform/user/header/platform-lib-calls.h | 4 ++-- .../libhyperdbg/code/debugger/user-level/pe-parser.cpp | 8 ++++---- hyperdbg/libhyperdbg/header/pe-parser.h | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index d6241284..659ecbc6 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -389,7 +389,7 @@ PlatformWriteConsole(const VOID * Buffer, DWORD NumberOfBytes) * @return HANDLE to the opened file, or INVALID_HANDLE_VALUE on failure */ HANDLE -PlatformOpenFileForWriting(const wchar_t * Path) +PlatformOpenFileForWriting(const WCHAR * Path) { #if defined(_WIN32) return CreateFileW(Path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); @@ -460,7 +460,7 @@ PlatformCloseFile(HANDLE FileHandle) * @return VOID* base address of the mapped file, or NULL on failure */ VOID * -PlatformMapFileReadOnly(const wchar_t * Path, PSIZE_T OutFileSize, PHANDLE OutFileHandle) +PlatformMapFileReadOnly(const WCHAR * Path, PSIZE_T OutFileSize, PHANDLE OutFileHandle) { #if defined(_WIN32) HANDLE FileHandle; diff --git a/hyperdbg/include/platform/user/header/platform-lib-calls.h b/hyperdbg/include/platform/user/header/platform-lib-calls.h index d24d2a91..89835a72 100644 --- a/hyperdbg/include/platform/user/header/platform-lib-calls.h +++ b/hyperdbg/include/platform/user/header/platform-lib-calls.h @@ -97,7 +97,7 @@ PlatformWriteConsole(const VOID * Buffer, DWORD NumberOfBytes); // FILE I/O // HANDLE -PlatformOpenFileForWriting(const wchar_t * Path); +PlatformOpenFileForWriting(const WCHAR * Path); BOOLEAN PlatformWriteFile(HANDLE FileHandle, const VOID * Buffer, DWORD NumberOfBytes); @@ -113,7 +113,7 @@ PlatformCloseFile(HANDLE FileHandle); // PlatformReadFileAtOffset; release everything with PlatformUnmapFile. // VOID * -PlatformMapFileReadOnly(const wchar_t * Path, PSIZE_T OutFileSize, PHANDLE OutFileHandle); +PlatformMapFileReadOnly(const WCHAR * Path, PSIZE_T OutFileSize, PHANDLE OutFileHandle); BOOLEAN PlatformReadFileAtOffset(HANDLE FileHandle, UINT64 Offset, VOID * Buffer, DWORD NumberOfBytes, LPDWORD BytesRead); diff --git a/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp b/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp index a9c8078b..ce0dfc4a 100644 --- a/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp @@ -3724,9 +3724,9 @@ PeHexDump(CHAR * Ptr, SIZE_T Size, ULONGLONG SecAddress) * @return BOOLEAN */ BOOLEAN -PeShowSectionInformationAndDump(const wchar_t * AddressOfFile, - const CHAR * SectionToShow, - BOOLEAN Is32Bit) +PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, + const CHAR * SectionToShow, + BOOLEAN Is32Bit) { RICH_HEADER_INFO PeFileRichHeaderInfo {0}; RICH_HEADER PeFileRichHeader {0}; @@ -4480,7 +4480,7 @@ Finished: * @return BOOLEAN */ BOOLEAN -PeIsPE32BitOr64Bit(const wchar_t * AddressOfFile, PBOOLEAN Is32Bit) +PeIsPE32BitOr64Bit(const WCHAR * AddressOfFile, PBOOLEAN Is32Bit) { BOOLEAN Result = FALSE; HANDLE FileHandle = INVALID_HANDLE_VALUE; // Open file handle diff --git a/hyperdbg/libhyperdbg/header/pe-parser.h b/hyperdbg/libhyperdbg/header/pe-parser.h index fb7d32c6..af5ca293 100644 --- a/hyperdbg/libhyperdbg/header/pe-parser.h +++ b/hyperdbg/libhyperdbg/header/pe-parser.h @@ -46,12 +46,12 @@ typedef struct _PE_RAW_SECTION_RANGE ////////////////////////////////////////////////// BOOLEAN -PeShowSectionInformationAndDump(const wchar_t * AddressOfFile, - const CHAR * SectionToShow, - BOOLEAN Is32Bit); +PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, + const CHAR * SectionToShow, + BOOLEAN Is32Bit); BOOLEAN -PeIsPE32BitOr64Bit(const wchar_t * AddressOfFile, PBOOLEAN Is32Bit); +PeIsPE32BitOr64Bit(const WCHAR * AddressOfFile, PBOOLEAN Is32Bit); UINT32 PeGetSyscallNumber(LPCSTR NtFunctionName); From 10576b064a9824ab655e7bea0e35e9556ee68ca4 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Mon, 15 Jun 2026 16:21:12 +0200 Subject: [PATCH 29/57] Added typecast for wchar, mismatch on Linux and Windows --- .../debugger/commands/meta-commands/dump.cpp | 12 ++++++++ .../debugger/commands/meta-commands/pe.cpp | 28 +++++++++++++++++-- .../commands/meta-commands/restart.cpp | 22 +++++++++++++-- .../debugger/commands/meta-commands/start.cpp | 22 +++++++++++++-- 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp index 0f8d0e5e..2ac5bc92 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp @@ -226,7 +226,19 @@ CommandDump(vector CommandTokens, string Command) // // Create or open the file for writing the dump file // + // TEMPORARY LINUX SHIM (same as in pe.cpp): std::wstring stores native + // wchar_t (4 bytes on Linux), but the HyperDbg WCHAR type is 2 bytes + // (UINT16) and PlatformOpenFileForWriting takes a const WCHAR *. On Linux + // the cast is a bogus 2-byte reinterpretation, acceptable only because + // Linux file I/O is still stubbed (the path is never opened). On Windows + // WCHAR == wchar_t, so it is a plain correct pointer. TODO(Linux): remove + // once real file I/O lands and convert wchar_t -> 2-byte WCHAR properly. + // +#ifdef __linux__ + DumpFileHandle = PlatformOpenFileForWriting((const WCHAR *)Filepath.c_str()); +#else DumpFileHandle = PlatformOpenFileForWriting(Filepath.c_str()); +#endif if (DumpFileHandle == INVALID_HANDLE_VALUE) { diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pe.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pe.cpp index 7fb5a8f4..1d9fe58e 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pe.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pe.cpp @@ -106,10 +106,32 @@ CommandPe(vector CommandTokens, string Command) // StringToWString(Filepath, TempFilePath); + // + // TEMPORARY LINUX SHIM: + // std::wstring stores native wchar_t, which is 4 bytes on Linux, but the + // HyperDbg WCHAR type is 2 bytes (UINT16) and the PE-parser path APIs take + // a const WCHAR *. The cast below exists ONLY so the project compiles on + // Linux. On Linux it produces a bogus 2-byte reinterpretation of the 4-byte + // buffer; that is acceptable for now only because Linux file I/O is still + // stubbed (the path is never actually opened). On Windows WCHAR == wchar_t, + // so it is a plain, correct pointer with no reinterpretation. + // + // TODO (Linux): remove this cast once real Linux file I/O lands. The proper + // fix is to convert the 4-byte wchar_t path into a 2-byte WCHAR/UTF-16 + // buffer (e.g. a std::wstring -> WCHAR helper) and pass that, so the PE + // parser receives a valid path. The same conversion is needed by + // PlatformMapFileReadOnly / PlatformOpenFileForWriting. + // +#ifdef __linux__ + const WCHAR * FilepathW = (const WCHAR *)Filepath.c_str(); +#else + const WCHAR * FilepathW = Filepath.c_str(); +#endif + // // Detect whether PE is 32-bit or 64-bit // - if (!PeIsPE32BitOr64Bit(Filepath.c_str(), &Is32Bit)) + if (!PeIsPE32BitOr64Bit(FilepathW, &Is32Bit)) { // // File was invalid, the error message is shown in the above function @@ -122,10 +144,10 @@ CommandPe(vector CommandTokens, string Command) // if (!ShowDumpOfSection) { - PeShowSectionInformationAndDump(Filepath.c_str(), NULL, Is32Bit); + PeShowSectionInformationAndDump(FilepathW, NULL, Is32Bit); } else { - PeShowSectionInformationAndDump(Filepath.c_str(), GetCaseSensitiveStringFromCommandToken(CommandTokens.at(2)).c_str(), Is32Bit); + PeShowSectionInformationAndDump(FilepathW, GetCaseSensitiveStringFromCommandToken(CommandTokens.at(2)).c_str(), Is32Bit); } } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/restart.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/restart.cpp index c0cc2d96..626856b7 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/restart.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/restart.cpp @@ -89,15 +89,33 @@ CommandRestart(vector CommandTokens, string Command) // if (g_StartCommandPathAndArguments.empty()) { + // + // TEMPORARY LINUX SHIM (same as in pe.cpp/dump.cpp): the path is a + // std::wstring of native wchar_t (4 bytes on Linux), but UdAttachToProcess + // takes a 2-byte HyperDbg WCHAR * (UINT16). The cast is a bogus 2-byte + // reinterpretation on Linux, acceptable only because the user-debugger + // path is still stubbed there. On Windows WCHAR == wchar_t, so it is a + // plain correct pointer. TODO(Linux): convert wchar_t -> 2-byte WCHAR + // properly once the Linux user-debugger path is real. + // UdAttachToProcess(NULL, - g_StartCommandPath.c_str(), + (WCHAR *)g_StartCommandPath.c_str(), NULL, FALSE); } else { + // + // TEMPORARY LINUX SHIM (same as in pe.cpp/dump.cpp): the path/arguments + // are std::wstring of native wchar_t (4 bytes on Linux), but + // UdAttachToProcess takes 2-byte HyperDbg WCHAR * (UINT16). The casts are + // a bogus 2-byte reinterpretation on Linux, acceptable only because the + // user-debugger path is still stubbed there. On Windows WCHAR == wchar_t, + // so they are plain correct pointers. TODO(Linux): convert wchar_t -> + // 2-byte WCHAR properly once the Linux user-debugger path is real. + // UdAttachToProcess(NULL, - g_StartCommandPath.c_str(), + (WCHAR *)g_StartCommandPath.c_str(), (WCHAR *)g_StartCommandPathAndArguments.c_str(), FALSE); } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/start.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/start.cpp index 6141f4a0..c628cdb2 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/start.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/start.cpp @@ -147,8 +147,17 @@ CommandStart(vector CommandTokens, string Command) // if (Arguments.empty()) { + // + // TEMPORARY LINUX SHIM (same as in pe.cpp/dump.cpp): the path is a + // std::wstring of native wchar_t (4 bytes on Linux), but UdAttachToProcess + // takes a 2-byte HyperDbg WCHAR * (UINT16). The cast is a bogus 2-byte + // reinterpretation on Linux, acceptable only because the user-debugger + // path is still stubbed there. On Windows WCHAR == wchar_t, so it is a + // plain correct pointer. TODO(Linux): convert wchar_t -> 2-byte WCHAR + // properly once the Linux user-debugger path is real. + // UdAttachToProcess(NULL, - g_StartCommandPath.c_str(), + (WCHAR *)g_StartCommandPath.c_str(), NULL, FALSE); } @@ -164,8 +173,17 @@ CommandStart(vector CommandTokens, string Command) // StringToWString(g_StartCommandPathAndArguments, Arguments); + // + // TEMPORARY LINUX SHIM (same as in pe.cpp/dump.cpp): the path/arguments + // are std::wstring of native wchar_t (4 bytes on Linux), but + // UdAttachToProcess takes 2-byte HyperDbg WCHAR * (UINT16). The casts are + // a bogus 2-byte reinterpretation on Linux, acceptable only because the + // user-debugger path is still stubbed there. On Windows WCHAR == wchar_t, + // so they are plain correct pointers. TODO(Linux): convert wchar_t -> + // 2-byte WCHAR properly once the Linux user-debugger path is real. + // UdAttachToProcess(NULL, - g_StartCommandPath.c_str(), + (WCHAR *)g_StartCommandPath.c_str(), (WCHAR *)g_StartCommandPathAndArguments.c_str(), FALSE); } From 622db82af5f535446a1f356e793a54d0fd8da5a4 Mon Sep 17 00:00:00 2001 From: sina Date: Mon, 15 Jun 2026 22:24:36 +0200 Subject: [PATCH 30/57] remove old pdbex and zydis submodules --- .gitmodules | 6 ------ examples/kernel/hyperdbg_driver/code/driver/Ioctl.c | 2 +- examples/user/hyperdbg_app/hyperdbg_app.vcxproj | 4 ++-- hyperdbg/dependencies/pdbex | 1 - hyperdbg/dependencies/zydis | 1 - hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj | 4 ++-- hyperdbg/hyperdbg-test/hyperdbg-test.vcxproj | 6 +++--- hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 4 ++-- hyperdbg/script-engine/script-engine.vcxproj | 4 ++-- hyperdbg/symbol-parser/symbol-parser.vcxproj | 6 +++--- 10 files changed, 15 insertions(+), 23 deletions(-) delete mode 160000 hyperdbg/dependencies/pdbex delete mode 160000 hyperdbg/dependencies/zydis diff --git a/.gitmodules b/.gitmodules index 8cd3de74..42fb5203 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "hyperdbg/dependencies/pdbex"] - path = hyperdbg/dependencies/pdbex - url = https://github.com/HyperDbg/pdbex.git [submodule "hyperdbg/tests/script-engine-test"] path = hyperdbg/tests/script-engine-test url = https://github.com/HyperDbg/script-engine-test.git @@ -10,6 +7,3 @@ [submodule "hyperdbg/dependencies/ia32-doc"] path = hyperdbg/dependencies/ia32-doc url = https://github.com/HyperDbg/ia32-doc.git -[submodule "hyperdbg/dependencies/zydis"] - path = hyperdbg/dependencies/zydis - url = https://github.com/HyperDbg/zydis.git diff --git a/examples/kernel/hyperdbg_driver/code/driver/Ioctl.c b/examples/kernel/hyperdbg_driver/code/driver/Ioctl.c index 679a8aef..8d7b1c5c 100644 --- a/examples/kernel/hyperdbg_driver/code/driver/Ioctl.c +++ b/examples/kernel/hyperdbg_driver/code/driver/Ioctl.c @@ -34,7 +34,7 @@ DrvDispatchIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) // // DO NOT CHANGE CALLING OF THE FOLLOWING FUNCTION // - PoolManagerCheckAndPerformAllocationAndDeallocation(); + // PoolManagerCheckAndPerformAllocationAndDeallocation(); if (g_VmmInitialized) { diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj index 305d9cc1..3072d9ac 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj @@ -30,13 +30,13 @@ Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode diff --git a/hyperdbg/dependencies/pdbex b/hyperdbg/dependencies/pdbex deleted file mode 160000 index 42a9fbb8..00000000 --- a/hyperdbg/dependencies/pdbex +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 42a9fbb821c7d140c5e00da60f5a5887c2fd8444 diff --git a/hyperdbg/dependencies/zydis b/hyperdbg/dependencies/zydis deleted file mode 160000 index e61f5933..00000000 --- a/hyperdbg/dependencies/zydis +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e61f59332ce49f8853006573ca853e404fafdd08 diff --git a/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj b/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj index 29d0f676..958dcc98 100644 --- a/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj +++ b/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj @@ -22,14 +22,14 @@ Application true - v143 + v145 Unicode false Application false - v143 + v145 true Unicode false diff --git a/hyperdbg/hyperdbg-test/hyperdbg-test.vcxproj b/hyperdbg/hyperdbg-test/hyperdbg-test.vcxproj index 1ce467c7..b50f7554 100644 --- a/hyperdbg/hyperdbg-test/hyperdbg-test.vcxproj +++ b/hyperdbg/hyperdbg-test/hyperdbg-test.vcxproj @@ -21,14 +21,14 @@ Application true - v143 + v145 Unicode false Application false - v143 + v145 true Unicode false @@ -134,4 +134,4 @@ - + \ No newline at end of file diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index 7a9c8171..309a9efa 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -21,14 +21,14 @@ DynamicLibrary true - v143 + v145 NotSet false DynamicLibrary false - v143 + v145 true NotSet false diff --git a/hyperdbg/script-engine/script-engine.vcxproj b/hyperdbg/script-engine/script-engine.vcxproj index dfe4b2d0..e6abe194 100644 --- a/hyperdbg/script-engine/script-engine.vcxproj +++ b/hyperdbg/script-engine/script-engine.vcxproj @@ -21,14 +21,14 @@ DynamicLibrary true - v143 + v145 Unicode false DynamicLibrary false - v143 + v145 true Unicode diff --git a/hyperdbg/symbol-parser/symbol-parser.vcxproj b/hyperdbg/symbol-parser/symbol-parser.vcxproj index b087b735..62dc6ef9 100644 --- a/hyperdbg/symbol-parser/symbol-parser.vcxproj +++ b/hyperdbg/symbol-parser/symbol-parser.vcxproj @@ -21,13 +21,13 @@ DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode @@ -119,4 +119,4 @@ - + \ No newline at end of file From 70954f89b478de9ac7ff054895d80587fdd86af4 Mon Sep 17 00:00:00 2001 From: sina Date: Mon, 15 Jun 2026 22:55:40 +0200 Subject: [PATCH 31/57] add v145 platform of zydis and pdbex --- .gitmodules | 6 ++++++ hyperdbg/dependencies/pdbex | 1 + hyperdbg/dependencies/zydis | 1 + 3 files changed, 8 insertions(+) create mode 160000 hyperdbg/dependencies/pdbex create mode 160000 hyperdbg/dependencies/zydis diff --git a/.gitmodules b/.gitmodules index 42fb5203..bc4340e1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,9 @@ [submodule "hyperdbg/dependencies/ia32-doc"] path = hyperdbg/dependencies/ia32-doc url = https://github.com/HyperDbg/ia32-doc.git +[submodule "hyperdbg/dependencies/pdbex"] + path = hyperdbg/dependencies/pdbex + url = https://github.com/HyperDbg/pdbex.git +[submodule "hyperdbg/dependencies/zydis"] + path = hyperdbg/dependencies/zydis + url = https://github.com/HyperDbg/zydis.git diff --git a/hyperdbg/dependencies/pdbex b/hyperdbg/dependencies/pdbex new file mode 160000 index 00000000..0e114472 --- /dev/null +++ b/hyperdbg/dependencies/pdbex @@ -0,0 +1 @@ +Subproject commit 0e1144729a5b2d737cce11d1c0e083033630fff8 diff --git a/hyperdbg/dependencies/zydis b/hyperdbg/dependencies/zydis new file mode 160000 index 00000000..e910df2f --- /dev/null +++ b/hyperdbg/dependencies/zydis @@ -0,0 +1 @@ +Subproject commit e910df2fa54273b1a489b39771d331036e8de2db From 0f2a339e0bbcc9e0722b9a18b6231387b86467aa Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:34:09 +0300 Subject: [PATCH 32/57] Added VPID check support --- hyperdbg/hyperhv/code/vmm/ept/Ept.c | 12 +++++++++++- hyperdbg/hyperhv/code/vmm/ept/Vpid.c | 6 ++++++ hyperdbg/hyperhv/code/vmm/vmx/Vmx.c | 18 ++++++++++-------- .../hyperhv/header/globals/GlobalVariables.h | 5 +++++ 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/hyperdbg/hyperhv/code/vmm/ept/Ept.c b/hyperdbg/hyperhv/code/vmm/ept/Ept.c index f237b710..00d81777 100644 --- a/hyperdbg/hyperhv/code/vmm/ept/Ept.c +++ b/hyperdbg/hyperhv/code/vmm/ept/Ept.c @@ -23,7 +23,7 @@ EptCheckFeatures(VOID) { IA32_VMX_EPT_VPID_CAP_REGISTER VpidRegister; IA32_MTRR_DEF_TYPE_REGISTER MTRRDefType; - + g_IsVpidSupported = FALSE; VpidRegister.AsUInt = CpuReadMsr(IA32_VMX_EPT_VPID_CAP); MTRRDefType.AsUInt = CpuReadMsr(IA32_MTRR_DEF_TYPE); @@ -37,6 +37,16 @@ EptCheckFeatures(VOID) LogDebugInfo("The processor doesn't report advanced VM-exit information for EPT violations"); } + if (VpidRegister.Invvpid && + VpidRegister.InvvpidAllContexts && + VpidRegister.InvvpidIndividualAddress && + VpidRegister.InvvpidSingleContext && + VpidRegister.InvvpidSingleContextRetainGlobals) + { + g_IsVpidSupported = TRUE; + LogDebugInfo("The processor supports VPID"); + } + if (!VpidRegister.ExecuteOnlyPages) { g_CompatibilityCheck.ExecuteOnlySupport = FALSE; diff --git a/hyperdbg/hyperhv/code/vmm/ept/Vpid.c b/hyperdbg/hyperhv/code/vmm/ept/Vpid.c index a4874c4d..cd587b57 100644 --- a/hyperdbg/hyperhv/code/vmm/ept/Vpid.c +++ b/hyperdbg/hyperhv/code/vmm/ept/Vpid.c @@ -25,6 +25,12 @@ VpidInvvpid(INVVPID_TYPE Type, INVVPID_DESCRIPTOR * Descriptor) INVVPID_DESCRIPTOR * TargetDescriptor = NULL; INVVPID_DESCRIPTOR ZeroDescriptor = {0}; + // No need to do anything if VPID is not supported. + if (!g_IsVpidSupported) + { + return; + } + if (!Descriptor) { TargetDescriptor = &ZeroDescriptor; diff --git a/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c b/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c index ab4eab29..3db6b7b7 100644 --- a/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c +++ b/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c @@ -733,15 +733,17 @@ VmxSetupVmcs(VIRTUAL_MACHINE_STATE * VCpu, PVOID GuestStack) LogDebugInfo("CPU Based VM Exec Controls (Based on %s) : 0x%x", VmxBasicMsr.VmxControls ? "IA32_VMX_TRUE_PROCBASED_CTLS" : "IA32_VMX_PROCBASED_CTLS", CpuBasedVmExecControls); + UINT32 SecondaryProcBasedVmExecControlsFlags = IA32_VMX_PROCBASED_CTLS2_ENABLE_RDTSCP_FLAG | + IA32_VMX_PROCBASED_CTLS2_ENABLE_EPT_FLAG | + IA32_VMX_PROCBASED_CTLS2_ENABLE_INVPCID_FLAG | + IA32_VMX_PROCBASED_CTLS2_ENABLE_XSAVES_FLAG | + IA32_VMX_PROCBASED_CTLS2_ENABLE_USER_WAIT_PAUSE_FLAG; - SecondaryProcBasedVmExecControls = HvAdjustControls( - IA32_VMX_PROCBASED_CTLS2_ENABLE_RDTSCP_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_EPT_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_INVPCID_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_XSAVES_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_VPID_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_USER_WAIT_PAUSE_FLAG, - IA32_VMX_PROCBASED_CTLS2); + if (g_IsVpidSupported) + { + SecondaryProcBasedVmExecControlsFlags |= IA32_VMX_PROCBASED_CTLS2_ENABLE_VPID_FLAG; + } + SecondaryProcBasedVmExecControls = HvAdjustControls(SecondaryProcBasedVmExecControlsFlags, IA32_VMX_PROCBASED_CTLS2); VmxVmwrite64(VMCS_CTRL_SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, SecondaryProcBasedVmExecControls); diff --git a/hyperdbg/hyperhv/header/globals/GlobalVariables.h b/hyperdbg/hyperhv/header/globals/GlobalVariables.h index 8a4681bb..f9f6dafe 100644 --- a/hyperdbg/hyperhv/header/globals/GlobalVariables.h +++ b/hyperdbg/hyperhv/header/globals/GlobalVariables.h @@ -208,3 +208,8 @@ UINT64 g_PageFaultInjectionAddressTo; * */ UINT32 g_PageFaultInjectionErrorCode; + +/** + * @brief Whether VPID is supported or not + */ +BOOLEAN g_IsVpidSupported; \ No newline at end of file From e56730ee2534db1a39233f7cdd6b011e7c810dac Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:45:32 +0300 Subject: [PATCH 33/57] Added check for whether hyper-v is the top level hypervisor --- hyperdbg/hyperhv/code/vmm/ept/Ept.c | 3 ++- hyperdbg/hyperhv/code/vmm/vmx/Vmx.c | 22 ++++++++++++++++++++++ hyperdbg/hyperhv/header/vmm/vmx/Vmx.h | 9 +++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/hyperdbg/hyperhv/code/vmm/ept/Ept.c b/hyperdbg/hyperhv/code/vmm/ept/Ept.c index 00d81777..d2c73af6 100644 --- a/hyperdbg/hyperhv/code/vmm/ept/Ept.c +++ b/hyperdbg/hyperhv/code/vmm/ept/Ept.c @@ -41,7 +41,8 @@ EptCheckFeatures(VOID) VpidRegister.InvvpidAllContexts && VpidRegister.InvvpidIndividualAddress && VpidRegister.InvvpidSingleContext && - VpidRegister.InvvpidSingleContextRetainGlobals) + VpidRegister.InvvpidSingleContextRetainGlobals && + !VmxIsTopLevelHypervisorHyperV()) { g_IsVpidSupported = TRUE; LogDebugInfo("The processor supports VPID"); diff --git a/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c b/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c index 3db6b7b7..fa8c432b 100644 --- a/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c +++ b/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c @@ -1757,3 +1757,25 @@ VmxCompatibleMemcmp(const CHAR * Address1, const CHAR * Address2, SIZE_T Count) CpuWriteCr3(OriginalCr3.Flags); return Result; } + +/** + * @brief checks if the current top level hypervisor is Hyper-V + * + * + * @return BOOLEAN TRUE indicates that the current top level hypervisor is Hyper-V, FALSE otherwise. + */ +BOOLEAN +VmxIsTopLevelHypervisorHyperV() { + int processorFeatures[4] = {0}; + __cpuidex(processorFeatures, CPUID_PROCESSOR_AND_PROCESSOR_FEATURE_IDENTIFIERS, 0); + + if (!((ULONG)(processorFeatures[2]) & HYPERV_HYPERVISOR_PRESENT_BIT)) + return FALSE; + + int hypervisorVendor[4] = {0}; + __cpuidex(hypervisorVendor, HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS, 0); + + return (ULONG)(hypervisorVendor[1]) == HYPERV_CPUID_VENDOR_MICROSOFT_EBX && + (ULONG)(hypervisorVendor[2]) == HYPERV_CPUID_VENDOR_MICROSOFT_ECX && + (ULONG)(hypervisorVendor[3]) == HYPERV_CPUID_VENDOR_MICROSOFT_EDX; +} \ No newline at end of file diff --git a/hyperdbg/hyperhv/header/vmm/vmx/Vmx.h b/hyperdbg/hyperhv/header/vmm/vmx/Vmx.h index 0ff36878..2cb7b05d 100644 --- a/hyperdbg/hyperhv/header/vmm/vmx/Vmx.h +++ b/hyperdbg/hyperhv/header/vmm/vmx/Vmx.h @@ -205,6 +205,12 @@ enum HYPERCALL_CODE HvCallFlushGuestPhysicalAddressList = 0x00B0 }; +/** @brief Hyper-V CPUID leaves +*/ +#define HYPERV_CPUID_VENDOR_MICROSOFT_EBX 0x7263694d // "Micr" +#define HYPERV_CPUID_VENDOR_MICROSOFT_ECX 0x666f736f // "osof" +#define HYPERV_CPUID_VENDOR_MICROSOFT_EDX 0x76482074 // "t Hv" + ////////////////////////////////////////////////// // Enums // ////////////////////////////////////////////////// @@ -229,6 +235,9 @@ typedef enum _MOV_TO_DEBUG_REG // Functions // ////////////////////////////////////////////////// +BOOLEAN +VmxIsTopLevelHypervisorHyperV(); + BOOLEAN VmxCheckVmxSupport(); From e94a86fcfbe6377c107d3f5e02371b9067f8238c Mon Sep 17 00:00:00 2001 From: sina Date: Fri, 19 Jun 2026 18:45:50 +0200 Subject: [PATCH 34/57] add vs2026 workflow and temporarily disabling CI CD --- .github/workflows/vs2022.yml | 47 +++++++------- .github/workflows/vs2026.yml | 116 +++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/vs2026.yml diff --git a/.github/workflows/vs2022.yml b/.github/workflows/vs2022.yml index 287cf15e..aa834a05 100644 --- a/.github/workflows/vs2022.yml +++ b/.github/workflows/vs2022.yml @@ -1,26 +1,31 @@ name: vs2022-ci on: - push: - branches: - - master - - dev - tags: - - 'v*' - paths-ignore: - - '.gitignore' - - '.gitattributes' - - '**.cmd' - - '**.bat' - - '**.md' - - pull_request: - paths-ignore: - - '.gitignore' - - '.gitattributes' - - '**.cmd' - - '**.bat' - - '**.md' + workflow_dispatch: + +# Disabled, uncomment the below line and remove the above lines to activate +# on: +# push: +# branches: +# - master +# - dev +# tags: +# - 'v*' +# paths-ignore: +# - '.gitignore' +# - '.gitattributes' +# - '**.cmd' +# - '**.bat' +# - '**.md' +# +# pull_request: +# paths-ignore: +# - '.gitignore' +# - '.gitattributes' +# - '**.cmd' +# - '**.bat' +# - '**.md' + env: # WDK for Windows 11, version 22H2 WDK_URL: https://go.microsoft.com/fwlink/?linkid=2196230 @@ -73,7 +78,7 @@ jobs: # working-directory: ${{env.GITHUB_WORKSPACE}} # run: msbuild /m /p:Configuration="Release MT" /p:Platform=${{matrix.PLATFORM}} /target:zydis /target:zycore /p:OutDir=${{ github.workspace }}/hyperdbg/libraries/zydis/ ${{ github.workspace }}/hyperdbg/dependencies/zydis/msvc/Zydis.sln - - name: Build Hyperdbg solution + - name: Build HyperDbg solution working-directory: ${{env.GITHUB_WORKSPACE}} run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.PLATFORM}} ${{env.SOLUTION_FILE_PATH}} diff --git a/.github/workflows/vs2026.yml b/.github/workflows/vs2026.yml new file mode 100644 index 00000000..af489992 --- /dev/null +++ b/.github/workflows/vs2026.yml @@ -0,0 +1,116 @@ +name: vs2026-ci + +on: + workflow_dispatch: + +# Disabled, uncomment the below line and remove the above lines to activate +#on: +# push: +# branches: +# - master +# - dev +# tags: +# - 'v*' +# paths-ignore: +# - '.gitignore' +# - '.gitattributes' +# - '**.cmd' +# - '**.bat' +# - '**.md' +# +# pull_request: +# paths-ignore: +# - '.gitignore' +# - '.gitattributes' +# - '**.cmd' +# - '**.bat' +# - '**.md' + +env: + # Updated WDK URL for VS 2026 + SDK_URL: https://go.microsoft.com/fwlink/?linkid=2361309 + WDK_URL: https://go.microsoft.com/fwlink/?LinkId=2362091 + + SOLUTION_FILE_PATH: ./hyperdbg/hyperdbg.sln + RELEASE_ZIP_FILE_NAME: hyperdbg + BUILD_BIN_DIR: ./hyperdbg/build/bin/ + +jobs: + win-amd64-build: + runs-on: windows-2025-vs2026 + strategy: + matrix: + BUILD_CONFIGURATION: [release, debug] + PLATFORM: [x64] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Checkout submodules recursively + run: git submodule update --init --recursive --remote + + - name: Setup SDK for VS 2026 + run: | + $sdkSetupPath = "$Env:TEMP\sdksetup.exe" + (New-Object Net.WebClient).DownloadFile('${{env.SDK_URL}}', $sdkSetupPath) + Start-Process -FilePath $sdkSetupPath -ArgumentList "/quiet" -NoNewWindow -Wait + + - name: Setup WDK for VS 2026 + run: | + $wdkSetupPath = "$Env:TEMP\wdksetup.exe" + (New-Object Net.WebClient).DownloadFile('${{env.WDK_URL}}', $wdkSetupPath) + Start-Process -FilePath $wdkSetupPath -ArgumentList "/quiet" -NoNewWindow -Wait + + - name: List SDK folders 1 + shell: pwsh + run: | + Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\Include" + + - name: List SDK folders 2 + shell: pwsh + run: | + Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\Include\10.0.28000.0" + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v3 + + - name: Build HyperDbg solution + working-directory: ${{env.GITHUB_WORKSPACE}} + run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.PLATFORM}} /p:EnableInfVerif=false /p:EnableDriverApiValidation=false ${{env.SOLUTION_FILE_PATH}} + + - name: Upload build directory + uses: actions/upload-artifact@v4.4.0 + with: + name: build_files_${{matrix.BUILD_CONFIGURATION}} + path: ${{ env.BUILD_BIN_DIR }} + + deploy-release: + name: Deploy release + needs: win-amd64-build + runs-on: windows-2025-vs2026 + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Download build files from "build" job + uses: actions/download-artifact@v4.1.7 + with: + name: build_files_release + path: ${{ env.BUILD_BIN_DIR }} + + - name: Archive release + uses: thedoctor0/zip-release@master + with: + path: ${{ env.BUILD_BIN_DIR }}release/ + type: 'zip' + filename: ${{env.RELEASE_ZIP_FILE_NAME}}-${{ github.ref_name }}.zip + + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + ${{env.RELEASE_ZIP_FILE_NAME}}-${{ github.ref_name }}.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 240feadc0ceee17c6ef2cf219d1318beae925f1c Mon Sep 17 00:00:00 2001 From: sina Date: Fri, 19 Jun 2026 20:16:50 +0200 Subject: [PATCH 35/57] check for hyper-v as the top level hypervisor --- CHANGELOG.md | 8 +++- hyperdbg/hyperhv/code/vmm/ept/Ept.c | 2 +- hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c | 34 ++++++++------- hyperdbg/hyperhv/code/vmm/vmx/Vmx.c | 42 ++++++++++++++----- .../hyperhv/header/globals/GlobalVariables.h | 7 +++- 5 files changed, 63 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8639a2a7..3ec2bdb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,11 +13,15 @@ New release of the HyperDbg Debugger. - Added Linux distinction and pragmas together with new platform functions ([link](https://github.com/HyperDbg/HyperDbg/commit/9c3e0ed23b5f4bb64bd305e546dd0e86ee1910d9)) - Added interfaces to Linux port for the serial devices ([link](https://github.com/HyperDbg/HyperDbg/commit/31b514f8df40d81d44ae07a2328c02360ce488c3)) - Added platform IOCTL interface ([link](https://github.com/HyperDbg/HyperDbg/pull/619)) +- Added typecast for wchar, mismatch on Linux and Windows ([link](https://github.com/HyperDbg/HyperDbg/commit/10576b064a9824ab655e7bea0e35e9556ee68ca4)) +- Added missing IOCTLs for Intel PT ([link](https://github.com/HyperDbg/HyperDbg/commit/df12e9fd79851c8f378ec82a45066da510da507a)) ### Changed -- Synthetic MSRs are commented for further tests -- Fix adding missing IOCTL for Intel PT ([link](https://github.com/HyperDbg/HyperDbg/commit/df12e9fd79851c8f378ec82a45066da510da507a)) +- Moved to Visual Studio 2026 ([link](https://github.com/HyperDbg/HyperDbg/pull/626)) +- Zydis and pdbex submodules are updated to VS2026 - Fix the new form of loading and unloading in the example app and PT app ([link](https://github.com/HyperDbg/HyperDbg/commit/9ad48d30dcf6b409ae86b2d08262584cd06f606e)) +- Check whether the top-level hypervisor is Hyper-V and disable/enable VPIDs based on that, thanks to [@Idov31](https://github.com/Idov31) ([link](https://github.com/HyperDbg/HyperDbg/pull/625)) +- Fix PT operation IOCTL buffer size ([link](https://github.com/HyperDbg/HyperDbg/commit/dabf132d31503843f90949f35f8dce4601d43126)) ## [0.19.0.0-beta] - 2026-06-10 New release of the HyperDbg Debugger. diff --git a/hyperdbg/hyperhv/code/vmm/ept/Ept.c b/hyperdbg/hyperhv/code/vmm/ept/Ept.c index d2c73af6..4295aca0 100644 --- a/hyperdbg/hyperhv/code/vmm/ept/Ept.c +++ b/hyperdbg/hyperhv/code/vmm/ept/Ept.c @@ -42,7 +42,7 @@ EptCheckFeatures(VOID) VpidRegister.InvvpidIndividualAddress && VpidRegister.InvvpidSingleContext && VpidRegister.InvvpidSingleContextRetainGlobals && - !VmxIsTopLevelHypervisorHyperV()) + !g_IsTopLevelHypervisorHyperV) { g_IsVpidSupported = TRUE; LogDebugInfo("The processor supports VPID"); diff --git a/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c b/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c index 36971745..2600002a 100644 --- a/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c +++ b/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c @@ -96,17 +96,19 @@ MsrHandleRdmsrVmexit(VIRTUAL_MACHINE_STATE * VCpu) // LogInfo("MSR read (RDMSR) VM-exit, MSR: %x, from: %llx", // TargetMsr, // VCpu->LastVmexitRip); + // - // Checking whether it is a synthetic MSR for Hyper-V. - // if (MsrHandleIsHypervSyntheticMsr(TargetMsr)) - // { - // Msr.Flags = CpuReadMsr(TargetMsr); + // Checking whether it is a synthetic MSR for Hyper-V // - // GuestRegs->rax = Msr.Fields.Low; - // GuestRegs->rdx = Msr.Fields.High; - // - // return; - // } + if (g_IsTopLevelHypervisorHyperV && MsrHandleIsHypervSyntheticMsr(TargetMsr)) + { + Msr.Flags = CpuReadMsr(TargetMsr); + + GuestRegs->rax = Msr.Fields.Low; + GuestRegs->rdx = Msr.Fields.High; + + return; + } // // Check for sanity of MSR if they're valid or they're for reserved range for WRMSR and RDMSR @@ -257,13 +259,15 @@ MsrHandleWrmsrVmexit(VIRTUAL_MACHINE_STATE * VCpu) // GuestRegs->rax, // GuestRegs->rdx, // VCpu->LastVmexitRip); + // - // Checking whether it is a synthetic MSR for Hyper-V. - // if (MsrHandleIsHypervSyntheticMsr(TargetMsr)) - // { - // CpuWriteMsr(TargetMsr, Msr.Flags); - // return; - // } + // Checking whether it is a synthetic MSR for Hyper-V + // + if (g_IsTopLevelHypervisorHyperV && MsrHandleIsHypervSyntheticMsr(TargetMsr)) + { + CpuWriteMsr(TargetMsr, Msr.Flags); + return; + } // // Check for sanity of MSR if they're valid or they're for reserved range for WRMSR and RDMSR diff --git a/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c b/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c index fa8c432b..1de5c030 100644 --- a/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c +++ b/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c @@ -274,6 +274,23 @@ VmxPerformVirtualizationOnAllCores() return FALSE; } + // + // Check if the top level hypervisor is Hyper-V or not + // + if (VmxIsTopLevelHypervisorHyperV()) + { + // + // The top-level hypervisor is hyper-v + // + g_IsTopLevelHypervisorHyperV = TRUE; + + LogDebugInfo(" Hyper-V is detected as the top-level hypervisor"); + } + else + { + LogDebugInfo(" Hyper-V is not detected as the top-level hypervisor"); + } + // // Allocate global variable to hold Ept State // @@ -1760,22 +1777,25 @@ VmxCompatibleMemcmp(const CHAR * Address1, const CHAR * Address2, SIZE_T Count) /** * @brief checks if the current top level hypervisor is Hyper-V - * + * * * @return BOOLEAN TRUE indicates that the current top level hypervisor is Hyper-V, FALSE otherwise. */ BOOLEAN -VmxIsTopLevelHypervisorHyperV() { - int processorFeatures[4] = {0}; - __cpuidex(processorFeatures, CPUID_PROCESSOR_AND_PROCESSOR_FEATURE_IDENTIFIERS, 0); +VmxIsTopLevelHypervisorHyperV() +{ + INT32 ProcessorFeatures[4] = {0}; + CpuCpuIdEx(ProcessorFeatures, CPUID_PROCESSOR_AND_PROCESSOR_FEATURE_IDENTIFIERS, 0); - if (!((ULONG)(processorFeatures[2]) & HYPERV_HYPERVISOR_PRESENT_BIT)) + if (!((ULONG)(ProcessorFeatures[2]) & HYPERV_HYPERVISOR_PRESENT_BIT)) + { return FALSE; + } - int hypervisorVendor[4] = {0}; - __cpuidex(hypervisorVendor, HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS, 0); + INT32 HypervisorVendor[4] = {0}; + CpuCpuIdEx(HypervisorVendor, HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS, 0); - return (ULONG)(hypervisorVendor[1]) == HYPERV_CPUID_VENDOR_MICROSOFT_EBX && - (ULONG)(hypervisorVendor[2]) == HYPERV_CPUID_VENDOR_MICROSOFT_ECX && - (ULONG)(hypervisorVendor[3]) == HYPERV_CPUID_VENDOR_MICROSOFT_EDX; -} \ No newline at end of file + return (ULONG)(HypervisorVendor[1]) == HYPERV_CPUID_VENDOR_MICROSOFT_EBX && + (ULONG)(HypervisorVendor[2]) == HYPERV_CPUID_VENDOR_MICROSOFT_ECX && + (ULONG)(HypervisorVendor[3]) == HYPERV_CPUID_VENDOR_MICROSOFT_EDX; +} diff --git a/hyperdbg/hyperhv/header/globals/GlobalVariables.h b/hyperdbg/hyperhv/header/globals/GlobalVariables.h index f9f6dafe..6eec6d2e 100644 --- a/hyperdbg/hyperhv/header/globals/GlobalVariables.h +++ b/hyperdbg/hyperhv/header/globals/GlobalVariables.h @@ -212,4 +212,9 @@ UINT32 g_PageFaultInjectionErrorCode; /** * @brief Whether VPID is supported or not */ -BOOLEAN g_IsVpidSupported; \ No newline at end of file +BOOLEAN g_IsVpidSupported; + +/** + * @brief Whether the top level hypervisor is Hyper-V or not + */ +BOOLEAN g_IsTopLevelHypervisorHyperV; \ No newline at end of file From 2d3941a43e56faefb8dba839f25264a2aaa48307 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Tue, 16 Jun 2026 15:17:13 +0200 Subject: [PATCH 36/57] signal handler platform independent API --- hyperdbg/CMakeLists.txt | 3 ++- .../include/platform/general/header/Environment.h | 15 +++++++++++++++ hyperdbg/libhyperdbg/CMakeLists.txt | 2 ++ .../code/debugger/core/interpreter.cpp | 2 +- hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 2 ++ hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters | 6 ++++++ hyperdbg/libhyperdbg/pch.h | 5 +++++ 7 files changed, 33 insertions(+), 2 deletions(-) diff --git a/hyperdbg/CMakeLists.txt b/hyperdbg/CMakeLists.txt index bf2267a7..ba5b3855 100644 --- a/hyperdbg/CMakeLists.txt +++ b/hyperdbg/CMakeLists.txt @@ -74,7 +74,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") add_subdirectory(script-engine) link_directories(libraries/zydis/user libraries/keystone/release-lib) add_subdirectory(libhyperdbg) -target_link_libraries(libhyperdbg Zycore Zydis script-engine keystone) +find_package(Threads REQUIRED) +target_link_libraries(libhyperdbg Zycore Zydis script-engine keystone Threads::Threads) add_subdirectory(hyperdbg-cli) target_link_libraries(hyperdbg-cli libhyperdbg) diff --git a/hyperdbg/include/platform/general/header/Environment.h b/hyperdbg/include/platform/general/header/Environment.h index 59619c1c..4abf4d21 100644 --- a/hyperdbg/include/platform/general/header/Environment.h +++ b/hyperdbg/include/platform/general/header/Environment.h @@ -47,6 +47,9 @@ // wchar_t is a C++ built-in but needs this header in C # include +// POSIX sleep primitives (usleep) backing the Win32 Sleep() shim below +# include + // Windows string/char types typedef char TCHAR; typedef char * LPTSTR; @@ -78,4 +81,16 @@ typedef void * HMODULE; // Win32 invalid handle sentinel (returned by the cross-platform file/serial wrappers) # define INVALID_HANDLE_VALUE ((HANDLE)(SIZE_T)-1) +// Win32 console-control event codes (kept at their Windows values so the +// shared BreakController() switch compiles unchanged). On Linux these are +// produced by the platform-signal layer from POSIX signals. +# define CTRL_C_EVENT 0 +# define CTRL_BREAK_EVENT 1 +# define CTRL_CLOSE_EVENT 2 +# define CTRL_LOGOFF_EVENT 5 +# define CTRL_SHUTDOWN_EVENT 6 + +// Win32 Sleep(milliseconds) -> POSIX usleep(microseconds) +# define Sleep(Milliseconds) usleep((useconds_t)(Milliseconds) * 1000) + #endif // HYPERDBG_ENV_LINUX diff --git a/hyperdbg/libhyperdbg/CMakeLists.txt b/hyperdbg/libhyperdbg/CMakeLists.txt index a0bfa2df..fac7603f 100644 --- a/hyperdbg/libhyperdbg/CMakeLists.txt +++ b/hyperdbg/libhyperdbg/CMakeLists.txt @@ -31,6 +31,7 @@ set(SourceFiles "../include/platform/user/code/platform-lib-calls.c" "../include/platform/user/code/platform-serial.c" "../include/platform/user/code/platform-ioctl.c" + "../include/platform/user/code/platform-signal.c" "../script-eval/code/Functions.c" "../script-eval/code/Keywords.c" "../script-eval/code/PseudoRegisters.c" @@ -175,6 +176,7 @@ set_source_files_properties( "../include/platform/user/code/platform-lib-calls.c" "../include/platform/user/code/platform-serial.c" "../include/platform/user/code/platform-ioctl.c" + "../include/platform/user/code/platform-signal.c" "../script-eval/code/Functions.c" "../script-eval/code/Keywords.c" "../script-eval/code/PseudoRegisters.c" diff --git a/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp b/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp index 5d8055e2..62938326 100644 --- a/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp @@ -1300,7 +1300,7 @@ InitializeDebugger() // // Register the CTRL+C and CTRL+BREAK Signals handler // - if (!SetConsoleCtrlHandler(BreakController, TRUE)) + if (!PlatformInstallCtrlHandler(BreakController)) { ShowMessages("err, when registering CTRL+C and CTRL+BREAK Signals " "handler\n"); diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index 309a9efa..e1a15be8 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -136,6 +136,7 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R + @@ -175,6 +176,7 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R + diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters index ad369cec..4e3ef59f 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters @@ -203,6 +203,9 @@ header\platform + + header\platform + header @@ -658,6 +661,9 @@ code\platform + + code\platform + code\app diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index 5b9d0c1c..b78b022d 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -166,6 +166,11 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; // #include "platform/user/header/platform-ioctl.h" +// +// Platform signal (cross-platform console-control / CTRL+C handler registration) +// +#include "platform/user/header/platform-signal.h" + // // Platform-specific intrinsics // From 81b16de62bba2a343d6a3b8d8931bb3d581ca391 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Tue, 16 Jun 2026 15:40:52 +0200 Subject: [PATCH 37/57] added files for platform API --- .../include/platform/general/header/nt-list.h | 145 ++++++++++++++++++ .../platform/user/code/platform-signal.c | 137 +++++++++++++++++ .../platform/user/header/platform-signal.h | 43 ++++++ .../code/debugger/core/debugger.cpp | 39 +++-- hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 1 + .../libhyperdbg/libhyperdbg.vcxproj.filters | 3 + hyperdbg/libhyperdbg/pch.h | 6 + 7 files changed, 359 insertions(+), 15 deletions(-) create mode 100644 hyperdbg/include/platform/general/header/nt-list.h create mode 100644 hyperdbg/include/platform/user/code/platform-signal.c create mode 100644 hyperdbg/include/platform/user/header/platform-signal.h diff --git a/hyperdbg/include/platform/general/header/nt-list.h b/hyperdbg/include/platform/general/header/nt-list.h new file mode 100644 index 00000000..f53f174f --- /dev/null +++ b/hyperdbg/include/platform/general/header/nt-list.h @@ -0,0 +1,145 @@ +/** + * @file nt-list.h + * @author Max Raulea (max.raulea@gmail.com) + * @brief Cross-platform NT-style intrusive doubly-linked list helpers + CONTAINING_RECORD + * @details The shared debugger code uses the NT LIST_ENTRY API (InitializeListHead, + * InsertHeadList, RemoveEntryList, CONTAINING_RECORD, ...). On Windows these + * come from (or, under USE_NATIVE_SDK_HEADERS, from the in-tree + * platform/user/header/Windows.h). Platforms whose system headers don't ship + * them (Linux) get them here, so the shared code compiles unchanged. + * + * These operate on the exact LIST_ENTRY {Flink, Blink} layout defined in + * SDK/headers/DataTypes.h, which is part of the kernel<->user IOCTL ABI -- + * that's why we mirror the NT list rather than reaching for sys/queue.h + * (different layout + a colliding LIST_ENTRY name). + * + * The body is compiled only where the OS headers don't already provide these + * (i.e. not on Windows), so including this unconditionally is safe. + * + * @version 0.1 + * @date 2026-06-16 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#ifndef _WIN32 + +# include // offsetof + +// +// Given the address of a LIST_ENTRY field embedded in a struct, recover the +// address of the containing struct. +// +# ifndef CONTAINING_RECORD +# define CONTAINING_RECORD(address, type, field) \ + ((type *)((char *)(address) - offsetof(type, field))) +# endif + +// +// Initialize a list head to the empty (points-to-self) state. +// +static inline void +InitializeListHead(PLIST_ENTRY ListHead) +{ + ListHead->Flink = ListHead->Blink = ListHead; +} + +// +// TRUE if the list has no entries. +// +static inline BOOLEAN +IsListEmpty(const LIST_ENTRY * ListHead) +{ + return (BOOLEAN)(ListHead->Flink == ListHead); +} + +// +// Unlink an entry. Returns TRUE if the list is now empty. +// +static inline BOOLEAN +RemoveEntryList(PLIST_ENTRY Entry) +{ + PLIST_ENTRY Flink = Entry->Flink; + PLIST_ENTRY Blink = Entry->Blink; + + Blink->Flink = Flink; + Flink->Blink = Blink; + + return (BOOLEAN)(Flink == Blink); +} + +// +// Unlink and return the first entry. +// +static inline PLIST_ENTRY +RemoveHeadList(PLIST_ENTRY ListHead) +{ + PLIST_ENTRY Entry = ListHead->Flink; + PLIST_ENTRY Flink = Entry->Flink; + + ListHead->Flink = Flink; + Flink->Blink = ListHead; + + return Entry; +} + +// +// Unlink and return the last entry. +// +static inline PLIST_ENTRY +RemoveTailList(PLIST_ENTRY ListHead) +{ + PLIST_ENTRY Entry = ListHead->Blink; + PLIST_ENTRY Blink = Entry->Blink; + + ListHead->Blink = Blink; + Blink->Flink = ListHead; + + return Entry; +} + +// +// Insert an entry at the tail of the list. +// +static inline void +InsertTailList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) +{ + PLIST_ENTRY Blink = ListHead->Blink; + + Entry->Flink = ListHead; + Entry->Blink = Blink; + Blink->Flink = Entry; + ListHead->Blink = Entry; +} + +// +// Insert an entry at the head of the list. +// +static inline void +InsertHeadList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) +{ + PLIST_ENTRY Flink = ListHead->Flink; + + Entry->Flink = Flink; + Entry->Blink = ListHead; + Flink->Blink = Entry; + ListHead->Flink = Entry; +} + +// +// Splice the entries of ListToAppend onto the tail of ListHead. +// +static inline void +AppendTailList(PLIST_ENTRY ListHead, PLIST_ENTRY ListToAppend) +{ + PLIST_ENTRY ListEnd = ListHead->Blink; + + ListHead->Blink->Flink = ListToAppend; + ListHead->Blink = ListToAppend->Blink; + ListToAppend->Blink->Flink = ListHead; + ListToAppend->Blink = ListEnd; +} + +#endif // !_WIN32 diff --git a/hyperdbg/include/platform/user/code/platform-signal.c b/hyperdbg/include/platform/user/code/platform-signal.c new file mode 100644 index 00000000..4125f38f --- /dev/null +++ b/hyperdbg/include/platform/user/code/platform-signal.c @@ -0,0 +1,137 @@ +/** + * @file platform-signal.c + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform implementation of the console-control handler + * @details See platform-signal.h. The Windows branch forwards to SetConsoleCtrlHandler. + * The Linux branch blocks the handled signals and dispatches them from a + * dedicated sigwait() thread. sigwait() (rather than a sigaction() handler) + * is used deliberately: BreakController calls ShowMessages (printf), socket + * I/O and Sleep, none of which are async-signal-safe. Running the handler on + * a normal thread woken by sigwait() sidesteps that entirely and mirrors the + * Windows model where the console-control handler also runs on its own thread. + * + * @version 0.1 + * @date 2026-06-16 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +#if defined(__linux__) +# include "../header/platform-signal.h" +#endif // defined(__linux__) + +#if defined(_WIN32) + +BOOLEAN +PlatformInstallCtrlHandler(PLATFORM_CTRL_HANDLER Handler) +{ + return (BOOLEAN)SetConsoleCtrlHandler((PHANDLER_ROUTINE)Handler, TRUE); +} + +#elif defined(__linux__) + +# include +# include + +// +// The installed handler, read by the dispatch thread. +// +static PLATFORM_CTRL_HANDLER g_PlatformCtrlHandler = NULL; + +// +// Map a POSIX signal onto the Win32 console-control event the shared handler +// understands. Only the two interactive break signals are mapped: +// SIGINT (CTRL+C) -> CTRL_C_EVENT +// SIGQUIT (CTRL+\) -> CTRL_BREAK_EVENT +// SIGTERM/SIGHUP are intentionally left at their default disposition so the +// process stays killable/terminable the usual way; the CLOSE/LOGOFF/SHUTDOWN +// console events have no clean Linux analog. +// +static DWORD +PlatformMapSignalToCtrlType(int Signal) +{ + switch (Signal) + { + case SIGINT: + return CTRL_C_EVENT; + case SIGQUIT: + return CTRL_BREAK_EVENT; + default: + return (DWORD)-1; + } +} + +// +// Dedicated thread: waits for the handled signals and dispatches them to the +// installed handler in ordinary (async-signal-safe) thread context. +// +static void * +PlatformSignalThread(void * Arg) +{ + sigset_t WaitSet; + int Signal; + + (void)Arg; + + sigemptyset(&WaitSet); + sigaddset(&WaitSet, SIGINT); + sigaddset(&WaitSet, SIGQUIT); + + while (1) + { + if (sigwait(&WaitSet, &Signal) != 0) + { + continue; + } + + DWORD CtrlType = PlatformMapSignalToCtrlType(Signal); + + if (g_PlatformCtrlHandler != NULL && CtrlType != (DWORD)-1) + { + g_PlatformCtrlHandler(CtrlType); + } + } + + return NULL; +} + +BOOLEAN +PlatformInstallCtrlHandler(PLATFORM_CTRL_HANDLER Handler) +{ + sigset_t BlockSet; + pthread_t Thread; + + g_PlatformCtrlHandler = Handler; + + // + // Block the handled signals in this (main) thread. Done before any other + // thread is created, the mask is inherited by every thread, guaranteeing + // the signals are delivered to our sigwait() thread and nowhere else. + // + sigemptyset(&BlockSet); + sigaddset(&BlockSet, SIGINT); + sigaddset(&BlockSet, SIGQUIT); + + if (pthread_sigmask(SIG_BLOCK, &BlockSet, NULL) != 0) + { + return FALSE; + } + + if (pthread_create(&Thread, NULL, PlatformSignalThread, NULL) != 0) + { + return FALSE; + } + + // + // Fire-and-forget: the thread lives for the life of the process. + // + pthread_detach(Thread); + + return TRUE; +} + +#else +# error "Unsupported platform" +#endif diff --git a/hyperdbg/include/platform/user/header/platform-signal.h b/hyperdbg/include/platform/user/header/platform-signal.h new file mode 100644 index 00000000..963ba771 --- /dev/null +++ b/hyperdbg/include/platform/user/header/platform-signal.h @@ -0,0 +1,43 @@ +/** + * @file platform-signal.h + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform interface for the console-control (CTRL+C / CTRL+BREAK) handler + * @details HyperDbg installs a single handler (BreakController) that pauses the + * debuggee when the user hits CTRL+C / CTRL+BREAK. The handler body is + * platform independent; only the OS mechanism that delivers the event + * and the thread context it runs in are OS specific. This interface + * isolates that registration. Windows maps onto SetConsoleCtrlHandler; + * Linux maps onto POSIX signals serviced by a dedicated sigwait() thread + * so the handler runs in ordinary thread context (matching Windows, which + * also dispatches the handler on its own thread). + * + * @version 0.1 + * @date 2026-06-16 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#if defined(__linux__) +# include "../../../../include/SDK/HyperDbgSdk.h" +#endif // defined(__linux__) + +// +// Console-control handler signature. Matches the Win32 PHANDLER_ROUTINE shape +// (and BreakController) so the same function pointer is usable on both platforms. +// CtrlType is one of the CTRL_*_EVENT codes. +// +typedef BOOL (*PLATFORM_CTRL_HANDLER)(DWORD CtrlType); + +// +// INSTALL the process console-control handler. +// Windows: registers Handler via SetConsoleCtrlHandler. +// Linux: blocks the relevant signals process-wide and starts a dedicated +// sigwait() thread that translates them into CTRL_*_EVENT codes and +// invokes Handler. Must be called once, before other threads spawn, +// so the blocked-signal mask is inherited by every thread. +// Returns TRUE on success, FALSE on failure. +// +BOOLEAN +PlatformInstallCtrlHandler(PLATFORM_CTRL_HANDLER Handler); diff --git a/hyperdbg/libhyperdbg/code/debugger/core/debugger.cpp b/hyperdbg/libhyperdbg/code/debugger/core/debugger.cpp index 65cf542f..de827e8c 100644 --- a/hyperdbg/libhyperdbg/code/debugger/core/debugger.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/core/debugger.cpp @@ -656,6 +656,7 @@ ShowErrorMessage(UINT32 Error) UINT64 DebuggerGetNtoskrnlBase() { +#ifdef _WIN32 UINT64 NtoskrnlBase = NULL; PRTL_PROCESS_MODULES Modules = NULL; @@ -678,6 +679,14 @@ DebuggerGetNtoskrnlBase() free(Modules); return NtoskrnlBase; +#else + // + // TODO(Linux): NT-style system module enumeration (PRTL_PROCESS_MODULES via + // NtQuerySystemInformation) has no Linux analog. The kernel-module base lookup + // will need a Linux-specific mechanism once the kernel side exists. + // + return NULL64_ZERO; +#endif } /** @@ -719,7 +728,7 @@ DebuggerPauseDebuggee() // // Send a pause IOCTL // - StatusIoctl = DeviceIoControl(g_DeviceHandle, // Handle to device + StatusIoctl = PlatformDeviceIoControl(g_DeviceHandle, // Handle to device IOCTL_PAUSE_PACKET_RECEIVED, // IO Control Code (IOCTL) &PauseRequest, // Input Buffer to driver. SIZEOF_DEBUGGER_PAUSE_PACKET_RECEIVED, // Input buffer @@ -733,7 +742,7 @@ DebuggerPauseDebuggee() if (!StatusIoctl) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } @@ -1391,7 +1400,7 @@ SendEventToKernel(PDEBUGGER_GENERAL_EVENT_DETAIL Event, // Send IOCTL // - Status = DeviceIoControl(g_DeviceHandle, // Handle to device + Status = PlatformDeviceIoControl(g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_REGISTER_EVENT, // IO Control Code (IOCTL) Event, // Input Buffer to driver. EventBufferLength, // Input buffer length @@ -1408,7 +1417,7 @@ SendEventToKernel(PDEBUGGER_GENERAL_EVENT_DETAIL Event, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } } @@ -1560,7 +1569,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, // if (ActionBreakToDebugger != NULL) { - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL) ActionBreakToDebugger, // Input Buffer to driver. @@ -1578,7 +1587,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } } @@ -1588,7 +1597,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, // if (ActionCustomCode != NULL) { - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL) ActionCustomCode, // Input Buffer to driver. @@ -1606,7 +1615,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } } @@ -1616,7 +1625,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, // if (ActionScript != NULL) { - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL) ActionScript, // Input Buffer to driver. @@ -1634,7 +1643,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } } @@ -1800,7 +1809,7 @@ InterpretGeneralEventAndActionsFields( // PVOID BufferOfCommandString = malloc(BufferOfCommandStringLength); - RtlZeroMemory(BufferOfCommandString, BufferOfCommandStringLength); + PlatformZeroMemory(BufferOfCommandString, BufferOfCommandStringLength); // // Copy the string to the buffer @@ -2105,7 +2114,7 @@ InterpretGeneralEventAndActionsFields( LengthOfEventBuffer = sizeof(DEBUGGER_GENERAL_EVENT_DETAIL) + ConditionBufferLength; TempEvent = (PDEBUGGER_GENERAL_EVENT_DETAIL)malloc(LengthOfEventBuffer); - RtlZeroMemory(TempEvent, LengthOfEventBuffer); + PlatformZeroMemory(TempEvent, LengthOfEventBuffer); // // Check if buffer is available @@ -2187,7 +2196,7 @@ InterpretGeneralEventAndActionsFields( TempActionCustomCode = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfCustomCodeActionBuffer); - RtlZeroMemory(TempActionCustomCode, LengthOfCustomCodeActionBuffer); + PlatformZeroMemory(TempActionCustomCode, LengthOfCustomCodeActionBuffer); memcpy( (PVOID)((UINT64)TempActionCustomCode + sizeof(DEBUGGER_GENERAL_ACTION)), @@ -2226,7 +2235,7 @@ InterpretGeneralEventAndActionsFields( LengthOfScriptActionBuffer = sizeof(DEBUGGER_GENERAL_ACTION) + ScriptBufferLength; TempActionScript = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfScriptActionBuffer); - RtlZeroMemory(TempActionScript, LengthOfScriptActionBuffer); + PlatformZeroMemory(TempActionScript, LengthOfScriptActionBuffer); memcpy((PVOID)((UINT64)TempActionScript + sizeof(DEBUGGER_GENERAL_ACTION)), (PVOID)ScriptBufferAddress, @@ -2272,7 +2281,7 @@ InterpretGeneralEventAndActionsFields( TempActionBreak = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfBreakActionBuffer); - RtlZeroMemory(TempActionBreak, LengthOfBreakActionBuffer); + PlatformZeroMemory(TempActionBreak, LengthOfBreakActionBuffer); // // Set the action Tag diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index e1a15be8..30b4dbf2 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -137,6 +137,7 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R + diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters index 4e3ef59f..7c38052a 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters @@ -206,6 +206,9 @@ header\platform + + header\platform + header diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index b78b022d..f06ccdf7 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -171,6 +171,12 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; // #include "platform/user/header/platform-signal.h" +// +// NT-style intrusive linked-list helpers + CONTAINING_RECORD (self-guards to +// non-Windows; Windows gets these from / the native-SDK shim) +// +#include "platform/general/header/nt-list.h" + // // Platform-specific intrinsics // From 83ae005c70edb3d1559059cab24b303962c82bc0 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Sat, 20 Jun 2026 09:29:45 +0200 Subject: [PATCH 38/57] Added compatibility --- hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp b/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp index 62938326..edc0ab52 100644 --- a/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp @@ -1295,7 +1295,12 @@ InitializeDebugger() // // Set the callback for symbol message handler // - ScriptEngineSetTextMessageCallbackWrapper(ShowMessages); + // + // ShowMessages is passed as PVOID (the callback API stores it in a PVOID and + // casts to the concrete fn-ptr type at the invocation site); g++ requires the + // explicit function-pointer -> void* cast that MSVC performs implicitly. + // + ScriptEngineSetTextMessageCallbackWrapper((PVOID)ShowMessages); // // Register the CTRL+C and CTRL+BREAK Signals handler From 3a8d8ab2d1e17c92c7d37815fe34af04bbb78328 Mon Sep 17 00:00:00 2001 From: maxraulea Date: Sat, 20 Jun 2026 09:48:19 +0200 Subject: [PATCH 39/57] Add guidelines for contribution --- hyperdbg/linux/Contribute.md | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 hyperdbg/linux/Contribute.md diff --git a/hyperdbg/linux/Contribute.md b/hyperdbg/linux/Contribute.md new file mode 100644 index 00000000..b2954d9f --- /dev/null +++ b/hyperdbg/linux/Contribute.md @@ -0,0 +1,41 @@ +# Contributing to the Linux Port + +HyperDbg is being ported from Windows to Linux. The work is incremental: most +of the codebase compiles file-by-file as the Win32-specific calls get replaced +with a platform-independent interface. + +## Building + +```bash +cmake . # generate the Makefiles +make # build +``` + +Run these from the repo root (or the relevant subdirectory). `cmake .` only +needs to be re-run when the CMake files change; otherwise `make` is enough. + +## How to contribute + +The port progresses one source file at a time. To pick up work: + +1. Build and find the next file that fails to compile. +2. Go through it and make it Linux-compatible. +3. Where the code calls Windows-only APIs (serial, registry, signals, + threads, etc.), don't `#ifdef` inline — route the call through the + **platform-independent interface** so both Windows and Linux share the same + call site with separate backing implementations. +4. Rebuild until the file is clean, then move to the next one. + +## The platform interface + +User-mode abstractions live in `include/platform/user/` (`header/` for the +interface, `code/` for the implementations). See existing examples such as +`platform-serial`, `platform-signal`, and `platform-ioctl` for the pattern to +follow. Kernel-mode equivalents are under `include/platform/kernel/`. + +## Guidelines + +- Keep platform-specific code behind the platform abstraction, not scattered + through the logic. +- Match the surrounding code's style and conventions. +- Build before submitting — every changed file should compile. From 5a0fad490124268550f955f594d1211d5c74f03d Mon Sep 17 00:00:00 2001 From: sina Date: Sun, 21 Jun 2026 03:02:49 +0200 Subject: [PATCH 40/57] add WDK and SDK NuGet packages and Fix CI/CD --- .github/workflows/vs2026.yml | 86 ++++++++----------- .../hyperdbg_driver/hyperdbg_driver.vcxproj | 16 ++++ .../hyperdbg_driver.vcxproj.filters | 3 + .../kernel/hyperdbg_driver/packages.config | 6 ++ hyperdbg/hyperevade/hyperevade.vcxproj | 20 ++++- .../hyperevade/hyperevade.vcxproj.filters | 3 + hyperdbg/hyperevade/packages.config | 6 ++ .../hyperhv/header/globals/GlobalVariables.h | 2 +- hyperdbg/hyperhv/hyperhv.vcxproj | 27 +++++- hyperdbg/hyperhv/hyperhv.vcxproj.filters | 3 + hyperdbg/hyperhv/packages.config | 6 ++ hyperdbg/hyperkd/hyperkd.vcxproj | 27 +++++- hyperdbg/hyperkd/hyperkd.vcxproj.filters | 3 + hyperdbg/hyperkd/packages.config | 6 ++ hyperdbg/hyperlog/hyperlog.inf | 77 ----------------- hyperdbg/hyperlog/hyperlog.vcxproj | 23 +++-- hyperdbg/hyperlog/hyperlog.vcxproj.filters | 8 +- hyperdbg/hyperlog/packages.config | 6 ++ hyperdbg/hypertrace/hypertrace.vcxproj | 20 ++++- .../hypertrace/hypertrace.vcxproj.filters | 6 +- hyperdbg/hypertrace/packages.config | 6 ++ hyperdbg/kdserial/kdserial.vcxproj | 18 +++- hyperdbg/kdserial/packages.config | 6 ++ utils/replace-sdk-wdk.py | 80 +++++++++++++++++ 24 files changed, 309 insertions(+), 155 deletions(-) create mode 100644 examples/kernel/hyperdbg_driver/packages.config create mode 100644 hyperdbg/hyperevade/packages.config create mode 100644 hyperdbg/hyperhv/packages.config create mode 100644 hyperdbg/hyperkd/packages.config delete mode 100644 hyperdbg/hyperlog/hyperlog.inf create mode 100644 hyperdbg/hyperlog/packages.config create mode 100644 hyperdbg/hypertrace/packages.config create mode 100644 hyperdbg/kdserial/packages.config create mode 100644 utils/replace-sdk-wdk.py diff --git a/.github/workflows/vs2026.yml b/.github/workflows/vs2026.yml index af489992..1cf0b89f 100644 --- a/.github/workflows/vs2026.yml +++ b/.github/workflows/vs2026.yml @@ -1,36 +1,28 @@ name: vs2026-ci on: - workflow_dispatch: - -# Disabled, uncomment the below line and remove the above lines to activate -#on: -# push: -# branches: -# - master -# - dev -# tags: -# - 'v*' -# paths-ignore: -# - '.gitignore' -# - '.gitattributes' -# - '**.cmd' -# - '**.bat' -# - '**.md' -# -# pull_request: -# paths-ignore: -# - '.gitignore' -# - '.gitattributes' -# - '**.cmd' -# - '**.bat' -# - '**.md' + push: + branches: + - master + - dev + tags: + - 'v*' + paths-ignore: + - '.gitignore' + - '.gitattributes' + - '**.cmd' + - '**.bat' + - '**.md' + pull_request: + paths-ignore: + - '.gitignore' + - '.gitattributes' + - '**.cmd' + - '**.bat' + - '**.md' + env: - # Updated WDK URL for VS 2026 - SDK_URL: https://go.microsoft.com/fwlink/?linkid=2361309 - WDK_URL: https://go.microsoft.com/fwlink/?LinkId=2362091 - SOLUTION_FILE_PATH: ./hyperdbg/hyperdbg.sln RELEASE_ZIP_FILE_NAME: hyperdbg BUILD_BIN_DIR: ./hyperdbg/build/bin/ @@ -52,34 +44,26 @@ jobs: - name: Checkout submodules recursively run: git submodule update --init --recursive --remote - - name: Setup SDK for VS 2026 - run: | - $sdkSetupPath = "$Env:TEMP\sdksetup.exe" - (New-Object Net.WebClient).DownloadFile('${{env.SDK_URL}}', $sdkSetupPath) - Start-Process -FilePath $sdkSetupPath -ArgumentList "/quiet" -NoNewWindow -Wait - - - name: Setup WDK for VS 2026 - run: | - $wdkSetupPath = "$Env:TEMP\wdksetup.exe" - (New-Object Net.WebClient).DownloadFile('${{env.WDK_URL}}', $wdkSetupPath) - Start-Process -FilePath $wdkSetupPath -ArgumentList "/quiet" -NoNewWindow -Wait - - - name: List SDK folders 1 - shell: pwsh - run: | - Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\Include" - - - name: List SDK folders 2 - shell: pwsh - run: | - Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\Include\10.0.28000.0" - - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v3 + - name: Setup NuGet + uses: NuGet/setup-nuget@v2 + + - name: Restore NuGet packages + run: nuget restore ${{ env.SOLUTION_FILE_PATH }} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Update vcxproj files + run: python utils/replace-sdk-wdk.py + - name: Build HyperDbg solution working-directory: ${{env.GITHUB_WORKSPACE}} - run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.PLATFORM}} /p:EnableInfVerif=false /p:EnableDriverApiValidation=false ${{env.SOLUTION_FILE_PATH}} + run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.PLATFORM}} ${{env.SOLUTION_FILE_PATH}} - name: Upload build directory uses: actions/upload-artifact@v4.4.0 diff --git a/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj b/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj index 51e06464..fd70d5dc 100644 --- a/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj +++ b/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -110,7 +113,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj.filters b/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj.filters index 2a6aa09a..3345321d 100644 --- a/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj.filters +++ b/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj.filters @@ -56,4 +56,7 @@ code\driver + + + \ No newline at end of file diff --git a/examples/kernel/hyperdbg_driver/packages.config b/examples/kernel/hyperdbg_driver/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/examples/kernel/hyperdbg_driver/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperevade/hyperevade.vcxproj b/hyperdbg/hyperevade/hyperevade.vcxproj index 5acb9c74..ea3a07dc 100644 --- a/hyperdbg/hyperevade/hyperevade.vcxproj +++ b/hyperdbg/hyperevade/hyperevade.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -27,7 +30,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -36,7 +39,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -122,7 +125,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperevade/hyperevade.vcxproj.filters b/hyperdbg/hyperevade/hyperevade.vcxproj.filters index d96f4ba2..f7ed48f3 100644 --- a/hyperdbg/hyperevade/hyperevade.vcxproj.filters +++ b/hyperdbg/hyperevade/hyperevade.vcxproj.filters @@ -99,4 +99,7 @@ header\components\callback + + + \ No newline at end of file diff --git a/hyperdbg/hyperevade/packages.config b/hyperdbg/hyperevade/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperevade/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperhv/header/globals/GlobalVariables.h b/hyperdbg/hyperhv/header/globals/GlobalVariables.h index 6eec6d2e..b0b71f10 100644 --- a/hyperdbg/hyperhv/header/globals/GlobalVariables.h +++ b/hyperdbg/hyperhv/header/globals/GlobalVariables.h @@ -217,4 +217,4 @@ BOOLEAN g_IsVpidSupported; /** * @brief Whether the top level hypervisor is Hyper-V or not */ -BOOLEAN g_IsTopLevelHypervisorHyperV; \ No newline at end of file +BOOLEAN g_IsTopLevelHypervisorHyperV; diff --git a/hyperdbg/hyperhv/hyperhv.vcxproj b/hyperdbg/hyperhv/hyperhv.vcxproj index f2dc2413..53c55a14 100644 --- a/hyperdbg/hyperhv/hyperhv.vcxproj +++ b/hyperdbg/hyperhv/hyperhv.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -28,7 +31,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false false @@ -38,7 +41,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false false @@ -53,13 +56,16 @@ DbgengKernelDebugger - true + + $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false DbgengKernelDebugger - true + + $(SolutionDir)build\bin\$(Configuration)\ false $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ @@ -296,8 +302,21 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperhv/hyperhv.vcxproj.filters b/hyperdbg/hyperhv/hyperhv.vcxproj.filters index f8fcb900..fbbe5e33 100644 --- a/hyperdbg/hyperhv/hyperhv.vcxproj.filters +++ b/hyperdbg/hyperhv/hyperhv.vcxproj.filters @@ -649,4 +649,7 @@ code\assembly + + + \ No newline at end of file diff --git a/hyperdbg/hyperhv/packages.config b/hyperdbg/hyperhv/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperhv/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperkd/hyperkd.vcxproj b/hyperdbg/hyperkd/hyperkd.vcxproj index c7a6d222..b165f75d 100644 --- a/hyperdbg/hyperkd/hyperkd.vcxproj +++ b/hyperdbg/hyperkd/hyperkd.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -27,7 +30,7 @@ WindowsKernelModeDriver10.0 Driver KMDF - Universal + Desktop false @@ -36,7 +39,7 @@ WindowsKernelModeDriver10.0 Driver KMDF - Universal + Desktop false @@ -51,13 +54,16 @@ DbgengKernelDebugger $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - true + + + false DbgengKernelDebugger $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - true + + false @@ -205,7 +211,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperkd/hyperkd.vcxproj.filters b/hyperdbg/hyperkd/hyperkd.vcxproj.filters index 55eeeb30..8a1a1c5b 100644 --- a/hyperdbg/hyperkd/hyperkd.vcxproj.filters +++ b/hyperdbg/hyperkd/hyperkd.vcxproj.filters @@ -434,4 +434,7 @@ code\assembly + + + \ No newline at end of file diff --git a/hyperdbg/hyperkd/packages.config b/hyperdbg/hyperkd/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperkd/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperlog/hyperlog.inf b/hyperdbg/hyperlog/hyperlog.inf deleted file mode 100644 index 8601163e..00000000 --- a/hyperdbg/hyperlog/hyperlog.inf +++ /dev/null @@ -1,77 +0,0 @@ -; -; hyperlog.inf -; - -[Version] -Signature="$WINDOWS NT$" -Class=System ; TODO: specify appropriate Class -ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} ; TODO: specify appropriate ClassGuid -Provider=%ManufacturerName% -CatalogFile=hyperlog.cat -DriverVer= ; TODO: set DriverVer in stampinf property pages -PnpLockdown=1 - -[DestinationDirs] -DefaultDestDir = 12 -hyperlog_Device_CoInstaller_CopyFiles = 11 - -[SourceDisksNames] -1 = %DiskName%,,,"" - -[SourceDisksFiles] -hyperlog.sys = 1,, -WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames - -;***************************************** -; Install Section -;***************************************** - -[Manufacturer] -%ManufacturerName%=Standard,NT$ARCH$ - -[Standard.NT$ARCH$] -%hyperlog.DeviceDesc%=hyperlog_Device, Root\hyperlog ; TODO: edit hw-id - -[hyperlog_Device.NT] -CopyFiles=Drivers_Dir - -[Drivers_Dir] -hyperlog.sys - -;-------------- Service installation -[hyperlog_Device.NT.Services] -AddService = hyperlog,%SPSVCINST_ASSOCSERVICE%, hyperlog_Service_Inst - -; -------------- hyperlog driver install sections -[hyperlog_Service_Inst] -DisplayName = %hyperlog.SVCDESC% -ServiceType = 1 ; SERVICE_KERNEL_DRIVER -StartType = 3 ; SERVICE_DEMAND_START -ErrorControl = 1 ; SERVICE_ERROR_NORMAL -ServiceBinary = %12%\hyperlog.sys - -; -;--- hyperlog_Device Coinstaller installation ------ -; - -[hyperlog_Device.NT.CoInstallers] -AddReg=hyperlog_Device_CoInstaller_AddReg -CopyFiles=hyperlog_Device_CoInstaller_CopyFiles - -[hyperlog_Device_CoInstaller_AddReg] -HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" - -[hyperlog_Device_CoInstaller_CopyFiles] -WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll - -[hyperlog_Device.NT.Wdf] -KmdfService = hyperlog, hyperlog_wdfsect -[hyperlog_wdfsect] -KmdfLibraryVersion = $KMDFVERSION$ - -[Strings] -SPSVCINST_ASSOCSERVICE= 0x00000002 -ManufacturerName="" ;TODO: Replace with your manufacturer name -DiskName = "hyperlog Installation Disk" -hyperlog.DeviceDesc = "hyperlog Device" -hyperlog.SVCDESC = "hyperlog Service" diff --git a/hyperdbg/hyperlog/hyperlog.vcxproj b/hyperdbg/hyperlog/hyperlog.vcxproj index 4147f9d2..5e0ba787 100644 --- a/hyperdbg/hyperlog/hyperlog.vcxproj +++ b/hyperdbg/hyperlog/hyperlog.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -27,7 +30,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -36,7 +39,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -96,9 +99,6 @@ hyperlog.def - - - @@ -130,7 +130,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperlog/hyperlog.vcxproj.filters b/hyperdbg/hyperlog/hyperlog.vcxproj.filters index f6867f82..b7765775 100644 --- a/hyperdbg/hyperlog/hyperlog.vcxproj.filters +++ b/hyperdbg/hyperlog/hyperlog.vcxproj.filters @@ -20,11 +20,6 @@ {21f0281e-fc2a-4e13-97ac-e4b35a05a31e} - - - Driver Files - - code @@ -101,4 +96,7 @@ header\platform + + + \ No newline at end of file diff --git a/hyperdbg/hyperlog/packages.config b/hyperdbg/hyperlog/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperlog/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hypertrace/hypertrace.vcxproj b/hyperdbg/hypertrace/hypertrace.vcxproj index 794cfc67..1b0d38f2 100644 --- a/hyperdbg/hypertrace/hypertrace.vcxproj +++ b/hyperdbg/hypertrace/hypertrace.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -27,7 +30,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -36,7 +39,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -131,7 +134,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hypertrace/hypertrace.vcxproj.filters b/hyperdbg/hypertrace/hypertrace.vcxproj.filters index eec27b03..32ace76c 100644 --- a/hyperdbg/hypertrace/hypertrace.vcxproj.filters +++ b/hyperdbg/hypertrace/hypertrace.vcxproj.filters @@ -115,9 +115,6 @@ header\broadcast - - header\broadcast - header\broadcast @@ -152,4 +149,7 @@ header\components\callback + + + \ No newline at end of file diff --git a/hyperdbg/hypertrace/packages.config b/hyperdbg/hypertrace/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hypertrace/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/kdserial/kdserial.vcxproj b/hyperdbg/kdserial/kdserial.vcxproj index 5d67a92b..22ed068a 100644 --- a/hyperdbg/kdserial/kdserial.vcxproj +++ b/hyperdbg/kdserial/kdserial.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -31,6 +34,7 @@ + @@ -45,7 +49,7 @@ v4.5 14.0 kdserial - Universal + Desktop 10.0.10135.0 $(LatestTargetPlatformVersion) @@ -77,6 +81,7 @@ false $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false kdserial @@ -84,6 +89,7 @@ false $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false false @@ -138,5 +144,15 @@ + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/kdserial/packages.config b/hyperdbg/kdserial/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/kdserial/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/utils/replace-sdk-wdk.py b/utils/replace-sdk-wdk.py new file mode 100644 index 00000000..48a9cd73 --- /dev/null +++ b/utils/replace-sdk-wdk.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +""" +Walks the parent directory of this script (and all of its subdirectories) +looking for .vcxproj files. +""" + +import os + +TARGET_SDK_WDK_VERSION = "10.0.28000.0" # Update this version in case of updating NuGet packages + +# Only these .vcxproj files will be modified (matched by filename). +TARGET_PROJECTS = [ + "hyperhv.vcxproj", + "hyperkd.vcxproj", + "hypertrace.vcxproj", + "hyperlog.vcxproj", + "hyperevade.vcxproj", + "kdserial.vcxproj", + "hyperdbg_driver.vcxproj", +] + +REPLACEMENTS = { + "10.0": "" + TARGET_SDK_WDK_VERSION + "", + "$(LatestTargetPlatformVersion)": "" + TARGET_SDK_WDK_VERSION + "", +} + +# Directory *names* to skip wherever they appear in the tree. +EXCLUDED_DIR_NAMES = {".git"} + +def replace_in_file(filepath: str) -> bool: + with open(filepath, "r", encoding="utf-8", errors="ignore") as f: + content = f.read() + + original = content + for old, new in REPLACEMENTS.items(): + content = content.replace(old, new) + + if content != original: + with open(filepath, "w", encoding="utf-8") as f: + f.write(content) + return True + return False + + +def main() -> None: + script_path = os.path.abspath(__file__) + script_dir = os.path.dirname(script_path) + target_root = os.path.dirname(script_dir) # parent of this folder + + wanted = set(TARGET_PROJECTS) + found = set() + + print(f"Scanning under: {target_root}") + print(f"Looking for: {', '.join(sorted(wanted))}") + + changed_count = 0 + for root, dirs, files in os.walk(target_root): + # Prune in place so os.walk never descends into these directories. + dirs[:] = [d for d in dirs if d not in EXCLUDED_DIR_NAMES] + + for file in files: + if file in wanted: + filepath = os.path.join(root, file) + found.add(file) + + if replace_in_file(filepath): + print(f"Updated: {filepath}") + changed_count += 1 + else: + print(f"No matching text found, left unchanged: {filepath}") + + missing = wanted - found + for name in sorted(missing): + print(f"Warning: not found anywhere under {target_root}: {name}") + + print(f"Done. {changed_count} file(s) updated.") + + +if __name__ == "__main__": + main() From aa18675760512fb0c4716317e8535483a54e3402 Mon Sep 17 00:00:00 2001 From: sina Date: Sun, 21 Jun 2026 14:22:33 +0200 Subject: [PATCH 41/57] update changelog and fix minor issues --- .github/git/git-help.md | 19 ++++++ .github/workflows/vs2026.yml | 3 +- CHANGELOG.md | 6 +- hyperdbg/include/SDK/headers/Constants.h | 4 +- .../include/platform/general/header/nt-list.h | 10 +-- .../platform/user/code/platform-ioctl.c | 2 +- .../platform/user/code/platform-lib-calls.c | 2 +- .../platform/user/code/platform-serial.c | 8 +-- .../platform/user/code/platform-signal.c | 2 +- .../platform/user/header/platform-ioctl.h | 2 +- .../platform/user/header/platform-lib-calls.h | 2 +- .../platform/user/header/platform-serial.h | 2 +- .../platform/user/header/platform-signal.h | 2 +- hyperdbg/libhyperdbg/code/common/list.cpp | 68 ------------------- hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 1 - .../libhyperdbg/libhyperdbg.vcxproj.filters | 3 - hyperdbg/linux/Contribute.md | 41 ----------- hyperdbg/linux/README.md | 44 +++++++++++- 18 files changed, 86 insertions(+), 135 deletions(-) delete mode 100644 hyperdbg/libhyperdbg/code/common/list.cpp delete mode 100644 hyperdbg/linux/Contribute.md diff --git a/.github/git/git-help.md b/.github/git/git-help.md index 4a418ae9..61ad99df 100644 --- a/.github/git/git-help.md +++ b/.github/git/git-help.md @@ -51,6 +51,25 @@ git reset --hard git clean -fd ``` +To modify the last pushed commit: + +```bash +# Undo the last commit but keep the changes in your working tree +git reset --soft HEAD~1 + +# Make your additional modifications +# edit files... + +# Stage everything +git add . + +# Create a new commit +git commit -m "Updated commit message" + +# Force-push to replace the remote commit +git push --force-with-lease +``` + --------------- ## Releasing instructions diff --git a/.github/workflows/vs2026.yml b/.github/workflows/vs2026.yml index 1cf0b89f..0e587b16 100644 --- a/.github/workflows/vs2026.yml +++ b/.github/workflows/vs2026.yml @@ -3,8 +3,7 @@ name: vs2026-ci on: push: branches: - - master - - dev + - '**' # matches all branch names, including ones with slashes tags: - 'v*' paths-ignore: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ec2bdb8..d9a3959c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.20.0.0] - 2026-XX-XX +## [0.20.0.0-beta] - 2026-06-21 New release of the HyperDbg Debugger. ### Added @@ -15,6 +15,9 @@ New release of the HyperDbg Debugger. - Added platform IOCTL interface ([link](https://github.com/HyperDbg/HyperDbg/pull/619)) - Added typecast for wchar, mismatch on Linux and Windows ([link](https://github.com/HyperDbg/HyperDbg/commit/10576b064a9824ab655e7bea0e35e9556ee68ca4)) - Added missing IOCTLs for Intel PT ([link](https://github.com/HyperDbg/HyperDbg/commit/df12e9fd79851c8f378ec82a45066da510da507a)) +- Added NuGet packages for Windows SDK and WDK +- Added Signal handler platform API for Linux ([link](https://github.com/HyperDbg/HyperDbg/pull/627)) +- Added contribution guidelines for Linux ([link](https://github.com/HyperDbg/HyperDbg/tree/master/hyperdbg/linux#readme)) ### Changed - Moved to Visual Studio 2026 ([link](https://github.com/HyperDbg/HyperDbg/pull/626)) @@ -22,6 +25,7 @@ New release of the HyperDbg Debugger. - Fix the new form of loading and unloading in the example app and PT app ([link](https://github.com/HyperDbg/HyperDbg/commit/9ad48d30dcf6b409ae86b2d08262584cd06f606e)) - Check whether the top-level hypervisor is Hyper-V and disable/enable VPIDs based on that, thanks to [@Idov31](https://github.com/Idov31) ([link](https://github.com/HyperDbg/HyperDbg/pull/625)) - Fix PT operation IOCTL buffer size ([link](https://github.com/HyperDbg/HyperDbg/commit/dabf132d31503843f90949f35f8dce4601d43126)) +- CI/CD updated with NuGet packages for Visual Studio 2026 ([link](https://github.com/HyperDbg/HyperDbg/commit/5a0fad490124268550f955f594d1211d5c74f03d)) ## [0.19.0.0-beta] - 2026-06-10 New release of the HyperDbg Debugger. diff --git a/hyperdbg/include/SDK/headers/Constants.h b/hyperdbg/include/SDK/headers/Constants.h index 2f3c508d..4157305e 100644 --- a/hyperdbg/include/SDK/headers/Constants.h +++ b/hyperdbg/include/SDK/headers/Constants.h @@ -21,7 +21,7 @@ #define VERSION_MINOR 20 #define VERSION_PATCH 0 -#define BETA_VERSION 0 +#define BETA_VERSION TRUE // // Example of __DATE__ string: "Jul 27 2012" @@ -110,7 +110,7 @@ const UCHAR BuildDateTime[] = { // Complete version as a string -# if BETA_VERSION == 0 +# if BETA_VERSION == FALSE # define HYPERDBG_COMPLETE_VERSION "v" TOSTRING(VERSION_MAJOR) "." TOSTRING(VERSION_MINOR) "." TOSTRING(VERSION_PATCH) "\0" # else # define HYPERDBG_COMPLETE_VERSION "v" TOSTRING(VERSION_MAJOR) "." TOSTRING(VERSION_MINOR) "." TOSTRING(VERSION_PATCH) "-beta\0" diff --git a/hyperdbg/include/platform/general/header/nt-list.h b/hyperdbg/include/platform/general/header/nt-list.h index f53f174f..0dc4dd2b 100644 --- a/hyperdbg/include/platform/general/header/nt-list.h +++ b/hyperdbg/include/platform/general/header/nt-list.h @@ -16,7 +16,7 @@ * The body is compiled only where the OS headers don't already provide these * (i.e. not on Windows), so including this unconditionally is safe. * - * @version 0.1 + * @version 0.20 * @date 2026-06-16 * * @copyright This project is released under the GNU Public License v3. @@ -40,7 +40,7 @@ // // Initialize a list head to the empty (points-to-self) state. // -static inline void +static inline VOID InitializeListHead(PLIST_ENTRY ListHead) { ListHead->Flink = ListHead->Blink = ListHead; @@ -103,7 +103,7 @@ RemoveTailList(PLIST_ENTRY ListHead) // // Insert an entry at the tail of the list. // -static inline void +static inline VOID InsertTailList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) { PLIST_ENTRY Blink = ListHead->Blink; @@ -117,7 +117,7 @@ InsertTailList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) // // Insert an entry at the head of the list. // -static inline void +static inline VOID InsertHeadList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) { PLIST_ENTRY Flink = ListHead->Flink; @@ -131,7 +131,7 @@ InsertHeadList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) // // Splice the entries of ListToAppend onto the tail of ListHead. // -static inline void +static inline VOID AppendTailList(PLIST_ENTRY ListHead, PLIST_ENTRY ListToAppend) { PLIST_ENTRY ListEnd = ListHead->Blink; diff --git a/hyperdbg/include/platform/user/code/platform-ioctl.c b/hyperdbg/include/platform/user/code/platform-ioctl.c index 0c78ef42..edfc699d 100644 --- a/hyperdbg/include/platform/user/code/platform-ioctl.c +++ b/hyperdbg/include/platform/user/code/platform-ioctl.c @@ -7,7 +7,7 @@ * the home where the ioctl()-based implementation against the /dev/HyperDbg * character device will live once the kernel module exists. * - * @version 0.1 + * @version 0.20 * @date 2026-06-09 * * @copyright This project is released under the GNU Public License v3. diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index 659ecbc6..19768c55 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -1,6 +1,6 @@ /** * @file platform-lib-calls.c - * @author Max Raulea (max.raulea@gmail.com) + * @author Max Raulea (max.raulea@gmail.com) * @brief User mode Cross platform APIs for platofrm dependend library calls * @details * @version 0.19 diff --git a/hyperdbg/include/platform/user/code/platform-serial.c b/hyperdbg/include/platform/user/code/platform-serial.c index 29224214..6266c0a8 100644 --- a/hyperdbg/include/platform/user/code/platform-serial.c +++ b/hyperdbg/include/platform/user/code/platform-serial.c @@ -8,7 +8,7 @@ * branch is currently stubbed (returns FALSE/NULL) and is the home where the * termios-based implementation over /dev/tty* will live. * - * @version 0.1 + * @version 0.20 * @date 2026-06-08 * * @copyright This project is released under the GNU Public License v3. @@ -26,8 +26,8 @@ // Per-direction overlapped I/O state, owned by the transport layer. // static OVERLAPPED g_PlatformOverlappedReadDebugger = {0}; -static OVERLAPPED g_PlatformOverlappedReadDebuggee = {0}; -static OVERLAPPED g_PlatformOverlappedWriteDebugger = {0}; +static OVERLAPPED g_PlatformOverlappedReadDebuggee = {0}; +static OVERLAPPED g_PlatformOverlappedWriteDebugger = {0}; HANDLE PlatformSerialOpen(const char * PortName, PLATFORM_SERIAL_IO_ROLE Role) @@ -88,7 +88,7 @@ PlatformSerialOpen(const char * PortName, PLATFORM_SERIAL_IO_ROLE Role) BOOLEAN PlatformSerialConfigure(HANDLE Handle, DWORD BaudRate) { - DCB SerialParams = {0}; + DCB SerialParams = {0}; SerialParams.DCBlength = sizeof(SerialParams); if (GetCommState(Handle, &SerialParams) == FALSE) diff --git a/hyperdbg/include/platform/user/code/platform-signal.c b/hyperdbg/include/platform/user/code/platform-signal.c index 4125f38f..b0f83071 100644 --- a/hyperdbg/include/platform/user/code/platform-signal.c +++ b/hyperdbg/include/platform/user/code/platform-signal.c @@ -10,7 +10,7 @@ * a normal thread woken by sigwait() sidesteps that entirely and mirrors the * Windows model where the console-control handler also runs on its own thread. * - * @version 0.1 + * @version 0.20 * @date 2026-06-16 * * @copyright This project is released under the GNU Public License v3. diff --git a/hyperdbg/include/platform/user/header/platform-ioctl.h b/hyperdbg/include/platform/user/header/platform-ioctl.h index e300342a..c2c7ce14 100644 --- a/hyperdbg/include/platform/user/header/platform-ioctl.h +++ b/hyperdbg/include/platform/user/header/platform-ioctl.h @@ -10,7 +10,7 @@ * \\.\HyperDbgDebuggerDevice handle; Linux will map onto ioctl() against a * /dev/HyperDbg character device exposed by the (future) kernel module (TODO). * - * @version 0.1 + * @version 0.20 * @date 2026-06-09 * * @copyright This project is released under the GNU Public License v3. diff --git a/hyperdbg/include/platform/user/header/platform-lib-calls.h b/hyperdbg/include/platform/user/header/platform-lib-calls.h index 89835a72..021d4a5b 100644 --- a/hyperdbg/include/platform/user/header/platform-lib-calls.h +++ b/hyperdbg/include/platform/user/header/platform-lib-calls.h @@ -134,4 +134,4 @@ UINT32 PlatformGetCurrentProcessId(VOID); CHAR * -PlatformGetCurrentProcessName(VOID); + PlatformGetCurrentProcessName(VOID); diff --git a/hyperdbg/include/platform/user/header/platform-serial.h b/hyperdbg/include/platform/user/header/platform-serial.h index 8e49d5ba..f33b48ec 100644 --- a/hyperdbg/include/platform/user/header/platform-serial.h +++ b/hyperdbg/include/platform/user/header/platform-serial.h @@ -8,7 +8,7 @@ * can stay shared. Windows maps onto Win32 (CreateFile / Comm* / overlapped * ReadFile/WriteFile); Linux will map onto termios over /dev/tty* (TODO). * - * @version 0.1 + * @version 0.20 * @date 2026-06-08 * * @copyright This project is released under the GNU Public License v3. diff --git a/hyperdbg/include/platform/user/header/platform-signal.h b/hyperdbg/include/platform/user/header/platform-signal.h index 963ba771..9a3f73db 100644 --- a/hyperdbg/include/platform/user/header/platform-signal.h +++ b/hyperdbg/include/platform/user/header/platform-signal.h @@ -11,7 +11,7 @@ * so the handler runs in ordinary thread context (matching Windows, which * also dispatches the handler on its own thread). * - * @version 0.1 + * @version 0.20 * @date 2026-06-16 * * @copyright This project is released under the GNU Public License v3. diff --git a/hyperdbg/libhyperdbg/code/common/list.cpp b/hyperdbg/libhyperdbg/code/common/list.cpp deleted file mode 100644 index 58c2fc44..00000000 --- a/hyperdbg/libhyperdbg/code/common/list.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @file list.cpp - * @author Sina Karvandi (sina@hyperdbg.org) - * @brief The list working functions headers - * @details - * @version 0.1 - * @date 2020-04-11 - * - * @copyright This project is released under the GNU Public License v3. - * - */ -#include "pch.h" -// -///** -// * @brief List initializer -// * -// * @param ListHead -// */ -// void -// InitializeListHead(PLIST_ENTRY ListHead) -//{ -// ListHead->Flink = ListHead->Blink = ListHead; -//} -// -///** -// * @brief insert entry to the top of the list -// * -// * @param ListHead -// * @param Entry -// */ -// void -// InsertHeadList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) -//{ -// PLIST_ENTRY Flink; -// -// Flink = ListHead->Flink; -// Entry->Flink = Flink; -// Entry->Blink = ListHead; -// Flink->Blink = Entry; -// ListHead->Flink = Entry; -//} -// -///** -// * @brief remove the entry from the list -// * -// * @param Entry -// * @return BOOLEAN -// */ -// BOOLEAN -// RemoveEntryList(PLIST_ENTRY Entry) -//{ -// PLIST_ENTRY PrevEntry; -// PLIST_ENTRY NextEntry; -// -// NextEntry = Entry->Flink; -// PrevEntry = Entry->Blink; -// if ((NextEntry->Blink != Entry) || (PrevEntry->Flink != Entry)) -// { -// // -// // Error -// // -// _CrtDbgBreak(); -// } -// -// PrevEntry->Flink = NextEntry; -// NextEntry->Blink = PrevEntry; -// return (BOOLEAN)(PrevEntry == NextEntry); -//} diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index 30b4dbf2..c5a8ad58 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -250,7 +250,6 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R - diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters index 7c38052a..9b62836e 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters @@ -235,9 +235,6 @@ code\common - - code\common - code\debugger\commands\debugging-commands diff --git a/hyperdbg/linux/Contribute.md b/hyperdbg/linux/Contribute.md deleted file mode 100644 index b2954d9f..00000000 --- a/hyperdbg/linux/Contribute.md +++ /dev/null @@ -1,41 +0,0 @@ -# Contributing to the Linux Port - -HyperDbg is being ported from Windows to Linux. The work is incremental: most -of the codebase compiles file-by-file as the Win32-specific calls get replaced -with a platform-independent interface. - -## Building - -```bash -cmake . # generate the Makefiles -make # build -``` - -Run these from the repo root (or the relevant subdirectory). `cmake .` only -needs to be re-run when the CMake files change; otherwise `make` is enough. - -## How to contribute - -The port progresses one source file at a time. To pick up work: - -1. Build and find the next file that fails to compile. -2. Go through it and make it Linux-compatible. -3. Where the code calls Windows-only APIs (serial, registry, signals, - threads, etc.), don't `#ifdef` inline — route the call through the - **platform-independent interface** so both Windows and Linux share the same - call site with separate backing implementations. -4. Rebuild until the file is clean, then move to the next one. - -## The platform interface - -User-mode abstractions live in `include/platform/user/` (`header/` for the -interface, `code/` for the implementations). See existing examples such as -`platform-serial`, `platform-signal`, and `platform-ioctl` for the pattern to -follow. Kernel-mode equivalents are under `include/platform/kernel/`. - -## Guidelines - -- Keep platform-specific code behind the platform abstraction, not scattered - through the logic. -- Match the surrounding code's style and conventions. -- Build before submitting — every changed file should compile. diff --git a/hyperdbg/linux/README.md b/hyperdbg/linux/README.md index 6ee5f6a6..7071f314 100644 --- a/hyperdbg/linux/README.md +++ b/hyperdbg/linux/README.md @@ -6,4 +6,46 @@ Linux is currently **not supported**. -We are in the process of porting HyperDbg to Linux. This effort is ongoing and may take some time to complete. \ No newline at end of file +We are in the process of porting HyperDbg to Linux. This effort is ongoing and may take some time to complete. + +# Contributing to the Linux Port + +HyperDbg is being ported from Windows to Linux. The work is incremental: most +of the codebase compiles file-by-file as the Win32-specific calls get replaced +with a platform-independent interface. + +## Building + +```bash +cmake . # generate the Makefiles +make # build +``` + +Run these from the repo root (or the relevant subdirectory). `cmake .` only +needs to be re-run when the CMake files change; otherwise `make` is enough. + +## How to contribute + +The port progresses one source file at a time. To pick up work: + +1. Build and find the next file that fails to compile. +2. Go through it and make it Linux-compatible. +3. Where the code calls Windows-only APIs (serial, registry, signals, + threads, etc.), don't `#ifdef` inline — route the call through the + **platform-independent interface** so both Windows and Linux share the same + call site with separate backing implementations. +4. Rebuild until the file is clean, then move to the next one. + +## The platform interface + +User-mode abstractions live in `include/platform/user/` (`header/` for the +interface, `code/` for the implementations). See existing examples such as +`platform-serial`, `platform-signal`, and `platform-ioctl` for the pattern to +follow. Kernel-mode equivalents are under `include/platform/kernel/`. + +## Guidelines + +- Keep platform-specific code behind the platform abstraction, not scattered + through the logic. +- Match the surrounding code's style and conventions. +- Build before submitting — every changed file should compile. \ No newline at end of file From 7dfe5be9850ab6b33d37590b282919b4f62f4398 Mon Sep 17 00:00:00 2001 From: sina Date: Sun, 21 Jun 2026 15:02:56 +0200 Subject: [PATCH 42/57] change version to v0.21 --- CHANGELOG.md | 7 +++++++ hyperdbg/include/SDK/headers/Constants.h | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9a3959c..677a7c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.21.0.0] - 2026-XX-XX +New release of the HyperDbg Debugger. + +### Added + +### Changed + ## [0.20.0.0-beta] - 2026-06-21 New release of the HyperDbg Debugger. diff --git a/hyperdbg/include/SDK/headers/Constants.h b/hyperdbg/include/SDK/headers/Constants.h index 4157305e..dae3aa47 100644 --- a/hyperdbg/include/SDK/headers/Constants.h +++ b/hyperdbg/include/SDK/headers/Constants.h @@ -18,10 +18,10 @@ ////////////////////////////////////////////////// #define VERSION_MAJOR 0 -#define VERSION_MINOR 20 +#define VERSION_MINOR 21 #define VERSION_PATCH 0 -#define BETA_VERSION TRUE +#define BETA_VERSION FALSE // // Example of __DATE__ string: "Jul 27 2012" From c17ebc09c4d199a642352e66feebb3159582196c Mon Sep 17 00:00:00 2001 From: sina Date: Mon, 22 Jun 2026 16:19:49 +0200 Subject: [PATCH 43/57] add HyperPerf project structures --- CHANGELOG.md | 3 + hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj | 42 +++-- hyperdbg/hyperdbg.sln | 15 +- hyperdbg/hyperperf/CMakeLists.txt | 22 +++ hyperdbg/hyperperf/code/api/PerfApi.c | 84 +++++++++ hyperdbg/hyperperf/code/broadcast/Broadcast.c | 26 +++ .../hyperperf/code/broadcast/DpcRoutines.c | 35 ++++ hyperdbg/hyperperf/code/common/UnloadDll.c | 45 +++++ hyperdbg/hyperperf/header/api/PerfApi.h | 19 ++ .../hyperperf/header/broadcast/Broadcast.h | 20 +++ .../hyperperf/header/broadcast/DpcRoutines.h | 20 +++ hyperdbg/hyperperf/header/common/UnloadDll.h | 22 +++ .../header/globals/GlobalVariables.h | 35 ++++ hyperdbg/hyperperf/header/pch.h | 111 ++++++++++++ hyperdbg/hyperperf/header/pt/Pt.h | 167 ++++++++++++++++++ hyperdbg/hyperperf/hyperperf.def | 6 + hyperdbg/hyperperf/hyperperf.vcxproj | 145 +++++++++++++++ hyperdbg/hyperperf/hyperperf.vcxproj.filters | 119 +++++++++++++ hyperdbg/hyperperf/packages.config | 6 + hyperdbg/hypertrace/code/api/TraceApi.c | 2 +- .../SDK/imports/kernel/HyperDbgHyperPerf.h | 33 ++++ hyperdbg/include/SDK/modules/HyperPerf.h | 80 +++++++++ 22 files changed, 1038 insertions(+), 19 deletions(-) create mode 100644 hyperdbg/hyperperf/CMakeLists.txt create mode 100644 hyperdbg/hyperperf/code/api/PerfApi.c create mode 100644 hyperdbg/hyperperf/code/broadcast/Broadcast.c create mode 100644 hyperdbg/hyperperf/code/broadcast/DpcRoutines.c create mode 100644 hyperdbg/hyperperf/code/common/UnloadDll.c create mode 100644 hyperdbg/hyperperf/header/api/PerfApi.h create mode 100644 hyperdbg/hyperperf/header/broadcast/Broadcast.h create mode 100644 hyperdbg/hyperperf/header/broadcast/DpcRoutines.h create mode 100644 hyperdbg/hyperperf/header/common/UnloadDll.h create mode 100644 hyperdbg/hyperperf/header/globals/GlobalVariables.h create mode 100644 hyperdbg/hyperperf/header/pch.h create mode 100644 hyperdbg/hyperperf/header/pt/Pt.h create mode 100644 hyperdbg/hyperperf/hyperperf.def create mode 100644 hyperdbg/hyperperf/hyperperf.vcxproj create mode 100644 hyperdbg/hyperperf/hyperperf.vcxproj.filters create mode 100644 hyperdbg/hyperperf/packages.config create mode 100644 hyperdbg/include/SDK/imports/kernel/HyperDbgHyperPerf.h create mode 100644 hyperdbg/include/SDK/modules/HyperPerf.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 677a7c1a..89a9adf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 New release of the HyperDbg Debugger. ### Added +- Added structure for the hyperperf (Hardware Performance Counter) project ### Changed +- Separated SDK libraries for user mode and kernel mode modules +- Added hypertrace, hyperevade, and hyperperf DLL files to SDK ## [0.20.0.0-beta] - 2026-06-21 New release of the HyperDbg Debugger. diff --git a/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj b/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj index 958dcc98..5b16f0d4 100644 --- a/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj +++ b/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj @@ -84,14 +84,19 @@ xcopy /E /I /Y "$(SolutionDir)include\SDK" "$(OutDir)SDK" xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\examples" mkdir "$(OutDir)SDK\libraries" -copy "$(OutDir)pdbex.lib" "$(OutDir)SDK\libraries\pdbex.lib" -copy "$(OutDir)kdserial.lib" "$(OutDir)SDK\libraries\kdserial.lib" -copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\libraries\libhyperdbg.dll" -copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\libraries\script-engine.dll" -copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\libraries\symbol-parser.dll" -copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\libraries\hyperlog.dll" -copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\libraries\hyperhv.dll" -copy "$(OutDir)libipt.dll" "$(OutDir)SDK\libraries\libipt.dll" +mkdir "$(OutDir)SDK\libraries\kernel" +mkdir "$(OutDir)SDK\libraries\user" +copy "$(OutDir)pdbex.dll" "$(OutDir)SDK\libraries\user\pdbex.dll" +copy "$(OutDir)libipt.dll" "$(OutDir)SDK\libraries\user\libipt.dll" +copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\libraries\user\script-engine.dll" +copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\libraries\user\symbol-parser.dll" +copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\libraries\user\libhyperdbg.dll" +copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\libraries\kernel\hyperlog.dll" +copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\libraries\kernel\hyperhv.dll" +copy "$(OutDir)hypertrace.dll" "$(OutDir)SDK\libraries\kernel\hyperevade.dll" +copy "$(OutDir)hypertrace.dll" "$(OutDir)SDK\libraries\kernel\hypertrace.dll" +copy "$(OutDir)hyperperf.dll" "$(OutDir)SDK\libraries\kernel\hyperperf.dll" +copy "$(OutDir)kdserial.dll" "$(OutDir)SDK\libraries\kernel\kdserial.dll" if exist "$(OutDir)constants\" rd /q /s "$(OutDir)constants\" mkdir "$(OutDir)constants" copy "$(SolutionDir)miscellaneous\constants\pciid\pci.ids" "$(OutDir)constants\pci.ids" @@ -129,14 +134,19 @@ copy "$(SolutionDir)miscellaneous\constants\pciid\pci.ids" "$(OutDir)constants\p xcopy /E /I /Y "$(SolutionDir)include\SDK" "$(OutDir)SDK" xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\examples" mkdir "$(OutDir)SDK\libraries" -copy "$(OutDir)pdbex.lib" "$(OutDir)SDK\libraries\pdbex.lib" -copy "$(OutDir)kdserial.lib" "$(OutDir)SDK\libraries\kdserial.lib" -copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\libraries\libhyperdbg.dll" -copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\libraries\script-engine.dll" -copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\libraries\symbol-parser.dll" -copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\libraries\hyperlog.dll" -copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\libraries\hyperhv.dll" -copy "$(OutDir)libipt.dll" "$(OutDir)SDK\libraries\libipt.dll" +mkdir "$(OutDir)SDK\libraries\kernel" +mkdir "$(OutDir)SDK\libraries\user" +copy "$(OutDir)pdbex.dll" "$(OutDir)SDK\libraries\user\pdbex.dll" +copy "$(OutDir)libipt.dll" "$(OutDir)SDK\libraries\user\libipt.dll" +copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\libraries\user\script-engine.dll" +copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\libraries\user\symbol-parser.dll" +copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\libraries\user\libhyperdbg.dll" +copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\libraries\kernel\hyperlog.dll" +copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\libraries\kernel\hyperhv.dll" +copy "$(OutDir)hypertrace.dll" "$(OutDir)SDK\libraries\kernel\hyperevade.dll" +copy "$(OutDir)hypertrace.dll" "$(OutDir)SDK\libraries\kernel\hypertrace.dll" +copy "$(OutDir)hyperperf.dll" "$(OutDir)SDK\libraries\kernel\hyperperf.dll" +copy "$(OutDir)kdserial.dll" "$(OutDir)SDK\libraries\kernel\kdserial.dll" if exist "$(OutDir)constants\" rd /q /s "$(OutDir)constants\" mkdir "$(OutDir)constants" copy "$(SolutionDir)miscellaneous\constants\pciid\pci.ids" "$(OutDir)constants\pci.ids" diff --git a/hyperdbg/hyperdbg.sln b/hyperdbg/hyperdbg.sln index fff4dc56..9d71b79f 100644 --- a/hyperdbg/hyperdbg.sln +++ b/hyperdbg/hyperdbg.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32602.215 +# Visual Studio Version 18 +VisualStudioVersion = 18.7.11911.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hyperdbg-cli", "hyperdbg-cli\hyperdbg-cli.vcxproj", "{FBCBBBAD-4EAE-469E-827F-F59FE9E7375B}" ProjectSection(ProjectDependencies) = postProject @@ -90,6 +90,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "imports", "imports", "{B3D9 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hyperkd", "hyperkd\hyperkd.vcxproj", "{AFDD7028-1ED9-442E-8A3D-01CFA3AA1CAA}" ProjectSection(ProjectDependencies) = postProject + {360E54B1-0B92-4BCA-8111-4BF384292621} = {360E54B1-0B92-4BCA-8111-4BF384292621} {9FA45E25-DAEB-4C2D-806C-7908A180195D} = {9FA45E25-DAEB-4C2D-806C-7908A180195D} {AFDE69E9-EE3D-470E-8407-C1F0D98F9E3D} = {AFDE69E9-EE3D-470E-8407-C1F0D98F9E3D} {BB17323A-2460-4AE1-8AFE-B367400B934F} = {BB17323A-2460-4AE1-8AFE-B367400B934F} @@ -101,6 +102,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{13E4 ProjectSection(SolutionItems) = preProject include\SDK\modules\HyperEvade.h = include\SDK\modules\HyperEvade.h include\SDK\Modules\HyperLog.h = include\SDK\Modules\HyperLog.h + include\SDK\modules\HyperPerf.h = include\SDK\modules\HyperPerf.h include\SDK\modules\HyperTrace.h = include\SDK\modules\HyperTrace.h include\SDK\Modules\VMM.h = include\SDK\Modules\VMM.h EndProjectSection @@ -207,6 +209,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "kernel", "kernel", "{947577 include\SDK\imports\kernel\HyperDbgHyperEvade.h = include\SDK\imports\kernel\HyperDbgHyperEvade.h include\SDK\Imports\Kernel\HyperDbgHyperLogImports.h = include\SDK\Imports\Kernel\HyperDbgHyperLogImports.h include\SDK\Imports\Kernel\HyperDbgHyperLogIntrinsics.h = include\SDK\Imports\Kernel\HyperDbgHyperLogIntrinsics.h + include\SDK\imports\kernel\HyperDbgHyperPerf.h = include\SDK\imports\kernel\HyperDbgHyperPerf.h include\SDK\imports\kernel\HyperDbgHyperTrace.h = include\SDK\imports\kernel\HyperDbgHyperTrace.h include\SDK\Imports\Kernel\HyperDbgVmmImports.h = include\SDK\Imports\Kernel\HyperDbgVmmImports.h EndProjectSection @@ -327,6 +330,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "header", "header", "{CA2D9C include\components\pe\header\pe-image-reader.h = include\components\pe\header\pe-image-reader.h EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hyperperf", "hyperperf\hyperperf.vcxproj", "{360E54B1-0B92-4BCA-8111-4BF384292621}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution debug|x64 = debug|x64 @@ -384,6 +389,12 @@ Global {9FA45E25-DAEB-4C2D-806C-7908A180195D}.debug|x64.Build.0 = debug|x64 {9FA45E25-DAEB-4C2D-806C-7908A180195D}.release|x64.ActiveCfg = release|x64 {9FA45E25-DAEB-4C2D-806C-7908A180195D}.release|x64.Build.0 = release|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.debug|x64.ActiveCfg = debug|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.debug|x64.Build.0 = debug|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.debug|x64.Deploy.0 = debug|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.release|x64.ActiveCfg = release|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.release|x64.Build.0 = release|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.release|x64.Deploy.0 = release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/hyperdbg/hyperperf/CMakeLists.txt b/hyperdbg/hyperperf/CMakeLists.txt new file mode 100644 index 00000000..d6b1310f --- /dev/null +++ b/hyperdbg/hyperperf/CMakeLists.txt @@ -0,0 +1,22 @@ +# Code generated by Visual Studio kit, DO NOT EDIT. +set(SourceFiles + "../include/components/spinlock/code/Spinlock.c" + "../include/platform/kernel/code/PlatformMem.c" + "code/Logging.c" + "code/UnloadDll.c" + "../include/components/spinlock/header/Spinlock.h" + "../include/platform/kernel/header/Environment.h" + "../include/platform/kernel/header/PlatformMem.h" + "header/Logging.h" + "header/pch.h" + "header/UnloadDll.h" + "hyperperf.def" +) +include_directories( + "../include" + "header" +) +wdk_add_library(hyperperf SHARED + KMDF 1.15 + ${SourceFiles} +) diff --git a/hyperdbg/hyperperf/code/api/PerfApi.c b/hyperdbg/hyperperf/code/api/PerfApi.c new file mode 100644 index 00000000..59cef4c4 --- /dev/null +++ b/hyperdbg/hyperperf/code/api/PerfApi.c @@ -0,0 +1,84 @@ +/** + * @file PerfApi.c + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief PMU routines for HyperPerf module + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + */ +#include "pch.h" + +/** + * @brief Initialize the hyperperf module callbacks + * @details This only for callback initialization, not for PMU, etc. initialization + * + * @param HyperPerfCallbacks Pointer to the HyperPerf callbacks structure to be registered + * @param RunningOnHypervisorEnvironment Whether the initialization is being done for hypervisor environment or not, + * it can be used to skip some of the initialization steps if it is not for hypervisor environment and behave differently based on that + * + * @return BOOLEAN + */ +BOOLEAN +HyperPerfInitCallback(HYPERPERF_CALLBACKS * HyperPerfCallbacks, + BOOLEAN RunningOnHypervisorEnvironment) +{ + // + // Check if any of the required callbacks are NULL + // + for (UINT32 i = 0; i < sizeof(HYPERPERF_CALLBACKS) / sizeof(UINT64); i++) + { + if (((PVOID *)HyperPerfCallbacks)[i] == NULL) + { + // + // The callback has null entry, so we cannot proceed + // + return FALSE; + } + } + + // + // Save the callbacks + // + PlatformWriteMemory(&g_Callbacks, HyperPerfCallbacks, sizeof(HYPERPERF_CALLBACKS)); + + // + // Set the flag to indicate whether the initialization is being done for hypervisor environment or not + // + g_RunningOnHypervisorEnvironment = RunningOnHypervisorEnvironment; + + // + // Enable callbacks and set the initialized flag + // + g_HyperPerfCallbacksInitialized = TRUE; + + return TRUE; +} + +/** + * @brief Uninitialize the hypertrace module + * + * @return VOID + */ +VOID +HyperPerfUninit() +{ + // + // Check if the callbacks are initialized, if not, we don't need to handle anymore + // + if (!g_HyperPerfCallbacksInitialized) + { + return; + } + + // + // Reset the environment flag to default value + // + g_RunningOnHypervisorEnvironment = FALSE; + + // + // Set callbacks to not initialized + // + g_HyperPerfCallbacksInitialized = FALSE; +} diff --git a/hyperdbg/hyperperf/code/broadcast/Broadcast.c b/hyperdbg/hyperperf/code/broadcast/Broadcast.c new file mode 100644 index 00000000..ad97bd34 --- /dev/null +++ b/hyperdbg/hyperperf/code/broadcast/Broadcast.c @@ -0,0 +1,26 @@ +/** + * @file Broadcast.c + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Broadcasting functions + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +/** + * @brief Routines to enable LBR on all cores + * + * @return VOID + */ +VOID +BroadcastEnableLbrOnAllCores() +{ + // + // Broadcast to all cores + // + KeGenericCallDpc(DpcRoutineTestPmu, NULL); +} diff --git a/hyperdbg/hyperperf/code/broadcast/DpcRoutines.c b/hyperdbg/hyperperf/code/broadcast/DpcRoutines.c new file mode 100644 index 00000000..f5443618 --- /dev/null +++ b/hyperdbg/hyperperf/code/broadcast/DpcRoutines.c @@ -0,0 +1,35 @@ +/** + * @file DpcRoutines.c + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief DPC routines + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +/** + * @brief Broadcast enabling LBR + * + * @param Dpc + * @param DeferredContext + * @param SystemArgument1 + * @param SystemArgument2 + * @return BOOLEAN + */ +BOOLEAN +DpcRoutineTestPmu(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) +{ + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(DeferredContext); + + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + + return TRUE; +} diff --git a/hyperdbg/hyperperf/code/common/UnloadDll.c b/hyperdbg/hyperperf/code/common/UnloadDll.c new file mode 100644 index 00000000..17321dbf --- /dev/null +++ b/hyperdbg/hyperperf/code/common/UnloadDll.c @@ -0,0 +1,45 @@ +/** + * @file UnloadDll.c + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Unloading DLL in the target Windows + * @details + * @version 0.4 + * @date 2023-07-06 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +// +// We'll add these functions, so whenever HyperDbg's driver is unloaded +// DllUnload will be called to unload this dll from the memory. +// this way we can remove the HyperDbg after unloading as there is no +// other module remains loaded in the memory. +// + +/** + * @brief Routine called on DLL initialization + * + * @param RegistryPath The registry path of the driver + * @return NTSTATUS + */ +NTSTATUS +DllInitialize( + _In_ PUNICODE_STRING RegistryPath) +{ + UNREFERENCED_PARAMETER(RegistryPath); + + return STATUS_SUCCESS; +} + +/** + * @brief Routine called on DLL unload + * + * @return NTSTATUS + */ +NTSTATUS +DllUnload(VOID) +{ + return STATUS_SUCCESS; +} diff --git a/hyperdbg/hyperperf/header/api/PerfApi.h b/hyperdbg/hyperperf/header/api/PerfApi.h new file mode 100644 index 00000000..660f4d20 --- /dev/null +++ b/hyperdbg/hyperperf/header/api/PerfApi.h @@ -0,0 +1,19 @@ +/** + * @file PerfApi.h + * @author + * @brief Header for general PMU routines for HyperPerf module + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + */ +#pragma once + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +// +// Most of the functions are defined and exported +// diff --git a/hyperdbg/hyperperf/header/broadcast/Broadcast.h b/hyperdbg/hyperperf/header/broadcast/Broadcast.h new file mode 100644 index 00000000..8698872f --- /dev/null +++ b/hyperdbg/hyperperf/header/broadcast/Broadcast.h @@ -0,0 +1,20 @@ + +/** + * @file Broadcast.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers for broadcasting functions + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +VOID +BroadcastTestPmuOnAllCores(); diff --git a/hyperdbg/hyperperf/header/broadcast/DpcRoutines.h b/hyperdbg/hyperperf/header/broadcast/DpcRoutines.h new file mode 100644 index 00000000..397ef8b1 --- /dev/null +++ b/hyperdbg/hyperperf/header/broadcast/DpcRoutines.h @@ -0,0 +1,20 @@ + +/** + * @file DpcRoutines.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Definition for DPC functions + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +BOOLEAN +DpcRoutineTestPmu(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2); diff --git a/hyperdbg/hyperperf/header/common/UnloadDll.h b/hyperdbg/hyperperf/header/common/UnloadDll.h new file mode 100644 index 00000000..9bb252d2 --- /dev/null +++ b/hyperdbg/hyperperf/header/common/UnloadDll.h @@ -0,0 +1,22 @@ +/** + * @file UnloadDll.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers for unloading DLL in the target Windows + * + * @version 0.4 + * @date 2023-07-06 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Exported Functions // +////////////////////////////////////////////////// + +__declspec(dllexport) NTSTATUS +DllInitialize(_In_ PUNICODE_STRING RegistryPath); + +__declspec(dllexport) NTSTATUS + DllUnload(VOID); diff --git a/hyperdbg/hyperperf/header/globals/GlobalVariables.h b/hyperdbg/hyperperf/header/globals/GlobalVariables.h new file mode 100644 index 00000000..79c0f02e --- /dev/null +++ b/hyperdbg/hyperperf/header/globals/GlobalVariables.h @@ -0,0 +1,35 @@ + +/** + * @file GlobalVariables.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Definition for global variables + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Global Variables // +////////////////////////////////////////////////// + +/** + * @brief List of callbacks + * + */ +HYPERPERF_CALLBACKS g_Callbacks; + +/** + * @brief The flag indicating whether the hyperperf module callbacks is initialized or not + * + */ +BOOLEAN g_HyperPerfCallbacksInitialized; + +/** + * @brief The flag indicating whether the initialization is being done for hypervisor environment or not + * + */ +BOOLEAN g_RunningOnHypervisorEnvironment; diff --git a/hyperdbg/hyperperf/header/pch.h b/hyperdbg/hyperperf/header/pch.h new file mode 100644 index 00000000..35e14b66 --- /dev/null +++ b/hyperdbg/hyperperf/header/pch.h @@ -0,0 +1,111 @@ + +/** + * @file pch.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers of Message logging and tracing + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#define _NO_CRT_STDIO_INLINE + +#pragma warning(disable : 4201) // Suppress nameless struct/union warning + +// +// Environment headers +// +#include "platform/general/header/Environment.h" + +#ifdef HYPERDBG_ENV_WINDOWS + +// +// Windows defined functions +// +# include +# include +# include + +#endif // HYPERDBG_ENV_WINDOWS + +// +// Scope definitions +// +#define HYPERDBG_KERNEL_MODE +#define HYPERDBG_HYPERPERF + +// +// Add ia32-doc +// +#include "ia32-doc/out/ia32.h" + +// +// SDK headers +// +#include "SDK/HyperDbgSdk.h" + +// +// Configuration +// +#include "config/Configuration.h" + +// +// Platform independent headers +// +#include "platform/kernel/header/PlatformMem.h" +#include "platform/kernel/header/PlatformIntrinsics.h" +#include "platform/kernel/header/PlatformBroadcast.h" +#include "platform/kernel/header/PlatformCpu.h" +#include "platform/kernel/header/PlatformSpinlock.h" +#include "platform/kernel/header/PlatformIrql.h" +#include "platform/kernel/header/PlatformDpc.h" +#include "platform/kernel/header/PlatformTime.h" +#include "platform/kernel/header/PlatformDbg.h" +#include "platform/kernel/header/PlatformIo.h" +#include "platform/kernel/header/PlatformEvent.h" + +// +// Unload function (to be called when the driver is unloaded) +// +#include "common/UnloadDll.h" + +// +// Hyperlog headers +// +#include "components/callback/header/HyperLogCallback.h" +#include "SDK/imports/kernel/HyperDbgHyperLogIntrinsics.h" + +// +// Spinlock headers +// +#include "components/spinlock/header/Spinlock.h" + +// +// HyperPerf Callbacks +// +#include "SDK/modules/HyperPerf.h" + +// +// Definition of general tracing types +// +#include "api/PerfApi.h" + +// +// DPC and broadcasting function headers +// +#include "broadcast/DpcRoutines.h" +#include "broadcast/Broadcast.h" + +// +// Export functions +// +#include "SDK/imports/kernel/HyperDbgHyperPerf.h" + +// +// Global variables +// +#include "globals/GlobalVariables.h" diff --git a/hyperdbg/hyperperf/header/pt/Pt.h b/hyperdbg/hyperperf/header/pt/Pt.h new file mode 100644 index 00000000..07a10506 --- /dev/null +++ b/hyperdbg/hyperperf/header/pt/Pt.h @@ -0,0 +1,167 @@ +/** + * @file Pt.h + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) + * @brief Header for Processor Trace (PT) tracing routines for HyperTrace module + * @details Engine that programs Intel PT MSRs from VMX root or kernel context. + * Buffer / ToPA management is kept here; user-visible PT structures + * live in the SDK header [PtDefinitions.h]. + * @version 0.19 + * @date 2026-04-29 + * + * @copyright This project is released under the GNU Public License v3. + */ +#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. + * + * One MDL + user VA per CPU describes the main output buffer + * immediately followed by the 4 KB overflow page as a single + * virtually contiguous region in the mapping process. Lives in + * g_PtUserMappings; lifetime tied to the PT enable cycle. + */ +typedef struct _PT_USER_MAPPING +{ + PMDL Mdl; + PVOID UserVa; + +} PT_USER_MAPPING, *PPT_USER_MAPPING; + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +// +// HyperDbg-style wrappers (mirroring Lbr*) +// + +BOOLEAN +PtCheck(); + +BOOLEAN +PtStart(); + +VOID +PtStop(); + +VOID +PtPause(); + +VOID +PtResume(); + +UINT64 +PtSize(); + +VOID +PtDump(); + +VOID +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 +// (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); + +// +// PASSIVE_LEVEL helpers — call before / after the per-core DPC broadcasts. +// Required because MmAllocateContiguousMemorySpecifyCache and +// MmFreeContiguousMemory must run at IRQL == PASSIVE_LEVEL. +// + +BOOLEAN +PtAllocateAllCpuBuffers(); + +VOID +PtFreeAllCpuBuffers(); + +// +// User-mode mmap surface: map every per-CPU main output + overflow +// buffer into the calling user process. Idempotent within an enable +// cycle; torn down by PtFreeAllCpuBuffers (i.e. PT disable / flush). +// +INT32 +PtMmapAllCpuBuffersToUser(PT_USER_BUFFER_DESC * OutDescs, UINT32 MaxDescs, UINT32 * OutNumCpus); + +VOID +PtUnmapAllCpuBuffersFromUser(); + +// +// Engine routines (operate on a specific PT_PER_CPU instance) +// + +INT32 +PtEngineQueryCapabilities(PT_CAPABILITIES * OutCaps); + +VOID +PtEngineInitDefaultConfig(PT_TRACE_CONFIG * Config); + +INT32 +PtEngineAllocateBuffers(PT_PER_CPU * Cpu, const PT_TRACE_CONFIG * Config); + +VOID +PtEngineFreeBuffers(PT_PER_CPU * Cpu); + +INT32 +PtEngineStart(PT_PER_CPU * Cpu); + +UINT64 +PtEngineStop(PT_PER_CPU * Cpu, PT_OUTPUT_BUFFER * Out); + +INT32 +PtEnginePause(PT_PER_CPU * Cpu); + +INT32 +PtEngineResume(PT_PER_CPU * Cpu); + +UINT64 +PtEngineHandlePmi(PT_PER_CPU * Cpu, PT_OUTPUT_BUFFER * Out); + +BOOLEAN +PtEngineIsPtPmi(); + +INT32 +PtEngineSizeToTopaEncoding(UINT64 SizeInBytes); diff --git a/hyperdbg/hyperperf/hyperperf.def b/hyperdbg/hyperperf/hyperperf.def new file mode 100644 index 00000000..1415f880 --- /dev/null +++ b/hyperdbg/hyperperf/hyperperf.def @@ -0,0 +1,6 @@ +LIBRARY hyperperf + +EXPORTS + + DllInitialize PRIVATE + DllUnload PRIVATE \ No newline at end of file diff --git a/hyperdbg/hyperperf/hyperperf.vcxproj b/hyperdbg/hyperperf/hyperperf.vcxproj new file mode 100644 index 00000000..20624659 --- /dev/null +++ b/hyperdbg/hyperperf/hyperperf.vcxproj @@ -0,0 +1,145 @@ + + + + + + + + debug + x64 + + + release + x64 + + + + {360E54B1-0B92-4BCA-8111-4BF384292621} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 12.0 + Debug + x64 + hyperperf + $(LatestTargetPlatformVersion) + + + + Windows10 + true + WindowsKernelModeDriver10.0 + DynamicLibrary + KMDF + Desktop + false + + + Windows10 + false + WindowsKernelModeDriver10.0 + DynamicLibrary + KMDF + Desktop + false + + + + + + + + + + + DbgengKernelDebugger + $(SolutionDir)build\bin\$(Configuration)\ + $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false + + + DbgengKernelDebugger + $(SolutionDir)build\bin\$(Configuration)\ + $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false + + + + sha256 + + + $(SolutionDir)\include;$(ProjectDir)header;$(SolutionDir)dependencies;%(AdditionalIncludeDirectories) + true + Create + pch.h + stdcpp20 + + + true + + true + hyperperf.def + + + + + sha256 + + + $(SolutionDir)\include;$(ProjectDir)header;$(SolutionDir)dependencies;%(AdditionalIncludeDirectories) + true + Create + pch.h + stdcpp20 + Full + + + true + + true + hyperperf.def + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperperf/hyperperf.vcxproj.filters b/hyperdbg/hyperperf/hyperperf.vcxproj.filters new file mode 100644 index 00000000..d4254192 --- /dev/null +++ b/hyperdbg/hyperperf/hyperperf.vcxproj.filters @@ -0,0 +1,119 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {1ab177b4-9e6c-460e-834a-8fced04b42a3} + + + {21f0281e-fc2a-4e13-97ac-e4b35a05a31e} + + + {09d5457a-bade-4a3f-a171-641110492d57} + + + {b8b32a09-61fb-433a-ad79-eb7cfc5f3437} + + + {32ea8333-847b-443d-8992-4140d54dc1d3} + + + {df6eb164-34b2-4f2a-9530-b88443662fb7} + + + {cb4e742a-6e43-4798-af4a-72bcb11089c9} + + + {ab21116d-5f5b-4ef8-82bb-6585ac3dec95} + + + {57d56081-eede-41a3-b9f0-e7019d32c986} + + + {4f62540b-d186-479e-83a0-1b550134f487} + + + {9fe877a7-e261-4579-9752-a3156b6e69d9} + + + {8bc2336a-b1c5-4e93-9793-23a5cfdc741d} + + + {fc73555b-2be3-4898-bcdb-df83fa3e1388} + + + + + code\platform + + + code\broadcast + + + code\broadcast + + + code\common + + + code\api + + + code\platform + + + code\broadcast + + + code\platform + + + code\components\callback + + + + + header\platform + + + header\broadcast + + + header\broadcast + + + header\common + + + header\globals + + + header\api + + + header\platform + + + header\platform + + + header\platform + + + header\components\callback + + + header + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperperf/packages.config b/hyperdbg/hyperperf/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperperf/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hypertrace/code/api/TraceApi.c b/hyperdbg/hypertrace/code/api/TraceApi.c index 709f5c6f..94f3e961 100644 --- a/hyperdbg/hypertrace/code/api/TraceApi.c +++ b/hyperdbg/hypertrace/code/api/TraceApi.c @@ -11,7 +11,7 @@ #include "pch.h" /** - * @brief Initialize the hyper trace module callbacks + * @brief Initialize the hypertrace module callbacks * @details This only for callback initialization, not for LBR, PT, etc. initialization * * @param HyperTraceCallbacks Pointer to the HyperTrace callbacks structure to be registered diff --git a/hyperdbg/include/SDK/imports/kernel/HyperDbgHyperPerf.h b/hyperdbg/include/SDK/imports/kernel/HyperDbgHyperPerf.h new file mode 100644 index 00000000..f6997795 --- /dev/null +++ b/hyperdbg/include/SDK/imports/kernel/HyperDbgHyperPerf.h @@ -0,0 +1,33 @@ +/** + * @file HyperDbgHyperPerf.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers relating exported functions from hyperperf (pmu) module + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#ifdef HYPERDBG_HYPERPERF +# define IMPORT_EXPORT_HYPERPERF __declspec(dllexport) +#else +# define IMPORT_EXPORT_HYPERPERF __declspec(dllimport) +#endif + +////////////////////////////////////////////////// +// HyperPerf Functions // +////////////////////////////////////////////////// + +// +// Initialize the hyperperf module with the provided callbacks +// +IMPORT_EXPORT_HYPERPERF BOOLEAN +HyperPerfInitCallback(HYPERPERF_CALLBACKS * HyperPerfCallbacks, BOOLEAN RunningOnHypervisorEnvironment); + +// +// Uninitialize the HyperPerf module +// +IMPORT_EXPORT_HYPERPERF VOID +HyperPerfUninit(); diff --git a/hyperdbg/include/SDK/modules/HyperPerf.h b/hyperdbg/include/SDK/modules/HyperPerf.h new file mode 100644 index 00000000..fe76e097 --- /dev/null +++ b/hyperdbg/include/SDK/modules/HyperPerf.h @@ -0,0 +1,80 @@ +/** + * @file HyperPerf.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief HyperDbg's SDK for hyperperf project + * @details This file contains definitions of HyperPerf routines + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Callback Types // +////////////////////////////////////////////////// + +/** + * @brief A function from the message tracer that send the inputs to the + * queue of the messages + * + */ +typedef BOOLEAN (*LOG_CALLBACK_PREPARE_AND_SEND_MESSAGE_TO_QUEUE)(UINT32 OperationCode, + BOOLEAN IsImmediateMessage, + BOOLEAN ShowCurrentSystemTime, + BOOLEAN Priority, + const CHAR * Fmt, + va_list ArgList); + +/** + * @brief A function that sends the messages to message tracer buffers + * + */ +typedef BOOLEAN (*LOG_CALLBACK_SEND_MESSAGE_TO_QUEUE)(UINT32 OperationCode, BOOLEAN IsImmediateMessage, CHAR * LogMessage, UINT32 BufferLen, BOOLEAN Priority); + +/** + * @brief A function that sends the messages to message tracer buffers + * + */ +typedef BOOLEAN (*LOG_CALLBACK_SEND_BUFFER)(_In_ UINT32 OperationCode, + _In_reads_bytes_(BufferLength) PVOID Buffer, + _In_ UINT32 BufferLength, + _In_ BOOLEAN Priority); + +/** + * @brief A function that checks whether the priority or regular buffer is full or not + * + */ +typedef BOOLEAN (*LOG_CALLBACK_CHECK_IF_BUFFER_IS_FULL)(BOOLEAN Priority); + +/** + * @brief A function that checks whether the current execution mode is VMX-root mode or not + * + */ +typedef BOOLEAN (*VM_FUNC_VMX_GET_CURRENT_EXECUTION_MODE)(); + +////////////////////////////////////////////////// +// Callback Structure // +////////////////////////////////////////////////// + +/** + * @brief Prototype of each function needed by hyperperf module + * + */ +typedef struct _HYPERPERF_CALLBACKS +{ + // + // *** Log (Hyperlog) callbacks *** + // + LOG_CALLBACK_PREPARE_AND_SEND_MESSAGE_TO_QUEUE LogCallbackPrepareAndSendMessageToQueueWrapper; + LOG_CALLBACK_SEND_MESSAGE_TO_QUEUE LogCallbackSendMessageToQueue; + LOG_CALLBACK_SEND_BUFFER LogCallbackSendBuffer; + LOG_CALLBACK_CHECK_IF_BUFFER_IS_FULL LogCallbackCheckIfBufferIsFull; + + // + // *** Hypervisor (Hyperhv) callbacks *** + // + VM_FUNC_VMX_GET_CURRENT_EXECUTION_MODE VmFuncVmxGetCurrentExecutionMode; + +} HYPERPERF_CALLBACKS, *PHYPERPERF_CALLBACKS; From edf9f269d4d435ceda7466fcdd4bcb3d7d2618a7 Mon Sep 17 00:00:00 2001 From: sina Date: Tue, 23 Jun 2026 15:02:01 +0200 Subject: [PATCH 44/57] refactor Intel PT DPC broadcasting routines --- .../hypertrace/code/broadcast/DpcRoutines.c | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c b/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c index b9cc478e..0bd904a2 100644 --- a/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c +++ b/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c @@ -198,8 +198,11 @@ DpcRoutineEnablePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVO // PtStart(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -214,8 +217,11 @@ DpcRoutineDisablePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PV PtStop(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -230,8 +236,11 @@ DpcRoutinePausePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOI PtPause(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -246,8 +255,11 @@ DpcRoutineResumePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVO PtResume(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -269,8 +281,11 @@ DpcRoutineSizePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID if (Sizes != NULL && Core < PT_MAX_CPUS_FOR_MMAP) Sizes[Core] = PtSize(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -285,8 +300,11 @@ DpcRoutineDumpPt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID PtDump(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -301,8 +319,11 @@ DpcRoutineFlushPt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOI PtFlush(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -320,7 +341,10 @@ DpcRoutineFilterPt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVO PtFilter((const PT_FILTER_OPTIONS *)DeferredContext); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } From a0973894e874ab3fce5147aec7e52abb63d7ca77 Mon Sep 17 00:00:00 2001 From: jtaw5649 <213313463+jtaw5649@users.noreply.github.com> Date: Wed, 24 Jun 2026 16:45:25 +0100 Subject: [PATCH 45/57] fix: avoid pool list cursor use after free --- .../code/debugger/memory/PoolManager.c | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/hyperdbg/hyperkd/code/debugger/memory/PoolManager.c b/hyperdbg/hyperkd/code/debugger/memory/PoolManager.c index f676e3e7..3c195e68 100644 --- a/hyperdbg/hyperkd/code/debugger/memory/PoolManager.c +++ b/hyperdbg/hyperkd/code/debugger/memory/PoolManager.c @@ -92,7 +92,7 @@ PoolManagerInitialize() VOID PoolManagerUninitialize() { - PLIST_ENTRY ListTemp = &g_ListOfAllocatedPoolsHead; + PLIST_ENTRY Link; // // Pool manager is not initialized anymore @@ -101,14 +101,16 @@ PoolManagerUninitialize() SpinlockLock(&LockForReadingPool); - while (&g_ListOfAllocatedPoolsHead != ListTemp->Flink) + Link = g_ListOfAllocatedPoolsHead.Flink; + + while (Link != &g_ListOfAllocatedPoolsHead) { - ListTemp = ListTemp->Flink; + PLIST_ENTRY Next = Link->Flink; // // Get the head of the record // - PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(ListTemp, POOL_TABLE, PoolsList); + PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(Link, POOL_TABLE, PoolsList); // // Free the alloocated buffer (if not already changed) @@ -121,14 +123,20 @@ PoolManagerUninitialize() // // Unlink the PoolTable // - RemoveEntryList(&PoolTable->PoolsList); + RemoveEntryList(Link); // // Free the record itself // PlatformMemFreePool(PoolTable); + + Link = Next; } + InitializeListHead(&g_ListOfAllocatedPoolsHead); + g_IsNewRequestForDeAllocation = FALSE; + g_IsNewRequestForAllocationReceived = FALSE; + SpinlockUnlock(&LockForReadingPool); PlmgrFreeRequestNewAllocation(); @@ -311,8 +319,7 @@ PoolManagerAllocateAndAddToPoolTable(SIZE_T Size, UINT32 Count, POOL_ALLOCATION_ BOOLEAN PoolManagerCheckAndPerformAllocationAndDeallocation() { - BOOLEAN Result = TRUE; - PLIST_ENTRY ListTemp = 0; + BOOLEAN Result = TRUE; // // Make sure we're on vmx non-root and also we have new allocation @@ -364,16 +371,16 @@ PoolManagerCheckAndPerformAllocationAndDeallocation() // if (g_IsNewRequestForDeAllocation) { - ListTemp = &g_ListOfAllocatedPoolsHead; + PLIST_ENTRY Link = g_ListOfAllocatedPoolsHead.Flink; - while (&g_ListOfAllocatedPoolsHead != ListTemp->Flink) + while (Link != &g_ListOfAllocatedPoolsHead) { - ListTemp = ListTemp->Flink; + PLIST_ENTRY Next = Link->Flink; // // Get the head of the record // - PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(ListTemp, POOL_TABLE, PoolsList); + PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(Link, POOL_TABLE, PoolsList); // // Check whether this pool should be freed or not and @@ -394,13 +401,15 @@ PoolManagerCheckAndPerformAllocationAndDeallocation() // // Now we should remove the entry from the g_ListOfAllocatedPoolsHead // - RemoveEntryList(&PoolTable->PoolsList); + RemoveEntryList(Link); // // Free the structure pool // PlatformMemFreePool(PoolTable); } + + Link = Next; } } From b537e36696207a7e2a374739651d1df85f489347 Mon Sep 17 00:00:00 2001 From: sina Date: Wed, 24 Jun 2026 18:48:56 +0200 Subject: [PATCH 46/57] add hyperperf to version changer to fix CI CD and update changelog --- CHANGELOG.md | 1 + utils/replace-sdk-wdk.py | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89a9adf4..0bac0883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ New release of the HyperDbg Debugger. ### Changed - Separated SDK libraries for user mode and kernel mode modules - Added hypertrace, hyperevade, and hyperperf DLL files to SDK +- Fix pool manager uninit list corruption ([link](https://github.com/HyperDbg/HyperDbg/pull/629)) ## [0.20.0.0-beta] - 2026-06-21 New release of the HyperDbg Debugger. diff --git a/utils/replace-sdk-wdk.py b/utils/replace-sdk-wdk.py index 48a9cd73..ffecbc45 100644 --- a/utils/replace-sdk-wdk.py +++ b/utils/replace-sdk-wdk.py @@ -15,6 +15,7 @@ TARGET_PROJECTS = [ "hypertrace.vcxproj", "hyperlog.vcxproj", "hyperevade.vcxproj", + "hyperperf.vcxproj", "kdserial.vcxproj", "hyperdbg_driver.vcxproj", ] From 19e47c804f50f5dbb698f314e66b7d685a87ac1f Mon Sep 17 00:00:00 2001 From: sina Date: Mon, 29 Jun 2026 00:56:09 +0200 Subject: [PATCH 47/57] check if any module is loaded or not --- CHANGELOG.md | 2 ++ .../SDK/imports/user/HyperDbgLibImports.h | 3 +++ hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp | 23 +++++++++++++++++++ .../commands/debugging-commands/unload.cpp | 9 ++++++++ hyperdbg/libhyperdbg/code/export/export.cpp | 11 +++++++++ hyperdbg/libhyperdbg/header/libhyperdbg.h | 3 +++ 6 files changed, 51 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bac0883..aa3e63a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ New release of the HyperDbg Debugger. ### Added - Added structure for the hyperperf (Hardware Performance Counter) project +- The 'unload' command checks whether any module is loaded or not ([link](https://docs.hyperdbg.org/commands/debugging-commands/unload)) +- Exported SDK API for checking whether any module is loaded or not ### Changed - Separated SDK libraries for user mode and kernel mode modules diff --git a/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h b/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h index 5485c07f..2491fca1 100644 --- a/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h +++ b/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h @@ -49,6 +49,9 @@ hyperdbg_u_get_processor_vendor(); // // All Modules // +IMPORT_EXPORT_LIBHYPERDBG BOOLEAN +hyperdbg_u_is_any_module_loaded(); + IMPORT_EXPORT_LIBHYPERDBG INT hyperdbg_u_load_all_modules(); diff --git a/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp b/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp index a76945ce..7e9d1214 100644 --- a/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp +++ b/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp @@ -595,6 +595,29 @@ HyperDbgUnloadKd() return 0; } +/** + * @brief check if any module is loaded (KD, VMM, HyperTrace, etc.) + * + * @return BOOLEAN return TRUE if any module is loaded, otherwise return FALSE + */ +BOOLEAN +HyperDbgIsAnyModuleLoaded() +{ + INT RetVal = 0; + + // + // Check if any module is loaded (KD, VMM, HyperTrace, etc.) + // + if (g_IsKdModuleLoaded || g_IsVmmModuleLoaded || g_IsHyperTraceModuleLoaded) + { + return TRUE; + } + else + { + return FALSE; + } +} + /** * @brief unload all modules (KD, VMM, HyperTrace, etc.) * diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/unload.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/unload.cpp index 45726689..0738fc1b 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/unload.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/unload.cpp @@ -167,6 +167,15 @@ CommandUnload(vector CommandTokens, string Command) return; } + // + // Check if any module is loaded + // + if (!HyperDbgIsAnyModuleLoaded()) + { + ShowMessages("no module is loaded\n"); + return; + } + ShowMessages("unloading all modules\n"); // diff --git a/hyperdbg/libhyperdbg/code/export/export.cpp b/hyperdbg/libhyperdbg/code/export/export.cpp index 0649e8ae..f78f73a5 100644 --- a/hyperdbg/libhyperdbg/code/export/export.cpp +++ b/hyperdbg/libhyperdbg/code/export/export.cpp @@ -53,6 +53,17 @@ hyperdbg_u_load_vmm() return HyperDbgLoadVmmModule(); } +/** + * @brief Check if any module is loaded + * + * @return BOOLEAN Returns true if any module is loaded and false if no module is loaded + */ +BOOLEAN +hyperdbg_u_is_any_module_loaded() +{ + return HyperDbgIsAnyModuleLoaded(); +} + /** * @brief Unload all modules * diff --git a/hyperdbg/libhyperdbg/header/libhyperdbg.h b/hyperdbg/libhyperdbg/header/libhyperdbg.h index b43fce05..ca3dce77 100644 --- a/hyperdbg/libhyperdbg/header/libhyperdbg.h +++ b/hyperdbg/libhyperdbg/header/libhyperdbg.h @@ -15,6 +15,9 @@ // Functions // ////////////////////////////////////////////////// +BOOLEAN +HyperDbgIsAnyModuleLoaded(); + INT HyperDbgUnloadAllModules(); From 2b818a5116d80d466aab7b343d4aa9d1d640f082 Mon Sep 17 00:00:00 2001 From: sina Date: Mon, 29 Jun 2026 01:32:51 +0200 Subject: [PATCH 48/57] fix load all command access denied error --- CHANGELOG.md | 11 +++++----- hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp | 21 +++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3e63a6..639aa7e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,14 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 New release of the HyperDbg Debugger. ### Added -- Added structure for the hyperperf (Hardware Performance Counter) project +- Added structure for the hyperperf (Hardware Performance Counter) project ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) - The 'unload' command checks whether any module is loaded or not ([link](https://docs.hyperdbg.org/commands/debugging-commands/unload)) -- Exported SDK API for checking whether any module is loaded or not +- Exported SDK API for checking whether any module is loaded or not ([link](https://github.com/HyperDbg/HyperDbg/commit/19e47c804f50f5dbb698f314e66b7d685a87ac1f)) ### Changed -- Separated SDK libraries for user mode and kernel mode modules -- Added hypertrace, hyperevade, and hyperperf DLL files to SDK -- Fix pool manager uninit list corruption ([link](https://github.com/HyperDbg/HyperDbg/pull/629)) +- Separated SDK libraries for user mode and kernel mode modules ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) +- Added hypertrace, hyperevade, and hyperperf DLL files to SDK ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) +- Fix pool manager uninitialize list corruption ([link](https://github.com/HyperDbg/HyperDbg/pull/629)) +- Fix 'access denied' error on loading all modules using 'load all' ([link](https://docs.hyperdbg.org/commands/debugging-commands/load)) ## [0.20.0.0-beta] - 2026-06-21 New release of the HyperDbg Debugger. diff --git a/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp b/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp index 7e9d1214..5a2c812a 100644 --- a/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp +++ b/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp @@ -677,6 +677,18 @@ HyperDbgLoadKdModule() return 0; } + // + // Enable Debug privilege to the current token + // + if (!WindowsSetDebugPrivilege()) + { + ShowMessages("err, couldn't set debug privilege\n"); + return 1; + } + + // + // Create handle from the KD module + // if (HyperDbgCreateHandleFromKdModule() == 1) { // @@ -763,15 +775,6 @@ HyperDbgLoadVmmModule() return 1; } - // - // Enable Debug privilege to the current token - // - if (!WindowsSetDebugPrivilege()) - { - ShowMessages("err, couldn't set debug privilege\n"); - return 1; - } - // // Check if the processor is a genuine Intel processor (required for VT-x) // From 2b0cc18899ff532d9d590a71bf880fee5456c15f Mon Sep 17 00:00:00 2001 From: sina Date: Mon, 29 Jun 2026 18:35:14 +0200 Subject: [PATCH 49/57] Fix concatenation error for hyperkd string on the hypertrace project --- CHANGELOG.md | 3 ++- hyperdbg/hyperevade/header/SyscallFootprints.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 639aa7e6..b1a59fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ New release of the HyperDbg Debugger. - Separated SDK libraries for user mode and kernel mode modules ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) - Added hypertrace, hyperevade, and hyperperf DLL files to SDK ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) - Fix pool manager uninitialize list corruption ([link](https://github.com/HyperDbg/HyperDbg/pull/629)) -- Fix 'access denied' error on loading all modules using 'load all' ([link](https://docs.hyperdbg.org/commands/debugging-commands/load)) +- Fix 'access denied' error on loading all modules using 'load all' ([link](https://docs.hyperdbg.org/commands/debugging-commands/load))([link](https://github.com/HyperDbg/HyperDbg/commit/2b818a5116d80d466aab7b343d4aa9d1d640f082)) +- Fix concatenation error for "hyperkd" string on the hypertrace project ## [0.20.0.0-beta] - 2026-06-21 New release of the HyperDbg Debugger. diff --git a/hyperdbg/hyperevade/header/SyscallFootprints.h b/hyperdbg/hyperevade/header/SyscallFootprints.h index 40ab41dc..02e3d6a3 100644 --- a/hyperdbg/hyperevade/header/SyscallFootprints.h +++ b/hyperdbg/hyperevade/header/SyscallFootprints.h @@ -362,7 +362,7 @@ static const PWCH HV_FILES[] = { // HyperDbg Files // L"hyperhv", - L"hyperkd" + L"hyperkd", L"hyperlog", L"libhyperdbg", From 72a8aa325f81dc8c7da7a97d33be5ee60063e58c Mon Sep 17 00:00:00 2001 From: sina Date: Tue, 30 Jun 2026 20:30:25 +0200 Subject: [PATCH 50/57] add user mode PT parameter parser --- hyperdbg/hyperdbg.sln | 2 - hyperdbg/include/SDK/headers/PtDefinitions.h | 1 - .../include/SDK/headers/RequestStructures.h | 95 +- .../commands/extension-commands/pt.cpp | 941 ++++++++++++++---- 4 files changed, 829 insertions(+), 210 deletions(-) diff --git a/hyperdbg/hyperdbg.sln b/hyperdbg/hyperdbg.sln index 9d71b79f..90b129a9 100644 --- a/hyperdbg/hyperdbg.sln +++ b/hyperdbg/hyperdbg.sln @@ -391,10 +391,8 @@ Global {9FA45E25-DAEB-4C2D-806C-7908A180195D}.release|x64.Build.0 = release|x64 {360E54B1-0B92-4BCA-8111-4BF384292621}.debug|x64.ActiveCfg = debug|x64 {360E54B1-0B92-4BCA-8111-4BF384292621}.debug|x64.Build.0 = debug|x64 - {360E54B1-0B92-4BCA-8111-4BF384292621}.debug|x64.Deploy.0 = debug|x64 {360E54B1-0B92-4BCA-8111-4BF384292621}.release|x64.ActiveCfg = release|x64 {360E54B1-0B92-4BCA-8111-4BF384292621}.release|x64.Build.0 = release|x64 - {360E54B1-0B92-4BCA-8111-4BF384292621}.release|x64.Deploy.0 = release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/hyperdbg/include/SDK/headers/PtDefinitions.h b/hyperdbg/include/SDK/headers/PtDefinitions.h index bbe42791..cb827378 100644 --- a/hyperdbg/include/SDK/headers/PtDefinitions.h +++ b/hyperdbg/include/SDK/headers/PtDefinitions.h @@ -48,7 +48,6 @@ ////////////////////////////////////////////////// #define PT_PAGE_SIZE 0x1000ULL /* 4 KB */ -#define PT_DEFAULT_BUFFER_SIZE 0x200000ULL /* 2 MB */ #define PT_OVERFLOW_SIZE PT_PAGE_SIZE /* 4 KB overflow landing zone */ #define PT_MAX_ADDR_RANGES 4 diff --git a/hyperdbg/include/SDK/headers/RequestStructures.h b/hyperdbg/include/SDK/headers/RequestStructures.h index 4470cef8..fbd6510c 100644 --- a/hyperdbg/include/SDK/headers/RequestStructures.h +++ b/hyperdbg/include/SDK/headers/RequestStructures.h @@ -1355,28 +1355,71 @@ 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_DUMP, HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH, + HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DUMP, HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER, + HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PACKET, } HYPERTRACE_PT_OPERATION_REQUEST_TYPE; +/** + * @brief The maximum buffer size for PT + * + */ +#define PT_DEFAULT_BUFFER_SIZE 0x200000 // 2 MB + +/** + * @brief PT enable options structure + * + */ +typedef struct _PT_ENABLE_OPTIONS +{ + UINT32 EnableByCr3 : 1; // Enable by CR3 + UINT32 EnableByPid : 1; // Enable by PID + UINT32 EnableByTid : 1; // Enable by TID + UINT32 EnableByPname : 1; // Enable by process name + + UINT32 Pid; + UINT32 Tid; + UINT64 Cr3; + CHAR ProcessName[256]; + +} PT_ENABLE_OPTIONS, *PPT_ENABLE_OPTIONS; + +/** + * @brief PT filter options structure + * + */ +typedef struct _PT_FILTER_OPTIONS +{ + UINT32 TraceUser : 1; // Trace user mode + UINT32 TraceKernel : 1; // Trace kernel mode + + PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]; // Address ranges to filter by + +} PT_FILTER_OPTIONS, *PPT_FILTER_OPTIONS; + +/** + * @brief PT packet options structure + * + */ +typedef struct _PT_PACKET_OPTIONS +{ + UINT32 PSB : 1; // PSB packet + UINT32 PIP : 1; // PIP packet + UINT32 TSC : 1; // TSC packet + UINT32 MTC : 1; // MTC packet + UINT32 CYC : 1; // CYC packet + UINT32 TNT : 1; // TNT packet + UINT32 TIP : 1; // TIP packet + UINT32 FUP : 1; // FUP packet + UINT32 MODE : 1; // MODE packet + +} PT_PACKET_OPTIONS, *PPT_PACKET_OPTIONS; + /** * @brief The structure of HyperTrace PT result packet in HyperDbg * - * Configuration fields (TraceUser/TraceKernel/TargetCr3/BufferSize/ - * NumAddrRanges/AddrRanges) are populated by the caller for ENABLE - * and FILTER operations. For other operations they are ignored. - * - * BufferSize must be a power of two multiple of 4 KB (4KB ... 128MB). - * Pass 0 to keep the existing per-CPU value (default 2 MB on first - * enable). - * - * For SIZE operations the kernel fills NumCpus and BytesPerCpu[] - * with each CPU's current PT output position, i.e. how many bytes - * of valid trace data are currently sitting in that CPU's main + - * overflow buffer; the rest of the packet is unused on output. */ typedef struct _HYPERTRACE_PT_OPERATION_PACKETS { @@ -1384,24 +1427,20 @@ typedef struct _HYPERTRACE_PT_OPERATION_PACKETS UINT32 KernelStatus; // - // Filter / config (used by FILTER and ENABLE) + // Enable // - UINT32 TraceUser; /* Boolean: trace CPL > 0 */ - UINT32 TraceKernel; /* Boolean: trace CPL == 0 */ - UINT64 TargetCr3; /* CR3 to filter by (0 = no filter) */ - UINT64 BufferSize; /* Output buffer size (0 = keep current) */ - UINT32 NumAddrRanges; /* Number of valid AddrRanges entries */ - UINT32 TargetProcessId; /* Process to trace; kernel resolves it - to TargetCr3 when TargetCr3 == 0 - (0 = no PID-based CR3 filter) */ - PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]; + UINT64 BufferSize; /* Output buffer size (0 = default / PT_DEFAULT_BUFFER_SIZE) */ + PT_ENABLE_OPTIONS EnableOptions; /* Options for enabling PT (CPL, CR3, PID, TID, Pname) */ // - // SIZE output: per-CPU bytes-written snapshot + // Filter // - UINT32 NumCpus; /* CPUs populated in BytesPerCpu */ - UINT32 Reserved2; /* Padding to 8-align the array */ - UINT64 BytesPerCpu[PT_MAX_CPUS_FOR_MMAP]; + PT_FILTER_OPTIONS FilterOptions; /* Options for filtering PT (CPL, AddrRanges) */ + + // + // Packet + // + PT_PACKET_OPTIONS PacketOptions; /* Options for PT packets */ } HYPERTRACE_PT_OPERATION_PACKETS, *PHYPERTRACE_PT_OPERATION_PACKETS; diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp index e97e4a35..b188c6ee 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp @@ -25,39 +25,57 @@ extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee; VOID CommandPtHelp() { - ShowMessages("!pt : enables, disables and configures Intel Processor Trace (PT).\n"); + ShowMessages("!pt : enables, disables and configures Intel Processor Trace (PT).\n\n"); - ShowMessages("syntax : \t!pt [Function (string)]\n"); - ShowMessages("syntax : \t!pt filter [user] [kernel] [cr3 ] [buffer ]\n"); - ShowMessages("\t [range ] [stoprange ]\n"); + ShowMessages("syntax : \t!pt enable\n"); + ShowMessages("syntax : \t!pt enable [size BufferSize (hex)]\n"); + ShowMessages("syntax : \t!pt enable [pid ProcessId (hex)] [size BufferSize (hex)]\n"); + ShowMessages("syntax : \t!pt enable [tid ThreadId (hex)] [size BufferSize (hex)]\n"); + ShowMessages("syntax : \t!pt enable [pname ProcessName (string)] [size BufferSize (hex)]\n"); + ShowMessages("syntax : \t!pt enable [cr3 Cr3Value (hex)] [size BufferSize (hex)]\n"); + ShowMessages("syntax : \t!pt disable\n"); + ShowMessages("syntax : \t!pt pause\n"); + ShowMessages("syntax : \t!pt resume\n"); + ShowMessages("syntax : \t!pt flush\n"); + ShowMessages("syntax : \t!pt dump print [type TypeOfDump (string)]\n"); + ShowMessages("syntax : \t!pt dump path [type TypeOfDump (string)]\n"); + ShowMessages("syntax : \t!pt filter [Mode (string)]\n"); + ShowMessages("syntax : \t!pt filter [range1 FromAddress (hex) ToAddress (hex)] [range2 FromAddress (hex) ToAddress (hex)] [range3 FromAddress (hex) ToAddress (hex)] [range4 FromAddress (hex) ToAddress (hex)]\n"); + ShowMessages("syntax : \t!pt filter [range1 module ModuleName (string)] [range2 module ModuleName (string)] [range3 module ModuleName (string)] [range4 module ModuleName (string)]\n"); + ShowMessages("syntax : \t!pt filter [stoprange1 FromAddress (hex) ToAddress (hex)] [stoprange2 FromAddress (hex) ToAddress (hex)] [stoprange3 FromAddress (hex) ToAddress (hex)] [stoprange4 FromAddress (hex) ToAddress (hex)]\n"); + ShowMessages("syntax : \t!pt filter [stoprange1 module ModuleName (string)] [stoprange2 module ModuleName (string)] [stoprange3 module ModuleName (string)] [stoprange4 module ModuleName (string)]\n"); + ShowMessages("syntax : \t!pt packet [PacketType (string)]\n"); ShowMessages("\n"); ShowMessages("\t\te.g : !pt enable\n"); + ShowMessages("\t\te.g : !pt enable size 0x200000\n"); + ShowMessages("\t\te.g : !pt enable pid 0x4a8\n"); + ShowMessages("\t\te.g : !pt enable pname notepad.exe\n"); + ShowMessages("\t\te.g : !pt enable tid 0x1234\n"); + ShowMessages("\t\te.g : !pt enable cr3 0x1aabb000\n"); + ShowMessages("\t\te.g : !pt enable pid 0x4a8 size 0x200000\n"); ShowMessages("\t\te.g : !pt disable\n"); ShowMessages("\t\te.g : !pt pause\n"); ShowMessages("\t\te.g : !pt resume\n"); - ShowMessages("\t\te.g : !pt size\n"); - ShowMessages("\t\te.g : !pt dump\n"); ShowMessages("\t\te.g : !pt flush\n"); + ShowMessages("\t\te.g : !pt dump print type instruction\n"); + ShowMessages("\t\te.g : !pt dump print type packet\n"); + ShowMessages("\t\te.g : !pt dump path C:\\trace.txt type instruction\n"); + ShowMessages("\t\te.g : !pt filter user\n"); + ShowMessages("\t\te.g : !pt filter user kernel\n"); + ShowMessages("\t\te.g : !pt filter range1 0x140001000 0x140002000\n"); + ShowMessages("\t\te.g : !pt filter range1 module main\n"); + ShowMessages("\t\te.g : !pt filter range1 module ntdll range2 module nt\n"); + ShowMessages("\t\te.g : !pt filter stoprange1 0x140003000 0x140004000\n"); + ShowMessages("\t\te.g : !pt packet psb pip tsc\n"); ShowMessages("\n"); - ShowMessages("\t\te.g : !pt filter user\n"); - ShowMessages("\t\te.g : !pt filter kernel\n"); - ShowMessages("\t\te.g : !pt filter user kernel\n"); - ShowMessages("\t\te.g : !pt filter user cr3 0x1aabb000\n"); - ShowMessages("\t\te.g : !pt filter user buffer 0x100000\n"); - ShowMessages("\t\te.g : !pt filter user range 0x140001000 0x140002000\n"); - ShowMessages("\t\te.g : !pt filter user stoprange 0x140003000 0x140004000\n"); - - ShowMessages("\nlist of filter options: \n"); - ShowMessages("\t user : trace CPL > 0\n"); - ShowMessages("\t kernel : trace CPL == 0\n"); - ShowMessages("\t cr3 : only trace when CR3 matches (0 = no filter)\n"); - ShowMessages("\t buffer : per-CPU output buffer size, must be 4KB * 2^N\n"); - ShowMessages("\t (4KB, 8KB, ... up to 128MB; default 2MB)\n"); - ShowMessages("\t range : keep trace inside [start..end] (up to 4 ranges)\n"); - ShowMessages("\t stoprange : stop tracing when execution enters [s..e]\n"); - ShowMessages("\t (no option) : trace user + kernel, no CR3 / IP filter (default)\n"); + ShowMessages("Where:\n"); + ShowMessages("\t[Mode (string)] could be 'kernel' or/and 'user'\n"); + ShowMessages("\t[TypeOfDump (string)] could be 'instruction' or 'packet'\n"); + ShowMessages("\t[ModuleName (string)] could be 'main' (the main module of the process), 'nt', 'win32k', or any other module name\n"); + ShowMessages("\t[PacketType (string)] could be either or a combination of 'psb', 'pip', 'tsc', 'mtc', 'cyc', 'tnt', 'tip', 'fup', or 'mode'\n"); + ShowMessages("\n"); } /** @@ -186,127 +204,686 @@ HyperDbgPtMmapSendRequest(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest) } /** - * @brief Parse a `!pt filter ...` clause into a HYPERTRACE_PT_OPERATION_PACKETS. + * @brief Parse options for pt disable command * - * Returns TRUE on success, FALSE if the syntax was bad (caller - * should print help and bail). + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID */ -static BOOLEAN -CommandPtParseFilterOptions(vector & CommandTokens, - HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +static VOID +CommandPtParseDisable(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) { - BOOLEAN AnyMode = FALSE; + ShowMessages("PT disable requested\n"); - PtRequest->TraceUser = 0; - PtRequest->TraceKernel = 0; - PtRequest->TargetCr3 = 0; - PtRequest->BufferSize = 0; - PtRequest->NumAddrRanges = 0; + // + // Set the PtRequest structure for disable operation + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE; +} + +/** + * @brief Parse options for pt disable command + * + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParsePause(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + ShowMessages("PT pause requested\n"); + + // + // Set the PtRequest structure for pause operation + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE; +} + +/** + * @brief Parse options for pt disable command + * + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParseResume(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + ShowMessages("PT resume requested\n"); + + // + // Set the PtRequest structure for resume operation + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME; +} + +/** + * @brief Parse options for pt disable command + * + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParseFlush(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + ShowMessages("PT flush requested\n"); + + // + // Set the PtRequest structure for flush operation + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH; +} + +/** + * @brief Parse and display enable options for !pt enable command + * + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + BOOLEAN HasPid = FALSE; + BOOLEAN HasPname = FALSE; + BOOLEAN HasTid = FALSE; + BOOLEAN HasCr3 = FALSE; + BOOLEAN HasSize = FALSE; + UINT64 Pid = 0; + UINT64 Tid = 0; + UINT64 Cr3 = 0; + UINT64 Size = 0; + string Pname; for (size_t i = 2; i < CommandTokens.size(); i++) { - if (CompareLowerCaseStrings(CommandTokens.at(i), "user")) + if (CompareLowerCaseStrings(CommandTokens.at(i), "pid")) { - PtRequest->TraceUser = 1; - AnyMode = TRUE; + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, 'pid' expects a hex process ID\n\n"); + CommandPtHelp(); + return; + } + i++; + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Pid)) + { + ShowMessages("err, '%s' is not a valid hex process ID\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + HasPid = TRUE; } - else if (CompareLowerCaseStrings(CommandTokens.at(i), "kernel")) + else if (CompareLowerCaseStrings(CommandTokens.at(i), "pname")) { - PtRequest->TraceKernel = 1; - AnyMode = TRUE; + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, 'pname' expects a process name\n\n"); + CommandPtHelp(); + return; + } + i++; + Pname = GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)); + HasPname = TRUE; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "tid")) + { + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, 'tid' expects a hex thread ID\n\n"); + CommandPtHelp(); + return; + } + i++; + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Tid)) + { + ShowMessages("err, '%s' is not a valid hex thread ID\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + HasTid = TRUE; } else if (CompareLowerCaseStrings(CommandTokens.at(i), "cr3")) { if (i + 1 >= CommandTokens.size()) { - ShowMessages("err, '%s' expects a value\n", - GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + ShowMessages("err, 'cr3' expects a hex CR3 value\n\n"); + CommandPtHelp(); + return; } i++; - if (!ConvertTokenToUInt64(CommandTokens.at(i), &PtRequest->TargetCr3)) + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Cr3)) { - ShowMessages("err, '%s' is not a valid number\n", + ShowMessages("err, '%s' is not a valid hex CR3 value\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + CommandPtHelp(); + return; } + HasCr3 = TRUE; } - else if (CompareLowerCaseStrings(CommandTokens.at(i), "buffer")) + else if (CompareLowerCaseStrings(CommandTokens.at(i), "size")) { if (i + 1 >= CommandTokens.size()) { - ShowMessages("err, 'buffer' expects a size in bytes\n"); - return FALSE; + ShowMessages("err, 'size' expects a hex buffer size\n\n"); + CommandPtHelp(); + return; } i++; - if (!ConvertTokenToUInt64(CommandTokens.at(i), &PtRequest->BufferSize)) + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Size)) { - ShowMessages("err, '%s' is not a valid number\n", + ShowMessages("err, '%s' is not a valid hex size\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + CommandPtHelp(); + return; } - } - else if (CompareLowerCaseStrings(CommandTokens.at(i), "range") || - CompareLowerCaseStrings(CommandTokens.at(i), "stoprange")) - { - BOOLEAN IsStop = CompareLowerCaseStrings(CommandTokens.at(i), "stoprange"); - - if (i + 2 >= CommandTokens.size()) - { - ShowMessages("err, '%s' expects \n", - GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; - } - - if (PtRequest->NumAddrRanges >= PT_MAX_ADDR_RANGES) - { - ShowMessages("err, no more than %u address ranges supported\n", - (UINT32)PT_MAX_ADDR_RANGES); - return FALSE; - } - - UINT64 Start = 0, End = 0; - - i++; - if (!ConvertTokenToUInt64(CommandTokens.at(i), &Start)) - { - ShowMessages("err, '%s' is not a valid address\n", - GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; - } - i++; - if (!ConvertTokenToUInt64(CommandTokens.at(i), &End)) - { - ShowMessages("err, '%s' is not a valid address\n", - GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; - } - - UINT32 Idx = PtRequest->NumAddrRanges; - PtRequest->AddrRanges[Idx].Start = Start; - PtRequest->AddrRanges[Idx].End = End; - PtRequest->AddrRanges[Idx].IsStopRange = IsStop; - PtRequest->NumAddrRanges = Idx + 1; + HasSize = TRUE; } else { - ShowMessages("unknown filter option '%s'\n\n", + ShowMessages("err, unknown 'enable' option '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + CommandPtHelp(); + return; } } // - // Default to both modes if neither was specified — matches LBR's - // empty-filter behaviour. + // pid, pname, tid, cr3 are mutually exclusive target selectors // - if (!AnyMode) + INT32 SelectorCount = (HasPid ? 1 : 0) + (HasPname ? 1 : 0) + (HasTid ? 1 : 0) + (HasCr3 ? 1 : 0); + if (SelectorCount > 1) { - PtRequest->TraceUser = 1; - PtRequest->TraceKernel = 1; + ShowMessages("err, only one of 'pid', 'pname', 'tid', 'cr3' may be specified at a time\n\n"); + CommandPtHelp(); + return; } - return TRUE; + // + // Show parsed enable options + // + ShowMessages("PT enable:\n"); + + if (HasPid) + { + ShowMessages(" target pid : 0x%llx\n", Pid); + + PtRequest->EnableOptions.EnableByPid = 1; + PtRequest->EnableOptions.Pid = (UINT32)Pid; + } + else if (HasPname) + { + ShowMessages(" target pname : %s\n", Pname.c_str()); + + PtRequest->EnableOptions.EnableByCr3 = 1; + strcpy_s(PtRequest->EnableOptions.ProcessName, sizeof(PtRequest->EnableOptions.ProcessName), Pname.c_str()); + } + else if (HasTid) + { + ShowMessages(" target tid : 0x%llx\n", Tid); + + PtRequest->EnableOptions.EnableByTid = 1; + PtRequest->EnableOptions.Tid = (UINT32)Tid; + } + else if (HasCr3) + { + ShowMessages(" target cr3 : 0x%llx\n", Cr3); + + PtRequest->EnableOptions.EnableByCr3 = 1; + PtRequest->EnableOptions.Cr3 = Cr3; + } + else + { + ShowMessages(" target : all (no process/thread filter)\n"); + } + + if (HasSize) + { + ShowMessages(" buffer size : 0x%llx bytes\n", Size); + + PtRequest->BufferSize = Size; + } + else + { + ShowMessages(" buffer size : default (%llx bytes)\n", PT_DEFAULT_BUFFER_SIZE); + + PtRequest->BufferSize = PT_DEFAULT_BUFFER_SIZE; + } + + // + // Fill the PtRequest structure with parsed options + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE; +} + +/** + * @brief Parse and display !pt dump parameters + * + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParseDump(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + if (CommandTokens.size() < 3) + { + ShowMessages("err, 'dump' requires additional options\n\n"); + CommandPtHelp(); + return; + } + + if (CompareLowerCaseStrings(CommandTokens.at(2), "print")) + { + // + // !pt dump print type + // + if (CommandTokens.size() != 5 || !CompareLowerCaseStrings(CommandTokens.at(3), "type")) + { + ShowMessages("err, syntax: !pt dump print type \n\n"); + CommandPtHelp(); + return; + } + + if (!CompareLowerCaseStrings(CommandTokens.at(4), "instruction") && + !CompareLowerCaseStrings(CommandTokens.at(4), "packet")) + { + ShowMessages("err, dump type must be 'instruction' or 'packet', got '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(4)).c_str()); + CommandPtHelp(); + return; + } + + ShowMessages("PT dump to console:\n"); + ShowMessages(" output : console (print)\n"); + ShowMessages(" format : %s\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(4)).c_str()); + } + else if (CompareLowerCaseStrings(CommandTokens.at(2), "path")) + { + // + // !pt dump path type + // + if (CommandTokens.size() != 6 || !CompareLowerCaseStrings(CommandTokens.at(4), "type")) + { + ShowMessages("err, syntax: !pt dump path type \n\n"); + CommandPtHelp(); + return; + } + + if (!CompareLowerCaseStrings(CommandTokens.at(5), "instruction") && + !CompareLowerCaseStrings(CommandTokens.at(5), "packet")) + { + ShowMessages("err, dump type must be 'instruction' or 'packet', got '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(5)).c_str()); + CommandPtHelp(); + return; + } + + ShowMessages("PT dump to file:\n"); + ShowMessages(" output : file\n"); + ShowMessages(" path : %s\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(3)).c_str()); + ShowMessages(" format : %s\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(5)).c_str()); + } + else + { + ShowMessages("err, unknown 'dump' sub-option '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(2)).c_str()); + CommandPtHelp(); + } +} + +/** + * @brief Parse and display !pt filter parameters + * + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParseFilter(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + struct PtRangeEntry + { + BOOLEAN Active; + BOOLEAN IsModule; + UINT64 Start; + UINT64 End; + string ModuleName; + }; + + PtRangeEntry Ranges[4] = {}; + PtRangeEntry StopRanges[4] = {}; + BOOLEAN TraceUser = FALSE; + BOOLEAN TraceKernel = FALSE; + + for (size_t i = 2; i < CommandTokens.size(); i++) + { + if (CompareLowerCaseStrings(CommandTokens.at(i), "user")) + { + TraceUser = TRUE; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "kernel")) + { + TraceKernel = TRUE; + } + else + { + // + // Resolve range1..range4 / stoprange1..stoprange4 + // + BOOLEAN IsStop = FALSE; + INT32 RangeIdx = -1; + + if (CompareLowerCaseStrings(CommandTokens.at(i), "range1")) + { + IsStop = FALSE; + RangeIdx = 0; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "range2")) + { + IsStop = FALSE; + RangeIdx = 1; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "range3")) + { + IsStop = FALSE; + RangeIdx = 2; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "range4")) + { + IsStop = FALSE; + RangeIdx = 3; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "stoprange1")) + { + IsStop = TRUE; + RangeIdx = 0; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "stoprange2")) + { + IsStop = TRUE; + RangeIdx = 1; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "stoprange3")) + { + IsStop = TRUE; + RangeIdx = 2; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "stoprange4")) + { + IsStop = TRUE; + RangeIdx = 3; + } + else + { + ShowMessages("err, unknown filter option '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + + PtRangeEntry * Entry = IsStop ? &StopRanges[RangeIdx] : &Ranges[RangeIdx]; + + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, '%s' expects or 'module '\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + + if (CompareLowerCaseStrings(CommandTokens.at(i + 1), "module")) + { + // + // range module + // + if (i + 2 >= CommandTokens.size()) + { + ShowMessages("err, 'module' expects a module name\n\n"); + CommandPtHelp(); + return; + } + i += 2; + Entry->IsModule = TRUE; + Entry->ModuleName = GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)); + Entry->Active = TRUE; + } + else + { + // + // range + // + if (i + 2 >= CommandTokens.size()) + { + ShowMessages("err, '%s' expects \n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + i++; + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Entry->Start)) + { + ShowMessages("err, '%s' is not a valid address\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + i++; + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Entry->End)) + { + ShowMessages("err, '%s' is not a valid address\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + Entry->IsModule = FALSE; + Entry->Active = TRUE; + } + } + } + + // + // Show parsed filter options + // + ShowMessages("PT filter:\n"); + + if (TraceUser) + { + ShowMessages(" privilege : user (CPL > 0)\n"); + + PtRequest->FilterOptions.TraceUser = 1; + } + if (TraceKernel) + { + ShowMessages(" privilege : kernel (CPL == 0)\n"); + + PtRequest->FilterOptions.TraceKernel = 1; + } + if (!TraceUser && !TraceKernel) + { + ShowMessages(" privilege : (default - user + kernel)\n"); + + PtRequest->FilterOptions.TraceUser = 1; + PtRequest->FilterOptions.TraceKernel = 1; + } + + for (INT32 r = 0; r < 4; r++) + { + if (Ranges[r].Active) + { + if (Ranges[r].IsModule) + ShowMessages(" range%d : module '%s'\n", r + 1, Ranges[r].ModuleName.c_str()); + else + ShowMessages(" range%d : 0x%llx - 0x%llx\n", r + 1, Ranges[r].Start, Ranges[r].End); + } + + // + // Set the PtRequest structure for filter operation + // + PtRequest->FilterOptions.AddrRanges[r].IsStopRange = FALSE; + + PtRequest->FilterOptions.AddrRanges[r].Start = Ranges[r].Start; + PtRequest->FilterOptions.AddrRanges[r].End = Ranges[r].End; + } + + for (INT32 r = 0; r < 4; r++) + { + if (StopRanges[r].Active) + { + if (StopRanges[r].IsModule) + ShowMessages(" stoprange%d : module '%s'\n", r + 1, StopRanges[r].ModuleName.c_str()); + else + ShowMessages(" stoprange%d : 0x%llx - 0x%llx\n", r + 1, StopRanges[r].Start, StopRanges[r].End); + } + + // + // Set the PtRequest structure for filter operation + // + PtRequest->FilterOptions.AddrRanges[r].IsStopRange = TRUE; + + PtRequest->FilterOptions.AddrRanges[r].Start = Ranges[r].Start; + PtRequest->FilterOptions.AddrRanges[r].End = Ranges[r].End; + } + + // + // Set the PtRequest structure for filter operation + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER; +} + +/** + * @brief Parse and display !pt packet parameters + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParsePacket(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + if (CommandTokens.size() < 3) + { + ShowMessages("err, 'packet' requires at least one option\n\n"); + CommandPtHelp(); + return; + } + + BOOLEAN PktPsb = FALSE; + BOOLEAN PktPip = FALSE; + BOOLEAN PktTsc = FALSE; + BOOLEAN PktMtc = FALSE; + BOOLEAN PktCyc = FALSE; + BOOLEAN PktTnt = FALSE; + BOOLEAN PktTip = FALSE; + BOOLEAN PktFup = FALSE; + BOOLEAN PktMode = FALSE; + + for (size_t i = 2; i < CommandTokens.size(); i++) + { + if (CompareLowerCaseStrings(CommandTokens.at(i), "psb")) + PktPsb = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "pip")) + PktPip = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "tsc")) + PktTsc = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "mtc")) + PktMtc = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "cyc")) + PktCyc = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "tnt")) + PktTnt = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "tip")) + PktTip = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "fup")) + PktFup = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "mode")) + PktMode = TRUE; + else + { + ShowMessages("err, unknown 'packet' option '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + } + + // + // Show parsed packet options + // + ShowMessages("PT packet filter:\n"); + + if (PktPsb) + { + ShowMessages(" packet type: PSB (Packet Stream Boundary)\n"); + + PtRequest->PacketOptions.PSB = 1; + } + if (PktPip) + { + ShowMessages(" packet type: PIP (Paging Information Packet)\n"); + + PtRequest->PacketOptions.PIP = 1; + } + if (PktTsc) + { + ShowMessages(" packet type: TSC (Timestamp Counter)\n"); + + PtRequest->PacketOptions.TSC = 1; + } + if (PktMtc) + { + ShowMessages(" packet type: MTC (Mini Timestamp Counter)\n"); + + PtRequest->PacketOptions.MTC = 1; + } + if (PktCyc) + { + ShowMessages(" packet type: CYC (Cycle Counter)\n"); + + PtRequest->PacketOptions.CYC = 1; + } + if (PktTnt) + { + ShowMessages(" packet type: TNT (Taken/Not-Taken)\n"); + + PtRequest->PacketOptions.TNT = 1; + } + if (PktTip) + { + ShowMessages(" packet type: TIP (Target IP)\n"); + + PtRequest->PacketOptions.TNT = 1; + } + if (PktFup) + { + ShowMessages(" packet type: FUP (Flow Update Packet)\n"); + + PtRequest->PacketOptions.FUP = 1; + } + if (PktMode) + { + ShowMessages(" packet type: MODE (Mode packet)\n"); + + PtRequest->PacketOptions.MODE = 1; + } + + // + // Set the PtRequest structure for packet operation + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PACKET; } /** @@ -320,106 +897,112 @@ CommandPtParseFilterOptions(vector & CommandTokens, VOID CommandPt(vector CommandTokens, string Command) { - HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {0}; + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; if (CommandTokens.size() == 1) { ShowMessages("incorrect use of the '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(0)).c_str()); - CommandPtHelp(); return; } - if (CompareLowerCaseStrings(CommandTokens.at(1), "enable") && CommandTokens.size() == 2) + // + // Parse subcommands + // + if (CompareLowerCaseStrings(CommandTokens.at(1), "enable")) { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE; + // + // Parse and display enable options for !pt enable command + // + CommandPtParseEnable(CommandTokens, &PtRequest); } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "disable") && CommandTokens.size() == 2) + else if (CompareLowerCaseStrings(CommandTokens.at(1), "disable")) { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "pause") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "resume") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "size") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "dump") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DUMP; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "flush") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "filter")) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER; - - if (!CommandPtParseFilterOptions(CommandTokens, &PtRequest)) + if (CommandTokens.size() != 2) { + ShowMessages("err, 'disable' takes no arguments\n\n"); CommandPtHelp(); return; } + + // + // Parse and display disable options for !pt disable command + // + CommandPtParseDisable(CommandTokens, &PtRequest); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "pause")) + { + if (CommandTokens.size() != 2) + { + ShowMessages("err, 'pause' takes no arguments\n\n"); + CommandPtHelp(); + return; + } + + // + // Parse and display pause options for !pt pause command + // + CommandPtParsePause(CommandTokens, &PtRequest); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "resume")) + { + if (CommandTokens.size() != 2) + { + ShowMessages("err, 'resume' takes no arguments\n\n"); + CommandPtHelp(); + return; + } + + // + // Parse and display resume options for !pt resume command + // + CommandPtParseResume(CommandTokens, &PtRequest); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "flush")) + { + if (CommandTokens.size() != 2) + { + ShowMessages("err, 'flush' takes no arguments\n\n"); + CommandPtHelp(); + return; + } + + // + // Parse and display flush options for !pt flush command + // + CommandPtParseFlush(CommandTokens, &PtRequest); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "dump")) + { + // + // Parse and display dump options for !pt dump command + // + CommandPtParseDump(CommandTokens, &PtRequest); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "filter")) + { + // + // Parse and display filter options for !pt filter command + // + CommandPtParseFilter(CommandTokens, &PtRequest); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "packet")) + { + // + // Parse and display packet options for !pt packet command + // + CommandPtParsePacket(CommandTokens, &PtRequest); } else { ShowMessages("incorrect use of the '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(0)).c_str()); CommandPtHelp(); - return; } // - // Send the PT operation request + // Send the PT request to the debugger // - if (CommandPtSendRequest(&PtRequest)) - { - switch (PtRequest.PtOperationType) - { - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE: - ShowMessages("PT enabled successfully\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE: - ShowMessages("PT disabled successfully\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE: - ShowMessages("PT trace paused\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME: - ShowMessages("PT trace resumed\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE: - ShowMessages("PT buffer bytes-written per CPU:\n"); - for (UINT32 i = 0; i < PtRequest.NumCpus; i++) - { - ShowMessages(" core %u : 0x%llx\n", i, PtRequest.BytesPerCpu[i]); - } - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DUMP: - ShowMessages("PT trace state is shown\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH: - ShowMessages("PT trace state is flushed\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER: - ShowMessages("PT filter / config updated successfully\n"); - break; - default: - ShowMessages("unknown PT operation type\n"); - break; - } - } - else - { - ShowErrorMessage(PtRequest.KernelStatus); - return; - } + CommandPtSendRequest(&PtRequest); } From 5bccbff58b0a43a070743e9ce900484bc5805dd1 Mon Sep 17 00:00:00 2001 From: sina Date: Wed, 1 Jul 2026 19:44:15 +0200 Subject: [PATCH 51/57] fix IPT kernel structure based on the new model --- .../user/hyperdbg_app/code/hyperdbg-ipt.cpp | 16 ++--- hyperdbg/hyperkd/code/driver/Ioctl.c | 12 ++-- hyperdbg/hypertrace/code/api/PtApi.c | 60 ++++++++++--------- .../hypertrace/code/broadcast/Broadcast.c | 6 +- .../hypertrace/code/broadcast/DpcRoutines.c | 4 +- hyperdbg/hypertrace/code/pt/Pt.c | 26 ++++---- .../hypertrace/header/broadcast/Broadcast.h | 2 +- hyperdbg/hypertrace/header/pt/Pt.h | 47 ++++----------- .../include/SDK/headers/RequestStructures.h | 8 +++ 9 files changed, 86 insertions(+), 95 deletions(-) diff --git a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp index 280fde5e..aacd776f 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp @@ -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; } diff --git a/hyperdbg/hyperkd/code/driver/Ioctl.c b/hyperdbg/hyperkd/code/driver/Ioctl.c index b7bb7d16..ddd88593 100644 --- a/hyperdbg/hyperkd/code/driver/Ioctl.c +++ b/hyperdbg/hyperkd/code/driver/Ioctl.c @@ -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)); } // diff --git a/hyperdbg/hypertrace/code/api/PtApi.c b/hyperdbg/hypertrace/code/api/PtApi.c index 7945fbfd..408848c3 100644 --- a/hyperdbg/hypertrace/code/api/PtApi.c +++ b/hyperdbg/hypertrace/code/api/PtApi.c @@ -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) diff --git a/hyperdbg/hypertrace/code/broadcast/Broadcast.c b/hyperdbg/hypertrace/code/broadcast/Broadcast.c index 7dc5fce7..0fd14565 100644 --- a/hyperdbg/hypertrace/code/broadcast/Broadcast.c +++ b/hyperdbg/hypertrace/code/broadcast/Broadcast.c @@ -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); } diff --git a/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c b/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c index 0bd904a2..b17accdd 100644 --- a/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c +++ b/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c @@ -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 diff --git a/hyperdbg/hypertrace/code/pt/Pt.c b/hyperdbg/hypertrace/code/pt/Pt.c index cd8d6d7f..de2e6a01 100644 --- a/hyperdbg/hypertrace/code/pt/Pt.c +++ b/hyperdbg/hypertrace/code/pt/Pt.c @@ -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]; } // diff --git a/hyperdbg/hypertrace/header/broadcast/Broadcast.h b/hyperdbg/hypertrace/header/broadcast/Broadcast.h index 878cd3cd..eb857072 100644 --- a/hyperdbg/hypertrace/header/broadcast/Broadcast.h +++ b/hyperdbg/hypertrace/header/broadcast/Broadcast.h @@ -50,4 +50,4 @@ VOID BroadcastFlushPtOnAllCores(); VOID -BroadcastFilterPtOnAllCores(PT_FILTER_OPTIONS * FilterOptions); +BroadcastFilterPtOnAllCores(PT_APPLY_CORE_FILTER_REQUEST * FilterRequest); diff --git a/hyperdbg/hypertrace/header/pt/Pt.h b/hyperdbg/hypertrace/header/pt/Pt.h index 07a10506..f7801bbb 100644 --- a/hyperdbg/hypertrace/header/pt/Pt.h +++ b/hyperdbg/hypertrace/header/pt/Pt.h @@ -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. diff --git a/hyperdbg/include/SDK/headers/RequestStructures.h b/hyperdbg/include/SDK/headers/RequestStructures.h index fbd6510c..fdf53450 100644 --- a/hyperdbg/include/SDK/headers/RequestStructures.h +++ b/hyperdbg/include/SDK/headers/RequestStructures.h @@ -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; /** From 43cea334c2801b5ce85c34c6484b8c9d879756c2 Mon Sep 17 00:00:00 2001 From: sina Date: Thu, 2 Jul 2026 18:14:47 +0200 Subject: [PATCH 52/57] add core and path interpretation of PT --- .../include/SDK/headers/RequestStructures.h | 7 + .../commands/extension-commands/pt.cpp | 914 ++++++++++++++++-- hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 4 +- hyperdbg/libhyperdbg/pch.h | 41 +- 4 files changed, 862 insertions(+), 104 deletions(-) diff --git a/hyperdbg/include/SDK/headers/RequestStructures.h b/hyperdbg/include/SDK/headers/RequestStructures.h index fdf53450..5e5b66b4 100644 --- a/hyperdbg/include/SDK/headers/RequestStructures.h +++ b/hyperdbg/include/SDK/headers/RequestStructures.h @@ -1369,6 +1369,12 @@ typedef enum _HYPERTRACE_PT_OPERATION_REQUEST_TYPE */ #define PT_DEFAULT_BUFFER_SIZE 0x200000 // 2 MB +/** + * @brief The default core for PT + * + */ +#define PT_DEFAULT_PINNING_CORE 0x0 // 0th core + /** * @brief PT enable options structure * @@ -1433,6 +1439,7 @@ typedef struct _HYPERTRACE_PT_OPERATION_PACKETS // UINT64 BufferSize; /* Output buffer size (0 = default / PT_DEFAULT_BUFFER_SIZE) */ PT_ENABLE_OPTIONS EnableOptions; /* Options for enabling PT (CPL, CR3, PID, TID, Pname) */ + UINT32 CoreId; /* Core ID for running (pinning) process/thread */ // // Filter diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp index b188c6ee..c7c538e5 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp @@ -1,6 +1,7 @@ /** * @file pt.cpp * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) + * @author Sina Karvandi (sina@hyperdbg.org) * @brief !pt command * @details * @version 0.19 @@ -17,6 +18,11 @@ extern BOOLEAN g_IsHyperTraceModuleLoaded; extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee; +static UINT64 g_ImageBase = 0; +static UINT64 g_CodeBase = 0; +static UINT64 g_CodeSize = 0; +static UINT8 * g_Code = NULL; + /** * @brief help of the !pt command * @@ -29,9 +35,10 @@ CommandPtHelp() ShowMessages("syntax : \t!pt enable\n"); ShowMessages("syntax : \t!pt enable [size BufferSize (hex)]\n"); - ShowMessages("syntax : \t!pt enable [pid ProcessId (hex)] [size BufferSize (hex)]\n"); - ShowMessages("syntax : \t!pt enable [tid ThreadId (hex)] [size BufferSize (hex)]\n"); - ShowMessages("syntax : \t!pt enable [pname ProcessName (string)] [size BufferSize (hex)]\n"); + ShowMessages("syntax : \t!pt enable [pid ProcessId (hex)] [size BufferSize (hex)] [core CoreId (hex)]\n"); + ShowMessages("syntax : \t!pt enable [tid ThreadId (hex)] [size BufferSize (hex)] [core CoreId (hex)]\n"); + ShowMessages("syntax : \t!pt enable [pname ProcessName (string)] [size BufferSize (hex)] [core CoreId (hex)]\n"); + ShowMessages("syntax : \t!pt enable [path Path (string)] [size BufferSize (hex)] [core CoreId (hex)]\n"); ShowMessages("syntax : \t!pt enable [cr3 Cr3Value (hex)] [size BufferSize (hex)]\n"); ShowMessages("syntax : \t!pt disable\n"); ShowMessages("syntax : \t!pt pause\n"); @@ -51,9 +58,10 @@ CommandPtHelp() ShowMessages("\t\te.g : !pt enable size 0x200000\n"); ShowMessages("\t\te.g : !pt enable pid 0x4a8\n"); ShowMessages("\t\te.g : !pt enable pname notepad.exe\n"); - ShowMessages("\t\te.g : !pt enable tid 0x1234\n"); + ShowMessages("\t\te.g : !pt enable tid 0x1234 core 3\n"); ShowMessages("\t\te.g : !pt enable cr3 0x1aabb000\n"); ShowMessages("\t\te.g : !pt enable pid 0x4a8 size 0x200000\n"); + ShowMessages("\t\te.g : !pt enable path \"c:\\programs\\my exe file.exe\" size 0x200000 core 3\n"); ShowMessages("\t\te.g : !pt disable\n"); ShowMessages("\t\te.g : !pt pause\n"); ShowMessages("\t\te.g : !pt resume\n"); @@ -154,6 +162,743 @@ HyperDbgPerformPtOperation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) return CommandPtSendRequest(PtRequest); } +/** + * @brief Send pt enable command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendEnable() +{ + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; + + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt disable command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendDisable() +{ + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; + + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt pause command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendPause() +{ + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; + + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt resume command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendResume() +{ + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; + + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt flush command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendFlush() +{ + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; + + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt filter command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendFilterByPid(UINT32 ProcessId, + BOOLEAN IsUserMode, + BOOLEAN IsKernelMode, + UINT64 StartRange1, + UINT64 EndRange1, + UINT64 StartRange2, + UINT64 EndRange2, + UINT64 StartRange3, + UINT64 EndRange3, + UINT64 StartRange4, + UINT64 EndRange4) +{ + HYPERTRACE_PT_OPERATION_PACKETS Op = {}; + UINT8 NumberOfRanges = 0; + + // + // Set the Op structure for the operation + // + Op.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER; + + // + // Set execution modes + // + Op.FilterOptions.TraceUser = IsUserMode ? 1 : 0; + Op.FilterOptions.TraceKernel = IsKernelMode ? 1 : 0; + + // + // Set the process ID + // + Op.EnableOptions.Pid = ProcessId; + + // + // Set the first range if provided + // + if (StartRange1 != NULL && EndRange1 != NULL) + { + Op.FilterOptions.AddrRanges[NumberOfRanges].Start = StartRange1; + Op.FilterOptions.AddrRanges[NumberOfRanges].End = EndRange1; + NumberOfRanges++; + } + + // + // Set the second range if provided + // + if (StartRange2 != NULL && EndRange2 != NULL) + { + Op.FilterOptions.AddrRanges[NumberOfRanges].Start = StartRange2; + Op.FilterOptions.AddrRanges[NumberOfRanges].End = EndRange2; + NumberOfRanges++; + } + + // + // Set the third range if provided + // + if (StartRange3 != NULL && EndRange3 != NULL) + { + Op.FilterOptions.AddrRanges[NumberOfRanges].Start = StartRange3; + Op.FilterOptions.AddrRanges[NumberOfRanges].End = EndRange3; + NumberOfRanges++; + } + + // + // Set the fourth range if provided + // + if (StartRange4 != NULL && EndRange4 != NULL) + { + Op.FilterOptions.AddrRanges[NumberOfRanges].Start = StartRange4; + Op.FilterOptions.AddrRanges[NumberOfRanges].End = EndRange4; + NumberOfRanges++; + } + + // + // Set number of ranges + // + Op.FilterOptions.NumAddrRanges = NumberOfRanges; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&Op)) + { + return FALSE; + } + else + { + ShowMessages("[+] PT Filter: cr3=0x%llx, pid=0x%x, user=%x, kernel=%x, ranges=%x\n", + Op.EnableOptions.Cr3, + Op.EnableOptions.Pid, + Op.FilterOptions.TraceUser, + Op.FilterOptions.TraceKernel, + Op.FilterOptions.NumAddrRanges); + + return TRUE; + } + + return TRUE; +} + +static int +CommandPtReadImage(UINT8 * Buffer, SIZE_T Size, const struct pt_asid * Asid, UINT64 Ip, VOID * Context) +{ + (VOID) Asid; + (VOID) Context; + + if (g_Code == NULL || Ip < g_CodeBase || Ip >= g_CodeBase + g_CodeSize) + return -pte_nomap; + + UINT64 Available = g_CodeBase + g_CodeSize - Ip; + SIZE_T Count = (Size < Available) ? Size : (SIZE_T)Available; + + memcpy(Buffer, g_Code + (Ip - g_CodeBase), Count); + return (int)Count; +} + +typedef struct _PROC_BASIC_INFO +{ + LONG ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR Reserved[4]; +} PROC_BASIC_INFO; + +typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); + +static BOOLEAN +CommandPtCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) +{ + HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); + PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; + PROC_BASIC_INFO Pbi = {0}; + ULONG Ret = 0; + SIZE_T Got = 0; + UINT64 Base = 0; + IMAGE_DOS_HEADER Dos; + IMAGE_NT_HEADERS64 Nt; + UINT64 SectionBase; + + if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) + return FALSE; + + g_ImageBase = Base; + SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; + + for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) + { + IMAGE_SECTION_HEADER Section; + + if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) + return FALSE; + + if (memcmp(Section.Name, ".text", 6) != 0) + continue; + + UINT64 Start = Base + Section.VirtualAddress; + UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; + + if (Size == 0) + return FALSE; + + g_Code = (UINT8 *)malloc((SIZE_T)Size); + if (g_Code == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Start, g_Code, (SIZE_T)Size, &Got) || Got != Size) + { + free(g_Code); + g_Code = NULL; + return FALSE; + } + + g_CodeBase = Start; + g_CodeSize = Size; + *TextStart = Start; + *TextEnd = Start + Size - 1; + return TRUE; + } + + return FALSE; +} + +static BOOLEAN +CommandPtResolveFunction(HANDLE Process, const CHAR * Path, const CHAR * Name, UINT64 * Start, UINT64 * End) +{ + union + { + SYMBOL_INFO Info; + BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + } Symbol = {0}; + BOOLEAN Ok = FALSE; + + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + if (!SymInitialize(Process, NULL, FALSE)) + return FALSE; + + if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)g_ImageBase, 0, NULL, 0) != 0) + { + Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); + Symbol.Info.MaxNameLen = MAX_SYM_NAME; + + if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) + { + *Start = Symbol.Info.Address; + *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; + Ok = TRUE; + } + } + + SymCleanup(Process); + return Ok; +} + +static const CHAR * +CommandPtPacketName(enum pt_packet_type Type) +{ + switch (Type) + { + case ppt_psb: + return "PSB"; + case ppt_psbend: + return "PSBEND"; + case ppt_pad: + return "PAD"; + case ppt_fup: + return "FUP"; + case ppt_tip: + return "TIP"; + case ppt_tip_pge: + return "TIP.PGE"; + case ppt_tip_pgd: + return "TIP.PGD"; + case ppt_tnt_8: + return "TNT8"; + case ppt_tnt_64: + return "TNT64"; + case ppt_mode: + return "MODE"; + case ppt_pip: + return "PIP"; + case ppt_vmcs: + return "VMCS"; + case ppt_cbr: + return "CBR"; + case ppt_tsc: + return "TSC"; + case ppt_tma: + return "TMA"; + case ppt_mtc: + return "MTC"; + case ppt_cyc: + return "CYC"; + case ppt_ovf: + return "OVF"; + case ppt_stop: + return "STOP"; + case ppt_exstop: + return "EXSTOP"; + case ppt_mnt: + return "MNT"; + case ppt_ptw: + return "PTW"; + default: + return "?"; + } +} + +static UINT64 +CommandPtReconstructIp(const struct pt_packet_ip * Packet, UINT64 * LastIp) +{ + UINT64 Value = *LastIp; + + switch (Packet->ipc) + { + case pt_ipc_update_16: + Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); + break; + case pt_ipc_update_32: + Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); + break; + case pt_ipc_update_48: + Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); + break; + case pt_ipc_sext_48: + Value = Packet->ip & 0xffffffffffffull; + if (Value & 0x800000000000ull) + Value |= 0xffff000000000000ull; + break; + default: + Value = Packet->ip; + break; + } + + *LastIp = Value; + return Value; +} + +static UINT64 +CommandPtDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +{ + struct pt_config Config; + struct pt_packet_decoder * Decoder; + UINT64 Count = 0; + UINT64 LastIp = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (UINT8 *)Buffer; + Config.end = (UINT8 *)Buffer + Size; + + Decoder = pt_pkt_alloc_decoder(&Config); + if (Decoder == NULL) + { + ShowMessages("[-] core %u: cannot allocate packet decoder\n", Cpu); + return 0; + } + + for (;;) + { + Status = pt_pkt_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_packet Packet; + + Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); + if (Status < 0) + break; + + Count++; + + switch (Packet.type) + { + case ppt_tnt_8: + case ppt_tnt_64: + ShowMessages(" %-8s %2u ", CommandPtPacketName(Packet.type), Packet.payload.tnt.bit_size); + for (UINT8 Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) + putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); + putchar('\n'); + break; + + case ppt_tip: + case ppt_fup: + case ppt_tip_pge: + case ppt_tip_pgd: + if (Packet.payload.ip.ipc == pt_ipc_suppressed) + ShowMessages(" %-8s (ip suppressed)\n", CommandPtPacketName(Packet.type)); + else + { + UINT64 Ip = CommandPtReconstructIp(&Packet.payload.ip, &LastIp); + ShowMessages(" %-8s 0x%016llx exe+0x%llx\n", + CommandPtPacketName(Packet.type), + (UINT64)Ip, + (UINT64)(Ip - g_ImageBase)); + } + break; + + case ppt_pip: + ShowMessages(" %-8s cr3=0x%llx\n", CommandPtPacketName(Packet.type), (UINT64)Packet.payload.pip.cr3); + break; + + case ppt_cbr: + // ShowMessages(" %-8s ratio=%u\n", CommandPtPacketName(Packet.type), Packet.payload.cbr.ratio); + break; + + case ppt_tsc: + ShowMessages(" %-8s tsc=0x%llx\n", CommandPtPacketName(Packet.type), (UINT64)Packet.payload.tsc.tsc); + break; + + default: + // ShowMessages(" %-8s\n", CommandPtPacketName(Packet.type)); + break; + } + } + } + + pt_pkt_free_decoder(Decoder); + return Count; +} + +static UINT64 +CommandPtDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +{ + struct pt_config Config; + struct pt_insn_decoder * Decoder; + struct pt_image * Image; + UINT64 Count = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (UINT8 *)Buffer; + Config.end = (UINT8 *)Buffer + Size; + + Decoder = pt_insn_alloc_decoder(&Config); + if (Decoder == NULL) + { + ShowMessages("[-] core %u: cannot allocate instruction decoder\n", Cpu); + return 0; + } + + Image = pt_insn_get_image(Decoder); + pt_image_set_callback(Image, CommandPtReadImage, NULL); + + for (;;) + { + Status = pt_insn_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_insn Insn; + + while (Status & pts_event_pending) + { + struct pt_event Event; + Status = pt_insn_event(Decoder, &Event, sizeof(Event)); + if (Status < 0) + break; + } + + if (Status < 0 || (Status & pts_eos)) + break; + + Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); + if (Status < 0) + break; + + ZydisDisassembledInstruction Disasm; + ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; + + if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) + ShowMessages(" 0x%016llx exe+0x%-6llx %s\n", + (UINT64)Insn.ip, + (UINT64)(Insn.ip - g_ImageBase), + Disasm.text); + else + ShowMessages(" 0x%016llx (undecodable)\n", (UINT64)Insn.ip); + + Count++; + } + + if (Status >= 0 && (Status & pts_eos)) + break; + } + + pt_insn_free_decoder(Decoder); + return Count; +} + +static VOID +CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, int PinCore) +{ + STARTUPINFOA Startup = {}; + PROCESS_INFORMATION Process = {}; + HYPERTRACE_PT_MMAP_PACKETS Mmap = {}; + HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; + UINT64 TextStart = 0; + UINT64 TextEnd = 0; + UINT64 FilterStart = 0; + UINT64 FilterEnd = 0; + UINT64 Total = 0; + + Startup.cb = sizeof(Startup); + + if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) + { + ShowMessages("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); + return; + } + + ShowMessages("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); + + if (PinCore >= 0) + { + DWORD_PTR Mask = (DWORD_PTR)1 << PinCore; + if (SetProcessAffinityMask(Process.hProcess, Mask)) + ShowMessages("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); + else + ShowMessages("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); + } + else + { + ShowMessages("[*] target unpinned (scheduler may migrate it across cores)\n"); + } + + if (!CommandPtCaptureImage(Process.hProcess, &TextStart, &TextEnd)) + { + ShowMessages("[-] cannot read target image / .text section\n"); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + ShowMessages("[+] image base 0x%llx, .text 0x%llx-0x%llx (%llu bytes)\n", + (UINT64)g_ImageBase, + (UINT64)TextStart, + (UINT64)TextEnd, + (UINT64)g_CodeSize); + + FilterStart = TextStart; + FilterEnd = TextEnd; + + if (Function != NULL && CommandPtResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) + { + ShowMessages("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", + Function, + (UINT64)FilterStart, + (UINT64)FilterEnd, + (UINT64)(FilterEnd - FilterStart + 1)); + } + else + { + ShowMessages("[!] IP filter: whole .text (symbol '%s' not found - build the target with a PDB)\n", + Function ? Function : "(none)"); + } + + if (!CommandPtSendFilterByPid(Process.dwProcessId, TRUE, FALSE, FilterStart, FilterEnd, NULL, NULL, NULL, NULL, NULL, NULL) || + !CommandPtSendEnable()) + { + ShowMessages("[-] cannot enable Intel PT\n"); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + if (!hyperdbg_u_pt_mmap(&Mmap)) + { + ShowMessages("[-] pt_mmap failed\n"); + CommandPtSendDisable(); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + ShowMessages("[+] PT enabled, %u per-core buffers mapped\n", Mmap.NumCpus); + ShowMessages("[*] resuming target and waiting for it to exit...\n"); + + ResumeThread(Process.hThread); + WaitForSingleObject(Process.hProcess, INFINITE); + ShowMessages("[+] target exited, decoding trace\n"); + + CommandPtSendPause(); + + Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; + if (!hyperdbg_u_pt_operation(&Sizes)) + { + ShowMessages("[-] cannot query PT sizes\n"); + CommandPtSendDisable(); + + goto Cleanup; + } + + for (UINT32 i = 0; i < Mmap.NumCpus; i++) + { + UINT32 Cpu = Mmap.Cpus[i].CpuId; + UINT64 Bytes = (Cpu < Sizes.NumCpus) ? Sizes.BytesPerCpu[Cpu] : 0; + + if (Bytes == 0) + continue; + + if (Bytes > Mmap.Cpus[i].Size) + Bytes = Mmap.Cpus[i].Size; + + ShowMessages("\n[*] core %u: %llu bytes of trace\n", Cpu, (UINT64)Bytes); + Total += Packets + ? CommandPtDecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes) + : CommandPtDecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); + } + + ShowMessages("\n[+] decoded %llu %s total\n", (UINT64)Total, Packets ? "packet(s)" : "instruction(s)"); + + CommandPtSendDisable(); + +Cleanup: + if (g_Code != NULL) + { + free(g_Code); + g_Code = NULL; + } + if (Process.hThread != NULL) + CloseHandle(Process.hThread); + if (Process.hProcess != NULL) + CloseHandle(Process.hProcess); +} + /** * @brief Map the per-CPU PT output buffers into the current process * @@ -203,82 +948,6 @@ HyperDbgPtMmapSendRequest(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest) return MmapRequest->KernelStatus == DEBUGGER_OPERATION_WAS_SUCCESSFUL; } -/** - * @brief Parse options for pt disable command - * - * @param CommandTokens The command tokens to parse - * @param PtRequest The PT request structure to fill with parsed options - * - * @return VOID - */ -static VOID -CommandPtParseDisable(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) -{ - ShowMessages("PT disable requested\n"); - - // - // Set the PtRequest structure for disable operation - // - PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE; -} - -/** - * @brief Parse options for pt disable command - * - * @param CommandTokens The command tokens to parse - * @param PtRequest The PT request structure to fill with parsed options - * - * @return VOID - */ -static VOID -CommandPtParsePause(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) -{ - ShowMessages("PT pause requested\n"); - - // - // Set the PtRequest structure for pause operation - // - PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE; -} - -/** - * @brief Parse options for pt disable command - * - * @param CommandTokens The command tokens to parse - * @param PtRequest The PT request structure to fill with parsed options - * - * @return VOID - */ -static VOID -CommandPtParseResume(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) -{ - ShowMessages("PT resume requested\n"); - - // - // Set the PtRequest structure for resume operation - // - PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME; -} - -/** - * @brief Parse options for pt disable command - * - * @param CommandTokens The command tokens to parse - * @param PtRequest The PT request structure to fill with parsed options - * - * @return VOID - */ -static VOID -CommandPtParseFlush(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) -{ - ShowMessages("PT flush requested\n"); - - // - // Set the PtRequest structure for flush operation - // - PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH; -} - /** * @brief Parse and display enable options for !pt enable command * @@ -292,16 +961,20 @@ CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATI { BOOLEAN HasPid = FALSE; BOOLEAN HasPname = FALSE; + BOOLEAN HasPath = FALSE; BOOLEAN HasTid = FALSE; BOOLEAN HasCr3 = FALSE; BOOLEAN HasSize = FALSE; + BOOLEAN HasCore = FALSE; UINT64 Pid = 0; UINT64 Tid = 0; UINT64 Cr3 = 0; UINT64 Size = 0; + UINT32 Core = 0; string Pname; + string Path; - for (size_t i = 2; i < CommandTokens.size(); i++) + for (SIZE_T i = 2; i < CommandTokens.size(); i++) { if (CompareLowerCaseStrings(CommandTokens.at(i), "pid")) { @@ -333,6 +1006,18 @@ CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATI Pname = GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)); HasPname = TRUE; } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "path")) + { + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, 'path' expects a process executable path\n\n"); + CommandPtHelp(); + return; + } + i++; + Path = GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)); + HasPath = TRUE; + } else if (CompareLowerCaseStrings(CommandTokens.at(i), "tid")) { if (i + 1 >= CommandTokens.size()) @@ -387,6 +1072,24 @@ CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATI } HasSize = TRUE; } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "core")) + { + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, 'core' expects a hex value as the core number\n\n"); + CommandPtHelp(); + return; + } + i++; + if (!ConvertTokenToUInt32(CommandTokens.at(i), &Core)) + { + ShowMessages("err, '%s' is not a valid hex size\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + HasCore = TRUE; + } else { ShowMessages("err, unknown 'enable' option '%s'\n\n", @@ -399,10 +1102,10 @@ CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATI // // pid, pname, tid, cr3 are mutually exclusive target selectors // - INT32 SelectorCount = (HasPid ? 1 : 0) + (HasPname ? 1 : 0) + (HasTid ? 1 : 0) + (HasCr3 ? 1 : 0); + INT32 SelectorCount = (HasPid ? 1 : 0) + (HasPname ? 1 : 0) + (HasPath ? 1 : 0) + (HasTid ? 1 : 0) + (HasCr3 ? 1 : 0); if (SelectorCount > 1) { - ShowMessages("err, only one of 'pid', 'pname', 'tid', 'cr3' may be specified at a time\n\n"); + ShowMessages("err, only one of 'pid', 'pname', 'path', 'tid', 'cr3' may be specified at a time\n\n"); CommandPtHelp(); return; } @@ -426,6 +1129,12 @@ CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATI PtRequest->EnableOptions.EnableByCr3 = 1; strcpy_s(PtRequest->EnableOptions.ProcessName, sizeof(PtRequest->EnableOptions.ProcessName), Pname.c_str()); } + else if (HasPath) + { + ShowMessages(" target path : %s\n", Path.c_str()); + + PtRequest->EnableOptions.EnableByPid = 1; // Path is resolved to a PID + } else if (HasTid) { ShowMessages(" target tid : 0x%llx\n", Tid); @@ -445,6 +1154,9 @@ CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATI ShowMessages(" target : all (no process/thread filter)\n"); } + // + // Check for size options + // if (HasSize) { ShowMessages(" buffer size : 0x%llx bytes\n", Size); @@ -458,10 +1170,34 @@ CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATI PtRequest->BufferSize = PT_DEFAULT_BUFFER_SIZE; } + // + // Check for core options + // + if (HasCore) + { + ShowMessages(" core : 0x%x\n", Core); + PtRequest->CoreId = Core; + } + else + { + ShowMessages(" core : default (0x%x)\n", PT_DEFAULT_PINNING_CORE); + PtRequest->CoreId = PT_DEFAULT_PINNING_CORE; + } + // // Fill the PtRequest structure with parsed options // PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE; + + // + // Temporary workaround for testing , if a path is specified, resolve it to a PID and use that for the request + // TODO: Should be removed + // + if (HasPath) + { + ShowMessages(" Running '%s' on core: %llx\n", Path.c_str(), PtRequest->CoreId); + CommandPtRunAndTrace(Path.c_str(), NULL, FALSE, PtRequest->CoreId); + } } /** @@ -569,7 +1305,7 @@ CommandPtParseFilter(vector & CommandTokens, HYPERTRACE_PT_OPERATI BOOLEAN TraceUser = FALSE; BOOLEAN TraceKernel = FALSE; - for (size_t i = 2; i < CommandTokens.size(); i++) + for (SIZE_T i = 2; i < CommandTokens.size(); i++) { if (CompareLowerCaseStrings(CommandTokens.at(i), "user")) { @@ -791,7 +1527,7 @@ CommandPtParsePacket(vector & CommandTokens, HYPERTRACE_PT_OPERATI BOOLEAN PktFup = FALSE; BOOLEAN PktMode = FALSE; - for (size_t i = 2; i < CommandTokens.size(); i++) + for (SIZE_T i = 2; i < CommandTokens.size(); i++) { if (CompareLowerCaseStrings(CommandTokens.at(i), "psb")) PktPsb = TRUE; @@ -929,7 +1665,7 @@ CommandPt(vector CommandTokens, string Command) // // Parse and display disable options for !pt disable command // - CommandPtParseDisable(CommandTokens, &PtRequest); + CommandPtSendDisable(); } else if (CompareLowerCaseStrings(CommandTokens.at(1), "pause")) { @@ -943,7 +1679,7 @@ CommandPt(vector CommandTokens, string Command) // // Parse and display pause options for !pt pause command // - CommandPtParsePause(CommandTokens, &PtRequest); + CommandPtSendPause(); } else if (CompareLowerCaseStrings(CommandTokens.at(1), "resume")) { @@ -957,7 +1693,7 @@ CommandPt(vector CommandTokens, string Command) // // Parse and display resume options for !pt resume command // - CommandPtParseResume(CommandTokens, &PtRequest); + CommandPtSendResume(); } else if (CompareLowerCaseStrings(CommandTokens.at(1), "flush")) { @@ -971,7 +1707,7 @@ CommandPt(vector CommandTokens, string Command) // // Parse and display flush options for !pt flush command // - CommandPtParseFlush(CommandTokens, &PtRequest); + CommandPtSendFlush(); } else if (CompareLowerCaseStrings(CommandTokens.at(1), "dump")) { diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index c5a8ad58..bcb12486 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -78,7 +78,7 @@ Windows true false - $(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)build\bin\$(Configuration)\script-engine.lib;$(SolutionDir)libraries\keystone\$(Configuration)-lib\keystone.lib;%(AdditionalDependencies) + $(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)build\bin\$(Configuration)\script-engine.lib;$(SolutionDir)libraries\keystone\$(Configuration)-lib\keystone.lib;$(SolutionDir)libraries\libipt\libipt.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) false @@ -116,7 +116,7 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R true true false - $(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)build\bin\$(Configuration)\script-engine.lib;$(SolutionDir)libraries\keystone\$(Configuration)-lib\keystone.lib;%(AdditionalDependencies) + $(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)build\bin\$(Configuration)\script-engine.lib;$(SolutionDir)libraries\keystone\$(Configuration)-lib\keystone.lib;$(SolutionDir)libraries\libipt\libipt.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) true diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index f06ccdf7..a193f07f 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -41,30 +41,29 @@ typedef RFLAGS * PRFLAGS; #define USE_NATIVE_SDK_HEADERS #define _AMD64_ - #ifdef _WIN32 -#if defined(USE__NATIVE_PHNT_HEADERS) +# if defined(USE__NATIVE_PHNT_HEADERS) // // Dirty fix: the "PCWCHAR" in undefined in "ntrtl.h" so I deifined it here. // typedef const wchar_t *LPCWCHAR, *PCWCHAR; -# define PHNT_MODE PHNT_MODE_USER -# define PHNT_VERSION PHNT_WIN11 // Windows 11 -# define PHNT_PATCH_FOR_HYPERDBG TRUE +# define PHNT_MODE PHNT_MODE_USER +# define PHNT_VERSION PHNT_WIN11 // Windows 11 +# define PHNT_PATCH_FOR_HYPERDBG TRUE -# include -# include +# include +# include -#elif defined(USE_NATIVE_SDK_HEADERS) +# elif defined(USE_NATIVE_SDK_HEADERS) -# include -# include -# include -# include +# include +# include +# include +# include -#endif +# endif #endif //_WIN32 @@ -108,6 +107,7 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include #include #include +#include // // Scope definitions @@ -189,6 +189,11 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; // #include "header/pci-id.h" +// +// Intel PT +// +#include "../dependencies/libipt/intel-pt.h" + // // General // @@ -232,6 +237,11 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "header/hwdbg-interpreter.h" #include "header/hwdbg-scripts.h" +// +// Zydis headers +// +#include + // // Libraries // @@ -261,4 +271,9 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; # pragma comment(lib, "Psapi.lib") # pragma comment(lib, "Kernel32.lib") +// +// For resolving symbols on Intel PT +// +# pragma comment(lib, "dbghelp.lib") + #endif // HYPERDBG_ENV_WINDOWS From 16aebb0c5d194311ea2959c5328e224921719c9b Mon Sep 17 00:00:00 2001 From: sina Date: Thu, 2 Jul 2026 20:40:41 +0200 Subject: [PATCH 53/57] remove dependency on module details over global variables --- .../commands/extension-commands/pt.cpp | 71 +++++++++---------- hyperdbg/libhyperdbg/header/symbol.h | 12 ++++ 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp index c7c538e5..6d2a88d8 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp @@ -18,10 +18,7 @@ extern BOOLEAN g_IsHyperTraceModuleLoaded; extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee; -static UINT64 g_ImageBase = 0; -static UINT64 g_CodeBase = 0; -static UINT64 g_CodeSize = 0; -static UINT8 * g_Code = NULL; + /** * @brief help of the !pt command @@ -410,15 +407,16 @@ static int CommandPtReadImage(UINT8 * Buffer, SIZE_T Size, const struct pt_asid * Asid, UINT64 Ip, VOID * Context) { (VOID) Asid; - (VOID) Context; - if (g_Code == NULL || Ip < g_CodeBase || Ip >= g_CodeBase + g_CodeSize) + IMAGE_SYMBOL_CONTEXT * Ctx = (IMAGE_SYMBOL_CONTEXT *)Context; + + if (Ctx == NULL || Ctx->Code == NULL || Ip < Ctx->CodeBase || Ip >= Ctx->CodeBase + Ctx->CodeSize) return -pte_nomap; - UINT64 Available = g_CodeBase + g_CodeSize - Ip; + UINT64 Available = Ctx->CodeBase + Ctx->CodeSize - Ip; SIZE_T Count = (Size < Available) ? Size : (SIZE_T)Available; - memcpy(Buffer, g_Code + (Ip - g_CodeBase), Count); + memcpy(Buffer, Ctx->Code + (Ip - Ctx->CodeBase), Count); return (int)Count; } @@ -432,7 +430,7 @@ typedef struct _PROC_BASIC_INFO typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); static BOOLEAN -CommandPtCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) +CommandPtCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd, IMAGE_SYMBOL_CONTEXT * Ctx) { HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; @@ -456,8 +454,8 @@ CommandPtCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) return FALSE; - g_ImageBase = Base; - SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; + Ctx->ImageBase = Base; + SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) { @@ -475,21 +473,21 @@ CommandPtCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) if (Size == 0) return FALSE; - g_Code = (UINT8 *)malloc((SIZE_T)Size); - if (g_Code == NULL) + Ctx->Code = (UINT8 *)malloc((SIZE_T)Size); + if (Ctx->Code == NULL) return FALSE; - if (!ReadProcessMemory(Process, (PVOID)Start, g_Code, (SIZE_T)Size, &Got) || Got != Size) + if (!ReadProcessMemory(Process, (PVOID)Start, Ctx->Code, (SIZE_T)Size, &Got) || Got != Size) { - free(g_Code); - g_Code = NULL; + free(Ctx->Code); + Ctx->Code = NULL; return FALSE; } - g_CodeBase = Start; - g_CodeSize = Size; - *TextStart = Start; - *TextEnd = Start + Size - 1; + Ctx->CodeBase = Start; + Ctx->CodeSize = Size; + *TextStart = Start; + *TextEnd = Start + Size - 1; return TRUE; } @@ -497,7 +495,7 @@ CommandPtCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) } static BOOLEAN -CommandPtResolveFunction(HANDLE Process, const CHAR * Path, const CHAR * Name, UINT64 * Start, UINT64 * End) +CommandPtResolveFunction(HANDLE Process, const CHAR * Path, const CHAR * Name, UINT64 ImageBase, UINT64 * Start, UINT64 * End) { union { @@ -510,7 +508,7 @@ CommandPtResolveFunction(HANDLE Process, const CHAR * Path, const CHAR * Name, U if (!SymInitialize(Process, NULL, FALSE)) return FALSE; - if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)g_ImageBase, 0, NULL, 0) != 0) + if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)ImageBase, 0, NULL, 0) != 0) { Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); Symbol.Info.MaxNameLen = MAX_SYM_NAME; @@ -612,7 +610,7 @@ CommandPtReconstructIp(const struct pt_packet_ip * Packet, UINT64 * LastIp) } static UINT64 -CommandPtDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +CommandPtDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, UINT64 ImageBase) { struct pt_config Config; struct pt_packet_decoder * Decoder; @@ -669,7 +667,7 @@ CommandPtDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) ShowMessages(" %-8s 0x%016llx exe+0x%llx\n", CommandPtPacketName(Packet.type), (UINT64)Ip, - (UINT64)(Ip - g_ImageBase)); + (UINT64)(Ip - ImageBase)); } break; @@ -697,7 +695,7 @@ CommandPtDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) } static UINT64 -CommandPtDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +CommandPtDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, IMAGE_SYMBOL_CONTEXT * Ctx) { struct pt_config Config; struct pt_insn_decoder * Decoder; @@ -717,7 +715,7 @@ CommandPtDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) } Image = pt_insn_get_image(Decoder); - pt_image_set_callback(Image, CommandPtReadImage, NULL); + pt_image_set_callback(Image, CommandPtReadImage, Ctx); for (;;) { @@ -750,7 +748,7 @@ CommandPtDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) ShowMessages(" 0x%016llx exe+0x%-6llx %s\n", (UINT64)Insn.ip, - (UINT64)(Insn.ip - g_ImageBase), + (UINT64)(Insn.ip - Ctx->ImageBase), Disasm.text); else ShowMessages(" 0x%016llx (undecodable)\n", (UINT64)Insn.ip); @@ -773,6 +771,7 @@ CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, PROCESS_INFORMATION Process = {}; HYPERTRACE_PT_MMAP_PACKETS Mmap = {}; HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; + IMAGE_SYMBOL_CONTEXT Ctx = {}; UINT64 TextStart = 0; UINT64 TextEnd = 0; UINT64 FilterStart = 0; @@ -802,7 +801,7 @@ CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, ShowMessages("[*] target unpinned (scheduler may migrate it across cores)\n"); } - if (!CommandPtCaptureImage(Process.hProcess, &TextStart, &TextEnd)) + if (!CommandPtCaptureImage(Process.hProcess, &TextStart, &TextEnd, &Ctx)) { ShowMessages("[-] cannot read target image / .text section\n"); TerminateProcess(Process.hProcess, 1); @@ -810,15 +809,15 @@ CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, } ShowMessages("[+] image base 0x%llx, .text 0x%llx-0x%llx (%llu bytes)\n", - (UINT64)g_ImageBase, + (UINT64)Ctx.ImageBase, (UINT64)TextStart, (UINT64)TextEnd, - (UINT64)g_CodeSize); + (UINT64)Ctx.CodeSize); FilterStart = TextStart; FilterEnd = TextEnd; - if (Function != NULL && CommandPtResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) + if (Function != NULL && CommandPtResolveFunction(Process.hProcess, Path, Function, Ctx.ImageBase, &FilterStart, &FilterEnd)) { ShowMessages("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", Function, @@ -879,8 +878,8 @@ CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, ShowMessages("\n[*] core %u: %llu bytes of trace\n", Cpu, (UINT64)Bytes); Total += Packets - ? CommandPtDecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes) - : CommandPtDecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); + ? CommandPtDecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes, Ctx.ImageBase) + : CommandPtDecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes, &Ctx); } ShowMessages("\n[+] decoded %llu %s total\n", (UINT64)Total, Packets ? "packet(s)" : "instruction(s)"); @@ -888,10 +887,10 @@ CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, CommandPtSendDisable(); Cleanup: - if (g_Code != NULL) + if (Ctx.Code != NULL) { - free(g_Code); - g_Code = NULL; + free(Ctx.Code); + Ctx.Code = NULL; } if (Process.hThread != NULL) CloseHandle(Process.hThread); diff --git a/hyperdbg/libhyperdbg/header/symbol.h b/hyperdbg/libhyperdbg/header/symbol.h index ab6a177d..d516018f 100644 --- a/hyperdbg/libhyperdbg/header/symbol.h +++ b/hyperdbg/libhyperdbg/header/symbol.h @@ -26,6 +26,18 @@ typedef struct _LOCAL_FUNCTION_DESCRIPTION } LOCAL_FUNCTION_DESCRIPTION, *PLOCAL_FUNCTION_DESCRIPTION; +/** + * @brief Save the local module symbols' description + * + */ +typedef struct _IMAGE_SYMBOL_CONTEXT +{ + UINT64 ImageBase; + UINT64 CodeBase; + UINT64 CodeSize; + UINT8 * Code; +} IMAGE_SYMBOL_CONTEXT; + ////////////////////////////////////////////////// // Pdbex // ////////////////////////////////////////////////// From ee01458bc47fff25b2d54326676641bfc6e108fa Mon Sep 17 00:00:00 2001 From: sina Date: Thu, 2 Jul 2026 22:00:05 +0200 Subject: [PATCH 54/57] Organized header files for the libhyperdbg project --- CHANGELOG.md | 4 +- .../header/{ => app}/libhyperdbg.h | 0 .../libhyperdbg/header/{ => app}/messaging.h | 0 .../libhyperdbg/header/{ => app}/packets.h | 0 .../libhyperdbg/header/{ => common}/common.h | 0 .../libhyperdbg/header/{ => common}/list.h | 0 .../header/{ => debugger/commands}/commands.h | 0 .../header/{ => debugger/commands}/help.h | 0 .../communication}/communication.h | 0 .../{ => debugger/communication}/forwarding.h | 0 .../{ => debugger/communication}/namedpipe.h | 0 .../header/{ => debugger/core}/debugger.h | 0 .../header/{ => debugger/core}/steppings.h | 0 .../{ => debugger/driver-loader}/install.h | 0 .../header/{ => debugger/kernel-level}/kd.h | 0 .../header/{ => debugger/misc}/assembler.h | 0 .../header/{ => debugger/misc}/inipp.h | 0 .../header/{ => debugger/misc}/pci-id.h | 0 .../script-engine}/script-engine.h | 0 .../{ => debugger/script-engine}/symbol.h | 0 .../header/{ => debugger/tests}/tests.h | 0 .../transparency}/transparency.h | 0 .../{ => debugger/user-level}/pe-parser.h | 0 .../header/{ => debugger/user-level}/ud.h | 0 .../libhyperdbg/header/{ => export}/export.h | 0 .../header/{ => globals}/globals.h | 0 .../header/{ => hwdbg}/hwdbg-interpreter.h | 0 .../header/{ => hwdbg}/hwdbg-scripts.h | 0 .../header/{ => objects}/objects.h | 0 .../libhyperdbg/header/{ => rev}/rev-ctrl.h | 0 hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 58 ++--- .../libhyperdbg/libhyperdbg.vcxproj.filters | 228 +++++++++++------- hyperdbg/libhyperdbg/pch.cpp | 2 +- hyperdbg/libhyperdbg/pch.h | 56 ++--- 34 files changed, 202 insertions(+), 146 deletions(-) rename hyperdbg/libhyperdbg/header/{ => app}/libhyperdbg.h (100%) rename hyperdbg/libhyperdbg/header/{ => app}/messaging.h (100%) rename hyperdbg/libhyperdbg/header/{ => app}/packets.h (100%) rename hyperdbg/libhyperdbg/header/{ => common}/common.h (100%) rename hyperdbg/libhyperdbg/header/{ => common}/list.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/commands}/commands.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/commands}/help.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/communication}/communication.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/communication}/forwarding.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/communication}/namedpipe.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/core}/debugger.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/core}/steppings.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/driver-loader}/install.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/kernel-level}/kd.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/misc}/assembler.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/misc}/inipp.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/misc}/pci-id.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/script-engine}/script-engine.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/script-engine}/symbol.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/tests}/tests.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/transparency}/transparency.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/user-level}/pe-parser.h (100%) rename hyperdbg/libhyperdbg/header/{ => debugger/user-level}/ud.h (100%) rename hyperdbg/libhyperdbg/header/{ => export}/export.h (100%) rename hyperdbg/libhyperdbg/header/{ => globals}/globals.h (100%) rename hyperdbg/libhyperdbg/header/{ => hwdbg}/hwdbg-interpreter.h (100%) rename hyperdbg/libhyperdbg/header/{ => hwdbg}/hwdbg-scripts.h (100%) rename hyperdbg/libhyperdbg/header/{ => objects}/objects.h (100%) rename hyperdbg/libhyperdbg/header/{ => rev}/rev-ctrl.h (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1a59fb3..d1a01bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,13 +11,15 @@ New release of the HyperDbg Debugger. - Added structure for the hyperperf (Hardware Performance Counter) project ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) - The 'unload' command checks whether any module is loaded or not ([link](https://docs.hyperdbg.org/commands/debugging-commands/unload)) - Exported SDK API for checking whether any module is loaded or not ([link](https://github.com/HyperDbg/HyperDbg/commit/19e47c804f50f5dbb698f314e66b7d685a87ac1f)) +- Added user mode and kernel mode PT parameter parser ([link](https://github.com/HyperDbg/HyperDbg/pull/631)) ### Changed - Separated SDK libraries for user mode and kernel mode modules ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) - Added hypertrace, hyperevade, and hyperperf DLL files to SDK ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) - Fix pool manager uninitialize list corruption ([link](https://github.com/HyperDbg/HyperDbg/pull/629)) - Fix 'access denied' error on loading all modules using 'load all' ([link](https://docs.hyperdbg.org/commands/debugging-commands/load))([link](https://github.com/HyperDbg/HyperDbg/commit/2b818a5116d80d466aab7b343d4aa9d1d640f082)) -- Fix concatenation error for "hyperkd" string on the hypertrace project +- Fix the concatenation error for the "hyperkd" string on the hypertrace project (https://github.com/HyperDbg/HyperDbg/commit/2b0cc18899ff532d9d590a71bf880fee5456c15f)) +- Organized header files for the libhyperdbg project ([link](https://github.com/HyperDbg/HyperDbg/pull/632)) ## [0.20.0.0-beta] - 2026-06-21 New release of the HyperDbg Debugger. diff --git a/hyperdbg/libhyperdbg/header/libhyperdbg.h b/hyperdbg/libhyperdbg/header/app/libhyperdbg.h similarity index 100% rename from hyperdbg/libhyperdbg/header/libhyperdbg.h rename to hyperdbg/libhyperdbg/header/app/libhyperdbg.h diff --git a/hyperdbg/libhyperdbg/header/messaging.h b/hyperdbg/libhyperdbg/header/app/messaging.h similarity index 100% rename from hyperdbg/libhyperdbg/header/messaging.h rename to hyperdbg/libhyperdbg/header/app/messaging.h diff --git a/hyperdbg/libhyperdbg/header/packets.h b/hyperdbg/libhyperdbg/header/app/packets.h similarity index 100% rename from hyperdbg/libhyperdbg/header/packets.h rename to hyperdbg/libhyperdbg/header/app/packets.h diff --git a/hyperdbg/libhyperdbg/header/common.h b/hyperdbg/libhyperdbg/header/common/common.h similarity index 100% rename from hyperdbg/libhyperdbg/header/common.h rename to hyperdbg/libhyperdbg/header/common/common.h diff --git a/hyperdbg/libhyperdbg/header/list.h b/hyperdbg/libhyperdbg/header/common/list.h similarity index 100% rename from hyperdbg/libhyperdbg/header/list.h rename to hyperdbg/libhyperdbg/header/common/list.h diff --git a/hyperdbg/libhyperdbg/header/commands.h b/hyperdbg/libhyperdbg/header/debugger/commands/commands.h similarity index 100% rename from hyperdbg/libhyperdbg/header/commands.h rename to hyperdbg/libhyperdbg/header/debugger/commands/commands.h diff --git a/hyperdbg/libhyperdbg/header/help.h b/hyperdbg/libhyperdbg/header/debugger/commands/help.h similarity index 100% rename from hyperdbg/libhyperdbg/header/help.h rename to hyperdbg/libhyperdbg/header/debugger/commands/help.h diff --git a/hyperdbg/libhyperdbg/header/communication.h b/hyperdbg/libhyperdbg/header/debugger/communication/communication.h similarity index 100% rename from hyperdbg/libhyperdbg/header/communication.h rename to hyperdbg/libhyperdbg/header/debugger/communication/communication.h diff --git a/hyperdbg/libhyperdbg/header/forwarding.h b/hyperdbg/libhyperdbg/header/debugger/communication/forwarding.h similarity index 100% rename from hyperdbg/libhyperdbg/header/forwarding.h rename to hyperdbg/libhyperdbg/header/debugger/communication/forwarding.h diff --git a/hyperdbg/libhyperdbg/header/namedpipe.h b/hyperdbg/libhyperdbg/header/debugger/communication/namedpipe.h similarity index 100% rename from hyperdbg/libhyperdbg/header/namedpipe.h rename to hyperdbg/libhyperdbg/header/debugger/communication/namedpipe.h diff --git a/hyperdbg/libhyperdbg/header/debugger.h b/hyperdbg/libhyperdbg/header/debugger/core/debugger.h similarity index 100% rename from hyperdbg/libhyperdbg/header/debugger.h rename to hyperdbg/libhyperdbg/header/debugger/core/debugger.h diff --git a/hyperdbg/libhyperdbg/header/steppings.h b/hyperdbg/libhyperdbg/header/debugger/core/steppings.h similarity index 100% rename from hyperdbg/libhyperdbg/header/steppings.h rename to hyperdbg/libhyperdbg/header/debugger/core/steppings.h diff --git a/hyperdbg/libhyperdbg/header/install.h b/hyperdbg/libhyperdbg/header/debugger/driver-loader/install.h similarity index 100% rename from hyperdbg/libhyperdbg/header/install.h rename to hyperdbg/libhyperdbg/header/debugger/driver-loader/install.h diff --git a/hyperdbg/libhyperdbg/header/kd.h b/hyperdbg/libhyperdbg/header/debugger/kernel-level/kd.h similarity index 100% rename from hyperdbg/libhyperdbg/header/kd.h rename to hyperdbg/libhyperdbg/header/debugger/kernel-level/kd.h diff --git a/hyperdbg/libhyperdbg/header/assembler.h b/hyperdbg/libhyperdbg/header/debugger/misc/assembler.h similarity index 100% rename from hyperdbg/libhyperdbg/header/assembler.h rename to hyperdbg/libhyperdbg/header/debugger/misc/assembler.h diff --git a/hyperdbg/libhyperdbg/header/inipp.h b/hyperdbg/libhyperdbg/header/debugger/misc/inipp.h similarity index 100% rename from hyperdbg/libhyperdbg/header/inipp.h rename to hyperdbg/libhyperdbg/header/debugger/misc/inipp.h diff --git a/hyperdbg/libhyperdbg/header/pci-id.h b/hyperdbg/libhyperdbg/header/debugger/misc/pci-id.h similarity index 100% rename from hyperdbg/libhyperdbg/header/pci-id.h rename to hyperdbg/libhyperdbg/header/debugger/misc/pci-id.h diff --git a/hyperdbg/libhyperdbg/header/script-engine.h b/hyperdbg/libhyperdbg/header/debugger/script-engine/script-engine.h similarity index 100% rename from hyperdbg/libhyperdbg/header/script-engine.h rename to hyperdbg/libhyperdbg/header/debugger/script-engine/script-engine.h diff --git a/hyperdbg/libhyperdbg/header/symbol.h b/hyperdbg/libhyperdbg/header/debugger/script-engine/symbol.h similarity index 100% rename from hyperdbg/libhyperdbg/header/symbol.h rename to hyperdbg/libhyperdbg/header/debugger/script-engine/symbol.h diff --git a/hyperdbg/libhyperdbg/header/tests.h b/hyperdbg/libhyperdbg/header/debugger/tests/tests.h similarity index 100% rename from hyperdbg/libhyperdbg/header/tests.h rename to hyperdbg/libhyperdbg/header/debugger/tests/tests.h diff --git a/hyperdbg/libhyperdbg/header/transparency.h b/hyperdbg/libhyperdbg/header/debugger/transparency/transparency.h similarity index 100% rename from hyperdbg/libhyperdbg/header/transparency.h rename to hyperdbg/libhyperdbg/header/debugger/transparency/transparency.h diff --git a/hyperdbg/libhyperdbg/header/pe-parser.h b/hyperdbg/libhyperdbg/header/debugger/user-level/pe-parser.h similarity index 100% rename from hyperdbg/libhyperdbg/header/pe-parser.h rename to hyperdbg/libhyperdbg/header/debugger/user-level/pe-parser.h diff --git a/hyperdbg/libhyperdbg/header/ud.h b/hyperdbg/libhyperdbg/header/debugger/user-level/ud.h similarity index 100% rename from hyperdbg/libhyperdbg/header/ud.h rename to hyperdbg/libhyperdbg/header/debugger/user-level/ud.h diff --git a/hyperdbg/libhyperdbg/header/export.h b/hyperdbg/libhyperdbg/header/export/export.h similarity index 100% rename from hyperdbg/libhyperdbg/header/export.h rename to hyperdbg/libhyperdbg/header/export/export.h diff --git a/hyperdbg/libhyperdbg/header/globals.h b/hyperdbg/libhyperdbg/header/globals/globals.h similarity index 100% rename from hyperdbg/libhyperdbg/header/globals.h rename to hyperdbg/libhyperdbg/header/globals/globals.h diff --git a/hyperdbg/libhyperdbg/header/hwdbg-interpreter.h b/hyperdbg/libhyperdbg/header/hwdbg/hwdbg-interpreter.h similarity index 100% rename from hyperdbg/libhyperdbg/header/hwdbg-interpreter.h rename to hyperdbg/libhyperdbg/header/hwdbg/hwdbg-interpreter.h diff --git a/hyperdbg/libhyperdbg/header/hwdbg-scripts.h b/hyperdbg/libhyperdbg/header/hwdbg/hwdbg-scripts.h similarity index 100% rename from hyperdbg/libhyperdbg/header/hwdbg-scripts.h rename to hyperdbg/libhyperdbg/header/hwdbg/hwdbg-scripts.h diff --git a/hyperdbg/libhyperdbg/header/objects.h b/hyperdbg/libhyperdbg/header/objects/objects.h similarity index 100% rename from hyperdbg/libhyperdbg/header/objects.h rename to hyperdbg/libhyperdbg/header/objects/objects.h diff --git a/hyperdbg/libhyperdbg/header/rev-ctrl.h b/hyperdbg/libhyperdbg/header/rev/rev-ctrl.h similarity index 100% rename from hyperdbg/libhyperdbg/header/rev-ctrl.h rename to hyperdbg/libhyperdbg/header/rev/rev-ctrl.h diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index bcb12486..c943b6e8 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -140,35 +140,35 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters index 9b62836e..236c3074 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters @@ -102,95 +102,68 @@ {da7e68cc-540c-4efc-b4de-c23b4b13e2ec} + + {d6010267-4888-46f5-9bdb-2339565710d2} + + + {e81c07d3-5e98-4bdb-a00b-2b99e023553c} + + + {477d3059-08d7-433b-83c3-bbf57658deaf} + + + {559b68f3-8bf0-447a-a714-0e355c22516f} + + + {88de3dbf-90ab-47ef-94fe-8c21ce73c9f1} + + + {c1f438ff-fd3b-4fb4-989b-f145290e3837} + + + {c5b26259-abcc-459f-9b7e-fd90c34ad646} + + + {99425a01-168c-4b6b-a53f-d62fbb7c8cab} + + + {c7f85132-1802-4462-990f-3fffb0365dbc} + + + {4f4eca9f-c1f6-4342-91ef-2b533292e238} + + + {bde7408e-fad9-4329-98ac-e604674f7bbf} + + + {06e9888f-57f0-4d59-8f7e-1fdd336be9cc} + + + {0d88d581-8ff4-40f2-8585-3fdb97d4d211} + + + {439dcb49-1825-4076-affe-17b505e4033a} + + + {16331b3c-c8fd-4b12-abb4-7617d21db9d0} + + + {2d2c16a6-3fdc-4b63-a282-f2762a1f340d} + + + {9dcff6b3-9ebd-46f3-a0c5-c69d06357949} + + + {3e4dda9f-6050-409f-85e2-4cf7c8a95743} + header - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - header\platform - - header - - - header - - - header - - - header - - - header - - - header - header\platform @@ -209,18 +182,99 @@ header\platform - - header - - - header - header\platform\windows-only header\components\pe + + header\app + + + header\app + + + header\app + + + header\common + + + header\common + + + header\export + + + header\globals + + + header\hwdbg + + + header\hwdbg + + + header\rev + + + header\objects + + + header\debugger\commands + + + header\debugger\commands + + + header\debugger\communication + + + header\debugger\communication + + + header\debugger\communication + + + header\debugger\core + + + header\debugger\core + + + header\debugger\driver-loader + + + header\debugger\kernel-level + + + header\debugger\misc + + + header\debugger\misc + + + header\debugger\misc + + + header\debugger\user-level + + + header\debugger\user-level + + + header\debugger\transparency + + + header\debugger\tests + + + header\debugger\script-engine + + + header\debugger\script-engine + diff --git a/hyperdbg/libhyperdbg/pch.cpp b/hyperdbg/libhyperdbg/pch.cpp index 92958c22..fa15fce4 100644 --- a/hyperdbg/libhyperdbg/pch.cpp +++ b/hyperdbg/libhyperdbg/pch.cpp @@ -14,7 +14,7 @@ // // Global variables // -#include "header/globals.h" +#include "header/globals/globals.h" // // When you are using pre-compiled headers, this source file is necessary for diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index a193f07f..96640e8c 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -187,7 +187,7 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; // // PCI IDs // -#include "header/pci-id.h" +#include "header/debugger/misc/pci-id.h" // // Intel PT @@ -197,45 +197,45 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; // // General // -#include "header/libhyperdbg.h" -#include "header/export.h" -#include "header/inipp.h" -#include "header/commands.h" -#include "header/common.h" -#include "header/symbol.h" -#include "header/debugger.h" -#include "header/script-engine.h" -#include "header/help.h" +#include "header/app/libhyperdbg.h" +#include "header/export/export.h" +#include "header/debugger/misc/inipp.h" +#include "header/debugger/commands/commands.h" +#include "header/common/common.h" +#include "header/debugger/script-engine/symbol.h" +#include "header/debugger/core/debugger.h" +#include "header/debugger/script-engine/script-engine.h" +#include "header/debugger/commands/help.h" #ifdef _WIN32 -# include "header/install.h" +# include "header/debugger/driver-loader/install.h" #endif -#include "header/list.h" -#include "header/tests.h" -#include "header/messaging.h" -#include "header/packets.h" -#include "header/transparency.h" -#include "header/communication.h" -#include "header/namedpipe.h" -#include "header/forwarding.h" -#include "header/kd.h" +#include "header/common/list.h" +#include "header/debugger/tests/tests.h" +#include "header/app/messaging.h" +#include "header/app/packets.h" +#include "header/debugger/transparency/transparency.h" +#include "header/debugger/communication/communication.h" +#include "header/debugger/communication/namedpipe.h" +#include "header/debugger/communication/forwarding.h" +#include "header/debugger/kernel-level/kd.h" // // Components // #include "../include/components/pe/header/pe-image-reader.h" -#include "header/pe-parser.h" -#include "header/ud.h" -#include "header/objects.h" -#include "header/steppings.h" -#include "header/rev-ctrl.h" -#include "header/assembler.h" +#include "header/debugger/user-level/pe-parser.h" +#include "header/debugger/user-level/ud.h" +#include "header/objects/objects.h" +#include "header/debugger/core/steppings.h" +#include "header/rev/rev-ctrl.h" +#include "header/debugger/misc/assembler.h" // // hwdbg // -#include "header/hwdbg-interpreter.h" -#include "header/hwdbg-scripts.h" +#include "header/hwdbg/hwdbg-interpreter.h" +#include "header/hwdbg/hwdbg-scripts.h" // // Zydis headers From 5739d0ee4c0fe84cdfb95ac5f3fa1db861e67bc5 Mon Sep 17 00:00:00 2001 From: sina Date: Fri, 3 Jul 2026 17:26:01 +0200 Subject: [PATCH 55/57] add PT thread helper --- hyperdbg/hypertrace/code/pt/Pt.c | 40 +- .../commands/extension-commands/pt.cpp | 638 +++++++----------- .../code/debugger/misc/pt-helper.cpp | 432 ++++++++++++ .../libhyperdbg/header/debugger/misc/pci-id.h | 1 + .../header/debugger/misc/pt-helper.h | 28 + .../header/debugger/script-engine/symbol.h | 35 + hyperdbg/libhyperdbg/libhyperdbg.vcxproj | 2 + .../libhyperdbg/libhyperdbg.vcxproj.filters | 6 + hyperdbg/libhyperdbg/pch.h | 1 + 9 files changed, 756 insertions(+), 427 deletions(-) create mode 100644 hyperdbg/libhyperdbg/code/debugger/misc/pt-helper.cpp create mode 100644 hyperdbg/libhyperdbg/header/debugger/misc/pt-helper.h diff --git a/hyperdbg/hypertrace/code/pt/Pt.c b/hyperdbg/hypertrace/code/pt/Pt.c index de2e6a01..2dfe7ce5 100644 --- a/hyperdbg/hypertrace/code/pt/Pt.c +++ b/hyperdbg/hypertrace/code/pt/Pt.c @@ -639,9 +639,9 @@ PtEngineStart(PT_PER_CPU * Cpu) if (g_RunningOnHypervisorEnvironment) { IsOnVmxRootMode = g_Callbacks.VmFuncVmxGetCurrentExecutionMode(); - LogInfo("PT: PtEngineStart on core %u (vmx-root=%u)\n", - KeGetCurrentProcessorNumberEx(NULL), - (UINT32)IsOnVmxRootMode); + // LogInfo("PT: PtEngineStart on core %u (vmx-root=%u)\n", + // KeGetCurrentProcessorNumberEx(NULL), + // (UINT32)IsOnVmxRootMode); } // @@ -826,9 +826,9 @@ PtEngineStop(PT_PER_CPU * Cpu, PT_OUTPUT_BUFFER * Out) if (g_RunningOnHypervisorEnvironment) { IsOnVmxRootMode = g_Callbacks.VmFuncVmxGetCurrentExecutionMode(); - LogInfo("PT: PtEngineStop on core %u (vmx-root=%u)\n", - KeGetCurrentProcessorNumberEx(NULL), - (UINT32)IsOnVmxRootMode); + // LogInfo("PT: PtEngineStop on core %u (vmx-root=%u)\n", + // KeGetCurrentProcessorNumberEx(NULL), + // (UINT32)IsOnVmxRootMode); } // @@ -1027,9 +1027,9 @@ PtCheck() if (g_RunningOnHypervisorEnvironment && !Caps.VmxSupport) { - LogInfo("PT: IA32_VMX_MISC[14] is clear — Intel PT in VMX is not " - "advertised on this CPU. Direct MSR programming still works " - "from VMX non-root because PT MSRs are not trapped.\n"); + // LogInfo("PT: IA32_VMX_MISC[14] is clear - Intel PT in VMX is not " + // "advertised on this CPU. Direct MSR programming still works " + // "from VMX non-root because PT MSRs are not trapped.\n"); } return TRUE; @@ -1071,7 +1071,7 @@ PtAllocateAllCpuBuffers() if (PtEngineAllocateBuffers(Cpu, &Cfg) != 0) { - LogInfo("PT: buffer allocation failed on core %u\n", i); + // LogInfo("PT: buffer allocation failed on core %u\n", i); return FALSE; } } @@ -1233,13 +1233,13 @@ PtStart() // Allocating from a DPC is unsafe (MmAllocateContiguousMemory* is // a paged routine) so just bail out. // - LogInfo("PT: buffers not allocated for core %u\n", CurrentCore); + // LogInfo("PT: buffers not allocated for core %u\n", CurrentCore); return FALSE; } if (PtEngineStart(Cpu) != 0) { - LogInfo("PT: PtEngineStart failed on core %u (state=%d)\n", CurrentCore, Cpu->State); + // LogInfo("PT: PtEngineStart failed on core %u (state=%d)\n", CurrentCore, Cpu->State); return FALSE; } @@ -1263,7 +1263,7 @@ PtStop() CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: stopping trace on core %d\n", CurrentCore); + // LogInfo("PT: stopping trace on core %d\n", CurrentCore); PtEngineStop(Cpu, NULL); } @@ -1284,7 +1284,7 @@ PtPause() CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: pausing trace on core %u\n", CurrentCore); + // LogInfo("PT: pausing trace on core %u\n", CurrentCore); PtEnginePause(Cpu); } @@ -1304,7 +1304,7 @@ PtResume() CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: resuming trace on core %u\n", CurrentCore); + // LogInfo("PT: resuming trace on core %u\n", CurrentCore); PtEngineResume(Cpu); } @@ -1415,7 +1415,7 @@ PtFilter(const PT_APPLY_CORE_FILTER_REQUEST * FilterRequest) CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: applying filter on core %u\n", CurrentCore); + // LogInfo("PT: applying filter on core %u\n", CurrentCore); // // Stop tracing on this CPU first so we can safely mutate Cpu->Config @@ -1434,7 +1434,7 @@ PtFilter(const PT_APPLY_CORE_FILTER_REQUEST * FilterRequest) if (FilterRequest->EnableOptions.Cr3 != 0 && !Caps.Cr3Filtering) { - LogInfo("PT: CR3 filtering requested but not supported by CPU\n"); + // LogInfo("PT: CR3 filtering requested but not supported by CPU\n"); Cpu->Config.TargetCr3 = 0; } else @@ -1444,12 +1444,12 @@ PtFilter(const PT_APPLY_CORE_FILTER_REQUEST * FilterRequest) if (FilterRequest->FilterOptions.NumAddrRanges > Caps.NumAddrRanges) { - LogInfo("PT: requested %u IP filter ranges, but CPU only supports %u\n", FilterRequest->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 (FilterRequest->FilterOptions.NumAddrRanges > 0 && !Caps.IpFiltering) { - LogInfo("PT: IP filtering requested but not supported by CPU\n"); + // LogInfo("PT: IP filtering requested but not supported by CPU\n"); Cpu->Config.NumAddrRanges = 0; } else @@ -1499,7 +1499,7 @@ PtFlush() CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: flush on core %u\n", CurrentCore); + // LogInfo("PT: flush on core %u\n", CurrentCore); if (Cpu->State == PT_STATE_TRACING || Cpu->State == PT_STATE_PAUSED) { diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp index 6d2a88d8..7b158b08 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp @@ -18,8 +18,6 @@ extern BOOLEAN g_IsHyperTraceModuleLoaded; extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee; - - /** * @brief help of the !pt command * @@ -403,408 +401,59 @@ CommandPtSendFilterByPid(UINT32 ProcessId, return TRUE; } -static int -CommandPtReadImage(UINT8 * Buffer, SIZE_T Size, const struct pt_asid * Asid, UINT64 Ip, VOID * Context) -{ - (VOID) Asid; - - IMAGE_SYMBOL_CONTEXT * Ctx = (IMAGE_SYMBOL_CONTEXT *)Context; - - if (Ctx == NULL || Ctx->Code == NULL || Ip < Ctx->CodeBase || Ip >= Ctx->CodeBase + Ctx->CodeSize) - return -pte_nomap; - - UINT64 Available = Ctx->CodeBase + Ctx->CodeSize - Ip; - SIZE_T Count = (Size < Available) ? Size : (SIZE_T)Available; - - memcpy(Buffer, Ctx->Code + (Ip - Ctx->CodeBase), Count); - return (int)Count; -} - -typedef struct _PROC_BASIC_INFO -{ - LONG ExitStatus; - PVOID PebBaseAddress; - ULONG_PTR Reserved[4]; -} PROC_BASIC_INFO; - -typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); - -static BOOLEAN -CommandPtCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd, IMAGE_SYMBOL_CONTEXT * Ctx) -{ - HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); - PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; - PROC_BASIC_INFO Pbi = {0}; - ULONG Ret = 0; - SIZE_T Got = 0; - UINT64 Base = 0; - IMAGE_DOS_HEADER Dos; - IMAGE_NT_HEADERS64 Nt; - UINT64 SectionBase; - - if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) - return FALSE; - - if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) - return FALSE; - - if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) - return FALSE; - - if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) - return FALSE; - - Ctx->ImageBase = Base; - SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; - - for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) - { - IMAGE_SECTION_HEADER Section; - - if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) - return FALSE; - - if (memcmp(Section.Name, ".text", 6) != 0) - continue; - - UINT64 Start = Base + Section.VirtualAddress; - UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; - - if (Size == 0) - return FALSE; - - Ctx->Code = (UINT8 *)malloc((SIZE_T)Size); - if (Ctx->Code == NULL) - return FALSE; - - if (!ReadProcessMemory(Process, (PVOID)Start, Ctx->Code, (SIZE_T)Size, &Got) || Got != Size) - { - free(Ctx->Code); - Ctx->Code = NULL; - return FALSE; - } - - Ctx->CodeBase = Start; - Ctx->CodeSize = Size; - *TextStart = Start; - *TextEnd = Start + Size - 1; - return TRUE; - } - - return FALSE; -} - -static BOOLEAN -CommandPtResolveFunction(HANDLE Process, const CHAR * Path, const CHAR * Name, UINT64 ImageBase, UINT64 * Start, UINT64 * End) -{ - union - { - SYMBOL_INFO Info; - BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; - } Symbol = {0}; - BOOLEAN Ok = FALSE; - - SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); - if (!SymInitialize(Process, NULL, FALSE)) - return FALSE; - - if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)ImageBase, 0, NULL, 0) != 0) - { - Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); - Symbol.Info.MaxNameLen = MAX_SYM_NAME; - - if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) - { - *Start = Symbol.Info.Address; - *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; - Ok = TRUE; - } - } - - SymCleanup(Process); - return Ok; -} - -static const CHAR * -CommandPtPacketName(enum pt_packet_type Type) -{ - switch (Type) - { - case ppt_psb: - return "PSB"; - case ppt_psbend: - return "PSBEND"; - case ppt_pad: - return "PAD"; - case ppt_fup: - return "FUP"; - case ppt_tip: - return "TIP"; - case ppt_tip_pge: - return "TIP.PGE"; - case ppt_tip_pgd: - return "TIP.PGD"; - case ppt_tnt_8: - return "TNT8"; - case ppt_tnt_64: - return "TNT64"; - case ppt_mode: - return "MODE"; - case ppt_pip: - return "PIP"; - case ppt_vmcs: - return "VMCS"; - case ppt_cbr: - return "CBR"; - case ppt_tsc: - return "TSC"; - case ppt_tma: - return "TMA"; - case ppt_mtc: - return "MTC"; - case ppt_cyc: - return "CYC"; - case ppt_ovf: - return "OVF"; - case ppt_stop: - return "STOP"; - case ppt_exstop: - return "EXSTOP"; - case ppt_mnt: - return "MNT"; - case ppt_ptw: - return "PTW"; - default: - return "?"; - } -} - -static UINT64 -CommandPtReconstructIp(const struct pt_packet_ip * Packet, UINT64 * LastIp) -{ - UINT64 Value = *LastIp; - - switch (Packet->ipc) - { - case pt_ipc_update_16: - Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); - break; - case pt_ipc_update_32: - Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); - break; - case pt_ipc_update_48: - Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); - break; - case pt_ipc_sext_48: - Value = Packet->ip & 0xffffffffffffull; - if (Value & 0x800000000000ull) - Value |= 0xffff000000000000ull; - break; - default: - Value = Packet->ip; - break; - } - - *LastIp = Value; - return Value; -} - -static UINT64 -CommandPtDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, UINT64 ImageBase) -{ - struct pt_config Config; - struct pt_packet_decoder * Decoder; - UINT64 Count = 0; - UINT64 LastIp = 0; - int Status; - - pt_config_init(&Config); - Config.begin = (UINT8 *)Buffer; - Config.end = (UINT8 *)Buffer + Size; - - Decoder = pt_pkt_alloc_decoder(&Config); - if (Decoder == NULL) - { - ShowMessages("[-] core %u: cannot allocate packet decoder\n", Cpu); - return 0; - } - - for (;;) - { - Status = pt_pkt_sync_forward(Decoder); - if (Status < 0) - break; - - for (;;) - { - struct pt_packet Packet; - - Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); - if (Status < 0) - break; - - Count++; - - switch (Packet.type) - { - case ppt_tnt_8: - case ppt_tnt_64: - ShowMessages(" %-8s %2u ", CommandPtPacketName(Packet.type), Packet.payload.tnt.bit_size); - for (UINT8 Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) - putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); - putchar('\n'); - break; - - case ppt_tip: - case ppt_fup: - case ppt_tip_pge: - case ppt_tip_pgd: - if (Packet.payload.ip.ipc == pt_ipc_suppressed) - ShowMessages(" %-8s (ip suppressed)\n", CommandPtPacketName(Packet.type)); - else - { - UINT64 Ip = CommandPtReconstructIp(&Packet.payload.ip, &LastIp); - ShowMessages(" %-8s 0x%016llx exe+0x%llx\n", - CommandPtPacketName(Packet.type), - (UINT64)Ip, - (UINT64)(Ip - ImageBase)); - } - break; - - case ppt_pip: - ShowMessages(" %-8s cr3=0x%llx\n", CommandPtPacketName(Packet.type), (UINT64)Packet.payload.pip.cr3); - break; - - case ppt_cbr: - // ShowMessages(" %-8s ratio=%u\n", CommandPtPacketName(Packet.type), Packet.payload.cbr.ratio); - break; - - case ppt_tsc: - ShowMessages(" %-8s tsc=0x%llx\n", CommandPtPacketName(Packet.type), (UINT64)Packet.payload.tsc.tsc); - break; - - default: - // ShowMessages(" %-8s\n", CommandPtPacketName(Packet.type)); - break; - } - } - } - - pt_pkt_free_decoder(Decoder); - return Count; -} - -static UINT64 -CommandPtDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, IMAGE_SYMBOL_CONTEXT * Ctx) -{ - struct pt_config Config; - struct pt_insn_decoder * Decoder; - struct pt_image * Image; - UINT64 Count = 0; - int Status; - - pt_config_init(&Config); - Config.begin = (UINT8 *)Buffer; - Config.end = (UINT8 *)Buffer + Size; - - Decoder = pt_insn_alloc_decoder(&Config); - if (Decoder == NULL) - { - ShowMessages("[-] core %u: cannot allocate instruction decoder\n", Cpu); - return 0; - } - - Image = pt_insn_get_image(Decoder); - pt_image_set_callback(Image, CommandPtReadImage, Ctx); - - for (;;) - { - Status = pt_insn_sync_forward(Decoder); - if (Status < 0) - break; - - for (;;) - { - struct pt_insn Insn; - - while (Status & pts_event_pending) - { - struct pt_event Event; - Status = pt_insn_event(Decoder, &Event, sizeof(Event)); - if (Status < 0) - break; - } - - if (Status < 0 || (Status & pts_eos)) - break; - - Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); - if (Status < 0) - break; - - ZydisDisassembledInstruction Disasm; - ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; - - if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) - ShowMessages(" 0x%016llx exe+0x%-6llx %s\n", - (UINT64)Insn.ip, - (UINT64)(Insn.ip - Ctx->ImageBase), - Disasm.text); - else - ShowMessages(" 0x%016llx (undecodable)\n", (UINT64)Insn.ip); - - Count++; - } - - if (Status >= 0 && (Status & pts_eos)) - break; - } - - pt_insn_free_decoder(Decoder); - return Count; -} - +/** + * @brief Shared core: enable PT, wait for the target, decode and disable + * + * @details Called by the three specialised helpers after they have filled + * Process with a valid hProcess (and optionally hThread). + * Owns and closes all handles in Process before returning. + * + * @param Process Pointer to a PROCESS_INFORMATION whose hProcess (and + * optionally hThread) have already been opened/created + * @param Path Executable path used for symbol resolution, or NULL + * @param Function Symbol name to narrow the IP filter, or NULL + * @param Packets TRUE → decode raw packets; FALSE → decode instructions + * @param PinCore ≥ 0 → pin the target to that logical core; < 0 → free + * @param LaunchedNew TRUE → process was launched suspended here (will be + * resumed, and terminated on error); FALSE → the + * process was already running (never terminated) + * + * @return VOID + */ static VOID -CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, int PinCore) +CommandPtRunAndTraceCore(PROCESS_INFORMATION * Process, + const CHAR * Path, + const CHAR * Function, + BOOLEAN Packets, + int PinCore, + BOOLEAN LaunchedNew) { - STARTUPINFOA Startup = {}; - PROCESS_INFORMATION Process = {}; + DWORD_PTR Mask; HYPERTRACE_PT_MMAP_PACKETS Mmap = {}; HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; - IMAGE_SYMBOL_CONTEXT Ctx = {}; + IMAGE_SYMBOL_CONTEXT Ctx = {}; UINT64 TextStart = 0; UINT64 TextEnd = 0; UINT64 FilterStart = 0; UINT64 FilterEnd = 0; UINT64 Total = 0; - - Startup.cb = sizeof(Startup); - - if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) + if (PinCore < 0) { - ShowMessages("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); - return; + PinCore = PT_DEFAULT_PINNING_CORE; } - ShowMessages("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); + Mask = (DWORD_PTR)1 << PinCore; - if (PinCore >= 0) - { - DWORD_PTR Mask = (DWORD_PTR)1 << PinCore; - if (SetProcessAffinityMask(Process.hProcess, Mask)) - ShowMessages("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); - else - ShowMessages("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); - } + if (SetProcessAffinityMask(Process->hProcess, Mask)) + ShowMessages("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); else - { - ShowMessages("[*] target unpinned (scheduler may migrate it across cores)\n"); - } + ShowMessages("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); - if (!CommandPtCaptureImage(Process.hProcess, &TextStart, &TextEnd, &Ctx)) + if (!PtHelperCaptureImage(Process->hProcess, &TextStart, &TextEnd, &Ctx)) { ShowMessages("[-] cannot read target image / .text section\n"); - TerminateProcess(Process.hProcess, 1); + if (LaunchedNew) + TerminateProcess(Process->hProcess, 1); goto Cleanup; } @@ -817,7 +466,8 @@ CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, FilterStart = TextStart; FilterEnd = TextEnd; - if (Function != NULL && CommandPtResolveFunction(Process.hProcess, Path, Function, Ctx.ImageBase, &FilterStart, &FilterEnd)) + if (Function != NULL && Path != NULL && + PtHelperResolveFunction(Process->hProcess, Path, Function, Ctx.ImageBase, &FilterStart, &FilterEnd)) { ShowMessages("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", Function, @@ -827,41 +477,51 @@ CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, } else { - ShowMessages("[!] IP filter: whole .text (symbol '%s' not found - build the target with a PDB)\n", + ShowMessages("[!] IP filter: whole .text (symbol '%s' not found or not applicable)\n", Function ? Function : "(none)"); } - if (!CommandPtSendFilterByPid(Process.dwProcessId, TRUE, FALSE, FilterStart, FilterEnd, NULL, NULL, NULL, NULL, NULL, NULL) || + if (!CommandPtSendFilterByPid(Process->dwProcessId, TRUE, FALSE, FilterStart, FilterEnd, NULL, NULL, NULL, NULL, NULL, NULL) || !CommandPtSendEnable()) { ShowMessages("[-] cannot enable Intel PT\n"); - TerminateProcess(Process.hProcess, 1); + if (LaunchedNew) + TerminateProcess(Process->hProcess, 1); goto Cleanup; } - if (!hyperdbg_u_pt_mmap(&Mmap)) + if (!HyperDbgPtMmapSendRequest(&Mmap)) { ShowMessages("[-] pt_mmap failed\n"); CommandPtSendDisable(); - TerminateProcess(Process.hProcess, 1); + if (LaunchedNew) + TerminateProcess(Process->hProcess, 1); goto Cleanup; } ShowMessages("[+] PT enabled, %u per-core buffers mapped\n", Mmap.NumCpus); - ShowMessages("[*] resuming target and waiting for it to exit...\n"); - ResumeThread(Process.hThread); - WaitForSingleObject(Process.hProcess, INFINITE); + if (LaunchedNew) + { + ShowMessages("[*] resuming target and waiting for it to exit...\n"); + ResumeThread(Process->hThread); + } + else + { + ShowMessages("[*] waiting for target process to exit...\n"); + } + + WaitForSingleObject(Process->hProcess, INFINITE); ShowMessages("[+] target exited, decoding trace\n"); CommandPtSendPause(); Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; - if (!hyperdbg_u_pt_operation(&Sizes)) + + if (!CommandPtSendRequest(&Sizes)) { ShowMessages("[-] cannot query PT sizes\n"); CommandPtSendDisable(); - goto Cleanup; } @@ -878,8 +538,8 @@ CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, ShowMessages("\n[*] core %u: %llu bytes of trace\n", Cpu, (UINT64)Bytes); Total += Packets - ? CommandPtDecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes, Ctx.ImageBase) - : CommandPtDecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes, &Ctx); + ? PtHelperDecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes, Ctx.ImageBase) + : PtHelperDecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes, &Ctx); } ShowMessages("\n[+] decoded %llu %s total\n", (UINT64)Total, Packets ? "packet(s)" : "instruction(s)"); @@ -892,10 +552,162 @@ Cleanup: free(Ctx.Code); Ctx.Code = NULL; } - if (Process.hThread != NULL) - CloseHandle(Process.hThread); - if (Process.hProcess != NULL) - CloseHandle(Process.hProcess); + + if (Process->hThread != NULL) + CloseHandle(Process->hThread); + if (Process->hProcess != NULL) + CloseHandle(Process->hProcess); +} + +/** + * @brief Launch an executable at Path as a suspended process, enable PT, and + * decode the trace after the process exits + * + * @param Path Full path to the executable to launch + * @param Function Symbol name to narrow the IP filter, or NULL for whole .text + * @param Packets TRUE → decode raw packets; FALSE → decode instructions + * @param PinCore ≥ 0 → pin to that logical core; < 0 → unpinned + * + * @return VOID + */ +static VOID +CommandPtRunAndTraceByPath(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, int PinCore) +{ + STARTUPINFOA Startup = {}; + PROCESS_INFORMATION Process = {}; + + Startup.cb = sizeof(Startup); + + if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) + { + ShowMessages("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); + return; + } + + ShowMessages("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); + + CommandPtRunAndTraceCore(&Process, Path, Function, Packets, PinCore, TRUE); +} + +/** + * @brief Open an existing process by PID, enable PT, and decode the trace + * after the process exits + * + * @param ProcessId PID of the target process + * @param Function Symbol name to narrow the IP filter, or NULL for whole .text + * @param Packets TRUE → decode raw packets; FALSE → decode instructions + * @param PinCore ≥ 0 → pin to that logical core; < 0 → unpinned + * + * @return VOID + */ +static VOID +CommandPtRunAndTraceByPid(UINT32 ProcessId, const CHAR * Function, BOOLEAN Packets, int PinCore) +{ + PROCESS_INFORMATION Process = {}; + + Process.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)ProcessId); + + if (Process.hProcess == NULL) + { + ShowMessages("[-] cannot open process 0x%x (error 0x%x)\n", ProcessId, GetLastError()); + return; + } + + Process.dwProcessId = ProcessId; + + ShowMessages("[+] attached to pid 0x%x\n", ProcessId); + + CommandPtRunAndTraceCore(&Process, NULL, Function, Packets, PinCore, FALSE); +} + +/** + * @brief Open an existing thread by TID, derive its owning process, enable PT, + * and decode the trace after the process exits + * + * @param ThreadId TID of the target thread + * @param Function Symbol name to narrow the IP filter, or NULL for whole .text + * @param Packets TRUE → decode raw packets; FALSE → decode instructions + * @param PinCore ≥ 0 → pin to that logical core; < 0 → unpinned + * + * @return VOID + */ +static VOID +CommandPtRunAndTraceByTid(UINT32 ThreadId, const CHAR * Function, BOOLEAN Packets, int PinCore) +{ + PROCESS_INFORMATION Process = {}; + HANDLE ThreadHandle = NULL; + DWORD OwningPid = 0; + THREAD_BASIC_INFO_EX Tbi = {0}; + ULONG RetTid = 0; + HMODULE NtdllTid = GetModuleHandleA("ntdll.dll"); + PFN_NT_QIT NtQit = NtdllTid ? (PFN_NT_QIT)GetProcAddress(NtdllTid, "NtQueryInformationThread") : NULL; + + ThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)ThreadId); + + if (ThreadHandle == NULL) + { + ShowMessages("[-] cannot open thread 0x%x (error 0x%x)\n", ThreadId, GetLastError()); + return; + } + + if (NtQit == NULL || NtQit(ThreadHandle, 0, &Tbi, sizeof(Tbi), &RetTid) < 0 || Tbi.ClientId.UniqueProcess == NULL) + { + ShowMessages("[-] cannot get owning PID for thread 0x%x\n", ThreadId); + CloseHandle(ThreadHandle); + return; + } + + OwningPid = (DWORD)(ULONG_PTR)Tbi.ClientId.UniqueProcess; + + if (OwningPid == 0) + { + ShowMessages("[-] cannot get owning PID for thread 0x%x\n", ThreadId); + CloseHandle(ThreadHandle); + return; + } + + Process.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, OwningPid); + + if (Process.hProcess == NULL) + { + ShowMessages("[-] cannot open process %u (error 0x%x)\n", OwningPid, GetLastError()); + CloseHandle(ThreadHandle); + return; + } + + Process.hThread = ThreadHandle; + Process.dwProcessId = OwningPid; + Process.dwThreadId = ThreadId; + + ShowMessages("[+] attached to tid 0x%x (owning pid %u)\n", ThreadId, OwningPid); + + CommandPtRunAndTraceCore(&Process, NULL, Function, Packets, PinCore, FALSE); +} + +/** + * @brief Wrapper: dispatch to the appropriate trace helper based on whichever + * selector (Path, ProcessId, ThreadId) is provided + * + * @param Path Executable path, or NULL + * @param Function Symbol name for IP filter, or NULL + * @param Packets TRUE → raw packets; FALSE → instructions + * @param PinCore Logical core to pin the target to, or < 0 for unpinned + * @param ProcessId PID of an existing process, or 0 + * @param ThreadId TID of an existing thread, or 0 + * + * @return VOID + */ +static VOID +CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, int PinCore, UINT32 ProcessId, UINT32 ThreadId) +{ + if (Path != NULL) + CommandPtRunAndTraceByPath(Path, Function, Packets, PinCore); + else if (ThreadId != 0) + CommandPtRunAndTraceByTid(ThreadId, Function, Packets, PinCore); + else if (ProcessId != 0) + CommandPtRunAndTraceByPid(ProcessId, Function, Packets, PinCore); + else + ShowMessages("[-] no path, PID, or TID specified\n"); } /** @@ -1189,13 +1001,25 @@ CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATI PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE; // - // Temporary workaround for testing , if a path is specified, resolve it to a PID and use that for the request - // TODO: Should be removed + // Temporary workaround for testing: + // If a path, PID, or TID is specified, invoke CommandPtRunAndTrace directly + // to launch/attach and decode the trace in one step. + // TODO: Should be removed once the kernel-side enable/filter path is complete. // if (HasPath) { ShowMessages(" Running '%s' on core: %llx\n", Path.c_str(), PtRequest->CoreId); - CommandPtRunAndTrace(Path.c_str(), NULL, FALSE, PtRequest->CoreId); + CommandPtRunAndTrace(Path.c_str(), NULL, FALSE, PtRequest->CoreId, 0, 0); + } + else if (HasPid) + { + ShowMessages(" Tracing pid 0x%llx on core: %llx\n", Pid, PtRequest->CoreId); + CommandPtRunAndTrace(NULL, NULL, FALSE, PtRequest->CoreId, (UINT32)Pid, 0); + } + else if (HasTid) + { + ShowMessages(" Tracing tid 0x%llx on core: %llx\n", Tid, PtRequest->CoreId); + CommandPtRunAndTrace(NULL, NULL, FALSE, PtRequest->CoreId, 0, (UINT32)Tid); } } diff --git a/hyperdbg/libhyperdbg/code/debugger/misc/pt-helper.cpp b/hyperdbg/libhyperdbg/code/debugger/misc/pt-helper.cpp new file mode 100644 index 00000000..6f80b752 --- /dev/null +++ b/hyperdbg/libhyperdbg/code/debugger/misc/pt-helper.cpp @@ -0,0 +1,432 @@ +/** + * @file pt-help.cpp + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief PT helper functions + * @details + * @version 0.21 + * @date 2026-07-03 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +/** + * @brief Read the process image for PT decoding + * + * @param Buffer + * @param Size + * @param Asid + * @param Ip + * @param Context + * + * @return int + */ +int +PtHelperReadImage(UINT8 * Buffer, SIZE_T Size, const struct pt_asid * Asid, UINT64 Ip, VOID * Context) +{ + (VOID) Asid; + + IMAGE_SYMBOL_CONTEXT * Ctx = (IMAGE_SYMBOL_CONTEXT *)Context; + + if (Ctx == NULL || Ctx->Code == NULL || Ip < Ctx->CodeBase || Ip >= Ctx->CodeBase + Ctx->CodeSize) + return -pte_nomap; + + UINT64 Available = Ctx->CodeBase + Ctx->CodeSize - Ip; + SIZE_T Count = (Size < Available) ? Size : (SIZE_T)Available; + + memcpy(Buffer, Ctx->Code + (Ip - Ctx->CodeBase), Count); + return (int)Count; +} + +/** + * @brief Capture the .text section of a process image + * + * @param Process + * @param TextStart + * @param TextEnd + * @param Ctx + * + * @return BOOLEAN + */ +BOOLEAN +PtHelperCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd, IMAGE_SYMBOL_CONTEXT * Ctx) +{ + HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); + PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; + PROC_BASIC_INFO Pbi = {0}; + ULONG Ret = 0; + SIZE_T Got = 0; + UINT64 Base = 0; + IMAGE_DOS_HEADER Dos; + IMAGE_NT_HEADERS64 Nt; + UINT64 SectionBase; + + if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) + return FALSE; + + Ctx->ImageBase = Base; + SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; + + for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) + { + IMAGE_SECTION_HEADER Section; + + if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) + return FALSE; + + if (memcmp(Section.Name, ".text", 6) != 0) + continue; + + UINT64 Start = Base + Section.VirtualAddress; + UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; + + if (Size == 0) + return FALSE; + + Ctx->Code = (UINT8 *)malloc((SIZE_T)Size); + if (Ctx->Code == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Start, Ctx->Code, (SIZE_T)Size, &Got) || Got != Size) + { + free(Ctx->Code); + Ctx->Code = NULL; + return FALSE; + } + + Ctx->CodeBase = Start; + Ctx->CodeSize = Size; + *TextStart = Start; + *TextEnd = Start + Size - 1; + return TRUE; + } + + return FALSE; +} + +/** + * @brief Resolve function address using symbol information + * + * @param Process + * @param Path + * @param Name + * @param ImageBase + * @param Start + * @param End + * + * @return BOOLEAN + */ +BOOLEAN +PtHelperResolveFunction(HANDLE Process, const CHAR * Path, const CHAR * Name, UINT64 ImageBase, UINT64 * Start, UINT64 * End) +{ + union + { + SYMBOL_INFO Info; + BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + } Symbol = {0}; + BOOLEAN Ok = FALSE; + + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + if (!SymInitialize(Process, NULL, FALSE)) + return FALSE; + + if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)ImageBase, 0, NULL, 0) != 0) + { + Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); + Symbol.Info.MaxNameLen = MAX_SYM_NAME; + + if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) + { + *Start = Symbol.Info.Address; + *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; + Ok = TRUE; + } + } + + SymCleanup(Process); + return Ok; +} + +/** + * @brief Get PT packet name + * + * @param Type + * + * @return const CHAR * + */ +const CHAR * +PtHelperPacketName(enum pt_packet_type Type) +{ + switch (Type) + { + case ppt_psb: + return "PSB"; + case ppt_psbend: + return "PSBEND"; + case ppt_pad: + return "PAD"; + case ppt_fup: + return "FUP"; + case ppt_tip: + return "TIP"; + case ppt_tip_pge: + return "TIP.PGE"; + case ppt_tip_pgd: + return "TIP.PGD"; + case ppt_tnt_8: + return "TNT8"; + case ppt_tnt_64: + return "TNT64"; + case ppt_mode: + return "MODE"; + case ppt_pip: + return "PIP"; + case ppt_vmcs: + return "VMCS"; + case ppt_cbr: + return "CBR"; + case ppt_tsc: + return "TSC"; + case ppt_tma: + return "TMA"; + case ppt_mtc: + return "MTC"; + case ppt_cyc: + return "CYC"; + case ppt_ovf: + return "OVF"; + case ppt_stop: + return "STOP"; + case ppt_exstop: + return "EXSTOP"; + case ppt_mnt: + return "MNT"; + case ppt_ptw: + return "PTW"; + default: + return "?"; + } +} + +/** + * @brief Reconstruct IP from PT packet + * + * @param Packet + * @param LastIp + * + * @return UINT64 + */ +UINT64 +PtHelperReconstructIp(const struct pt_packet_ip * Packet, UINT64 * LastIp) +{ + UINT64 Value = *LastIp; + + switch (Packet->ipc) + { + case pt_ipc_update_16: + Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); + break; + case pt_ipc_update_32: + Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); + break; + case pt_ipc_update_48: + Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); + break; + case pt_ipc_sext_48: + Value = Packet->ip & 0xffffffffffffull; + if (Value & 0x800000000000ull) + Value |= 0xffff000000000000ull; + break; + default: + Value = Packet->ip; + break; + } + + *LastIp = Value; + return Value; +} + +/** + * @brief Decode PT packets + * + * @param Cpu + * @param Buffer + * @param Size + * @param ImageBase + * + * @return UINT64 + */ +UINT64 +PtHelperDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, UINT64 ImageBase) +{ + struct pt_config Config; + struct pt_packet_decoder * Decoder; + UINT64 Count = 0; + UINT64 LastIp = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (UINT8 *)Buffer; + Config.end = (UINT8 *)Buffer + Size; + + Decoder = pt_pkt_alloc_decoder(&Config); + if (Decoder == NULL) + { + ShowMessages("[-] core %u: cannot allocate packet decoder\n", Cpu); + return 0; + } + + for (;;) + { + Status = pt_pkt_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_packet Packet; + + Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); + if (Status < 0) + break; + + Count++; + + switch (Packet.type) + { + case ppt_tnt_8: + case ppt_tnt_64: + ShowMessages(" %-8s %2u ", PtHelperPacketName(Packet.type), Packet.payload.tnt.bit_size); + for (UINT8 Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) + putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); + putchar('\n'); + break; + + case ppt_tip: + case ppt_fup: + case ppt_tip_pge: + case ppt_tip_pgd: + if (Packet.payload.ip.ipc == pt_ipc_suppressed) + ShowMessages(" %-8s (ip suppressed)\n", PtHelperPacketName(Packet.type)); + else + { + UINT64 Ip = PtHelperReconstructIp(&Packet.payload.ip, &LastIp); + ShowMessages(" %-8s 0x%016llx exe+0x%llx\n", + PtHelperPacketName(Packet.type), + (UINT64)Ip, + (UINT64)(Ip - ImageBase)); + } + break; + + case ppt_pip: + ShowMessages(" %-8s cr3=0x%llx\n", PtHelperPacketName(Packet.type), (UINT64)Packet.payload.pip.cr3); + break; + + case ppt_cbr: + // ShowMessages(" %-8s ratio=%u\n", PtHelperPacketName(Packet.type), Packet.payload.cbr.ratio); + break; + + case ppt_tsc: + ShowMessages(" %-8s tsc=0x%llx\n", PtHelperPacketName(Packet.type), (UINT64)Packet.payload.tsc.tsc); + break; + + default: + // ShowMessages(" %-8s\n", PtHelperPacketName(Packet.type)); + break; + } + } + } + + pt_pkt_free_decoder(Decoder); + return Count; +} + +/** + * @brief Decode PT instructions + * + * @param Cpu + * @param Buffer + * @param Size + * @param Ctx + * + * @return UINT64 + */ +UINT64 +PtHelperDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, IMAGE_SYMBOL_CONTEXT * Ctx) +{ + struct pt_config Config; + struct pt_insn_decoder * Decoder; + struct pt_image * Image; + UINT64 Count = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (UINT8 *)Buffer; + Config.end = (UINT8 *)Buffer + Size; + + Decoder = pt_insn_alloc_decoder(&Config); + if (Decoder == NULL) + { + ShowMessages("[-] core %u: cannot allocate instruction decoder\n", Cpu); + return 0; + } + + Image = pt_insn_get_image(Decoder); + pt_image_set_callback(Image, PtHelperReadImage, Ctx); + + for (;;) + { + Status = pt_insn_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_insn Insn; + + while (Status & pts_event_pending) + { + struct pt_event Event; + Status = pt_insn_event(Decoder, &Event, sizeof(Event)); + if (Status < 0) + break; + } + + if (Status < 0 || (Status & pts_eos)) + break; + + Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); + if (Status < 0) + break; + + ZydisDisassembledInstruction Disasm; + ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; + + if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) + ShowMessages(" 0x%016llx exe+0x%-6llx %s\n", + (UINT64)Insn.ip, + (UINT64)(Insn.ip - Ctx->ImageBase), + Disasm.text); + else + ShowMessages(" 0x%016llx (undecodable)\n", (UINT64)Insn.ip); + + Count++; + } + + if (Status >= 0 && (Status & pts_eos)) + break; + } + + pt_insn_free_decoder(Decoder); + return Count; +} diff --git a/hyperdbg/libhyperdbg/header/debugger/misc/pci-id.h b/hyperdbg/libhyperdbg/header/debugger/misc/pci-id.h index 2ea4dabb..b2196274 100644 --- a/hyperdbg/libhyperdbg/header/debugger/misc/pci-id.h +++ b/hyperdbg/libhyperdbg/header/debugger/misc/pci-id.h @@ -46,6 +46,7 @@ typedef struct Vendor ////////////////////////////////////////////////// // Functions // ////////////////////////////////////////////////// + Vendor * GetVendorById(UINT16 VendorId); void diff --git a/hyperdbg/libhyperdbg/header/debugger/misc/pt-helper.h b/hyperdbg/libhyperdbg/header/debugger/misc/pt-helper.h new file mode 100644 index 00000000..b3f595f4 --- /dev/null +++ b/hyperdbg/libhyperdbg/header/debugger/misc/pt-helper.h @@ -0,0 +1,28 @@ +/** + * @file pt-helper.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers for PT helper functions + * @details + * @version 0.21 + * @date 2026-07-03 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +UINT64 +PtHelperDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, UINT64 ImageBase); + +UINT64 +PtHelperDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, IMAGE_SYMBOL_CONTEXT * Ctx); + +BOOLEAN +PtHelperCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd, IMAGE_SYMBOL_CONTEXT * Ctx); + +BOOLEAN +PtHelperResolveFunction(HANDLE Process, const CHAR * Path, const CHAR * Name, UINT64 ImageBase, UINT64 * Start, UINT64 * End); diff --git a/hyperdbg/libhyperdbg/header/debugger/script-engine/symbol.h b/hyperdbg/libhyperdbg/header/debugger/script-engine/symbol.h index d516018f..b80cdc45 100644 --- a/hyperdbg/libhyperdbg/header/debugger/script-engine/symbol.h +++ b/hyperdbg/libhyperdbg/header/debugger/script-engine/symbol.h @@ -38,6 +38,41 @@ typedef struct _IMAGE_SYMBOL_CONTEXT UINT8 * Code; } IMAGE_SYMBOL_CONTEXT; +/* + * @brief Process basic information structure + */ +typedef struct _PROC_BASIC_INFO +{ + LONG ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR Reserved[4]; +} PROC_BASIC_INFO; + +/** + * @brief Thread basic information + * + */ +typedef struct _THREAD_BASIC_INFO_EX +{ + LONG ExitStatus; + PVOID TebBaseAddress; + struct + { + HANDLE UniqueProcess; + HANDLE UniqueThread; + } ClientId; + ULONG_PTR AffinityMask; + LONG Priority; + LONG BasePriority; +} THREAD_BASIC_INFO_EX; + +////////////////////////////////////////////////// +// Function Defs // +////////////////////////////////////////////////// + +typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); +typedef LONG(NTAPI * PFN_NT_QIT)(HANDLE, ULONG, PVOID, ULONG, PULONG); + ////////////////////////////////////////////////// // Pdbex // ////////////////////////////////////////////////// diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index c943b6e8..cd1d0655 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -157,6 +157,7 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R + @@ -231,6 +232,7 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R + diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters index 236c3074..492c7f91 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters @@ -275,6 +275,9 @@ header\debugger\script-engine + + header\debugger\misc + @@ -730,6 +733,9 @@ code\components\pe + + code\debugger\misc + diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index 96640e8c..6a6eaee8 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -203,6 +203,7 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "header/debugger/commands/commands.h" #include "header/common/common.h" #include "header/debugger/script-engine/symbol.h" +#include "header/debugger/misc/pt-helper.h" #include "header/debugger/core/debugger.h" #include "header/debugger/script-engine/script-engine.h" #include "header/debugger/commands/help.h" From 9f113278ee03d64dfdcb54eebfadfa9aff1f5c86 Mon Sep 17 00:00:00 2001 From: sina Date: Fri, 3 Jul 2026 18:23:39 +0200 Subject: [PATCH 56/57] add comment for run and trace --- .../commands/extension-commands/pt.cpp | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp index 7b158b08..09a96caf 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp @@ -437,6 +437,7 @@ CommandPtRunAndTraceCore(PROCESS_INFORMATION * Process, UINT64 FilterStart = 0; UINT64 FilterEnd = 0; UINT64 Total = 0; + if (PinCore < 0) { PinCore = PT_DEFAULT_PINNING_CORE; @@ -444,16 +445,28 @@ CommandPtRunAndTraceCore(PROCESS_INFORMATION * Process, Mask = (DWORD_PTR)1 << PinCore; + // + // Set process affinity to the specified core so that all PT trace will land on that core + // if (SetProcessAffinityMask(Process->hProcess, Mask)) + { ShowMessages("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); + } else + { ShowMessages("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); + } + // + // Capture the .text section of the target process image for symbol resolution + // if (!PtHelperCaptureImage(Process->hProcess, &TextStart, &TextEnd, &Ctx)) { ShowMessages("[-] cannot read target image / .text section\n"); + if (LaunchedNew) TerminateProcess(Process->hProcess, 1); + goto Cleanup; } @@ -466,6 +479,9 @@ CommandPtRunAndTraceCore(PROCESS_INFORMATION * Process, FilterStart = TextStart; FilterEnd = TextEnd; + // + // Narrow the IP filter to the specified function if possible + // if (Function != NULL && Path != NULL && PtHelperResolveFunction(Process->hProcess, Path, Function, Ctx.ImageBase, &FilterStart, &FilterEnd)) { @@ -481,18 +497,28 @@ CommandPtRunAndTraceCore(PROCESS_INFORMATION * Process, Function ? Function : "(none)"); } + // + // Enable PT with the specified filter and wait for the target process to exit + // if (!CommandPtSendFilterByPid(Process->dwProcessId, TRUE, FALSE, FilterStart, FilterEnd, NULL, NULL, NULL, NULL, NULL, NULL) || !CommandPtSendEnable()) { ShowMessages("[-] cannot enable Intel PT\n"); + if (LaunchedNew) + { TerminateProcess(Process->hProcess, 1); + } + goto Cleanup; } + // + // Request the PT buffers to be mapped into user space so that we can decode them after the target exits + // if (!HyperDbgPtMmapSendRequest(&Mmap)) { - ShowMessages("[-] pt_mmap failed\n"); + ShowMessages("[-] MMAP failed\n"); CommandPtSendDisable(); if (LaunchedNew) TerminateProcess(Process->hProcess, 1); @@ -511,13 +537,23 @@ CommandPtRunAndTraceCore(PROCESS_INFORMATION * Process, ShowMessages("[*] waiting for target process to exit...\n"); } + // + // Wait for the target process to exit before decoding the trace + // WaitForSingleObject(Process->hProcess, INFINITE); + ShowMessages("[+] target exited, decoding trace\n"); + // + // Pause PT so that the buffers are not being written to while we decode them + // CommandPtSendPause(); Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; + // + // Request the sizes of the PT buffers for each core so that we know how much to decode + // if (!CommandPtSendRequest(&Sizes)) { ShowMessages("[-] cannot query PT sizes\n"); @@ -525,6 +561,9 @@ CommandPtRunAndTraceCore(PROCESS_INFORMATION * Process, goto Cleanup; } + // + // Decode the trace for each core that has a non-zero buffer size + // for (UINT32 i = 0; i < Mmap.NumCpus; i++) { UINT32 Cpu = Mmap.Cpus[i].CpuId; @@ -544,9 +583,13 @@ CommandPtRunAndTraceCore(PROCESS_INFORMATION * Process, ShowMessages("\n[+] decoded %llu %s total\n", (UINT64)Total, Packets ? "packet(s)" : "instruction(s)"); + // + // Disable PT now that we are done decoding the trace + // CommandPtSendDisable(); Cleanup: + if (Ctx.Code != NULL) { free(Ctx.Code); From a90513c71d84bd31aad536c6040e0b567f6e7599 Mon Sep 17 00:00:00 2001 From: sina Date: Sun, 5 Jul 2026 15:38:03 +0200 Subject: [PATCH 57/57] update changelog for v0.21 release --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1a01bee..f4f7ddac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.21.0.0] - 2026-XX-XX +## [0.21.0.0] - 2026-07-05 New release of the HyperDbg Debugger. ### Added @@ -12,6 +12,7 @@ New release of the HyperDbg Debugger. - The 'unload' command checks whether any module is loaded or not ([link](https://docs.hyperdbg.org/commands/debugging-commands/unload)) - Exported SDK API for checking whether any module is loaded or not ([link](https://github.com/HyperDbg/HyperDbg/commit/19e47c804f50f5dbb698f314e66b7d685a87ac1f)) - Added user mode and kernel mode PT parameter parser ([link](https://github.com/HyperDbg/HyperDbg/pull/631)) +- Added thread (TID) and process (PID) helper functions for Intel PT ([link](https://github.com/HyperDbg/HyperDbg/pull/633)) ### Changed - Separated SDK libraries for user mode and kernel mode modules ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c))