今天来分享下学习过程中,编写键盘过滤的心得。关于工作原理因为笔者也是一知半解,就不在阐述。
我们的目的就是将自己的驱动设备挂接/driver/kbdclass驱动下的所有设备,如图所示:
然后通过处理来达到过滤我们想要的按键信息。挂接后的驱动中的第一个设备就是我们的过滤设备,当有按键触发,按键信息首先会被我们自己写的设备所拦截,但是这时候拦截到的是没有处理的按键信息,那改怎么处理呢?我们去问键盘驱动,当我们拦截到按键IRP的时候先不做处理,给IRP设置完成回调函数并传递给键盘驱动的设备。这样一来,当按键IRP被键盘驱动处理完毕之后就会执行我们的回调函数,这时我们在处理按键信息。当卸载我们的过滤设备的时候会有个麻烦就是会有个IRP已经设备了回调例程,并且在等待按键触发。如果这个IRP在没有处理之前就卸载掉我们的过滤驱动,就会引发按键蓝盘。为什么会蓝屏呢?因为这个IRP是已经被设置了回调函数,当IRP被处理完成之后去找我们设置的回调函数,因为我们在IRP没有处理之前已经卸载了,所以这时IRP已经找不到回调函数了,所以导致蓝屏。大部分都的解决方案是在处理IRP的时候放置个计数器,当计数器不为0的时候说明还有IRP未完成,这是卸载的时候就用while来一直等待这个IRP完成,如果我们要是不按键盘的话,它会无休止的等待下去,并且也影响系统性能。
笔者通过相关资料的查阅,另个解决方案就是做个代理IRP,然后保存原来的IRP,因为我们可以取消自己的IRP。在卸载的时候先卸载我们的代理IRP,然后在发送原来保存的IRP,这样就很好的解决了无限的等待的BUG...但是笔者也没有找到相关代码,只好自己动手试。经过一下午的测试,笔者发现我们只需要做一个代理IRP即可,并不需要保存原来的IRP,卸载的时候直接取消我们的IRP,并不需要重新发送个IRP。下面我们来通过具体代码学习一下键盘过滤驱动。
首先:
- //驱动入口
- extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)
- {
- NTSTATUS status;
- DbgPrint("驱动加载开始.../n");
- pDriverObject->DriverUnload=FilterUnload;
- //设置读取派遣函数
- pDriverObject->MajorFunction[IRP_MJ_READ]=FilterDispatchRoutin;
- BindDevice(pDriverObject);
- DbgPrint("驱动加载结束.../n");
- return STATUS_SUCCESS;
-
} //驱动入口 extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath) { NTSTATUS status;
DbgPrint("驱动加载开始.../n"); pDriverObject->DriverUnload=FilterUnload; //设置读取派遣函数 pDriverObject->MajorFunction[IRP_MJ_READ]=FilterDispatchRoutin; BindDevice(pDriverObject); DbgPrint("驱动加载结束.../n"); return STATUS_SUCCESS; }
在主函数中,调用BindDevice来实现过滤驱动的创建与绑定,代码如下:
- //设备类型
- extern "C" POBJECT_TYPE IoDriverObjectType;
- NTSTATUS BindDevice(PDRIVER_OBJECT pDriverObject)
- {
- NTSTATUS status;
- UNICODE_STRING uniNtNameString;
- //要打开的驱动对象
- PDRIVER_OBJECT KbdDriverObject = NULL;
- //驱动对象的设备
- PDEVICE_OBJECT kbdDeviceOjbect;
- //初始化一个字符串,就是kbdclass驱动的名子
- RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);
- //根据名字打开驱动对象
- status=ObReferenceObjectByName(
- &uniNtNameString,
- OBJ_CASE_INSENSITIVE,
- NULL,
- 0,
- IoDriverObjectType,
- KernelMode,
- NULL,
- (PVOID*)&KbdDriverObject);
- //如果失败了就直接返回
- if(!NT_SUCCESS(status))
- {
- DbgPrint("打开设备失败.../n");
- return status;
- }
- //调用ObReferenceObjectByName会导致对驱动对象的引用计数增加
- //必须响应的调用解引用ObDereferenceObject
- ObDereferenceObject(pDriverObject);
- DbgPrint("打开成功,解除引用.../n");
- //键盘驱动的第一个设备
- kbdDeviceOjbect=KbdDriverObject->DeviceObject;
- while(kbdDeviceOjbect!=NULL)
- {
- //创建并绑定过滤设备
- CreateDevice(pDriverObject,kbdDeviceOjbect);
- //下一个设备
- kbdDeviceOjbect=kbdDeviceOjbect->NextDevice;
- }
- return status;
-
} //设备类型 extern "C" POBJECT_TYPE IoDriverObjectType; NTSTATUS BindDevice(PDRIVER_OBJECT pDriverObject) { NTSTATUS status; UNICODE_STRING
uniNtNameString; //要打开的驱动对象 PDRIVER_OBJECT KbdDriverObject = NULL; //驱动对象的设备 PDEVICE_OBJECT kbdDeviceOjbect; //初始化一个字符串,就是kbdclass驱动的名子 RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME); //根据名字打开驱动对象 status=ObReferenceObjectByName( &uniNtNameString, OBJ_CASE_INSENSITIVE,
NULL, 0, IoDriverObjectType, KernelMode, NULL, (PVOID*)&KbdDriverObject); //如果失败了就直接返回 if(!NT_SUCCESS(status)) { DbgPrint("打开设备失败.../n"); return status; } //调用ObReferenceObjectByName会导致对驱动对象的引用计数增加 //必须响应的调用解引用ObDereferenceObject ObDereferenceObject(pDriverObject);
DbgPrint("打开成功,解除引用.../n"); //键盘驱动的第一个设备 kbdDeviceOjbect=KbdDriverObject->DeviceObject; while(kbdDeviceOjbect!=NULL) { //创建并绑定过滤设备 CreateDevice(pDriverObject,kbdDeviceOjbect); //下一个设备 kbdDeviceOjbect=kbdDeviceOjbect->NextDevice; } return status; }
在这里说一下ObReferenceObjectByName函数,该方法没有被导出,知我我们在头文件中声明一下即可使用,声明如下:
- //根据名字获取设备对象,此函数没有公开,声明一下就可以直接使用了
- extern "C" NTSTATUS ObReferenceObjectByName(
- PUNICODE_STRING objectName,
- ULONG Attributes,
- PACCESS_STATE AccessState,
- ACCESS_MASK DesiredAccess,
- POBJECT_TYPE objectType,
- KPROCESSOR_MODE accessMode,
- PVOID ParseContext,
-
PVOID *Object); //根据名字获取设备对象,此函数没有公开,声明一下就可以直接使用了 extern "C" NTSTATUS ObReferenceObjectByName(
PUNICODE_STRING objectName, ULONG Attributes, PACCESS_STATE AccessState, ACCESS_MASK DesiredAccess, POBJECT_TYPE objectType, KPROCESSOR_MODE accessMode, PVOID ParseContext, PVOID *Object);
在BindDevice方法中,调用了一个CreateDevice方法,该方法负责创建过滤设备,并且附加在目标设备上,具体代码如下:
- //创建设备
- NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject,IN PDEVICE_OBJECT oldDevObj)
- {
- NTSTATUS status;
- PDEVICE_OBJECT pDevObj;
- //谁被扩展
- PDEVICE_EXTENSION pDevExt;
- status=IoCreateDevice(pDriverObject,
- sizeof(PDEVICE_EXTENSION),
- NULL,
- oldDevObj->DeviceType,//设备类型需要和被附加的设备类型相等
- 0,
- FALSE,//如果指定设备是独占的,大部分驱动程序设置这个值为FALSE,如果不是独占的话设置为TRUE.
- &pDevObj);
- if(!NT_SUCCESS(status))
- {
- DbgPrint("创建设备失败..../n");
- return NULL;
- }
- pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension;
- //存储设备对象
- pDevExt->pDevice=pDevObj;
- //绑定前原设备
- pDevExt->poldDevice=oldDevObj;
- //标志位
- pDevObj->Flags |=oldDevObj->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);
- //该标识指示I/O管理器对所有发送到控制设备对象的Open请求进行安全检测
- pDevObj->Characteristics=oldDevObj->Characteristics;
- //绑定设备
- PDEVICE_OBJECT topDev = IoAttachDeviceToDeviceStack(pDevObj,oldDevObj);
- if(topDev==NULL)
- {
- //如果绑定失败,销毁设备
- IoDeleteDevice(pDevObj);
- status=STATUS_UNSUCCESSFUL;
- return status;
- }
- //将绑定的设备和原始设备放入设备扩展中
- pDevExt->poldDevice=oldDevObj;
- pDevExt->pbindDevice=topDev;
- pDevObj->Flags=pDevObj->Flags & ~DO_DEVICE_INITIALIZING;
- KdPrint(("绑定成功../n"));
- return STATUS_SUCCESS;
-
} //创建设备 NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject,IN PDEVICE_OBJECT oldDevObj) { NTSTATUS status; PDEVICE_OBJECT
pDevObj; //谁被扩展 PDEVICE_EXTENSION pDevExt; status=IoCreateDevice(pDriverObject, sizeof(PDEVICE_EXTENSION), NULL, oldDevObj->DeviceType,//设备类型需要和被附加的设备类型相等 0, FALSE,//如果指定设备是独占的,大部分驱动程序设置这个值为FALSE,如果不是独占的话设置为TRUE. &pDevObj); if(!NT_SUCCESS(status)) { DbgPrint("创建设备失败..../n");
return NULL; } pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension; //存储设备对象 pDevExt->pDevice=pDevObj; //绑定前原设备 pDevExt->poldDevice=oldDevObj; //标志位 pDevObj->Flags |=oldDevObj->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE); //该标识指示I/O管理器对所有发送到控制设备对象的Open请求进行安全检测
pDevObj->Characteristics=oldDevObj->Characteristics; //绑定设备 PDEVICE_OBJECT topDev = IoAttachDeviceToDeviceStack(pDevObj,oldDevObj); if(topDev==NULL) { //如果绑定失败,销毁设备 IoDeleteDevice(pDevObj); status=STATUS_UNSUCCESSFUL; return status; } //将绑定的设备和原始设备放入设备扩展中
pDevExt->poldDevice=oldDevObj; pDevExt->pbindDevice=topDev; pDevObj->Flags=pDevObj->Flags & ~DO_DEVICE_INITIALIZING; KdPrint(("绑定成功../n")); return STATUS_SUCCESS; }
通过以上代码可以实现过滤设备的绑定,绑定了之后还是主要处理派遣函数,功能如下:
- //派遣函数
- NTSTATUS FilterDispatchRoutin(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)
- {
- PIO_STACK_LOCATION currentIrpStack;
- PDEVICE_EXTENSION pDevExt;
- //得到设备扩展
- pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension;
- //得到当前irp包
- currentIrpStack=IoGetCurrentIrpStackLocation(pIrp);
- //将当前irp复制到下层设备irp堆栈
- IoCopyCurrentIrpStackLocationToNext(pIrp);
- //保存原来的irp
- //pDevExt->tagIrp=pIrp;
- //代理irp
- pDevExt->proxyIrp=pIrp;
- //设置当irp完成时的回调例程
- IoSetCompletionRoutine(pDevExt->proxyIrp,CallBackKbdFilter,pDevObj,TRUE,TRUE,TRUE);
- DbgPrint("irp回调例程设置完毕.../n");
- return IoCallDriver(pDevExt->poldDevice,pDevExt->proxyIrp);
-
} //派遣函数 NTSTATUS FilterDispatchRoutin(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp) { PIO_STACK_LOCATION currentIrpStack; PDEVICE_EXTENSION
pDevExt; //得到设备扩展 pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension; //得到当前irp包 currentIrpStack=IoGetCurrentIrpStackLocation(pIrp); //将当前irp复制到下层设备irp堆栈 IoCopyCurrentIrpStackLocationToNext(pIrp); //保存原来的irp //pDevExt->tagIrp=pIrp; //代理irp pDevExt->proxyIrp=pIrp;
//设置当irp完成时的回调例程 IoSetCompletionRoutine(pDevExt->proxyIrp,CallBackKbdFilter,pDevObj,TRUE,TRUE,TRUE); DbgPrint("irp回调例程设置完毕.../n"); return IoCallDriver(pDevExt->poldDevice,pDevExt->proxyIrp); }
注意的是在处理派遣函数的时候我们将IRP换成我们自己的IRP,这样就能达到取消IRP的目的,我们给IRP设置了回调函数,当IRP处理完成的时候就去执行回调函数,回调函数如下:
- // flags for keyboard status
- #define S_SHIFT 1
- #define S_CAPS 2
-
#define S_NUM 4
- static int kb_status = S_NUM;
- void __stdcall print_keystroke(UCHAR sch)
- {
- UCHAR ch = 0;
- int off = 0;
- if ((sch & 0x80) == 0) //make
- {
- if ((sch < 0x47) ||
- ((sch >= 0x47 && sch < 0x54) && (kb_status & S_NUM))) // Num Lock
- {
- ch = asciiTbl[off+sch];
- }
- switch (sch)
- {
- case 0x3A:
- kb_status ^= S_CAPS;
- break;
- case 0x2A:
- case 0x36:
- kb_status |= S_SHIFT;
- break;
- case 0x45:
- kb_status ^= S_NUM;
- }
- }
- else //break
- {
- if (sch == 0xAA || sch == 0xB6)
- kb_status &= ~S_SHIFT;
- }
- if (ch >= 0x20 && ch < 0x7F)
- {
- DbgPrint("%C /n",ch);
- }
- }
- NTSTATUS CallBackKbdFilter( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
- {
- PIO_STACK_LOCATION currentIrp;
- PKEYBOARD_INPUT_DATA keyData;
- currentIrp=IoGetCurrentIrpStackLocation(Irp);
- if(NT_SUCCESS(Irp->IoStatus.Status))
- {
- keyData=(PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
- //DbgPrint("扫描码:%x",keyData->MakeCode);
- DbgPrint("键盘 :%s",keyData->Flags?"弹起":"按下");
- print_keystroke((UCHAR)keyData->MakeCode);
- }
- if( Irp->PendingReturned )
- {
- IoMarkIrpPending( Irp );
- }
- return Irp->IoStatus.Status;
-
} // flags for keyboard status #define S_SHIFT 1 #define S_CAPS 2 #define S_NUM 4 static int kb_status = S_NUM; void __stdcall
print_keystroke(UCHAR sch) { UCHAR ch = 0; int off = 0; if ((sch & 0x80) == 0) //make { if ((sch < 0x47) || ((sch >= 0x47 && sch < 0x54) && (kb_status & S_NUM))) // Num Lock { ch = asciiTbl[off+sch]; } switch (sch) { case 0x3A: kb_status ^= S_CAPS; break;
case 0x2A: case 0x36: kb_status |= S_SHIFT; break; case 0x45: kb_status ^= S_NUM; } } else //break { if (sch == 0xAA || sch == 0xB6) kb_status &= ~S_SHIFT; } if (ch >= 0x20 && ch < 0x7F) { DbgPrint("%C /n",ch); } } NTSTATUS CallBackKbdFilter( IN PDEVICE_OBJECT
DeviceObject, IN PIRP Irp, IN PVOID Context ) { PIO_STACK_LOCATION currentIrp; PKEYBOARD_INPUT_DATA keyData; currentIrp=IoGetCurrentIrpStackLocation(Irp); if(NT_SUCCESS(Irp->IoStatus.Status)) { keyData=(PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
//DbgPrint("扫描码:%x",keyData->MakeCode); DbgPrint("键盘 :%s",keyData->Flags?"弹起":"按下"); print_keystroke((UCHAR)keyData->MakeCode); } if( Irp->PendingReturned ) { IoMarkIrpPending( Irp ); } return Irp->IoStatus.Status; }
函数就不说明了,主要就是对makecode的处理,不过在回调函数中引用了对照表,如下:
- unsigned char asciiTbl[]={
- 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09, //normal
- 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x0D, 0x00, 0x61, 0x73,
- 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
- 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
- 0x32, 0x33, 0x30, 0x2E,
- 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09, //caps
- 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x5B, 0x5D, 0x0D, 0x00, 0x41, 0x53,
- 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x5A, 0x58, 0x43, 0x56,
- 0x42, 0x4E, 0x4D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
- 0x32, 0x33, 0x30, 0x2E,
- 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09, //shift
- 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0x0D, 0x00, 0x41, 0x53,
- 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
- 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
- 0x32, 0x33, 0x30, 0x2E,
- 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09, //caps + shift
- 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x7B, 0x7D, 0x0D, 0x00, 0x61, 0x73,
- 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x7A, 0x78, 0x63, 0x76,
- 0x62, 0x6E, 0x6D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
- 0x32, 0x33, 0x30, 0x2E
- };
就是卸载函数,在卸载的时候我们要删除设备和附加的设备,然后取消最后一个IRP,代码如下:
- //卸载例程
- void FilterUnload(IN PDRIVER_OBJECT pDriverObject)
- {
- //得到设备
- PDEVICE_OBJECT pDevObj=pDriverObject->DeviceObject;
-
while(pDevObj!=NULL)
- {
- //设备扩展
- PDEVICE_EXTENSION pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension;
- PDEVICE_OBJECT pTagObj=pDevExt->pbindDevice;
- //解除绑定
- if(pDevExt->pbindDevice!=NULL)
- {
- IoDetachDevice(pDevExt->pbindDevice);
- }
- //删除设备
- if(pDevExt->pDevice!=NULL)
- {
- IoDeleteDevice(pDevExt->pDevice);
- }
- if(pDevExt->proxyIrp!=NULL)
- {
- if(CancelIrp(pDevExt->proxyIrp))
- {
- DbgPrint("取消成功。。。/n");
- }
- else
- {
- DbgPrint("取消失败。。。/n");
- }
- }
- //下一个设备
- pDevObj=pDevObj->NextDevice;
- }
-
} //卸载例程 void FilterUnload(IN PDRIVER_OBJECT pDriverObject) { //得到设备 PDEVICE_OBJECT pDevObj=pDriverObject->DeviceObject; while(pDevObj!=NULL)
{ //设备扩展 PDEVICE_EXTENSION pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension; PDEVICE_OBJECT pTagObj=pDevExt->pbindDevice; //解除绑定 if(pDevExt->pbindDevice!=NULL) { IoDetachDevice(pDevExt->pbindDevice); } //删除设备 if(pDevExt->pDevice!=NULL) { IoDeleteDevice(pDevExt->pDevice);
} if(pDevExt->proxyIrp!=NULL) { if(CancelIrp(pDevExt->proxyIrp)) { DbgPrint("取消成功。。。/n"); } else { DbgPrint("取消失败。。。/n"); } } //下一个设备 pDevObj=pDevObj->NextDevice; } }
载函数中调用了个取消IRP的方法,代码如下:
- BOOLEAN CancelIrp(PIRP pIrp)
- {
- if(pIrp==NULL)
- {
- DbgPrint("取消irp错误.../n");
-
return FALSE;
- }
- if(pIrp->Cancel || pIrp->CancelRoutine==NULL)
- {
- DbgPrint("取消irp错误.../n");
- return FALSE;
- }
- if(FALSE==IoCancelIrp(pIrp))
- {
- DbgPrint("IoCancelIrp to irp错误.../n");
- return FALSE;
- }
- //取消后重设此例为空
- IoSetCancelRoutine(pIrp,NULL);
- return TRUE;
-
} BOOLEAN CancelIrp(PIRP pIrp) { if(pIrp==NULL) { DbgPrint("取消irp错误.../n"); return FALSE; } if(pIrp->Cancel || pIrp->CancelRoutine==NULL)
{ DbgPrint("取消irp错误.../n"); return FALSE; } if(FALSE==IoCancelIrp(pIrp)) { DbgPrint("IoCancelIrp to irp错误.../n"); return FALSE; } //取消后重设此例为空 IoSetCancelRoutine(pIrp,NULL); return TRUE; }
整个键盘过滤驱动就完成了,以后还得多多学习,多多总结。