我是靠谱客的博主 高贵项链,最近开发中收集的这篇文章主要介绍键盘过滤驱动学习,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述


--------------------------------------------------------------2016-7-15--------------

1.windows中的键盘技术原理(键盘过滤驱动是工作在异步模式下)

 

在任务管理器中,csrss.exe进程描述为:

 


Csrss.exe进程有一个线程叫做win32!RawInputThread,这个线程通过一个GUID

(GUID_CLASS_KEYBOARD)来获得键盘设备栈的PDO符号链接名。

Win32k!RawInputThread执行到函数win32k!OpenDevice,它的一个参数可以找到键盘设备栈的PDO的符号链接名。

Win32k!OpenDevice有一个OBJECT_ATTRIBUTES结构的局部变量,它自己初始化这个局部变量,用传入参数中的键盘设备栈的PDO的符号链接名赋值OBJECT_ATTRIBUTES+0x8处的PUNICODE_STRING ObjectName

然后,调用ZwCreateFileZwCreateFile完成打开设备的工作,最后通过传入的参数返回得到的句柄。Win32k!RawInputThread把得到的句柄保存起来,供后面的ReadFileDeviceIoControl等使用。

 

工作原理(循环操作):

为了得到一个按键操作,首先需要发送一个IRP_MJ_READ(transfer the data from its device to system)到驱动的设备栈,驱动收到这个IRP会做什么样的处理呢?它会一直保持这个IRP为 pending未确定状态,直到一个键被真正的按下,驱动此时就会立刻完成这个IRP,并将刚按下的键的相关数据作为该IRP的返回值

在该IRP带着对应的数据返回后,操作系统将这些值传递给对应的事件系统来处理,然后做什么呢?

系统紧接着会立刻发送一个IRP_MJ_READ请求,等待下次的按键操作,重复以上的步骤

也就是说,任何时候,设备栈底都会有一个键盘的IRP_MJ_READ请求处于pending未确定状态。这意味着只有在该IRP完 成返回,新的IRP请求还未发送到的时候才会有一个很短暂的时间

卸载蓝屏原因:

由此我们想到,按照一般的方式动态载键盘过滤驱动的时候,基本都是 IRP_MJ_READ请求处于pending未确定状态,而我们却卸载了驱动,以后按键的时候需要处理这个IRP却找不到对应的驱动,就会导致蓝屏。

栈底有IRP,为什么我们的驱动卸载就会有问题呢?这是由于IRM_MJ_READ是异步的,对于异步的请求,基本上我们会关心这个异步请求的结果。那么如何得到完成后的数据呢?

可以设置完成例程。对,就是这样,由于我们给IRP_MJ_READ设置了完成例程,该IRP完成后会调用我们的完成 例程,使我们有处理返回数据的机会。在这样的情况下,我们动态卸载了键盘过滤驱动,也就是说完成例程已经被我们卸载掉了,而以后的再次按键在完成这个 IRP后会调用这个根本已经不存在了的东东,结果蓝屏就可想而知了。

蓝屏解决方法:

1.当有IRP_MJ_READ到来的时候,不为这个IRP设置完成例程,也不将该IRP向下传递

2.而是创建一个自己的IRP,并参考前面的 IRP_MJ_READ做对应的设置,然后为自己的这个IRP设置完成例程后,将自己的IRP向下传递,并设置原来的IRP_MJ_READ pending状态。

3.有按键操作时,自己的IRP返回触发为它设置的完成例程,在这里取得返回的数据填充前面的IRP_MJ_READ后,将该IRP_MJ_READ完成返回。

 

相当于我们使用了一个代理,而这一切都是透明的。到这里我们实现了完成例程,也就是有了处理数据的机会。

 

