runrune
驱动牛犊
驱动牛犊
  • 注册日期2007-06-28
  • 最后登录2007-08-11
  • 粉丝0
  • 关注0
  • 积分170分
  • 威望18点
  • 贡献值0点
  • 好评度17点
  • 原创分0分
  • 专家分0分
阅读:3998回复:10

键盘过滤驱动(精简)

楼主#
更多 发布于:2007-07-15 02:06
发一份ctrl2cap代码(去除了许多功能部分).
对于初学者来说,我相信最简单的可用代码是学习入门的最好方法。
同样作为初学者的我,总希望能找到最简单的可用代码,以便于快速理解学习。
代码如下:

#include "ntddk.h"
#include "ntddkbd.h"
#include "stdio.h"        //包含三个要用到的头文件

#define KEY_UP         1
#define KEY_DOWN       0  //定义按键按下、弹起标志

#define LCONTROL       ((USHORT)0x1D)
#define CAPS_LOCK      ((USHORT)0x3A)  //定义按键扫描码

PDEVICE_OBJECT HookDeviceObject;
PDEVICE_OBJECT kbdDevice;              //声明两个全局变量---设备对象

NTSTATUS Ctrl2capInit( IN PDRIVER_OBJECT DriverObject );
NTSTATUS Ctrl2capDispatchRead( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
NTSTATUS Ctrl2capReadComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );
NTSTATUS Ctrl2capDispatchGeneral(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp );                   //函数使用前的声明

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)         //驱动入口
{
    DbgPrint("DriverEntry starting...\n");   //输出调试信息,可用Dbgview查看。

    DriverObject->MajorFunction[IRP_MJ_READ] = Ctrl2capDispatchRead;    //读按键分派例程,有按键按下时才开始执行。
    DriverObject->MajorFunction[IRP_MJ_CREATE]         =
    DriverObject->MajorFunction[IRP_MJ_CLOSE]          =
    DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]  =
    DriverObject->MajorFunction[IRP_MJ_CLEANUP]        =
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Ctrl2capDispatchGeneral; //有建立事件,关闭事件,应用程序调用时才执行。

    return Ctrl2capInit(DriverObject);  //直接执行的初始化例程
}

NTSTATUS Ctrl2capInit( IN PDRIVER_OBJECT DriverObject )
{
    CCHAR         ntNameBuffer[64];
    STRING         ntNameString;
    UNICODE_STRING       ntUnicodeString;
    NTSTATUS             status;                //定义一些要用到的变量

    DbgPrint("Ctrl2capInit starting...\n");     //调试用信息

    sprintf( ntNameBuffer, "\\Device\\KeyboardClass0" );
    RtlInitAnsiString( &ntNameString, ntNameBuffer );
    RtlAnsiStringToUnicodeString( &ntUnicodeString, &ntNameString, TRUE );

    status = IoCreateDevice( DriverObject,          
                 0,
                 NULL,
                 FILE_DEVICE_KEYBOARD,
                 0,
                 FALSE,
                 &HookDeviceObject );             //建立一键盘类设备

    if( !NT_SUCCESS(status) ) {

     DbgPrint("Ctrl2cap: Keyboard hook failed to create device!\n");

     RtlFreeUnicodeString( &ntUnicodeString );

     return STATUS_SUCCESS;
    }

    HookDeviceObject->Flags |= DO_BUFFERED_IO;

    status = IoAttachDevice( HookDeviceObject, &ntUnicodeString, &kbdDevice );       //连接我们的过滤设备到\\Device\\KeyboardClass0设备上

    if( !NT_SUCCESS(status) ) {

     DbgPrint("Ctrl2cap: Connect with keyboard failed!\n");

     IoDeleteDevice( HookDeviceObject );

     RtlFreeUnicodeString( &ntUnicodeString );
    
     return STATUS_SUCCESS;
    }

    RtlFreeUnicodeString( &ntUnicodeString );

    DbgPrint("Ctrl2cap: Successfully connected to keyboard device\n");

    return STATUS_SUCCESS;
}

