Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trigger or simulate keyboard interrupt?

Tags:

c

windows

drivers

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:

  1. 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.

  2. 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!

like image 461
Nick Borodulin Avatar asked Sep 01 '11 05:09

Nick Borodulin


1 Answers

Four options which I think should work:

1) You could create a new IRP to call the kbdclass driver with, as opposed to passing the IRP that you received down. You would complete the original IRP whenever you wanted to insert data as well as whenever you had real keystrokes to pass on.

2) You could have two devices, the second being a keyboard device. You would then use the kbdclass filter for removing keystrokes and the keyboard device for adding them.

3) You could redesign your driver to be an upper filter for the keyboard devices, similar to the MSDN sample driver kbfiltr.

4) You could have two devices, the second being an upper filter for one or more of the keyboard devices. You would use the kbdclass filter for removing keystrokes and the keyboard device filter for adding them.

I think the first option would be best, but I'm no expert.

like image 95
Harry Johnston Avatar answered Sep 20 '22 09:09

Harry Johnston