驱动开发 键盘过滤驱动程序-- 传统的键盘过滤

最近看 《树木和独钓 windows内核编程》,看到键盘过滤部分,做笔记,仅供参考,那里被理解为是不正确的,同时,我们也希望大家指正。

如今来说一下传统型键盘过滤,就是把自己的设备对象绑定在KbdClass设备对象之上。那么发送到KbdClass的IRP都会先经过自己的设备对象,我们能够在读派遣函数中设置完毕例程,当IRP完毕后在完毕历程中得到按键信息。

KbdClass被称为键盘类驱动,在windows中,类驱动一般是指统管一类设备的驱动程序。无论是USB键盘,还是PS/2键盘均经过它,所以在这层做拦截,能获得非常好的通用性。

用工具Devicetree查看


安装 《寒江独钓 windows内核编程》中实例 ctl2cap。再次查看


代码例如以下:

#include <wdm.h>

// Kbdclass驱动的名字
#define KBD_DRIVER_NAME  L"\Driver\Kbdclass"

typedef struct _C2P_DEV_EXT 
{ 
    // 这个结构的大小
    ULONG NodeSize; 
    // 过滤设备对象
    PDEVICE_OBJECT pFilterDeviceObject;
    // 同一时候调用时的保护锁
    KSPIN_LOCK IoRequestsSpinLock;
    // 进程间同步处理  
    KEVENT IoInProgressEvent; 
    // 绑定的设备对象
    PDEVICE_OBJECT TargetDeviceObject; 
    // 绑定前底层设备对象
    PDEVICE_OBJECT LowerDeviceObject; 
} C2P_DEV_EXT, *PC2P_DEV_EXT;

NTSTATUS 
c2pDevExtInit( 
    IN PC2P_DEV_EXT devExt, 
    IN PDEVICE_OBJECT pFilterDeviceObject, 
    IN PDEVICE_OBJECT pTargetDeviceObject, 
    IN PDEVICE_OBJECT pLowerDeviceObject ) 
{ 
    memset(devExt, 0, sizeof(C2P_DEV_EXT)); 
    devExt->NodeSize = sizeof(C2P_DEV_EXT); 
    devExt->pFilterDeviceObject = pFilterDeviceObject; 
    KeInitializeSpinLock(&(devExt->IoRequestsSpinLock)); 
    KeInitializeEvent(&(devExt->IoInProgressEvent), NotificationEvent, FALSE); 
    devExt->TargetDeviceObject = pTargetDeviceObject; 
    devExt->LowerDeviceObject = pLowerDeviceObject; 
    return( STATUS_SUCCESS ); 
}

// 这个函数是事实存在的。仅仅是文档中没有公开。声明一下
// 就能够直接使用了。

NTSTATUS ObReferenceObjectByName( PUNICODE_STRING ObjectName, ULONG Attributes, PACCESS_STATE AccessState, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, PVOID ParseContext, PVOID *Object ); extern POBJECT_TYPE IoDriverObjectType; ULONG gC2pKeyCount = 0; PDRIVER_OBJECT gDriverObject = NULL; // 这个函数经过改造。能打开驱动对象Kbdclass,然后绑定 // 它以下的全部的设备: NTSTATUS c2pAttachDevices( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { NTSTATUS status = 0; UNICODE_STRING uniNtNameString; PC2P_DEV_EXT devExt; PDEVICE_OBJECT pFilterDeviceObject = NULL; PDEVICE_OBJECT pTargetDeviceObject = NULL; PDEVICE_OBJECT pLowerDeviceObject = NULL; PDRIVER_OBJECT KbdDriverObject = NULL; KdPrint(("MyAttach ")); // 初始化一个字符串。就是Kdbclass驱动的名字。 RtlInitUnicodeString(&uniNtNameString, KBD_DRIVER_NAME); // 请參照前面打开设备对象的样例。仅仅是这里打开的是驱动对象。 status = ObReferenceObjectByName ( &uniNtNameString, OBJ_CASE_INSENSITIVE, NULL, 0, IoDriverObjectType, KernelMode, NULL, &KbdDriverObject ); // 假设失败了就直接返回 if(!NT_SUCCESS(status)) { KdPrint(("MyAttach: Couldn't get the MyTest Device Object ")); return( status ); } else { // 这个打开须要解应用。

早点解除了免得之后忘记。 ObDereferenceObject(DriverObject); } // 这是设备链中的第一个设备 pTargetDeviceObject = KbdDriverObject->DeviceObject; // 如今開始遍历这个设备链 while (pTargetDeviceObject) { // 生成一个过滤设备。这是前面读者学习过的。这里的IN宏和OUT宏都是 // 空宏,仅仅有标志性意义。表明这个參数是一个输入或者输出參数。

