Windows驱动开发技术具体解释(一)
在笔者接触驱动到如今以来一以后大半个月的时间,从中让我深深的体会到了万事开头难,以及学习持之以恒的重要性。笔者也是个驱动新人,開始接触驱动的时候看着张帆的《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。以下我们来通过详细代码学习一下键盘过滤驱动。
首先:
//驱动入口 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; }
在这里说一下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方法,该方法负责创建过滤设备,而且附加在目标设备上,详细代码例如以下:
//创建设备 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); }
注意的是在处理派遣函数的时候我们将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; }
函数就不说明了,主要就是对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; } }
载函数中调用了个取消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; }
整个键盘过滤驱动就完毕了,以后还得多多学习,多多总结。
转载请注明来自:http://blog.csdn.net/ms2146
转载于:https://www.cnblogs.com/mengfanrong/p/4344489.html
标签:
相关推荐:
最新新闻:
- 磁盘被写保护怎么解除?c盘哪些文件可以删除?
- kernelupadate.exe是什么程序?提示已停止工作的解决方法
- chkdsk工具怎么运行?chkdsk工具使用方法命令
- ie打开后自动关闭是怎么回事?ie打开后自动关闭的解决方法
- 弹出winlogon.exe应用程序错误怎么办?弹出winlogon.exe应用程序错误原因分析及解决方法
- 品牌机和组装机有什么区别?品牌机和组装机区别介绍
- 世界滚动:第二章Python入门2.1环境安装 Python详情介绍
- Windows驱动开发技术具体解释(一)
- Vagaa搜索不到资源是怎么回事?介绍Vagaa搜索设置技巧及解决方法
- 超级本是什么意思?超级本与笔记本有什么区别?
- 怎么样爬qq好友空间?怎么样爬qq好友空间的留言?
- XP系统支持多大的内存?XP可以支持4G以上的内存吗?
- 不用光盘怎么重装系统?重装系统的详细步骤
- 手机白屏是怎么一回事?诺基亚手机很卡怎么办?
- 电驴未连接到服务器怎么办?电驴现在还能用吗?
- 哪些华硕笔记本电脑的性价比最高?华硕笔记本大全
- 笔记本电池第一次充电要充满吗?笔记本电池怎么取下来?
- 数据寄存器是什么?它的作用有哪些?
- 传奇黑屏补丁怎么用?传奇进去之后黑屏怎么办?
- 语言栏消失如何修复?语言栏消失修复方法
- 百度网盘下载慢怎么办?百度网盘下载慢解决方法
- 遇到d3dx9_42.dll文件丢失该怎么办?遇到d3dx9_42.dll文件丢失解决方法步骤
- 打印机什么牌子好用?打印机品牌推荐 焦点信息
- SWAP是什么?SWAP信令特点介绍
- 手机显示单卡双模什么意思?单卡双模手机特点及分辨方法
- 每日速看!机房设备如何维护?机房日常维护的八点措施
- XP系统工作组计算机无法访问怎么解决?无法访问的解决技巧
- 如何查看电信宽带用时方式流程?查宽带上网时间的方式
- Ubuntu10.10下连接上无线网卡后无反应怎么办?无线上网设置方法
- 如何解除dnf的红字?解除dnf红字的方法步骤
- 《使命召唤16》及战区迎来万圣节限时活动 迅游加速器助力流畅体验
- 热文:《红色警戒3:起义时刻》游戏介绍 红色警戒3起义时刻配置要求
- egui.exe是什么进程?如何创建主键?
- 昱怎么读?昱的拼音是什么?昱的含义|动态焦点
- 中国电脑品牌排行榜:联想电脑销售量世界第一_每日视讯
- 每日速看!【设计函数】1010一元多项式求导(25分)设计解析
- Photoshop中overlay方式是什么?Photoshop中的overlay模式:环球时快讯
- 【实验】电容式点动型触摸开关模块控制led灯_即时焦点
- 全球动态:三星i408是多少钱?三星i408报价及详细测评介绍
- 诺基亚5800xm、5530xm、5230有什么区别?详细对比与区别介绍
- 环球速看:看图软件哪个好?看图工具那个好?
- 开源社区网:FileillaClient3.5.2正式版发布|全球快报
- 奇兔刷机怎么用?奇兔刷机使用教程
- 手机tf卡哪个牌子好?选购的注意事项有哪些?|新视野
- 当前快报:CollapsingToolbarLayout的使用方法介绍
- 如何判断浮子上升?压力水位传感器原理及六种方法
- 诺基亚X7-00如何插入存储卡?插入存储卡的方法
- 什么是指纹识别?在笔记本电脑中的应用有哪些? 焦点快看
- 阿里旺旺无法登陆怎么办?阿里旺旺无法登陆的解决措施
- hold是什么意思?hold住的简单介绍|全球资讯
- 国外投票过半玩家对JRPG存在偏见:怪不得吉田直树讨厌这种说法:热头条
- 米哈游申请多个“热血”商标 疑似新项目
- Windows美国份额创史低 苹果谷歌疯抢市场
- 《守望先锋2》开发人员:炸鱼行为困扰颇深
- 《大侠立志传》抢先体验版本登陆Steam 首发优惠价60元
- 《命运2》新增隐藏异域武器任务 可获Vex偃月
- 美国两架飞机相撞坠入湖中:至少1人死亡 飞机残骸漂浮画面曝光
- 调整人才结构聚焦供需错配,找工呀为汽车行业蓝领人才困境提供解决方案
- iPhone 15真机曝光 直角边框有弧度了
- 当前看点!追觅吸尘器女神节抖音专场:解放双手,追求更好的自己
- R7-7735HS神U加持!粉色限定色迷你主机低至2499元
- iPhone 14黄色版开箱:香蕉黄外观不错
- 世界动态:instead和insteadof的区别是什么?instead和insteadof的含义
- 光纤宽带的电话线是怎么用的?光纤宽带和ADSL宽带有什么区别? 环球播资讯
- Sta和Stakeholder是什么意思?Sta和Stakeholder有什么区别?
- 什么是OTS?阿里云飞天分布式系统之上的数据库 微动态
- 今日热议:【技术】设备控制用通讯协定——GEM&Control
- 十进制数如何转换为浮点数?十进制数转换为浮点数的方法
- 全球观点:Cosmos-1-理论知识全解析 gumptlu.work/Cosmos-pdf下载教程
- 如何在搜寻数据库时快速找到档案?locate命令用法举例
- METER功能信号的分类及频谱分析
- 案例分享:感染Synaptics蠕虫病毒的360安全卫士
- find命令详解 linux下find命令的使用方法
- 什么是Oops?linux之Oops原理及解析|动态
- 天天实时:CAP为什么没有明确考虑收敛性?分布式系统中的收敛性