From 72a8aa325f81dc8c7da7a97d33be5ee60063e58c Mon Sep 17 00:00:00 2001 From: sina Date: Tue, 30 Jun 2026 20:30:25 +0200 Subject: [PATCH] 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); }