如何触发或模拟键盘中断?

Nic*_*lin 5 c windows drivers

我正在为Windows编写键盘过滤器驱动程序,我需要将自定义击键数据插入Windows消息队列.我已经设法捕获所有按下的键,在我的驱动程序的Read()函数中将OnReadCompletion()回调设置为IoSetCompletionRoutine(),如下所示:

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)
{
// ...
}
Run Code Online (Sandbox Code Playgroud)

此过滤器驱动程序附加到kbdclass驱动程序,如下所示:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
// ...
CCHAR ntNameBuffer[64] = "\\Device\\KeyboardClass0";
status = IoAttachDevice(deviceObject, &uKeyboardDeviceName, &DeviceExtension->pKeyboardDevice);
// ...
Run Code Online (Sandbox Code Playgroud)

}

所以,我可以捕获OnReadCompletion()中按下的所有键.但我需要将自己的信息插入键盘消息流中.这有两个问题:

  1. OnReadCompletion()仅在按下某个键时调用.理想情况下,我想以某种方式在没有按下时调用它.我能以某种方式这样做吗?我需要触发键盘中断吗?我尝试使用WRITE_PORT_UCHAR()将命令写入键盘端口(0x60和0x64)但是没有用完.

  2. 我试图在OnReadCompletion()中将我的数据插入到IRP中,使其看起来像一个按键被按下两次,而实际上只按了一次.也有人可以帮我解决这个问题,因为下面没有成功吗?

    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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

当我在记事本中测试它时,我得到的只是每次击键一个字母.我真的很绝望.请帮忙!

Har*_*ton 1

我认为应该有效的四个选项:

1) 您可以创建一个新的 IRP 来调用 kbdclass 驱动程序,而不是传递您收到的 IRP。每当您想要插入数据以及每当您有真正的击键要传递时,您都可以完成原始 IRP。

2)您可以有两个设备,第二个是键盘设备。然后,您可以使用 kbdclass 过滤器来删除击键,并使用键盘设备来添加击键。

3) 您可以将驱动程序重新设计为键盘设备的上层过滤器,类似于 MSDN 示例驱动程序 kbfiltr。

4) 您可以有两个设备,第二个是一个或多个键盘设备的上部过滤器。您可以使用 kbdclass 过滤器来删除击键,并使用键盘设备过滤器来添加它们。

我认为第一个选择是最好的,但我不是专家。

  • 不,您的驱动程序与 Kbdfiltr 不同 - 您的驱动程序是 kbdclass 驱动程序之上的过滤器,而 kbdfiltr 是键盘驱动程序本身之上的过滤器。有很大的不同。键盘驱动程序上的过滤器可以捕获允许键盘驱动程序将击键发送到 kbdclass 的回调函数,因此您可以随时发送击键。 (2认同)