NTSTATUS Ctrl2capDispatchRead( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )        //有按键按下时执行
{
    PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);         //获取当前的IRP包
    PIO_STACK_LOCATION nextIrpStack    = IoGetNextIrpStackLocation(Irp);
    *nextIrpStack = *currentIrpStack;
    IoSetCompletionRoutine( Irp, Ctrl2capReadComplete, DeviceObject, TRUE, TRUE, TRUE );     //调用完成例程
    return IoCallDriver( kbdDevice, Irp );
}

NTSTATUS Ctrl2capReadComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
{
    PIO_STACK_LOCATION        IrpSp;
    PKEYBOARD_INPUT_DATA      KeyData;

    IrpSp = IoGetCurrentIrpStackLocation( Irp );
    if( NT_SUCCESS( Irp->IoStatus.Status ) ) {
      KeyData = Irp->AssociatedIrp.SystemBuffer;

        DbgPrint("ScanCode: %x ", KeyData->MakeCode );
        DbgPrint("%s\n", KeyData->Flags ? "Up" : "Down" );      //输出按键的扫描码

    if( KeyData->MakeCode == CAPS_LOCK) {

        KeyData->MakeCode = LCONTROL;                   //修改按键

        }  
    }

    if( Irp->PendingReturned ) {

        IoMarkIrpPending( Irp );

    }

    return Irp->IoStatus.Status;
}

NTSTATUS Ctrl2capDispatchGeneral(                 //通用事件处理例程
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp )
{
    PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
    PIO_STACK_LOCATION nextIrpStack    = IoGetNextIrpStackLocation(Irp);

    Irp->IoStatus.Status      = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;

   if( DeviceObject == HookDeviceObject ) {

       *nextIrpStack = *currentIrpStack;

       return IoCallDriver( kbdDevice, Irp );

   } else {

       return STATUS_SUCCESS;

   }
}

以上代码可直接编译成驱动文件,运行后可用Dbgview查看按键的扫描码。
runrune
驱动牛犊
驱动牛犊
  • 注册日期2007-06-28
  • 最后登录2007-08-11
  • 粉丝0
  • 关注0
  • 积分170分
  • 威望18点
  • 贡献值0点
  • 好评度17点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2007-07-15 02:24
在此顺便向大牛们请教个问题:应用程序 如何控制 键盘过滤驱动实现获取按键信息(可查看,修改)???
也就是说上面的代码虽然能获取键盘按键(整个系统的按健)。
但是我想做的是:当应用程序需要获取按键的扫描码时,驱动才开始获取,当应用程序不需要时,就停止。
想到头疼都不知如何实现,希望大牛们能出手帮忙,给个代码,或是思路!!!

想到的几个方法:
1:由应用程序加载驱动,开始获取按键信息(可查看,修改),不需要时卸载驱动。
  不过过滤驱动要动态卸载实现起来很有难度,也不是个好方法。
2:由应用程序ReadFile实现?不知可行不?
3:由应用程序DeviceIoControl实现?可能这方法可以,不过我不知道如何实现!!!
4:由应用程序事件方面实现,没往这方面想。

想到希望能快点解决!!!
mycro
驱动牛犊
驱动牛犊
  • 注册日期2006-02-23
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分211分
  • 威望32点
  • 贡献值0点
  • 好评度21点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2007-07-18 15:48
看来你和我一样是个牛犊,你的问题说白了就是,
应用程序 与 驱动程序 通讯的问题,

通过这个函数 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]

来发送指令,就可以了 至于指令,使自己定义的,类似于 MFC自己定义的消息,

就是  CTL_CODE  宏,

例如:#define IOCTL_SET_KEYBOARD_DOWN     CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0405, METHOD_BUFFERED, FILE_ANY_ACCESS)

能通讯了,那么 事情就好办了,你可以设置一个事件 或者开关变量,

如果开关打开了,你就截获,否则不解货 不久完了,我也是个菜鸟...

