内核驱动中实现键盘过滤 在WINDOWS中所有接入的键盘设备,都存储在 kbdclass.sys
这个驱动中。 我们可以通过监控 键盘设备接入
的动作,来遍历 kbdclass.sys
驱动中所有的设备。 然后创建 过滤设备
并附加到 键盘设备
上,就可以拦截到该设备 键盘按键
的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <ntifs.h> #include <ntddkbd.h> #define KBD_DRIVER_NAME L"\\Driver\\kbdclass" extern POBJECT_TYPE* IoDriverObjectType;typedef struct _ATTACH_ENTRY { LIST_ENTRY Entry; PDEVICE_OBJECT FilterDevice; PDEVICE_OBJECT PhysicalDevice; } ATTACH_ENTRY, *PATTACH_ENTRY; typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT AttachedToDevice; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; NTKERNELAPI NTSTATUS ObReferenceObjectByName ( IN PUNICODE_STRING ObjectName, IN ULONG Attributes, IN PACCESS_STATE AccessState, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN OUT PVOID ParseContext, OUT PVOID* Object) ;
另外我们需要处理 已经附加的设备
的列表,已附加的设备要跳过,设备移除也要删除对应节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 PATTACH_ENTRY FindPhysicalEntry (PDEVICE_OBJECT DeviceObject) { KIRQL OldIrql = 0 ; PLIST_ENTRY Entry = NULL ; PATTACH_ENTRY AttachEntry = NULL ; if (DeviceObject == NULL ) return NULL ; KeAcquireSpinLock(&AttachLock, &OldIrql); for (Entry = AttachList.Flink; Entry != &AttachList; Entry = Entry->Flink) { AttachEntry = CONTAINING_RECORD(Entry, ATTACH_ENTRY, Entry); if (AttachEntry->PhysicalDevice == DeviceObject) { KeReleaseSpinLock(&AttachLock, OldIrql); return AttachEntry; } } KeReleaseSpinLock(&AttachLock, OldIrql); return NULL ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 PATTACH_ENTRY RemoveFilterEntry (PDEVICE_OBJECT DeviceObject) { KIRQL OldIrql = 0 ; PLIST_ENTRY Entry = NULL ; PATTACH_ENTRY AttachEntry = NULL ; if (DeviceObject == NULL ) return NULL ; KeAcquireSpinLock(&AttachLock, &OldIrql); for (Entry = AttachList.Flink; Entry != &AttachList; Entry = Entry->Flink) { AttachEntry = CONTAINING_RECORD(Entry, ATTACH_ENTRY, Entry); if (AttachEntry->FilterDevice == DeviceObject) { RemoveEntryList(&AttachEntry->Entry); KeReleaseSpinLock(&AttachLock, OldIrql); return AttachEntry; } } KeReleaseSpinLock(&AttachLock, OldIrql); return NULL ; }
过滤驱动的处理 我们需要处理的驱动 派遣函数
为 IRP_MJ_READ
IRP_MJ_POWER
IRP_MJ_PNP
共三项
1 2 3 DriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead; DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower; DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
在 IRP_MJ_READ
中,我们处理获取到的 键盘按键
信息,注意是在 完成函数
中处理
1 2 3 4 5 6 7 8 NTSTATUS DispatchRead (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, ReadCompletion, NULL , TRUE, TRUE, TRUE); return IoCallDriver(DevExt->AttachedToDevice, Irp); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 NTSTATUS ReadCompletion (PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context) { ULONG_PTR Index = 0 ; ULONG_PTR NumKeys = 0 ; PIO_STACK_LOCATION IrpSp = NULL ; PKEYBOARD_INPUT_DATA KeyData = NULL ; UNREFERENCED_PARAMETER(DeviceObject); UNREFERENCED_PARAMETER(Context); IrpSp = IoGetCurrentIrpStackLocation(Irp); if (Irp->IoStatus.Status == STATUS_SUCCESS) { KeyData = Irp->AssociatedIrp.SystemBuffer; if (KeyData != NULL ) { NumKeys = Irp->IoStatus.Information / sizeof (KEYBOARD_INPUT_DATA); for (Index = 0 ; Index < NumKeys; Index++) { if (bKeyboardLock) { if ((KeyData[Index].MakeCode == 1 ) || (KeyData[Index].MakeCode == 56 ) || (KeyData[Index].MakeCode == 91 )) { if (KeyData[Index].Flags == 0 ) KeyData[Index].Flags = 1 ; if (KeyData[Index].Flags == 2 ) KeyData[Index].Flags = 3 ; } } } } } if (Irp->PendingReturned) IoMarkIrpPending(Irp); return Irp->IoStatus.Status; }
在 IRP_MJ_POWER
中处理电源管理,因为我们是过滤驱动,只需要把IRP直接往下转发就行
1 2 3 4 5 6 7 8 9 10 11 12 13 NTSTATUS DispatchPower (PDEVICE_OBJECT DeviceObject, PIRP Irp) { PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension); #if (NTDDI_VERSION < NTDDI_VISTA) PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(DevExt->AttachedToDevice, Irp); #else IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(DevExt->AttachedToDevice, Irp); #endif }
在 IRP_MJ_PNP
中,我们要处理 键盘设备移除
时,解绑和删除 过滤设备
的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 NTSTATUS DispatchPnp (PDEVICE_OBJECT DeviceObject, PIRP Irp) { PIO_STACK_LOCATION IrpSp = NULL ; PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension); IrpSp = IoGetCurrentIrpStackLocation(Irp); switch (IrpSp->MinorFunction) { case IRP_MN_REMOVE_DEVICE: AttachEntry = RemoveFilterEntry(DeviceObject); if (AttachEntry != NULL ) ExFreePool(AttachEntry); IoDetachDevice(DevExt->AttachedToDevice); IoDeleteDevice(DeviceObject); break ; } IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(DevExt->AttachedToDevice, Irp); }
键盘设备接入的监控 如何知道有键盘设备接入了呢,可以通过 IoRegisterPlugPlayNotification
函数,来注册一个PNP设备通知
1 2 3 4 5 6 7 8 9 NTSTATUS IoRegisterPlugPlayNotification ( _In_ IO_NOTIFICATION_EVENT_CATEGORY EventCategory, _In_ ULONG EventCategoryFlags, _In_opt_ PVOID EventCategoryData, _In_ PDRIVER_OBJECT DriverObject, _In_ PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine, _In_opt_ PVOID Context, _Out_ PVOID *NotificationEntry ) ;
通过指定参数 EventCategory
的类型,可以监控PNP设备的某些特定行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 EventCategoryDeviceInterfaceChange EventCategoryHardwareProfileChange EventCategoryTargetDeviceChange
我们这里关注的是 EventCategoryDeviceInterfaceChange
类型,可以监控到 设备的接入
动作, 相关的GUID信息都在 wdmguid.h
中定义,这里因为只用其中几个信息,就单独拿出来重新定义
1 2 3 4 5 6 GUID GUID_INTERFACE_ARRIVAL = { 0xcb3a4004 , 0x46f0 , 0x11d0 , 0xb0 , 0x8f , 0x00 , 0x60 , 0x97 , 0x13 , 0x05 , 0x3f }; GUID GUID_INTERFACE_KEYBOARD = { 0x884b96c3 , 0x56ef , 0x11d1 , 0xbc , 0x8c , 0x00 , 0xa0 , 0xc9 , 0x14 , 0x05 , 0xdd };
我们要在 DriverEntry
中保存 DriverObject
到全局变量中,因为在 附加设备
时要用
1 2 3 4 5 6 7 8 9 10 11 CurrentDriver = DriverObject; Status = IoRegisterPlugPlayNotification( EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, &GUID_INTERFACE_KEYBOARD, DriverObject, PnpNotifyCallback, NULL , &NotificationEntry);
PNP通知的回调函数代码,主要是检测是不是有 键盘设备接入
的动作
1 2 3 4 5 6 7 8 9 10 11 12 13 NTSTATUS PnpNotifyCallback (IN PVOID Structure, IN PVOID Context) { PGUID pEvent = NULL ; UNREFERENCED_PARAMETER(Context); if (Structure == NULL ) return STATUS_SUCCESS; pEvent = &(((PDEVICE_INTERFACE_CHANGE_NOTIFICATION)Structure)->Event); if (RtlEqualMemory(pEvent, &GUID_INTERFACE_ARRIVAL, sizeof (GUID))) { EnumKeyboardDevice(); } return STATUS_SUCCESS; }
如下为遍历 kbdclass.sys
驱动中所有键盘设备,并进行附加的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 NTSTATUS EnumKeyboardDevice () { PATTACH_ENTRY AttachEntry = NULL ; NTSTATUS Status = STATUS_SUCCESS; PDRIVER_OBJECT KBDDriverObject = NULL ; PDEVICE_OBJECT KBDDeviceObject = NULL ; UNICODE_STRING KBDDriverName = RTL_CONSTANT_STRING(KBD_DRIVER_NAME); Status = ObReferenceObjectByName(&KBDDriverName, OBJ_CASE_INSENSITIVE, NULL , 0 , *IoDriverObjectType, KernelMode, NULL , &KBDDriverObject); if (!NT_SUCCESS(Status)) { return Status; } KBDDeviceObject = KBDDriverObject->DeviceObject; while (KBDDeviceObject != NULL ) { AttachEntry = FindPhysicalEntry(KBDDeviceObject); if (AttachEntry == NULL ) Status = AttachKeyboardDevice(KBDDeviceObject); KBDDeviceObject = KBDDeviceObject->NextDevice; } ObDereferenceObject(KBDDriverObject); return STATUS_SUCCESS; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 NTSTATUS AttachKeyboardDevice (IN PDEVICE_OBJECT PhysicalDevice) { KIRQL OldIrql = 0 ; PATTACH_ENTRY AttachEntry = NULL ; NTSTATUS Status = STATUS_SUCCESS; PDEVICE_OBJECT FilterDevice = NULL ; PDEVICE_EXTENSION DevExt = NULL ; if (PhysicalDevice == NULL ) return STATUS_UNSUCCESSFUL; Status = IoCreateDevice(CurrentDriver, sizeof (DEVICE_EXTENSION), NULL , PhysicalDevice->DeviceType, PhysicalDevice->Characteristics, FALSE, &FilterDevice); if (!NT_SUCCESS(Status)) { return Status; } AttachEntry = (PATTACH_ENTRY)EXALLOCATE(sizeof (ATTACH_ENTRY)); if (AttachEntry == NULL ) { IoDeleteDevice(FilterDevice); return STATUS_UNSUCCESSFUL; } RtlZeroMemory(AttachEntry, sizeof (ATTACH_ENTRY)); AttachEntry->FilterDevice = FilterDevice; AttachEntry->PhysicalDevice = PhysicalDevice; FilterDevice->Flags |= PhysicalDevice->Flags; DevExt = (PDEVICE_EXTENSION)FilterDevice->DeviceExtension; DevExt->AttachedToDevice = IoAttachDeviceToDeviceStack( FilterDevice, PhysicalDevice); if (DeviceExtension->AttachedToDevice == NULL ) { ExFreePool(AttachEntry); IoDeleteDevice(FilterDevice); return STATUS_UNSUCCESSFUL; } FilterDevice->Flags &= ~DO_DEVICE_INITIALIZING; KeAcquireSpinLock(&AttachLock, &OldIrql); InsertTailList(&AttachList, &AttachEntry->Entry); KeReleaseSpinLock(&AttachLock, OldIrql); return STATUS_SUCCESS; }
这里需要注意的是,如果是在 DriverObject->DriverExtension->AddDevice
对应的函数中,进行 过滤设备的新建和附加,驱动自己会帮我们去除 过滤设备初始化
的标记,而这里需要我们自己手动去除。