You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
456 lines
12 KiB
456 lines
12 KiB
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ntddk.h>
|
|
#include <wmistr.h>
|
|
#include <evntrace.h>
|
|
#include <wmikm.h>
|
|
|
|
#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;
|
|
}
|
|
|
|
|