概述
irp请求会从io管理器中传递到设备栈中依次向下发送,当到达底层真实设备处理完成后,会依次返回,这时如果在设备栈中有我们自己注册的设备,就可以起到一个过滤的功能。键盘过滤驱动就是如此,通过附加到原本存在的设备KeyboardClass0中,这时当发生按键时irp也会返回到我们的设备中,进而实现起响应。
注意
在整个键盘响应过程中,有一个名为csrss.exe的程序,其会不断的向KeyboardClass0发送读取类型的irp请求,如果我们直接通过断开设备附加再卸载我们的驱动时,就会蓝屏。原因是csrss.exe最终发送的读irp请求已经通过我们最先注册的过滤驱动向下传递了,这时我们卸载了我们自己的驱动,底层返回的irp找不到回调,这时就会蓝屏,所以代码中应该对irp的数量做一个统计,保证其正确处理。
源码
c
#include<ntifs.h>
typedef struct
{
PDEVICE_OBJECT LowerKbdDevice;
}DEVICE_EXTENTION,*PDEVICE_EXTENTION;
PDEVICE_OBJECT myKbdDevice = NULL;
typedef struct _KEYBOARD_INPUT_DATA {
USHORT UnitId;
USHORT MakeCode;
USHORT Flags;
USHORT Reserved;
ULONG ExtraInformation;
} KEYBOARD_INPUT_DATA, * PKEYBOARD_INPUT_DATA;
//IRP等待处理的数量
ULONG pendingkey = 0;
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
LARGE_INTEGER interval = { 0 };
PDEVICE_OBJECT DeviceObject = DriverObject->DeviceObject;
IoDetachDevice(((PDEVICE_EXTENTION)DeviceObject->DeviceExtension)->LowerKbdDevice);
interval.QuadPart = -10 * 1000 * 1000;
//判断是否还有未处理的IRP
while (pendingkey)
{
KeDelayExecutionThread(KernelMode, FALSE, &interval);
}
IoDeleteDevice(myKbdDevice);
KdPrint(("驱动卸载结束!\n"));
}
NTSTATUS ReadComplete(PDEVICE_OBJECT DeviceObject, PIRP irp, PVOID Context)
{
PKEYBOARD_INPUT_DATA KeyBuffer = irp->AssociatedIrp.SystemBuffer;
char* key[4] = { "KeyDown","KeyUp","E0","E1" };
int structnum = irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);
if (irp->IoStatus.Status == STATUS_SUCCESS)
{
for (int i = 0; i < structnum; i++)
{
KdPrint(("MakeCode = %x,Flags = %s\n", KeyBuffer->MakeCode, key[KeyBuffer->Flags]));
}
}
//处理完成需要将标志位设置,以声明以完成此irp的返回处理
if (irp->PendingReturned)
{
IoMarkIrpPending(irp);
}
pendingkey--;
return irp->IoStatus.Status;
}
NTSTATUS DispatchPass(PDEVICE_OBJECT pDeviceObject,PIRP irp)
{
IoCopyCurrentIrpStackLocationToNext(irp);
return IoCallDriver(((PDEVICE_EXTENTION)pDeviceObject->DeviceExtension)->LowerKbdDevice, irp);
}
NTSTATUS DispatchRead(PDEVICE_OBJECT pDeviceObject, PIRP irp)
{
IoCopyCurrentIrpStackLocationToNext(irp);
IoSetCompletionRoutine(irp,ReadComplete,NULL,TRUE,TRUE,TRUE,TRUE);
pendingkey++;
return IoCallDriver(((PDEVICE_EXTENTION)pDeviceObject->DeviceExtension)->LowerKbdDevice, irp);
}
NTSTATUS MyAttachDevice(PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING kbdName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardClass0");
NTSTATUS status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENTION), NULL, 0, FILE_DEVICE_KEYBOARD, NULL, &myKbdDevice);
if (!NT_SUCCESS(status))
{
return status;
}
myKbdDevice->Flags |= DO_BUFFERED_IO;
myKbdDevice->Flags &= ~DO_DEVICE_INITIALIZING;
RtlZeroMemory(myKbdDevice->DeviceExtension,sizeof(DEVICE_EXTENTION));
status = IoAttachDevice(myKbdDevice, &kbdName, &((PDEVICE_EXTENTION)myKbdDevice->DeviceExtension)->LowerKbdDevice);
if (!NT_SUCCESS(status))
{
IoDeleteDevice(myKbdDevice);
return status;
}
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
int i = 0;
NTSTATUS Status = STATUS_SUCCESS;
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
pDriverObject->MajorFunction[i] = DispatchPass;
}
pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
Status = MyAttachDevice(pDriverObject);
if (!NT_SUCCESS(Status))
{
KdPrint(("AttachDevice ERROR!\n"));
}
else
{
KdPrint(("AttachDevice SUCCESS!\n"));
}
pDriverObject->DriverUnload = DriverUnload;
return Status;
}