I'm writing a keyboard filter driver for Windows and I need to insert my custom keystrokes data into Windows message queue. I've managed to capture all the keys that are pressed setting OnReadCompletion() callback to IoSetCompletionRoutine() in my driver's Read() function like so:
NTSTATUS Read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, OnReadCompletion, DeviceObject, TRUE, TRUE, TRUE);
return IoCallDriver (deviceExtension->pKeyboardDevice, Irp);
}
NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
// ...
}
This filter driver is attached to the kbdclass driver like so:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
// ...
CCHAR ntNameBuffer[64] = "\\Device\\KeyboardClass0";
status = IoAttachDevice(deviceObject, &uKeyboardDeviceName, &DeviceExtension->pKeyboardDevice);
// ...
}
So, I can catch all keys pressed in OnReadCompletion(). But I need to insert my own information into keyboard message flow. Here are 2 problems with that:
OnReadCompletion() is only invoked when a key pressed. Ideally I'd like somehow it to be called when nothing is pressed. Can I do that somehow? I need to trigger a keyboard interrupt? I tried to write commands to a keyboard ports (0x60 and 0x64) with WRITE_PORT_UCHAR() but that didn't work out.
I tried to insert my data into IRP in OnReadCompletion() to make it look like for example a key was pressed twice while actually it was pressed only once. Can someone help me out on that one too, because the following didn't work out?
NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PIO_STACK_LOCATION IrpStackLocation = NULL; INT BufferLength; INT numKeys = 0, i = 0; PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; IrpStackLocation = IoGetCurrentIrpStackLocation(Irp); BufferLength = IrpStackLocation->Parameters.Read.Length; if(Irp->IoStatus.Status == STATUS_SUCCESS) { PCHAR newSystemBuffer, oldSystemBuffer; PKEYBOARD_INPUT_DATA keys = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer; numKeys = Irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA); for(i = 0; i < numKeys; i++) { // here we print whatever was pressed DbgPrint("%s -- ScanCode: %x\n", __FUNCTION__, keys[i].MakeCode); } // allocate new buffer twice as big as original newSystemBuffer = ExAllocatePool(NonPagedPool, Irp->IoStatus.Information * 2); // copy existing buffer twice into new buffer RtlCopyMemory(newSystemBuffer, keys, Irp->IoStatus.Information); RtlCopyMemory(newSystemBuffer + Irp->IoStatus.Information, keys, Irp->IoStatus.Information); // assign new buffer to Irp->AssociatedIrp.SystemBuffer oldSystemBuffer = Irp->AssociatedIrp.SystemBuffer; Irp->AssociatedIrp.SystemBuffer = newSystemBuffer; // tell IRP that we now have twice as much data Irp->IoStatus.Information *= 2; // free the old buffer ExFreePool(oldSystemBuffer); } if(Irp->PendingReturned) IoMarkIrpPending(Irp); return Irp->IoStatus.Status; }
And when I test it for example in Notepad, all I get is just one letter per keystroke. I'm really desperate. Please help!