假设现在我们收到卸载的请求,看看当前所有的IRP处于何种状态。
(1)一个我们保存的原来的IRP_MJ_READ处于pending,注意它并没向下传递,也未设置完成例程。
(2)一个我们自己构造的IRP处于栈底,并注意我们为自己的这个IRP设置了完成例程。
基本上就这2个IRP。由于我们自己的IRP有完成例程,所以直接卸载会出现和上面一样的情况,导致蓝屏。如何处理呢?因为是我们自己构造的IRP,所以可以将其取消,这样并不会有太大的影响。然后将原来的这个IRP_MJ_READ向下传递,这里千万注意,我们自己的驱动马上要卸载,所以传递原来的 IRP_MJ_READ的时候不要给它设置完成例程。向下传递后卸载我们的驱动。

这里还有更简单的办法,使用计数器也可以实现,还简单得多

 

2.原理实现:

 

找出所有键盘设备,并Attach it

 

NTSTATUS ObReferenceObjectByName(//通过ObjectName获得对象指针Object

                        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( //Attach函数,传入驱动对象和注册路径

                  IN PDRIVER_OBJECT DriverObject,

                  IN PUNICODE_STRING RegistryPath

                  ){

    NTSTATUS status = 0;

    UNICODE_STRING uniNtNameString;

    PC2P_DEV_EXT devExt;   //创建的结合型结构devExt

    PDEVICE_OBJECT pFilterDeviceObject = NULL; //要绑定的过滤设备

    PDEVICE_OBJECT pTargetDeviceObject = NULL; //被绑定的过滤设备

    PDEVICE_OBJECT pLowerDeviceObject = NULL;  //绑定后设备对象,pFilterDO和pTargetDO绑定后的对象

    PDRIVER_OBJECT KbdDriverObject = NULL;     //打开的以KBD_DRIVER_NAME为名的设备对象

 

    DbgPrint(("MyAttachn"));

 

    // 初始化一个字符串,就是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 Objectn"));

        return( status );

    }else{

        // 这个打开需要解应用。早点解除了免得之后忘记。

        ObDerefearenceObject(DriverObject);

    }

    // 这是设备链中的第一个设备

    pTargetDeviceObject = KbdDriverObject->DeviceObject;

    // 现在开始遍历这个设备链

    while (pTargetDeviceObject){

        // 生成一个过滤设备pFilterDeviceObject

                   // 这里的IN宏和OUT宏都是空宏,只有标志性意义,表明这个参数是一个输入或者输出参数。

        status = IoCreateDevice(

            IN DriverObject,

            IN sizeof(C2P_DEV_EXT), //C2P_DEV_EXIT类型

            IN NULL,

            IN pTargetDeviceObject->DeviceType,

            IN pTargetDeviceObject->Characteristics,

            IN FALSE,

            OUT &pFilterDeviceObject

            );

        // 如果失败了就直接退出。

        if (!NT_SUCCESS(status)) {

            DbgPrint(("MyAttach: Couldn't create the MyFilter Filter Device Objectn"));

            return (status);

        }

        // 绑定。pLowerDeviceObject是绑定之后得到的下一个设备。

                   // pFilterDeviceObject绑定到pTargetDeviceObject上,再组成pLowerDeviceObject

        // 也就是前面常常说的所谓真实设备。

        pLowerDeviceObject =

            IoAttachDeviceToDeviceStack(pFilterDeviceObject, pTargetDeviceObject);

 

        // 如果绑定失败了,放弃之前的操作,退出。

        if(!pLowerDeviceObject){

            DbgPrint(("MyAttach: Couldn't attach to MyTest Device Objectn"));

            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;

}

 

创建一个新结构

目的是为了存储锁,事件,绑定的设备对象和底层设备对象:

 

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;

 

初始化新结构:(c2pDev)

 

NTSTATUS c2pDevExtInit(  //初始化C2PDev结构

     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 DriverEntry(

                     IN PDRIVER_OBJECT DriverObject,

                     IN PUNICODE_STRING RegistryPath

                     ){

    ULONG i;

    NTSTATUS status;

    DbgPrint (("c2p.SYS: entering DriverEntryn"));

    // 填写所有的分发函数的指针

    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;

}

 

 

 

DisPatch函数:(DispatchGeneral,Power,PnP)

 

 


 

//通常处理的dispatch,主要是为了处理普通的IRP操作(除了PNP,POWER,IRP_MJ_READ)

NTSTATUS c2pDispatchGeneral(

                                 IN PDEVICE_OBJECT DeviceObject,

                                 IN PIRP Irp

                                 ) { //跳过这层处理,而不是跳过此IRP

    // 其他的分发函数,直接skip然后用IoCallDriver把IRP发送到真实设备

    // 的设备对象。

    DbgPrint(("Other Diapatch!"));

               //IoSkipCurrentIrpStackLocation宏的作用就是使IO_STACK_LOCATION指针少前进一步

    IoSkipCurrentIrpStackLocation(Irp);

    //IoCallDriver函数会使IO_STACK_LOCATION指针向前一步,中和的结果就是

      IO_STACK_LOCATION指针不变向低层传递IRP

    return  IoCallDriver(((PC2P_DEV_EXT)DeviceObject->DeviceExtension)->LowerDeviceObject, Irp);

}

 

NTSTATUS c2pPower(   //Power的dispatch

                       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( //PnP设备的dispatch

                     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:

        DbgPrint(("IRP_MN_REMOVE_DEVICEn"));

        // 首先把请求发下去

        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( //Irp回调函数

                              IN PDEVICE_OBJECT DeviceObject,

                              IN PIRP Irp,

                              IN PVOID Context

                              )

{

     PIO_STACK_LOCATION IrpSp;

     ULONG buf_len = 0;

     PUCHAR buf = NULL;

     size_t i,numKeys;

     PKEYBOARD_INPUT_DATA KeyData;

     IrpSp = IoGetCurrentIrpStackLocation( Irp );

     //  如果这个请求是成功的。很显然,如果请求失败了,这么获取

     //   进一步的信息是没意义的。

     if( NT_SUCCESS( Irp->IoStatus.Status ) )

     {

        // 获得读请求完成后输出的缓冲区

        buf = Irp->AssociatedIrp.SystemBuffer;

KeyData = (PKEYBOARD_INPUT_DATA)buf;

        // 获得这个缓冲区的长度。一般的说返回值有多长都保存在

        // Information中。

        buf_len = Irp->IoStatus.Information;

        numKeys = buf_len / sizeof(KEYBOARD_INPUT_DATA);

        //这里可以做进一步的处理。我这里很简单的打印出所有的扫

        // 描码。

        //for(i=0;i<buf_len;++i)

for(i=0;i<numKeys;++i)

        {

            //DbgPrint("ctrl2cap: %2xrn", buf[i]);

DbgPrint("n");

DbgPrint("numKeys : %d",numKeys);

DbgPrint("ScanCode: %x ", KeyData->MakeCode );

DbgPrint("%sn", KeyData->Flags ?"Up" : "Down" );

print_keystroke((UCHAR)KeyData->MakeCode);

if( KeyData->MakeCode == CAPS_LOCK)

KeyData->MakeCode = LCONTROL;

        }

    }

    gC2pKeyCount--;

if( Irp->PendingReturned )

IoMarkIrpPending( Irp );

    return Irp->IoStatus.Status;

}

 

 

NTSTATUS c2pDispatchRead( //当irp为IRP_MJ_READ

                              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;

DbgPrint(("Dispatch encountered bogus current locationn"));

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 );

}


c2pAttachDevices(

IN PDRIVER_OBJECT DriverObject;

IN PUNICODE_STRING RegistryPath

)

利用#define  KBD_DRIVER_NAME L"\Driver\Kdbclass"打开键盘

设备对象。

其中,有四个设备对象:

KbdDriverObject:打开的键盘设备对象(物理设备对象)

ObReferenceObjectbyName.,若失败需要

解引用。

pTargetDeviceObject:KbdDO中的DO赋值给它,所以,在设备链中,

    此对象可作为第一个设备。

pFilterDeviceObject:过滤设备对象。

    新创建的设备对象,与pTargetDeviceObject类型

    和属性相同,并绑定在pTargetDeviceObject上。

pLowerDeviceObject:pFilterDeviceObjectpTargetDeviceObject绑定

   后的设备对象。

一个自建结构struct:

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;

 

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 );

}

 

DeviceExtension

A pointer to the device extension. The structure and contents of

the device extension are driver-defined. The size is driver-determined,

specified in the driver's call to IoCreateDevice or IoCreateDeviceSecure.

For more information about device extensions, see Device Extensions.

This is a read-only member. However, the object that the member points

to can be modified by the driver

 

devExt = (PC2P_DEV_EXT)(pFilterDeviceObject->DeviceExtension);

DeviceExtension初始化devExt.

在串口过滤驱动中,用了俩数组,俩数组起到一一对应的作用,拿到过滤设备的指针,

立马就可以找到真实设备的指针。

可是,也可以在生成一个过滤设备时,给这个设备指定一个任意长度的“设备扩展”,

这个扩展中的内容可以任意填写,作为一个自定义的数据结构。这样就可以把真实的

指针保存在设备对象中了,就没有必要将两个数组对应起来,每次查找。

PC2P_DEV_EXT devExt ---->用来存储pFilterDeviceObjectDeviceExtension,从而,

pFilterDeviceObjectDeviceObject的地址赋给devExt,所以,当前在函数

c2pDevExtInit中初始化的pFiterDeviceObjectDeviceExtensiondevExt)的地址一

定的,就是DeviceExtension.

也可以理解为devExt没有分配内存地址,现在将pFilterDeviceObjectDE的地址给它。

 

c2pDispatchGeneral(

                           IN PDEVICE_OBJECT DeviceObject,

                           IN PIRP Irp

                           )

这个函数为除IRP_MJ_READPnPPower之外的IRP分发函数。

其中有IoSkipCurrentIrpStackLocation(irp),用来跳过当前IrpLocation

(CurrentStackLocation加一)

IoCallDriver,为了使CurrentStackLoaction减一。为了将其发送给真实设备,将Device

Object透明化。

 

c2pPower(

                 IN PDEVICE_OBJECT DeviceObject,

                 IN PIRP Irp

                 )

传入DeviceObjectIrp.

首先,创建一个PC2P_DEV_EXT结构对象,同理,将其作为DeviceObjectDeviceExtension

意思为直接调用真实设备。(放过)

 

c2PnP(

IN PDEVICE_OBJECT DeviceObject,

IN PIRP Irp

      )

传入IrpDeviceObject

MinorFunction

A subfunction code for MajorFunction. The PnP manager, the power manager,

file system drivers, and SCSI class drivers set this member for some requests.

 

1.倘若irpStackLocationMinorFunctionIRP_MN_REMOVE_DEVIC.

唯一需要处理的是当有一个设备拔出时,解除绑定,并删除过滤设备。

首先把请求发送下去:

IoSkipCurrentIrpStackLocation.

IoCallDriver.

然后解除绑定,删除自己生成的虚拟设备DOExtension:

IoDetachDevice.

IoDeleteDevice.

2.defaultirp->MinorFunction.

IoSkipCurrentStackLocation.

IoCallDriver.

返回status.

 

c2pDispatchRead(

                        IN PDEVICE_OBJECT DeviceObject,

                        IN PIRP Irp

 )

特殊IRP处理:

当一个读请求来时,只是说Windows要从键盘驱动读取一个键扫描码值,但是在完成

之前显然不知。过滤的目的,就是要获得按下了什么键,所以不得不换一种处理方

法,就是把这个请求下发完成后,再去看这个值为多少。

可采用以下步骤:

1.调用IoCopyCurrentIrpStackLocationToNext把当前栈空间拷贝到下一个栈空间,

和前面的调用IoSkipCurrentIrpStackLocation跳过当前栈空间形成对比。

2.给这个IRP设置一个完成函数。如果IRP完成,则回调此完成函数。

3.调用IoCallDriver把请求发送到下一个设备。

代码如下:

1.IoCopyCurrentIrpStackLocationToNext(Irp);

2.IoSetComletionRoutine(Irp,c2pReadComplete,DeviceObject,

TRUE,TRUE,TRUE);

3.IoCallDriver(NextDevice,Irp);

4.需要一个键计数器。

  即请求到来时,把全局变量count1,完成后减1.

 

实在不能理解的是有个Irp->CurrentLocation==1这是什么逻辑?

 

先当作Irp->CurrentLocation不等于1吧,IRP_MJ_READ请求一次,便使其gC2pKeyCount

加一(全局变量,记录读次数).

先得到devExt(存于设备扩展).

再得到IrpCurrentLocation.

然后Copy当前的Irp位置给next-lower driver's stack location.

再,设置完成例程IoSetCompletionRoutine(Irp,c2pReadComplete,DeviceObject,TRUE,TRUE

TRUE).设置c2pReadComplete为完成例程,传入DeviceObjectIrp.

VOID

   IoSetCompletionRoutine(

     IN PIRP  Irp,

     IN PIO_COMPLETION_ROUTINE  CompletionRoutine  OPTIONAL,

     IN PVOID  Context  OPTIONAL,

     IN BOOLEAN  InvokeOnSuccess,

     IN BOOLEAN  InvokeOnError,

     IN BOOLEAN  InvokeOnCancel

     );

最后,用IoCallDriver回调decExtLowerObject,并传入Irp.

 

c2pReadComplete(

                        IN PDEVICE_OBJECT DeviceObject,

                        IN PIRP Irp,

                        IN PVOID Context

                        )

1,定义PIO_STACK_LOCATION IrpSpPKEYBOARD_INPUT_DATA KeyData等变量.

   typedef struct

_KEYBOARD_INPUT_DATA {

   USHORT  UnitId;

   USHORT  MakeCode;  

//MakCode:Specifies the scan code associated with a key press.

    USHORT  Flags;

   USHORT  Reserved;

   ULONG  ExtraInformation;

} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;

2,获得IrpStackLocation.

3,如果IrpIO状态为真,则获得系统缓冲区(请求完成后的输出缓冲区),获得缓冲区的

   数据,并存在KeyData里,从Information获得缓冲区长度,再获得缓冲区长度除以

   KEYBOARD_INPUT_DATA的大小(numKeys)。

4,再For循环中利用以下方法print所有的键数(numKeys),键值(KeyData->MakeCode,键状态(UP,DOWN),

   击打的键(print_keystroke((UNCHAR)KeyData->MakeCode)).

   DbgPrint("numKeys : %d",numKeys);

   DbgPrint("ScanCode: %x ", KeyData->MakeCode );

   DbgPrint("%sn", KeyData->Flags ?"Up" : "Down" );

   print_keystroke((UCHAR)KeyData->MakeCode);

   if( KeyData->MakeCode == CAPS_LOCK) //CAPS_LOCK大写键

    KeyData->MakeCode = LCONTROL;

5.gC2pKeyCount递减.

6.Irp->PendingReturned为真.

  "If set to TRUE, a driver has marked the IRP pending."这时,IoMarkIrpPending(Irp).标记IrpPending

  "Unless the driver's dispatch routine completes the IRP (by calling IoCompleteRequest) or passes

   the IRP on to lower drivers, it must call IoMarkIrpPending with the IRP."

 


最后

以上就是高贵项链为你收集整理的键盘过滤驱动学习的全部内容,希望文章能够帮你解决键盘过滤驱动学习所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部