Class filter driver, IRP_MJ_READ not called

I’m trying to make a keyboard class filter and it works fine if I do not make it as a class filter and I only attach to one device (my IRP_MJ_READ callback is called).
However, after installing it as a class filter driver, my IRP_MJ_READ callback is never called, even tho the device is registered and attached successfully.

My DriverEntry where I register the functions callbacks:
`NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);

// Set driver unload callback
DriverObject->DriverUnload = KeyboardFilterDriverUnload;

// Set general pass through
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
	DriverObject->MajorFunction[i] = KeyboardFilterIrpPassthrough;
}

// Set read callback
DriverObject->MajorFunction[IRP_MJ_READ] = KeyboardFilterIrpRead;

// Set AddDevice callback
DriverObject->DriverExtension->AddDevice = KeyboardFilterAddDevice;

// Set PnP callback
DriverObject->MajorFunction[IRP_MJ_PNP] = KeyboardFilterIrpPnP;

KdPrint(("[KB] Loaded driver"));

return STATUS_SUCCESS;

}`

My AddDevice handler:
`
NTSTATUS KeyboardFilterAddDevice(IN struct _DRIVER_OBJECT* DriverObject, IN struct _DEVICE_OBJECT* PhysicalDeviceObject) {
PDEVICE_OBJECT DeviceObject;
NTSTATUS status;
PDEVICE_EXTENSION deviceExtension;

status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL, FILE_DEVICE_KEYBOARD, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);

if (!NT_SUCCESS(status)) {
	KdPrint(("[KB] Failed to create device"));
	return status;
}

// Initialize extension
RtlZeroMemory(DeviceObject->DeviceExtension, sizeof(DEVICE_EXTENSION));
deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;

// Set buffer io flag
DeviceObject->Flags |= DO_BUFFERED_IO;

// Get device name
WCHAR wcsDeviceID[256] = { 0 };
ULONG dataLen;
NTSTATUS nameStatus = IoGetDeviceProperty(PhysicalDeviceObject, DevicePropertyHardwareID, sizeof(wcsDeviceID), &wcsDeviceID, &dataLen);

UNICODE_STRING nameUnicode = { 0 };
RtlInitEmptyUnicodeString(&nameUnicode, wcsDeviceID, 256);

if (nameStatus == STATUS_SUCCESS) {
	RtlAppendUnicodeToString(&nameUnicode, wcsDeviceID);
}
else {
	RtlAppendUnicodeToString(&nameUnicode, L"UNKNOWN");
}

deviceExtension->DeviceID = &nameUnicode;

KdPrint(("[KB] KeyboardID: %wZ \n", deviceExtension->DeviceID));

// Attach to keyboard driver chain
deviceExtension->LowerDeviceObject = IoAttachDeviceToDeviceStack(
	DeviceObject,
	PhysicalDeviceObject);
deviceExtension->DeviceObject = DeviceObject;

// Set done flag
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

KdPrint(("[KB] Successfully attached to keyboard chain"));

return STATUS_SUCCESS;

}
And here is my Pnp Remove Device handler (for the problem I listed below):
VOID KeyboardFilterDeleteDeviceFromExtension(PDEVICE_EXTENSION DeviceExtension) {
KdPrint((“[KB] Deleting device: %wZ \n”, DeviceExtension->DeviceID));

RtlFreeUnicodeString(DeviceExtension->DeviceID);

IoDetachDevice(DeviceExtension->LowerDeviceObject);
IoDeleteDevice(DeviceExtension->DeviceObject);

KdPrint(("[KB] Device deleted"));

return;

}

NTSTATUS KeyboardFilterIrpPnP(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
IO_STACK_LOCATION* stack = IoGetCurrentIrpStackLocation(Irp);

switch (stack->MinorFunction) {
case IRP_MN_START_DEVICE:
case IRP_MN_SURPRISE_REMOVAL:
	Irp->IoStatus.Status = STATUS_SUCCESS;
	break;
case IRP_MN_REMOVE_DEVICE:
	Irp->IoStatus.Status = STATUS_SUCCESS;
	IoSkipCurrentIrpStackLocation(Irp);
	NTSTATUS ret = IoCallDriver(deviceExtension->LowerDeviceObject, Irp);
	KeyboardFilterDeleteDeviceFromExtension(deviceExtension);
	return ret;
}

IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(deviceExtension->LowerDeviceObject, Irp);

}
`

Another problem I’m facing off is when there are multiple devices connected and I disconnect one device (IrpPNP callback is called with IRP_MN_REMOVE_DEVICE minor), however for an unknown reason, I can’t detach/delete my objects, the device extension returned from the DeviceObject that comes as parameter doesn’t contains what PDEVICE_EXTENSION contains).
When I mount a device, I get the following WinDbg:
[KB] KeyboardID: HID\VID_04F2&PID_1516&REV_0111&MI_00 [KB] Successfully attached to keyboard chain
However, when I dismount a device, I get the following WinDbg then the system crashes:
`[KB] Deleting device: KDTARGET: Refreshing KD connection

*** Fatal System Error: 0x0000007e
(0xFFFFFFFFC0000005,0xFFFFF8060A6407E7,0xFFFF880EBA3DB178,0xFFFF880EBA3DA9B0)

Break instruction exception - code 80000003 (first chance)`