希望没有误导你...
runrune
驱动牛犊
驱动牛犊
  • 注册日期2007-06-28
  • 最后登录2007-08-11
  • 粉丝0
  • 关注0
  • 积分170分
  • 威望18点
  • 贡献值0点
  • 好评度17点
  • 原创分0分
  • 专家分0分
地板#
发布于:2007-07-19 02:26
楼上兄台,具体要怎么做?
已实现用户程序通过DeviceIoControl(控制码为自己定义)和驱动程序通讯。
问题是有按键时,按键的IRP是发给键盘过滤驱动(也就是整个系统),而不是单单在我的用户程序中实现。

期待有人浪费一点时间帮我搞定这个让我郁闷了好久的问题,谢谢先!!!
tracera
禁止发言
禁止发言
  • 注册日期2006-01-09
  • 最后登录2018-09-25
  • 粉丝8
  • 关注0
  • 积分-5788分
  • 威望135321点
  • 贡献值0点
  • 好评度625点
  • 原创分1分
  • 专家分0分
  • 社区居民
地下室#
发布于:2007-07-19 09:41
用户被禁言,该主题自动屏蔽!
runrune
驱动牛犊
驱动牛犊
  • 注册日期2007-06-28
  • 最后登录2007-08-11
  • 粉丝0
  • 关注0
  • 积分170分
  • 威望18点
  • 贡献值0点
  • 好评度17点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2007-07-20 04:19
楼上兄弟给的网址已去看过,看过之后发现那样好象很复杂,不过倒又学到了一些。

现在发现其实用动态加载和卸载键盘过滤驱动的方法,应该更简单些,不知是否?

另:楼上兄弟可会韩语,韩国MM真漂亮呀(虽说大多数整过容),但要是会韩语,那可就太好了。
tracera
禁止发言
禁止发言
  • 注册日期2006-01-09
  • 最后登录2018-09-25
  • 粉丝8
  • 关注0
  • 积分-5788分
  • 威望135321点
  • 贡献值0点
  • 好评度625点
  • 原创分1分
  • 专家分0分
  • 社区居民
6楼#
发布于:2007-07-20 09:25
用户被禁言,该主题自动屏蔽!
runrune
驱动牛犊
驱动牛犊
  • 注册日期2007-06-28
  • 最后登录2007-08-11
  • 粉丝0
  • 关注0
  • 积分170分
  • 威望18点
  • 贡献值0点
  • 好评度17点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2007-07-20 15:33
在家呀,白天没时间,活动时间一般是晚上1点过后。
其实我要实现的跟Ctrl2cap例子一样,就是获取键盘按键的IRP包,然后修改(Caps Lock键转为Control键),并不需要自己发IRP模拟按键。
只不Ctrl2cap是针对整个系统,而我要实现的是局限于个人程序。
zgen_xiong
驱动牛犊
驱动牛犊
  • 注册日期2008-11-13
  • 最后登录2009-03-02
  • 粉丝0
  • 关注0
  • 积分3分
  • 威望31点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
8楼#
发布于:2008-12-22 17:24
我是菜鸟来的,请问一下“ntddkbd.h”引入为什么用引号方式?不是用<ntddkbd.h>的吗?两者有什么区别?哪位高手能否指点一下?多谢了
KillerFeng
驱动牛犊
驱动牛犊
  • 注册日期2008-12-16
  • 最后登录2009-01-14
  • 粉丝0
  • 关注0
  • 积分4分
  • 威望41点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
9楼#
发布于:2009-01-08 10:19
<>是先在源文件所在目录中查找ntddkbd.h文件
""是先在环境变量指定目录查找ntddkbd.h文件
查找ntddkbd.h文件的顺序不同而已,功能基本一样的
Crazy Coder
woo东去来袭
驱动牛犊
驱动牛犊
  • 注册日期2008-12-12
  • 最后登录2009-09-30
  • 粉丝0
  • 关注0
  • 积分41分
  • 威望341点
  • 贡献值1点
  • 好评度0点
  • 原创分0分
  • 专家分0分
10楼#
发布于:2009-02-19 18:44
  *nextIrpStack = *currentIrpStack;  鸡肋
游客

返回顶部