mirror of
https://github.com/intelpt/WindowsIntelPT
synced 2026-06-08 14:50:14 +00:00
0ab296f21c
Implemented support for HyperV Root Partitions (finally)
208 lines
7.4 KiB
C++
208 lines
7.4 KiB
C++
/**********************************************************************
|
|
* Windows Intel Processor Trace (PT) Driver
|
|
* Filename: PtTests.cpp
|
|
* Implements some tests and use cases, especially for kernel tracing
|
|
* Last revision: 01/06/2016
|
|
*
|
|
* Copyright© 2016 Andrea Allievi, Richard Johnson
|
|
* Microsoft Ltd & TALOS Research and Intelligence Group
|
|
* All right reserved
|
|
**********************************************************************/
|
|
#include "stdafx.h"
|
|
#include "IntelPt.h"
|
|
#include "UndocNt.h"
|
|
#include "KernelTracing.h"
|
|
#include "Ntstrsafe.h"
|
|
|
|
#ifdef _DEBUG
|
|
// Find a Kernel module in memory using documented method
|
|
NTSTATUS GetKernelModule(LPTSTR lpName, SYSTEM_MODULE_INFORMATION * pSysModuleDesc);
|
|
|
|
struct {
|
|
PPT_BUFFER_DESCRIPTOR pBuffDesc;
|
|
KEVENT workItemEvt;
|
|
NTSTATUS workItmStatus;
|
|
BOOLEAN bKernelExcRaised;
|
|
} g_testData = { 0 };
|
|
|
|
int DriverExcFilter(DWORD excCode, struct _EXCEPTION_POINTERS *ep, LPTSTR lpDrvFileName) {
|
|
// Process here the exception and continue the execution if possible
|
|
NTSTATUS ntStatus = 0;
|
|
PT_TRACE_DESC ptDesc = { 0 };
|
|
SYSTEM_MODULE_INFORMATION sysModInfo = { 0 };
|
|
UNREFERENCED_PARAMETER(excCode);
|
|
|
|
// Search the actual loaded module
|
|
ntStatus = GetKernelModule(lpDrvFileName, &sysModInfo);
|
|
if (!NT_SUCCESS(ntStatus))
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
|
|
// Modify the guilty opcode in a NOP
|
|
ULONG_PTR lpAddr = (ULONG_PTR)ep->ContextRecord->Rip;
|
|
DWORD dwOffset = (DWORD)(lpAddr % PAGE_SIZE);
|
|
PMDL pNewMdl = IoAllocateMdl((LPVOID)(lpAddr - dwOffset), PAGE_SIZE, NULL, FALSE, NULL);
|
|
MmProbeAndLockPages(pNewMdl, KernelMode, IoWriteAccess);
|
|
LPBYTE lpNewAddr = (LPBYTE)MmMapLockedPagesSpecifyCache(pNewMdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);
|
|
|
|
if (lpNewAddr[dwOffset+1] == 0x89)
|
|
lpNewAddr[dwOffset] = 0x48; // Put a MOV QWORD PTR opcode
|
|
else
|
|
lpNewAddr[dwOffset] = 0x90; // Put a NOP opcode
|
|
MmUnmapLockedPages(lpNewAddr, pNewMdl);
|
|
IoFreeMdl(pNewMdl);
|
|
|
|
// Enable here processor trace
|
|
ptDesc.bTraceKernel = TRUE;
|
|
ptDesc.dwNumOfRanges = 1;
|
|
ptDesc.Ranges[0].lpStartVa = sysModInfo.Base;
|
|
ptDesc.Ranges[0].lpEndVa = (LPVOID)((QWORD)sysModInfo.Base + sysModInfo.Size);
|
|
ntStatus = IntelPtStartTracing(ptDesc, g_testData.pBuffDesc);
|
|
g_testData.bKernelExcRaised = TRUE;
|
|
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
|
|
// The Driver Trace Test work item (runs in System process context)
|
|
VOID KernelTraceWorkItem(PVOID Parameter) {
|
|
// Get the parameters
|
|
LPTSTR lpDrvFileName = *((LPTSTR*)Parameter);
|
|
LPTSTR lpDumpFile = *((LPTSTR*)Parameter + 1);
|
|
DWORD dwBuffSize = *((DWORD*)Parameter + 2);
|
|
KIRQL kIrql = KeGetCurrentIrql();
|
|
PEPROCESS pCurProc = IoGetCurrentProcess();
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
ASSERT(kIrql < DISPATCH_LEVEL && pCurProc == PsInitialSystemProcess);
|
|
|
|
DWORD dwCurCpuId = KeGetCurrentProcessorNumber();
|
|
KAFFINITY kAffinity = (KAFFINITY)(1i64 << dwCurCpuId);
|
|
KeSetSystemAffinityThread(kAffinity);
|
|
ntStatus = DoDriverTraceTest(lpDrvFileName, lpDumpFile, dwBuffSize);
|
|
g_testData.workItmStatus = ntStatus;
|
|
KeSetEvent(&g_testData.workItemEvt, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
|
|
NTSTATUS DoDriverTraceTest(LPTSTR lpDrvFileName, LPTSTR lpDumpFile, DWORD dwBuffSize) {
|
|
NTSTATUS ntStatus = 0; // Returned NTSTATUS
|
|
KIRQL kIrql = KeGetCurrentIrql(); // Current IRQL
|
|
TCHAR lpDrvRegPath[0x200] = { 0 }; // The complete driver's service registry path
|
|
LPTSTR lpDotPtr = NULL;
|
|
DWORD dwStrLen = 0; // String size in characters
|
|
UNICODE_STRING drvRegString = { 0 };
|
|
ASSERT(kIrql == PASSIVE_LEVEL);
|
|
|
|
if (PsGetCurrentProcess() != PsInitialSystemProcess) {
|
|
WORK_QUEUE_ITEM workItem = { 0 };
|
|
KeInitializeEvent(&g_testData.workItemEvt, NotificationEvent, FALSE);
|
|
ExInitializeWorkItem(&workItem, KernelTraceWorkItem, (LPVOID)&lpDrvFileName);
|
|
ExQueueWorkItem(&workItem, DelayedWorkQueue);
|
|
KeWaitForSingleObject((LPVOID)&g_testData.workItemEvt, Executive, KernelMode, FALSE, NULL);
|
|
return g_testData.workItmStatus;
|
|
|
|
}
|
|
|
|
// Check the parameters:
|
|
if (!dwBuffSize) dwBuffSize = 512 * 1024;
|
|
if (!lpDumpFile || lpDumpFile[0] == 0) lpDumpFile = L"\\??\\c:\\pt_dump.bin";
|
|
|
|
// Compose the full registry path
|
|
RtlStringCchCopyW(lpDrvRegPath, COUNTOF(lpDrvRegPath), L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
|
|
lpDotPtr = wcsrchr(lpDrvFileName, L'.');
|
|
if (lpDotPtr) dwStrLen = (DWORD)(lpDotPtr - lpDrvFileName);
|
|
else dwStrLen = (DWORD)wcslen(lpDrvFileName);
|
|
RtlStringCchCatNW(lpDrvRegPath, COUNTOF(lpDrvRegPath), lpDrvFileName, dwStrLen);
|
|
RtlInitUnicodeString(&drvRegString, lpDrvRegPath);
|
|
|
|
// Allocate a buffer big enough for processor trace
|
|
ntStatus = IntelPtAllocBuffer(&g_testData.pBuffDesc, dwBuffSize, TRUE, TRUE);
|
|
if (!NT_SUCCESS(ntStatus)) return ntStatus;
|
|
|
|
__try {
|
|
ntStatus = ZwLoadDriver(&drvRegString);
|
|
}
|
|
__except (DriverExcFilter(GetExceptionCode(), GetExceptionInformation(), lpDrvFileName)) {
|
|
// PASS
|
|
ntStatus = STATUS_UNHANDLED_EXCEPTION;
|
|
}
|
|
|
|
// Check the exception:
|
|
if (!g_testData.bKernelExcRaised) {
|
|
// The exception handler has not run exception :-)
|
|
ZwUnloadDriver(&drvRegString);
|
|
ntStatus = STATUS_INVALID_EXCEPTION_HANDLER;
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
FreePtBuffer(g_testData.pBuffDesc);
|
|
g_testData.pBuffDesc = NULL;
|
|
return ntStatus;
|
|
}
|
|
ntStatus = ZwUnloadDriver(&drvRegString);
|
|
|
|
// Stop the PT Trace
|
|
IntelPtStopTrace();
|
|
// Dump the buffer
|
|
g_testData.pBuffDesc->lpKernelVa = MmGetSystemAddressForMdlSafe(g_testData.pBuffDesc->pTraceMdl, NormalPagePriority);
|
|
|
|
// Create a target file
|
|
HANDLE hOutFile = NULL;
|
|
OBJECT_ATTRIBUTES outFileOa = { 0 };
|
|
UNICODE_STRING outFileName = { 0 };
|
|
IO_STATUS_BLOCK ioSb = { 0 };
|
|
LARGE_INTEGER fileOffset = { 0 };
|
|
|
|
RtlInitUnicodeString(&outFileName, lpDumpFile);
|
|
InitializeObjectAttributes(&outFileOa, &outFileName, OBJ_KERNEL_HANDLE, NULL, NULL);
|
|
ntStatus = ZwCreateFile(&hOutFile, FILE_ALL_ACCESS, &outFileOa, &ioSb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_SUPERSEDE, FILE_NON_DIRECTORY_FILE, NULL, 0);
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
ntStatus = ZwWriteFile(hOutFile, NULL, NULL, NULL, &ioSb, g_testData.pBuffDesc->lpKernelVa, (DWORD)g_testData.pBuffDesc->qwBuffSize, &fileOffset, NULL);
|
|
ZwClose(hOutFile);
|
|
}
|
|
|
|
FreePtBuffer(g_testData.pBuffDesc);
|
|
g_testData.pBuffDesc = NULL;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
// Find a Kernel module in memory using documented method
|
|
NTSTATUS GetKernelModule(LPTSTR lpName, SYSTEM_MODULE_INFORMATION * pSysModuleDesc) {
|
|
NTSTATUS ntStatus = 0;
|
|
SYSTEM_ALL_MODULES * pAllModules = NULL;
|
|
ULONG dwBuffSize = 0,
|
|
dwRetLength = 0;
|
|
KIRQL kIrql = KeGetCurrentIrql(); // Current IRQL
|
|
ASSERT(kIrql == PASSIVE_LEVEL);
|
|
|
|
ntStatus = ZwQuerySystemInformation(SystemModuleInformation, (PVOID)pAllModules, NULL, &dwBuffSize);
|
|
if (ntStatus != STATUS_INFO_LENGTH_MISMATCH) return ntStatus;
|
|
|
|
pAllModules = (SYSTEM_ALL_MODULES*)ExAllocatePoolWithTag(PagedPool, dwBuffSize, MEMTAG);
|
|
RtlZeroMemory(pAllModules, dwBuffSize);
|
|
ntStatus = ZwQuerySystemInformation(SystemModuleInformation, (PVOID)pAllModules, dwBuffSize, &dwRetLength);
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
ExFreePool(pAllModules);
|
|
return ntStatus;
|
|
}
|
|
|
|
ntStatus = STATUS_NOT_FOUND;
|
|
for (unsigned i = 0; i < pAllModules->dwNumOfModules; i++) {
|
|
TCHAR lpCurModName[0x100] = { 0 };
|
|
SYSTEM_MODULE_INFORMATION * pCurModule = &pAllModules->modules[i];
|
|
|
|
RtlStringCchPrintfW(lpCurModName, COUNTOF(lpCurModName), L"%S", pCurModule->ImageName + pCurModule->ModuleNameOffset);
|
|
if (_wcsicmp(lpCurModName, lpName) == 0) {
|
|
// Found
|
|
if (pSysModuleDesc != NULL)
|
|
(*pSysModuleDesc) = (*pCurModule);
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ExFreePool(pAllModules);
|
|
return ntStatus;
|
|
}
|