It doesn’t know what the value of DeviceID is (so I’m guessing the pointer to the DeviceExtension is not actually a PDEVICE_EXTENSION?)

I have one word for you: WDF.

Peter

@“Peter_Viscarola_(OSR)” said:
I have one word for you: WDF.

Peter

Hello, Peter. First I want to thank you because you took the time to answer to my newbie questions.
I went with WDM because I could find more samples and more discussion on the internet about every function/problem than WDF. However, I want to give WDF a try. I cloned the “kbdiltr” sample from microsoft to give it a try before I modify it to my needs but I can’t get it to compile. I’m getting the following errors even tho I added all the additional dependencies in Visual Studio:
1>rawpdo.obj : error LNK2019: unresolved external symbol RtlUnicodeStringValidateDestWorker referenced in function RtlUnicodeStringPrintf 1>rawpdo.obj : error LNK2019: unresolved external symbol RtlWideCharArrayVPrintfWorker referenced in function RtlUnicodeStringPrintf 1>rawpdo.obj : error LNK2001: unresolved external symbol SDDL_DEVOBJ_SYS_ALL_ADM_ALL

Hnmmmmm… You’re putting the additional include libraries in the wrong place. The tip-off is that they’re not directories… they are libraries.

You want to include them under Linker… Input… Additional Dependencies…

Peter

1 Like

@“Peter_Viscarola_(OSR)” said:
Hnmmmmm… You’re putting the additional include libraries in the wrong place. The tip-off is that they’re not directories… they are libraries.

You want to include them under Linker… Input… Additional Dependencies…

Peter

Thanks again!
So I’ve cleaned up the driver from functions I don’t need for my purpose and now I have a *clean and compact source code.
I do have some more questions related to unplug and device deletion procedure.
This is my DEVICE_EXTENSION structure:

typedef struct _DEVICE_EXTENSION
{
	WDFDEVICE Wdfdevice;

	//
	// The real connect data that this driver reports to
	//
	CONNECT_DATA UpperConnectData;

	//
	// Cached Keyboard Attributes
	//
	KEYBOARD_ATTRIBUTES KeyboardAttributes;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

I want to properly delete and unplug the device. KeyboardAttributes is allocated with WdfMemoryCopyToBuffer. Do I need to free up this memory or is it automatically freed up when the parent object (DEVICE_EXTENSION structure) gets deleted? On the Docs I only found about memory allocated using ExAllocatePoolWithTag. From what I assume I do not need to free it up myself as it is not a pointer (it’s KEYBOARD_ATTRIBUTES and not PKEYBOARD_ATTRIBUTES or *KEYBOARD_ATTRIBUTES) and it should be automatically deleted if I delete the parent object.
Now, about the parent object (PDEVICE_EXTENSION), do I need to delete it or will it automatically be deleted when I call WdfObjectDelete on the extension->Wdfdevice? Except setting UpperConnectData properties to NULL, do I need to dettach something before deleting the Wdfdevice like I needed to do with WDM, or I can just safely delete it? Sorry if those questions or some of them sounds stupid, I just got into learning kernel programming. Thanks again for taking from your time to answer to my questions!

Edit: Also, to get the device name (HID), with WDM I was using IoGetDeviceProperty(PhysicalDeviceObject, DevicePropertyHardwareID, sizeof(wcsDeviceID), &wcsDeviceID, &dataLen), I see that WDF has a function called WdfDeviceRetrieveDeviceName, is it equivalent or is something totally different and should I stick to get HID using IoGetDeviceProperty?

Hmmmm… You MAY misunderstand what we mean in WDF by “Parent Object”? We mean the Framework Object the handle to which is placed in the ParentObject field of the WDF_OBJECT_ATTRIBUTES structure when the new Object is created. Some Objects have default parents, others do not.

I do not see how WdfMemoryCopyToBuffer could allocate an object… so I’m a bit confused.

Likewise, your “device extension”… if it is a CONTEXT on the WDFDEVICE Object (as it should be), it’ll be declared in the WDF_OBJECT_ATTRIBUTES structure when your WDFDEVICE is created… and yes, it’s lifetime will be managed by the Framework and will be the same as the Object instance that it is the context for. So, in the example of a Device Context, when the WDFDEVICE instance goes away, its associated Context instance goes away.

… IoGetDeviceProperty…

You probably want WdfDeviceAllocAndQueryProperty. There’s nothing wrong with intermixing WDF and WDM, but if you can it’s always best to stay within the same model.

Peter

1 Like

KeyboardAttributes is allocated with WdfMemoryCopyToBuffer.

No, it’s not. It is part of the device extension. You might have used WdmMemoryCopyToBuffer to copy data in there, but the memory itself is part of the device extension, and will automatically be freed when the device instance is unloaded.