3207145141 发表于 2017-1-17 12:34:37

IRP操作文件填坑日记

背景:
近段时间一个项目需要IRP操作文件.于是.搜索硬盘.把好几年前的代码找出来.说起这个代码,需要感谢黑月教主(achillis)和炉子.当时炉子开源了PsVoid.
里面就有Irp操作文件.但是抄了之后.win7蓝屏.当时水平太菜.只好在Q上麻烦教主帮忙.教主帮忙分析一番之后.于是网上就有了.那个经典的ULONG
UnKnow. 然后各个博客转载..当然了.当时我自己也先存了一份.不过后来没啥用一直扔硬盘上..直到这次使用...

过程:
代码大致逻辑为:[和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就一句话.
INLINE
VOID
FatFreeCcb (
    IN PCCB Ccb
    )
{
#if FAT_FILL_FREE
    RtlFillMemoryUlong(Ccb, sizeof(CCB), FAT_FILL_FREE);
#endif

    ExFreePool( Ccb );
}
那么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.但随着操作系统版本升级.win8以后还是相对小了.仍然有几率蓝屏.. 最保险起见
ULONG Unknow.(当然了.我这里是偷懒了,准确的方法是沿着黑月教主当年的思路分析一下SeCreateAccessState.)


填完以上BUG之后.目前暂时未发现新的蓝屏点.但其中仍然有许多不足.
例如Read Write时候.IRP Flags的设置等.也需要参照WRK设置..(猜的.下一步用到再验证.但思路都差不多.)


补充:**** Hidden Message *****

Tesla.Angela 发表于 2017-1-17 12:39:33

我对LZ的填坑功力深表佩服!

wtxpwh 发表于 2017-1-17 12:42:08

不错!

meesong 发表于 2017-1-17 12:50:29

很详细!非常感谢!

125096 发表于 2017-1-17 13:03:04


我对LZ的填坑功力深表佩服!

flac 发表于 2017-1-17 13:12:31

卧槽强帖留名,之前对这个IRP打开文件一直持敬畏精神,担心啥时候一不小心就蓝屏了

renminbi 发表于 2017-1-17 13:31:23

看下,还有些什么补充的。

deegar 发表于 2017-1-17 13:38:41

很详细!

tangptr@126.com 发表于 2017-1-17 13:51:06

好!

木~~水 发表于 2017-2-5 10:40:03

楼主厉害了

NOW刘 发表于 2017-5-9 16:45:02

调试的过程来看看

Murray 发表于 2017-6-1 14:22:14

真的佩服,

锅炉猫MC 发表于 2017-6-15 21:50:50

感谢分享                        

c3358 发表于 2017-7-21 19:01:23

谢谢分享~~~

sc12345 发表于 2018-8-3 20:27:56

感谢分享,看看

qingyang 发表于 2019-6-13 10:28:15

感谢分享,学习了

beijing0605 发表于 2019-6-13 10:32:52

感谢分享,学习了

mmlai8 发表于 2019-6-18 13:28:20


楼主厉害了

caizhe666 发表于 2020-4-1 13:18:55

厉害啊楼主

Henry 发表于 2021-7-2 19:39:26

学习了,楼主最帅!

AJin 发表于 2021-8-8 22:42:27

谢谢分享

IBinary 发表于 2022-11-21 13:31:50

跟着楼主学习下逆向思路.分析思路.
页: [1]
查看完整版本: IRP操作文件填坑日记