status = IoCreateDevice( IN DriverObject, IN sizeof(C2P_DEV_EXT), IN NULL, IN pTargetDeviceObject->DeviceType, IN pTargetDeviceObject->Characteristics, IN FALSE, OUT &pFilterDeviceObject ); // 假设失败了就直接退出。

if (!NT_SUCCESS(status)) { KdPrint(("MyAttach: Couldn't create the MyFilter Filter Device Object ")); return (status); } // 绑定。pLowerDeviceObject是绑定之后得到的下一个设备。也就是 // 前面经常说的所谓真实设备。 pLowerDeviceObject = IoAttachDeviceToDeviceStack(pFilterDeviceObject, pTargetDeviceObject); // 假设绑定失败了,放弃之前的操作,退出。 if(!pLowerDeviceObject) { KdPrint(("MyAttach: Couldn't attach to MyTest Device Object ")); IoDeleteDevice(pFilterDeviceObject); pFilterDeviceObject = NULL; return( status ); } // 设备扩展!以下要具体讲述设备扩展的应用。

devExt = (PC2P_DEV_EXT)(pFilterDeviceObject->DeviceExtension); c2pDevExtInit( devExt, pFilterDeviceObject, pTargetDeviceObject, pLowerDeviceObject ); // 以下的操作和前面过滤串口的操作基本一致。这里不再解释了。 pFilterDeviceObject->DeviceType=pLowerDeviceObject->DeviceType; pFilterDeviceObject->Characteristics=pLowerDeviceObject->Characteristics; pFilterDeviceObject->StackSize=pLowerDeviceObject->StackSize+1; pFilterDeviceObject->Flags |= pLowerDeviceObject->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE) ; //next device pTargetDeviceObject = pTargetDeviceObject->NextDevice; } return status; } VOID c2pDetach(IN PDEVICE_OBJECT pDeviceObject) { PC2P_DEV_EXT devExt; BOOLEAN NoRequestsOutstanding = FALSE; devExt = (PC2P_DEV_EXT)pDeviceObject->DeviceExtension; __try { __try { IoDetachDevice(devExt->TargetDeviceObject); devExt->TargetDeviceObject = NULL; IoDeleteDevice(pDeviceObject); devExt->pFilterDeviceObject = NULL; DbgPrint(("Detach Finished ")); } __except (EXCEPTION_EXECUTE_HANDLER){} } __finally{} return; } #define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000) #define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000) VOID c2pUnload(IN PDRIVER_OBJECT DriverObject) { PDEVICE_OBJECT DeviceObject; PDEVICE_OBJECT OldDeviceObject; PC2P_DEV_EXT devExt; LARGE_INTEGER lDelay; PRKTHREAD CurrentThread; //delay some time lDelay = RtlConvertLongToLargeInteger(100 * DELAY_ONE_MILLISECOND); CurrentThread = KeGetCurrentThread(); // 把当前线程设置为低实时模式。以便让它的执行尽量少影响其它程序。 KeSetPriorityThread(CurrentThread, LOW_REALTIME_PRIORITY); UNREFERENCED_PARAMETER(DriverObject); KdPrint(("DriverEntry unLoading... ")); // 遍历全部设备并一律解除绑定 DeviceObject = DriverObject->DeviceObject; while (DeviceObject) { // 解除绑定并删除全部的设备 c2pDetach(DeviceObject); DeviceObject = DeviceObject->NextDevice; } ASSERT(NULL == DriverObject->DeviceObject); while (gC2pKeyCount) { KeDelayExecutionThread(KernelMode, FALSE, &lDelay); } KdPrint(("DriverEntry unLoad OK! ")); return; } NTSTATUS c2pDispatchGeneral( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { // 其它的分发函数,直接skip然后用IoCallDriver把IRP发送到真实设备 // 的设备对象。 KdPrint(("Other Diapatch!")); IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(((PC2P_DEV_EXT) DeviceObject->DeviceExtension)->LowerDeviceObject, Irp); } NTSTATUS c2pPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PC2P_DEV_EXT devExt; devExt = (PC2P_DEV_EXT)DeviceObject->DeviceExtension; PoStartNextPowerIrp( Irp ); IoSkipCurrentIrpStackLocation( Irp ); return PoCallDriver(devExt->LowerDeviceObject, Irp ); } NTSTATUS c2pPnP( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PC2P_DEV_EXT devExt; PIO_STACK_LOCATION irpStack; NTSTATUS status = STATUS_SUCCESS; KIRQL oldIrql; KEVENT event; // 获得真实设备。

