本文出自悠然品鉴原创,转载请注明出处:http://www.youranshare.com/codeorg/sid/133.html
今天研究了Windows内核的过滤驱动的写法,看着书把原理搞懂了,本想着写个实例试试看,结果进入了蓝屏的死循环中,不过还好,最后找到了错误的原因,还是编写成功了,完成了驱动对键盘的过滤,并且这个键盘驱动是可以及时卸载的,不用按下任意按键后才能完成卸载操作。
在驱动中有个设备栈的概念,类似与数据结构的堆栈,栈内的成员是设备Device,对于在同一个设备栈中的设备,IRP的路由方向是从栈顶à栈底,而对于新添加的设备总是位于设备栈的顶部。
所以,我们要想过滤键盘的信息,那么直接创建一个我们自定义的过滤驱动,然后挂载在键盘驱动之上形成设备栈,这样对于键盘操作的IRP我们就了如指掌了.
键盘过滤驱动的基本步骤:
① 获取到键盘设备的指针,如果失败就终止
② 创建自己的过滤设备,失败就终止
③ 设置READ等例程(通过Read拦截的)
④ 将自己的过滤设备挂载到键盘设备上,失败就终止
⑤ 在READ例程中转发IRP到底层设备,并切对IRP设置完成例程,因为在底层设备中这些IRP将会被序列化,然后一个一个的被完成,所以我们这里需要记录一共向底层设备发送了多少了IRP
⑥ 当底层完成了一个IRP处理后会调用我们设置的完成例程,在完成例程中可以获取到按键的信息,每当完成一个IRP,我们的IRP计数器要减少一个,这样就可以知道当前还有多少个IRP是在完成中的(挂起)
⑦ 卸载过滤驱动的时候首先移除掉设备栈内的当前的设备,然后判断是否有IRP还处于完成中,因为这些IRP在完成后会调用我们设置的完成例程,所以必须要等待经过当前例程转发的IRP全部完成才能删除当前的设备,否则如果直接删除当前设备的话会照成后续的IRP无法找到完成例程,导致蓝屏
//设备扩展
typedef struct DEVICE_EXTENSION { PDRIVER_OBJECT pDriverObject;//驱动对象 PDEVICE_OBJECT pDevice;//设备对象 PDEVICE_OBJECT pTarget;//挂载的下层 LONG nIrpsInQueue; //Irp 队列中剩余 未操作完成的IRP数量 PIRP pLastIrp; //保存每次转发的IRP,当卸载驱动的时候这个保存的是最后一个IRP //通过取消这个IRP 来完成所有的IRP }*PDEVICE_EXTENSION;
//Read转发操作
NTSTATUS HandlerRead(PDEVICE_OBJECT DeviceObject,PIRP Irp){ NTSTATUS status = STATUS_UNSUCCESSFUL; KdPrint(("READ:")); PDEVICE_EXTENSION pExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp,ReadCompleteRoutine,DeviceObject,TRUE,TRUE,TRUE); //增加IRP 队列长度 pExt->nIrpsInQueue++; pExt->pLastIrp = Irp; return IoCallDriver(pExt->pTarget,Irp); }
//卸载例程
void UnLoad(PDRIVER_OBJECT pDriverObject){ PDEVICE_OBJECT pDevice = NULL; LARGE_INTEGER liDelay; pDevice = pDriverObject->DeviceObject; if (NULL==pDevice) { return; } if (pDevice->DeviceExtension==NULL) { return; } PDEVICE_EXTENSION pExt = (PDEVICE_EXTENSION)pDevice->DeviceExtension; //首先要移除设备栈,移除之后的IRP将不会被过滤 IoDetachDevice(pExt->pTarget); //对于为完成的IRP,因为只前通过IoSetCompletionRoutine已经设置IO完成例程 //那么对于未完成的IRP ,在完成之后会调用 该层设备的函数 //如果 IRP队列还在,这时候直接删除设备 //当剩余的IRP 完成之后 之前设置的IO完成例程已经不存在了 //这样会照成 蓝屏 //所以需要等待 IO 完成之后再去删除 该层设备 liDelay.QuadPart = -1000000; while(pExt->nIrpsInQueue>0) { KeDelayExecutionThread(KernelMode,FALSE,&liDelay); KdPrint(("剩余挂起IRP:%dn",pExt->nIrpsInQueue)); if(pExt->nIrpsInQueue==1) { //取消掉最后一个IRP IoCancelIrp(pExt->pLastIrp); } } //移除设备 KdPrint(("移除设备n")); IoDeleteDevice(pDevice); return ; }
解压密码 yscode
百度网盘下载地址 http://pan.baidu.com/s/14H7Ue