#include #include #include #include #include #include #define TRACEKMP_NT_DEVICE_NAME L"\\Device\\TraceKmp" #define TRACEKMP_MOF_FILE L"MofResourceName" PDEVICE_OBJECT pTracekmpDeviceObject; UNICODE_STRING KmpRegistryPath; typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT DeviceObject; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; // // ETW Globals // // 1. A control guid to identify this driver to ETW. The enable/disable state // of this Guid controls enable/disable state of tracing for this driver. GUID ControlGuid = \ {0xce5b1120, 0x8ea9, 0x11d1, 0xa4, 0xec, 0x00, 0xa0, 0xc9, 0x06, 0x29, 0x10}; // 2. EventGuids to fire events with. Can have more than one EventGuid. GUID TracekmpGuid = \ {0xbc8700cb, 0x120b, 0x4aad, 0xbf, 0xbf, 0x99, 0x6e, 0x57, 0x60, 0xcb, 0x85}; // 3. EtwLoggerHandle to use with IoWMIWriteEvent. TRACEHANDLE EtwLoggerHandle = 0; // 4. EtwTraceEnable to indicate whether or not tracing is currently on. ULONG EtwTraceEnable = 0; // 5. EtwTraceLevel to indicate the current Level of logging ULONG EtwTraceLevel = 0; // Note: EtwLoggerHandle, EtwTraceEnable and EtwTraceLevel are set through // ENABLE_EVENTS irp. // NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); NTSTATUS EtwDispatch( IN PDEVICE_OBJECT pDO, IN PIRP Irp ); NTSTATUS EtwRegisterGuids( IN PWMIREGINFO WmiRegInfo, IN ULONG wmiRegInfoSize, IN PULONG pReturnSize ); VOID TracekmpDriverUnload( IN PDRIVER_OBJECT DriverObject ); #ifdef ALLOC_PRAGMA #pragma alloc_text( INIT, DriverEntry ) #pragma alloc_text( PAGE, EtwDispatch ) #pragma alloc_text( PAGE, EtwRegisterGuids ) #pragma alloc_text( PAGE, TracekmpDriverUnload ) #endif // ALLOC_PRAGMA 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. 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; UNICODE_STRING deviceName; KmpRegistryPath.Length = 0; KmpRegistryPath.MaximumLength = RegistryPath->Length; KmpRegistryPath.Buffer = ExAllocatePool(PagedPool, RegistryPath->Length+2); RtlCopyUnicodeString(&KmpRegistryPath, RegistryPath); DriverObject->DriverUnload = TracekmpDriverUnload; // // STEP 1. Wire a function to start fielding WMI IRPS // DriverObject->MajorFunction[ IRP_MJ_SYSTEM_CONTROL ] = EtwDispatch; RtlInitUnicodeString( &deviceName, TRACEKMP_NT_DEVICE_NAME ); status = IoCreateDevice( DriverObject, sizeof( DEVICE_EXTENSION ), &deviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pTracekmpDeviceObject); if( !NT_SUCCESS( status )) { return status; } pTracekmpDeviceObject->Flags |= DO_BUFFERED_IO; // // STEP 2. Register with ETW here // status = IoWMIRegistrationControl(pTracekmpDeviceObject, WMIREG_ACTION_REGISTER); if (!NT_SUCCESS(status)) { KdPrint(( "TRACEKMP: IoWMIRegistrationControl failed with %x\n", status )); } return STATUS_SUCCESS; } VOID TracekmpDriverUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Unregister from ETW logging and Unload this driver Arguments: DriverObject - Supplies a pointer to the driver object Return Value: --*/ { PDEVICE_OBJECT pDevObj; NTSTATUS status; ExFreePool(KmpRegistryPath.Buffer); pDevObj = DriverObject->DeviceObject; // // STEP 3: Unregister with ETW. // if (pDevObj != NULL) { status = IoWMIRegistrationControl(pDevObj, WMIREG_ACTION_DEREGISTER); if (!NT_SUCCESS(status)) { KdPrint(( "TracekmpDriverUnload: Failed to unregister for ETW support\n" )); } } IoDeleteDevice( pDevObj ); } // // STEP 4: Wire the ETW Dispatch function. // NTSTATUS EtwDispatch( IN PDEVICE_OBJECT pDO, IN PIRP Irp ) /*++ Routine Description: This is the dispatch routine for MJ_SYSTEM_CONTROL irps. Arguments: pDO - Pointer to the target device object. Irp - Pointer to IRP Return Value: NTSTATUS - Completion status. --*/ { 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; UNREFERENCED_PARAMETER(pDO); switch (irpSp->MinorFunction) { case IRP_MN_REGINFO: { status = EtwRegisterGuids( (PWMIREGINFO) Buffer, BufferSize, &ReturnSize); Irp->IoStatus.Information = ReturnSize; Irp->IoStatus.Status = status; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return status; } case IRP_MN_ENABLE_EVENTS: { if ( (BufferSize < sizeof(WNODE_HEADER)) || (Buffer == NULL) ) { status = STATUS_INVALID_PARAMETER; } else { // // The Buffer that came is a WNODE_HEADER. Now Validate the // Wnode before using it. // PWNODE_HEADER Wnode = (PWNODE_HEADER)Buffer; if ( (Wnode->BufferSize < sizeof(WNODE_HEADER)) || !IsEqualGUID(&Wnode->Guid, &ControlGuid) ) { status = STATUS_INVALID_PARAMETER; } // // and the LoggerHandle // is in its HistoricalContext field. // We can pick up the Enable Level and Flags by using // the WmiGetLoggerEnableLevel and WmiGetLoggerEnableFlags calls // EtwLoggerHandle = Wnode->HistoricalContext; EtwTraceLevel = (ULONG) WmiGetLoggerEnableLevel( EtwLoggerHandle ); // // After picking up the LoggerHandle and EnableLevel we can // set the flag EtwTraceEnable to true. // EtwTraceEnable = TRUE; // // Now this driver is enabled and ready to send traces to the // EventTrace session specified by the EtwLoggerHandle. // // The commented code fragment below shows a typical example of // sending an event to an Event Trace session. Insert this code // fragment (and remove the comments) wherever you want to // send traces to ETW from this driver. // // if (EtwTraceEnable) { // EVENT_TRACE_HEADER Header; // PEVENT_TRACE_HEADER Wnode; // NTSTATUS status; // 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 = EtwLoggerHandle; // status = IoWMIWriteEvent((PVOID)Wnode); // } // STEP 6: Add IoWMIWriteEvent calls from various locations // of the driver code to trace its operation. // } Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return status; } case IRP_MN_DISABLE_EVENTS: { EtwTraceEnable = FALSE; EtwTraceLevel = 0; EtwLoggerHandle = 0; Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return status; } default: { status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return status; } } return status; } // // STEP 5: RegisterGuids function // NTSTATUS EtwRegisterGuids( IN PWMIREGINFO EtwRegInfo, IN ULONG etwRegInfoSize, IN PULONG pReturnSize ) /*++ Routine Description: This function handles ETW GUID registration. Arguments: EtwRegInfo etwRegInfoSize, pReturnSize Return Value: NTSTATUS - Completion status. --*/ { // // Register a Control Guid as a Trace Guid. // ULONG SizeNeeded; PWMIREGGUIDW EtwRegGuidPtr; ULONG RegistryPathSize; ULONG MofResourceSize; PUCHAR ptmp; // // We either have a valid buffer to fill up or have at least // enough room to return the SizeNeeded. // if ( (pReturnSize == NULL) || (EtwRegInfo == NULL) || (etwRegInfoSize < sizeof(ULONG)) ) { return STATUS_INVALID_PARAMETER; } *pReturnSize = 0; // // Allocate WMIREGINFO for controlGuid // RegistryPathSize = KmpRegistryPath.Length + sizeof(USHORT); MofResourceSize = sizeof(TRACEKMP_MOF_FILE) - sizeof(WCHAR) + sizeof(USHORT); SizeNeeded = sizeof(WMIREGINFOW) + sizeof(WMIREGGUIDW) + RegistryPathSize + MofResourceSize; // // If there is not sufficient space, return the size required as // a ULONG and WMI will send another request with the right size buffer. // if (SizeNeeded > etwRegInfoSize) { *((PULONG)EtwRegInfo) = SizeNeeded; *pReturnSize = sizeof(ULONG); return STATUS_BUFFER_TOO_SMALL; } RtlZeroMemory(EtwRegInfo, SizeNeeded); EtwRegInfo->BufferSize = SizeNeeded; EtwRegInfo->GuidCount = 1; EtwRegInfo->RegistryPath = sizeof(WMIREGINFOW) + sizeof(WMIREGGUIDW); EtwRegInfo->MofResourceName = EtwRegInfo->RegistryPath + RegistryPathSize; EtwRegGuidPtr = &EtwRegInfo->WmiRegGuid[0]; EtwRegGuidPtr->Guid = ControlGuid; EtwRegGuidPtr->Flags |= WMIREG_FLAG_TRACED_GUID; EtwRegGuidPtr->Flags |= WMIREG_FLAG_TRACE_CONTROL_GUID; EtwRegGuidPtr->InstanceCount = 0; EtwRegGuidPtr->InstanceInfo = 0; ptmp = (PUCHAR)&EtwRegInfo->WmiRegGuid[1]; *((PUSHORT)ptmp) = KmpRegistryPath.Length; ptmp += sizeof(USHORT); RtlCopyMemory(ptmp, KmpRegistryPath.Buffer, KmpRegistryPath.Length); ptmp = (PUCHAR)EtwRegInfo + EtwRegInfo->MofResourceName; *((PUSHORT)ptmp) = sizeof(TRACEKMP_MOF_FILE) - sizeof(WCHAR); ptmp += sizeof(USHORT); RtlCopyMemory(ptmp, TRACEKMP_MOF_FILE, sizeof(TRACEKMP_MOF_FILE) - sizeof(WCHAR) ); *pReturnSize = SizeNeeded; return STATUS_SUCCESS; }