Files
Andrea Allievi 0ab296f21c HyperV support v0.1
Implemented support for HyperV Root Partitions (finally)
2018-04-15 15:01:22 -07:00

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;
}