From 7dfe5be9850ab6b33d37590b282919b4f62f4398 Mon Sep 17 00:00:00 2001 From: sina Date: Sun, 21 Jun 2026 15:02:56 +0200 Subject: [PATCH 01/16] 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 02/16] 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 03/16] 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 04/16] 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 05/16] 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 06/16] 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 07/16] 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 08/16] 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 09/16] 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 10/16] 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 11/16] 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 12/16] 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 13/16] 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 14/16] 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 15/16] 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 16/16] 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))