devExt = (PC2P_DEV_EXT)(DeviceObject->DeviceExtension); irpStack = IoGetCurrentIrpStackLocation(Irp); switch (irpStack->MinorFunction) { case IRP_MN_REMOVE_DEVICE: KdPrint(("IRP_MN_REMOVE_DEVICE ")); // 首先把请求发下去 IoSkipCurrentIrpStackLocation(Irp); IoCallDriver(devExt->LowerDeviceObject, Irp); // 然后解除绑定。 IoDetachDevice(devExt->LowerDeviceObject); // 删除我们自己生成的虚拟设备。 IoDeleteDevice(DeviceObject); status = STATUS_SUCCESS; break; default: // 对于其它类型的IRP。全部都直接下发就可以。 IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(devExt->LowerDeviceObject, Irp); } return status; } // 这是一个IRP完毕回调函数的原型 NTSTATUS c2pReadComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PIO_STACK_LOCATION IrpSp; ULONG buf_len = 0; PUCHAR buf = NULL; size_t i; IrpSp = IoGetCurrentIrpStackLocation( Irp ); // 假设这个请求是成功的。非常显然。假设请求失败了,这么获取 // 进一步的信息是没意义的。

if( NT_SUCCESS( Irp->IoStatus.Status ) ) { // 获得读请求完毕后输出的缓冲区 buf = Irp->AssociatedIrp.SystemBuffer; // 获得这个缓冲区的长度。一般的说返回值有多长都保存在 // Information中。 buf_len = Irp->IoStatus.Information; //… 这里能够做进一步的处理。

我这里非常easy的打印出全部的扫 // 描码。 for(i=0;i<buf_len;++i) { DbgPrint("ctrl2cap: %2x ", buf[i]); } } gC2pKeyCount--; if( Irp->PendingReturned ) { IoMarkIrpPending( Irp ); } return Irp->IoStatus.Status; } NTSTATUS c2pDispatchRead( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status = STATUS_SUCCESS; PC2P_DEV_EXT devExt; PIO_STACK_LOCATION currentIrpStack; KEVENT waitEvent; KeInitializeEvent( &waitEvent, NotificationEvent, FALSE ); if (Irp->CurrentLocation == 1) { ULONG ReturnedInformation = 0; KdPrint(("Dispatch encountered bogus current location ")); status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Status = status; Irp->IoStatus.Information = ReturnedInformation; IoCompleteRequest(Irp, IO_NO_INCREMENT); return(status); } // 全局变量键计数器加1 gC2pKeyCount++; // 得到设备扩展。目的是之后为了获得下一个设备的指针。 devExt = (PC2P_DEV_EXT)DeviceObject->DeviceExtension; // 设置回调函数并把IRP传递下去。 之后读的处理也就结束了。 // 剩下的任务是要等待读请求完毕。

currentIrpStack = IoGetCurrentIrpStackLocation(Irp); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine( Irp, c2pReadComplete, DeviceObject, TRUE, TRUE, TRUE ); return IoCallDriver( devExt->LowerDeviceObject, Irp ); } NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { ULONG i; NTSTATUS status; KdPrint (("c2p.SYS: entering DriverEntry ")); // 填写全部的分发函数的指针 for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] = c2pDispatchGeneral; } // 单独的填写一个Read分发函数。由于要的过滤就是读取来的按键信息 // 其它的都不重要。这个分发函数单独写。 DriverObject->MajorFunction[IRP_MJ_READ] = c2pDispatchRead; // 单独的填写一个IRP_MJ_POWER函数。这是由于这类请求中间要调用 // 一个PoCallDriver和一个PoStartNextPowerIrp,比較特殊。 DriverObject->MajorFunction [IRP_MJ_POWER] = c2pPower; // 我们想知道什么时候一个我们绑定过的设备被卸载了(比方从机器上 // 被拔掉了?)所以专门写一个PNP(即插即用)分发函数 DriverObject->MajorFunction [IRP_MJ_PNP] = c2pPnP; // 卸载函数。 DriverObject->DriverUnload = c2pUnload; gDriverObject = DriverObject; // 绑定全部键盘设备 status =c2pAttachDevices(DriverObject, RegistryPath); return status; }







版权声明:本文博客原创文章,博客,未经同意,不得转载。

原文地址:https://www.cnblogs.com/blfshiye/p/4616947.html