大家好,又见面了,我是你们的朋友全栈君。
在我,因为现在是半个月后的时间与驱动器接触,我深深体会到开头难,和学习毅力的重要性。同时推动新。当看着帆驱动的开始《Windows驱动开发技术具体解释》讲的挺细。对新手来说是个不错的学习资料,可是更重要的还是自己要多动手练习。笔者在学习到同步操作的相关知识的时候。实在是看天书。
最后还是放弃了学习本书。再找了本楚狂人的资料学习。感觉本书对新手来说还是比較吃力的,当中笔者就是这样,非常多知识点不是非常明确。仅仅能凭借自己的感觉去做,只是造成的后果就是无情的蓝屏^_^。终于要的是笔者坚持下来了。
今天来分享下学习过程中,编写键盘过滤的心得。关于工作原理由于笔者也是一知半解,就不在阐述。
我们的目的就是将自己的驱动设备挂接/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。以下我们来通过详细代码学习一下键盘过滤驱动。
首先:
//设置读取派遣函数
pDriverObject->MajorFunction[IRP_MJ_READ]=FilterDispatchRoutin;
BindDevice(pDriverObject);
DbgPrint(“驱动载入结束…/n”);
return STATUS_SUCCESS;
}
在主函数中,调用BindDevice来实现过滤驱动的创建与绑定,代码例如以下:
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);
在BindDevice方法中,调用了一个CreateDevice方法。该方法负责创建过滤设备,而且附加在目标设备上。详细代码例如以下:
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;
}
通过以上代码能够实现过滤设备的绑定,绑定了之后还是主要处理派遣函数,功能例如以下:
//得到设备扩展
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处理完毕的时候就去运行回调函数,回调函数例如以下:
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的处理,只是在回调函数中引用了对比表,例如以下:
就是卸载函数,在卸载的时候我们要删除设备和附加的设备,然后取消最后一个IRP。代码例如以下:
//得到设备
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(“取消成功。。</p><p>。</p><p>/n”);
}
else
{
DbgPrint(“取消失败。。</p><p>。/n”);
}
}
//下一个设备
pDevObj=pDevObj->NextDevice;
}
}
载函数中调用了个取消IRP的方法,代码例如以下:
//取消后重设此例为空
IoSetCancelRoutine(pIrp,NULL);
return TRUE;
}
整个键盘过滤驱动就完毕了,以后还得多多学习,多多总结。
转载请注明来自:http://blog.csdn.net/ms2146
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/154970.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...