概述
背景:
近段时间一个项目需要IRP操作文件.于是.搜索硬盘.把好几年前的代码找出来.说起这个代码,需要感谢黑月教主(achillis)和炉子.当时炉子开源了PsVoid.
里面就有Irp操作文件.但是抄了之后.win7蓝屏.当时水平太菜.只好在Q上麻烦教主帮忙.教主帮忙分析一番之后.于是网上就有了.那个经典的ULONG
UnKnow[41]. 然后各个博客转载…当然了.当时我自己也先存了一份.不过后来没啥用一直扔硬盘上…直到这次使用…
过程:
代码大致逻辑为:[和PsVoid类似].其实网上随便找一个IRP操作文件的帖子或者源码.都差不多.
Status = IrpCreateFile(
FilePath,
FILE_READ_ATTRIBUTES,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN,
FILE_NO_INTERMEDIATE_BUFFERING|FILE_NON_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT,
&FileObject
);
Status = FsRtlGetFileSize(FileObject,XXX);
Status = IrpReadFile(FileObject,X,X......);
最后
IrpClose(FileObject);
简单实验了一下,没有出现什么明显错误.于是就准备windbg双机调试走一遍,然后交付测试,在调试走的时候,一时好奇.在IrpCreateFile之后 !object FileObject. 查看了一下引用计数,
然后等IrpClose(FileObject)完再次!object FileObject. 一查看…Name没有了.但引用计数依然是1.
这是典型的对象泄漏啊…顿时来了精神.是不是这个代码几年前就有BUG.当时没注意…想着既然是对象引用计数不平衡.那就手动减少一下吧.
ObDereferenceObject(FileObject);
悲惨的蓝屏生活就此拉开了序幕.[中间甚至怀疑自己代码写的有问题.直接拿PsVoid的源码加一句ObDereferenceObject(FileObject);也是蓝屏的.]
加了这一行之后.在windowsXP SP3上测试的过程中偶尔就会蓝屏.但是又不是必现(后来我找到了一种必现的方法.).下边是蓝屏时候的堆栈.这两个堆栈交替出现.但必然是之一.
或者
顿时一脸懵逼啊.我就加了这一行.怎么NtFs创建CCB就有问题了…反复测试几次.在确定必然蓝屏而且可重现之后…看来是创建CCB或者从FCB抓什么锁导致的蓝屏.那么是不是这个时候文件对象里面什么成员无效了.导致从FileObject取什么东西然后访问蓝屏的…于是想带源码调试一下…
本来找盟主开源的那份Ntfs源码.编译之后替换进去.不知道怎么搞的.开机的时候巨卡.而且用kmd工具加载我的驱动就会卡在加载驱动那一直不返回…无奈.重新装了一个虚拟机.虚拟机采用Fat32分区.然后从WDK7601中找到FastFat源码(WXP版本).编译然后替换到windowsXP SP3虚拟机内,然后重新开机.开始了调试过程.
这次一加载驱动.fastfat内直接被断言下来了.(实际上走到IrpClose的发送Clean Irp那里.下发下去后击中断言.)
大致意思是说.一个引用计数必须不等于0.然后查看FCB内我的引用计数.的确就是0… 输入忽略之后继续走.引用技术减1成了负数.(0xFFFFFFFFF).
然后.为什么会是0呢.查找引用看看在那初始化的.或者说增加的.按照句柄的套路.打开的时候增加引用计数1.关闭减1.应该配对.这里估计猜着也差不多…
于是打开FastFat源码找到.然后对这个变量查找引用.最后定位到
很明显.只有FileObject–>Flags有这个标志位.才会加1.而我这个FileObject是自己创建出来的.Flags内正好没有这个标志位…那么为什么没有呢.系统是怎么处理这个标志位的.或者说在那初始化呢.在WRK内搜索这个宏.
最后来到了.IopParseDevice内的如下片段.很明显.这个是创建完FileObject后,应该根据CreateOptions然后主动的置位的.原来的代码内没有… 于是.这就是引出了.这个IRP操作文件的第一个BUG…
[应该根据不同情况设置不同的Flags.(实际上还是WRK考虑的周全.我这里只是简单的解决了我加的这个非缓存读的标志.实际上还有其他的要处理.)而不能简单的_FileObject->Flags = FO_SYNCHRONOUS_IO;]
解决之后,继续调试.最后发现FastFat上蓝屏出现在.[中间跟踪Clean过程以及Close过程就不说了.纯体力+F10]
源码调试就是不一样,直接说明了FatFreeCCB的地方蓝屏.并且.堆栈很明显的指出.这是ObDereferenceObject(FileObject);引发的…
在这里.我走了不少弯路.那就是.分析FatFreeCCB为什么会蓝屏,跟着FastFat绕了好几圈.最后得出结论.蓝屏的时候的这个CCB值是无效的.
那这个CCB是从那来的.怎么就无效了呢…通过代码内查找引用.发现
[ TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); 从FileObject的FsContext2取出CCB然后传递给FatCommonClose释放的.]
FatFreeCCB就一句话.
那么FileObject的CCB怎么就无端端的无效了呢… 或者说FileObject->FsContext2.怎么就无效了.于是又跟了一圈.这次对FileObject的
FsContext2下硬断点.看看是怎么回事.最后得到如下堆栈.
看到这.和上边蓝屏的堆栈一结合.顿时明白了.内存重复释放了…而这个重复释放第一次释放是我们自己的代码.IrpClose内.发IRP_MJ_CLOSE.引发的.
第二次.是ObDereferenceObject(FileObject);引发的…
当时在这个时候.潜意识里一直在想.IrpCreateFile完了就应该IrpClose.是ObDereferenceObject(FileObject)引发了问题…直到我去根据蓝屏堆栈.在WRK找到内IopDeleteFile…彻底明白了… ObDereferenceObject把对象减到0.自动触发对象删除例程.然后在这个例程内.做一些判断…然后就会调用IopCloseFile来发IRP_MJ_CLEANUP.后回到IopDeleteFile继续发IRP_MJ_CLOSE…这才是正常流程…
而我们自己发IRP_MJ_CLEANUP.和IRP_MJ_CLOSE. 让文件系统提前把相对应的信息释放了.等文件对象销毁的时候再给文件系统发IRP的时候.内存被重复释放.最终引发蓝屏… 脉络清楚之后.解决起来就非常简单了.
一句话.IrpCreateFile之后.不要调用IrpClose.直接一句ObDereferenceObject(FileObject); 搞定…系统自动把CLEANUP和CLOSE全发了.
修改之后,再次测试.Ntfs上也不再蓝屏.Bug解决.
备注:
再说三个BUG.但这三个BUG就不用再细说了.一来.简单.二来网上的IRP操作文件代码也已经解决了.
1.ObCreateObject时候FileObject大小.VISTA以前和以后是不一样的.不过这个不同版本DDK或者WDK的WDM.H头文件内有详细定义照抄即可.
2.创建出来的FileObject内的FileName的Buffer.最好是动态申请的.至于申请出来的Buffer需不需要自己释放.这个可以自由选择.但如果像代码过Verifer的话.
最好.在我上边说的ObDereferenceObject(FileObject);前主动释放掉.[如果不自己释放,IopDeleteFile内也会检测到来释放.]
3.黑月教主当年分析出结构大小需要添加ULONG Unknow[41].但随着操作系统版本升级.win8以后还是相对小了.仍然有几率蓝屏… 最保险起见
ULONG Unknow[100].(当然了.我这里是偷懒了,准确的方法是沿着黑月教主当年的思路分析一下SeCreateAccessState.)
填完以上BUG之后.目前暂时未发现新的蓝屏点.但其中仍然有许多不足.
例如Read Write时候.IRP Flags的设置等.可能也需要参照WRK设置…(猜的.下一步用到再验证.但思路都差不多.)
补充:
IrpCreateFile打开文件后,如果要进行Read.或者Write等.那么IrpCreateFile的时候FILE_NO_INTERMEDIATE_BUFFERING标志需要设置.这样
ObDereferenceObject(FileObject);完才会立即销毁文件对象并发送CLEANUP和CLOSE.否则.Read的时候缓存增加了对象引用计数.会延迟销毁.[这个后续还需要再研究.]
最后
以上就是开朗香氛为你收集整理的IRP操作文件填坑日记的全部内容,希望文章能够帮你解决IRP操作文件填坑日记所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复