|
|
/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
tracekmp.c
Abstract:
Sample kernel mode trace provider/driver.
Author:
Jee Fung Pang (jeepang) 03-Dec-1997
Revision History:
--*/
#include <stdio.h>
#include <stdlib.h>
#include <ntddk.h>
#include <wmistr.h>
#include <evntrace.h>
#include "kmpioctl.h"
#include <wmikm.h>
#define TRACEKMP_NT_DEVICE_NAME L"\\Device\\TraceKmp"
#define TRACEKMP_WIN32_DEVICE_NAME L"\\DosDevices\\TRACEKMP"
#define TRACEKMP_DRIVER_NAME L"TRACEKMP"
typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT DeviceObject;
ULONG IrpSequenceNumber; UCHAR IrpRetryCount;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
//
// Proc Ctrs Device Extension Object
//
//extern PDEVICE_OBJECT pTracekmpDeviceObject;
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); VOID TraceEventLogger( IN PTRACEHANDLE pLoggerHandle );
NTSTATUS TracekmpDispatchOpen( IN PDEVICE_OBJECT pDO, IN PIRP Irp );
NTSTATUS TracekmpDispatchClose( IN PDEVICE_OBJECT pDO, IN PIRP Irp );
NTSTATUS TracekmpDispatchDeviceControl( IN PDEVICE_OBJECT pDO, IN PIRP Irp );
NTSTATUS PcWmiRegisterGuids( IN PWMIREGINFO WmiRegInfo, IN ULONG wmiRegInfoSize, IN PULONG pReturnSize );
#ifdef USE_CALLBACK
NTSTATUS TracekmpControlCallback( IN ULONG ActionCode, IN PVOID DataPath, IN ULONG BufferSize, IN OUT PVOID Buffer, IN PVOID Context, OUT PULONG Size ); #else
NTSTATUS PcWmiDispatch( IN PDEVICE_OBJECT pDO, IN PIRP Irp ); #endif
VOID TracekmpDriverUnload( IN PDRIVER_OBJECT DriverObject );
#ifdef ALLOC_PRAGMA
#pragma alloc_text( page, TracekmpDispatchOpen )
#pragma alloc_text( page, TracekmpDispatchClose )
#pragma alloc_text( page, TracekmpDispatchDeviceControl )
#ifdef USE_CALLBACK
#pragma alloc_text( page, TracekmpControlCallback )
#else
#pragma alloc_text( page, PcWmiDispatch )
#endif
#pragma alloc_text( init, DriverEntry )
#pragma alloc_text( page, TracekmpDriverUnload )
#endif // ALLOC_PRAGMA
#define PROC_REG_PATH L"System\\CurrentControlSet\\Services\\TRACEKMP"
#define MAXEVENTS 100
GUID TracekmpGuid = {0xd58c126f, 0xb309, 0x11d1, 0x96, 0x9e, 0x00, 0x00, 0xf8, 0x75, 0xa5, 0xbc};
ULONG TracekmpEnable = 0; TRACEHANDLE LoggerHandle;
PDEVICE_OBJECT pTracekmpDeviceObject;
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++
Routine Description:
This is the callback function when we call IoCreateDriver to create a WMI Driver Object. In this function, we need to remember the DriverObject, create a device object and then create a Win32 visible symbolic link name so that the WMI user mode component can access us.
Arguments: DriverObject - pointer to the driver object RegistryPath - pointer to a unicode string representing the path to driver-specific key in the registry
Return Value:
STATUS_SUCCESS if successful STATUS_UNSUCCESSFUL otherwise
--*/ { NTSTATUS status = STATUS_SUCCESS; ULONG i; UNICODE_STRING deviceName; UNICODE_STRING linkName;
//
// Create Dispatch Entry Points.
//
DriverObject->DriverUnload = TracekmpDriverUnload; DriverObject->MajorFunction[ IRP_MJ_CREATE ] = TracekmpDispatchOpen; DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = TracekmpDispatchClose; DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = TracekmpDispatchDeviceControl;
//
// Wire a function to start fielding WMI IRPS
//
#ifndef USE_CALLBACK
DriverObject->MajorFunction[ IRP_MJ_SYSTEM_CONTROL ] = PcWmiDispatch; #endif
RtlInitUnicodeString( &deviceName, TRACEKMP_NT_DEVICE_NAME );
//
// Create the Device object
//
status = IoCreateDevice( DriverObject, sizeof( DEVICE_EXTENSION ), &deviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pTracekmpDeviceObject);
if( !NT_SUCCESS( status )) { return status; }
RtlInitUnicodeString( &linkName, TRACEKMP_WIN32_DEVICE_NAME ); status = IoCreateSymbolicLink( &linkName, &deviceName );
if( !NT_SUCCESS( status )) { IoDeleteDevice( pTracekmpDeviceObject ); return status; }
//
// Choose a buffering mechanism
//
pTracekmpDeviceObject->Flags |= DO_BUFFERED_IO;
//
// Register with WMI here
//
#ifdef USE_CALLBACK
status = IoWMIRegistrationControl( (PDEVICE_OBJECT) TracekmpControlCallback, WMIREG_ACTION_REGISTER | WMIREG_FLAG_CALLBACK); #else
status = IoWMIRegistrationControl(pTracekmpDeviceObject, WMIREG_ACTION_REGISTER); #endif
if (!NT_SUCCESS(status)) { DbgPrint("TRACEKMP: Failed to register for WMI support\n"); }
return STATUS_SUCCESS; }
NTSTATUS TracekmpDispatchOpen( IN PDEVICE_OBJECT pDO, IN PIRP Irp ) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_SUCCESS; }
//++
// Function:
// TracekmpDispatchClose
//
// Description:
// This function dispatches CloseHandle
// requests from Win32
//
// Arguments:
// Pointer to Device object
// Pointer to IRP for this request
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS TracekmpDispatchClose( IN PDEVICE_OBJECT pDO, IN PIRP Irp ) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_SUCCESS; }
//++
// Function:
// TracekmpDispatchDeviceControl
//
// Description:
// This function dispatches DeviceIoControl
// requests from Win32
//
// Arguments:
// Pointer to Device object
// Pointer to IRP for this request
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS TracekmpDispatchDeviceControl( IN PDEVICE_OBJECT pDO, IN PIRP Irp ) { NTSTATUS status; EVENT_TRACE_HEADER Header, *Wnode; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp ); PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; ULONG InBufferLen = irpStack->Parameters.DeviceIoControl.InputBufferLength; ULONG OutBufferLen= irpStack->Parameters.DeviceIoControl.OutputBufferLength; // WMI_LOGGER_INFORMATION LoggerInfo;
ULONG ControlCode = irpStack->Parameters. DeviceIoControl.IoControlCode;
// RtlZeroMemory(&LoggerInfo, sizeof(LoggerInfo));
// LoggerInfo.Wnode.BufferSize = sizeof(LoggerInfo);
// LoggerInfo.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
// RtlInitUnicodeString(&LoggerInfo.LoggerName, L"TraceKmp");
// RtlInitUnicodeString(&LoggerInfo.LogFileName, L"C:\\tracekmp.etl");
Irp->IoStatus.Information = OutBufferLen;
switch( ControlCode ) { case IOCTL_TRACEKMP_TRACE_EVENT:
//
// Every time you get this IOCTL, we also log a trace event
// to illustrate that the event can be caused by user-mode
//
if (TracekmpEnable) { Wnode = &Header; RtlZeroMemory(Wnode, sizeof(EVENT_TRACE_HEADER)); Wnode->Size = sizeof(EVENT_TRACE_HEADER); Wnode->Flags |= WNODE_FLAG_TRACED_GUID; Wnode->Guid = TracekmpGuid; ((PWNODE_HEADER)Wnode)->HistoricalContext = LoggerHandle; status = IoWMIWriteEvent((PVOID)Wnode); } else { status = STATUS_SUCCESS; } Irp->IoStatus.Information = 0; break;
case IOCTL_TRACEKMP_START: status = WmiStartTrace(Buffer); DbgPrint("Start status = %X\n", status); break;
case IOCTL_TRACEKMP_STOP: status = WmiStopTrace(Buffer); DbgPrint("Stop status = %X\n", status); break;
case IOCTL_TRACEKMP_QUERY: status = WmiQueryTrace(Buffer); DbgPrint("Query status = %X\n", status); break;
case IOCTL_TRACEKMP_UPDATE: status = WmiUpdateTrace(Buffer); DbgPrint("Update status = %X\n", status); break;
case IOCTL_TRACEKMP_FLUSH: status = WmiFlushTrace(Buffer); DbgPrint("Flush status = %X\n", status); break; //
// Not one we recognize. Error.
//
default: status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; break; }
//
// Get rid of this request
//
Irp->IoStatus.Status = status; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return status; }
NTSTATUS PcWmiRegisterGuids( IN PWMIREGINFO WmiRegInfo, IN ULONG wmiRegInfoSize, IN PULONG pReturnSize ) { //
// Register a Control Guid as a Trace Guid.
//
ULONG SizeNeeded; PWMIREGGUIDW WmiRegGuidPtr; ULONG Status; ULONG GuidCount; LPGUID ControlGuid; ULONG RegistryPathSize; PUCHAR ptmp;
*pReturnSize = 0; GuidCount = 1; ControlGuid = &TracekmpGuid;
//
// Allocate WMIREGINFO for controlGuid + GuidCount.
//
RegistryPathSize = sizeof(PROC_REG_PATH) - sizeof(WCHAR) + sizeof(USHORT); SizeNeeded = sizeof(WMIREGINFOW) + GuidCount * sizeof(WMIREGGUIDW) + RegistryPathSize;
if (SizeNeeded > wmiRegInfoSize) { *((PULONG)WmiRegInfo) = SizeNeeded; *pReturnSize = sizeof(ULONG); return STATUS_SUCCESS; }
RtlZeroMemory(WmiRegInfo, SizeNeeded); WmiRegInfo->BufferSize = SizeNeeded; WmiRegInfo->GuidCount = GuidCount; WmiRegInfo->NextWmiRegInfo = WmiRegInfo->RegistryPath = WmiRegInfo->MofResourceName = 0;
WmiRegGuidPtr = &WmiRegInfo->WmiRegGuid[0]; WmiRegGuidPtr->Guid = *ControlGuid; WmiRegGuidPtr->Flags |= WMIREG_FLAG_TRACED_GUID; WmiRegGuidPtr->Flags |= WMIREG_FLAG_TRACE_CONTROL_GUID; WmiRegGuidPtr->InstanceCount = 0; WmiRegGuidPtr->InstanceInfo = 0;
ptmp = (PUCHAR)&WmiRegInfo->WmiRegGuid[1]; WmiRegInfo->RegistryPath = PtrToUlong((PVOID) (ptmp - (PUCHAR)WmiRegInfo)); *((PUSHORT)ptmp) = sizeof(PROC_REG_PATH) - sizeof(WCHAR);
ptmp += sizeof(USHORT); RtlCopyMemory(ptmp, PROC_REG_PATH, sizeof(PROC_REG_PATH) - sizeof(WCHAR));
*pReturnSize = SizeNeeded; return(STATUS_SUCCESS);
}
#ifndef USE_CALLBACK
NTSTATUS PcWmiDispatch( IN PDEVICE_OBJECT pDO, IN PIRP Irp ) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); ULONG BufferSize = irpSp->Parameters.WMI.BufferSize; PVOID Buffer = irpSp->Parameters.WMI.Buffer; ULONG ReturnSize = 0; NTSTATUS Status = STATUS_SUCCESS; PWNODE_HEADER Wnode=NULL; HANDLE ThreadHandle;
UNREFERENCED_PARAMETER(pDO);
switch (irpSp->MinorFunction) {
case IRP_MN_REGINFO: #if DBG
DbgPrint("IRP_MN_REG_INFO\n"); #endif
Status = PcWmiRegisterGuids( Buffer, BufferSize, &ReturnSize); break;
case IRP_MN_ENABLE_EVENTS:
InterlockedExchange(&TracekmpEnable, 1);
Wnode = (PWNODE_HEADER)Buffer; if (BufferSize >= sizeof(WNODE_HEADER)) { LoggerHandle = Wnode->HistoricalContext; #if DBG
DbgPrint("LoggerHandle %I64u\n", Wnode->HistoricalContext);
DbgPrint("BufferSize %d\n", Wnode->BufferSize); DbgPrint("Flags %x\n", Wnode->Flags); DbgPrint("Version %x\n", Wnode->Version); #endif
}
//
// After getting enabled, create a thread to log a few events
// to test this out.
Status = PsCreateSystemThread( &ThreadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, TraceEventLogger, &LoggerHandle );
if (NT_SUCCESS(Status)) { // if SystemThread is started
ZwClose (ThreadHandle); }
#if DBG
DbgPrint("IRP_MN_ENABLE_EVENTS\n"); #endif
break;
case IRP_MN_DISABLE_EVENTS: InterlockedExchange(&TracekmpEnable, 0); #if DBG
DbgPrint(" IRP_MN_DISABLE_EVENTS\n"); #endif
break;
case IRP_MN_ENABLE_COLLECTION:
#if DBG
DbgPrint("IRP_MN_ENABLE_COLLECTION\n"); #endif
break;
case IRP_MN_DISABLE_COLLECTION: #if DBG
DbgPrint("IRP_MN_DISABLE_COLLECTION\n"); #endif
break; default: Status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; #if DBG
DbgPrint("DEFAULT\n"); #endif
return Status; }
//
// Before the packet goes back to the
// I/O Manager, log a message.
//
Irp->IoStatus.Status = Status; Irp->IoStatus.Information = ReturnSize; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return Status; } #endif
VOID TraceEventLogger( IN PTRACEHANDLE pLoggerHandle ) { NTSTATUS status; EVENT_TRACE_HEADER Header, *Wnode; PULONG TraceMarker; ULONG i = 0; TRACEHANDLE LoggerHandle = *pLoggerHandle;
while (TracekmpEnable) { //
// NOTE: For optimization, the set up of the HEADER should be done
// one outside of this while loop. It is left here so that the caller
// at least needs to be conscious of what is being sent
//
Wnode = &Header; RtlZeroMemory(Wnode, sizeof(EVENT_TRACE_HEADER)); Wnode->Size = sizeof(EVENT_TRACE_HEADER);
Wnode->Flags = WNODE_FLAG_TRACED_GUID; Wnode->Guid = TracekmpGuid; ((PWNODE_HEADER)Wnode)->HistoricalContext = LoggerHandle;
//
// Call TraceLogger to write this event
//
status = IoWMIWriteEvent((PVOID)Wnode);
#ifdef DBG
if ( !(i%100) ) { DbgPrint("Another Hundred Events Written \n"); DbgPrint("Status %x LoggerHandle %I64X\n", status, LoggerHandle); } #endif
if (i++ > MAXEVENTS) break;
}
PsTerminateSystemThread(status); return;
}
VOID TracekmpDriverUnload( IN PDRIVER_OBJECT DriverObject ) { PDEVICE_OBJECT pDevObj; UNICODE_STRING linkName;
#if DBG
DbgPrint("Unloading the Driver\n"); #endif
//
// Get pointer to Device object
//
pDevObj = DriverObject->DeviceObject;
//
IoWMIRegistrationControl(pDevObj, WMIREG_ACTION_DEREGISTER);
//
// Form the Win32 symbolic link name.
//
RtlInitUnicodeString( &linkName, TRACEKMP_WIN32_DEVICE_NAME ); //
// Remove symbolic link from Object
// namespace...
//
IoDeleteSymbolicLink( &linkName );
//
// Unload the callbacks from the kernel to this driver
//
IoDeleteDevice( pDevObj );
}
#ifdef USE_CALLBACK
NTSTATUS TracekmpControlCallback( IN WMIACTIONCODE ActionCode, IN PVOID DataPath, IN ULONG BufferSize, IN OUT PVOID Buffer, IN PVOID Context, OUT PULONG Size ) //
// This callback routine is called to process enable/disable action codes
//
{ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); ULONG BufferSize = irpSp->Parameters.WMI.BufferSize; PVOID Buffer = irpSp->Parameters.WMI.Buffer; ULONG ReturnSize = 0; NTSTATUS Status = STATUS_SUCCESS; PWNODE_HEADER Wnode=NULL; HANDLE ThreadHandle;
UNREFERENCED_PARAMETER(pDO);
switch (ActionCode) {
case IRP_MN_REG_INFO : #if DBG
DbgPrint("IRP_MN_REG_INFO\n"); #endif
Status = PcWmiRegisterGuids( Buffer, BufferSize, &ReturnSize); break;
case IRP_MN_ENABLE_EVENTS :
InterlockedExchange(&TracekmpEnable, 1);
Wnode = (PWNODE_HEADER)Buffer; if (BufferSize >= sizeof(WNODE_HEADER)) { LoggerHandle = Wnode->HistoricalContext; #if DBG
DbgPrint("LoggerHandle %I64u\n", Wnode->HistoricalContext);
DbgPrint("BufferSize %d\n", Wnode->BufferSize); DbgPrint("Flags %x\n", Wnode->Flags); DbgPrint("Version %x\n", Wnode->Version); #endif
}
//
// After getting enabled, create a thread to log a few events
// to test this out.
Status = PsCreateSystemThread( &ThreadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, TraceEventLogger, &LoggerHandle );
if (NT_SUCCESS(Status)) { // if SystemThread is started
ZwClose (ThreadHandle); }
#if DBG
DbgPrint("IRP_MN_ENABLE_EVENTS\n"); #endif
break;
case IRP_MN_DISABLE_EVENTS : InterlockedExchange(&TracekmpEnable, 0); #if DBG
DbgPrint(" IRP_MN_DISABLE_EVENTS\n"); #endif
break;
case IRP_MN_ENABLE_COLLECTION :
#if DBG
DbgPrint("IRP_MN_ENABLE_COLLECTION\n"); #endif
break;
case IRP_MN_DISABLE_COLLECTION : #if DBG
DbgPrint("IRP_MN_DISABLE_COLLECTION\n"); #endif
break; default: Status = STATUS_INVALID_DEVICE_REQUEST; ReturnSize = 0; #if DBG
DbgPrint("DEFAULT\n"); #endif
return Status; }
//
// Before the packet goes back to the
// I/O Manager, log a message.
//
if (Size) *Size = ReturnSize; return Status; } #endif // USE_CALLBACK
|