【转】缓存管理器的一处需要注意的小细节

 

 

 

转自盟主的文章

 

在研究改别人的一个过滤驱动的时候,发现有的人会在pre Create回调里调用IoCreateFileSpecifyDeviceObjectHint打开Create参数里的文件名,并且用ZwReadFile读取文件并判断是否为pe文件。这种做法会导致一些小问题。

 

其中过滤驱动并没处理fast io。然后我发现pre write回调偶尔会收不到消息。仔细跟踪下去,发现write消息发送了,但参数里的fileobject并不是Create回调里产生的,而居然是IoCreateFileSpecifyDeviceObjectHint产生的文件对象。

而这个文件对象会有什么问题呢?

见如下堆栈:

 

f9efe9cc 804e4d77 FastFat!FatFsdWrite

f9efe9dc 804f0b8d nt!IopfCallDriver+0x31

f9efe9f0 804f0756 nt!IoSynchronousPageWrite+0xaf

f9efeacc 804f050d nt!MiFlushSectionInternal+0x38b

f9efeb08 804f0e18 nt!MmFlushSection+0x1e0

f9efeb90 f81e3043 nt!CcFlushCache+0x372

f9efebb8 f81d6006 Fastfat!FatFlushFile+0x20

f9efec0c f81d8bfc Fastfat!FatCommonFlushBuffers+0x12c

f9efec50 804e4d77 Fastfat!FatFsdFlushBuffers+0x3e

f9efec60 f8034432 nt!IopfCallDriver+0x31

f9efec78 f80338a2 xxx!IoCallDriverEx+0x22

f9efec98 f802a722 xxx!OnDispatch+0x142

f9efecb4 804e4d77 xxx!OnDriverDispatch+0x52

f9efecc4 8056b9ab nt!IopfCallDriver+0x31

f9efecd8 8057a8be nt!IopSynchronousServiceTail+0x60

f9efed54 804e006b nt!NtFlushBuffersFile+0x1c1

f9efed54 7c92eb94 nt!KiFastCallEntry+0xf8

0012eef4 7c92d9d6 ntdll!KiFastSystemCallRet

奇怪的是,IoSynchronousPageWrite并没有进入我们的过滤驱动而是直接到了文件系统上。继续跟踪,发现IoSynchronousPageWrite-》

IoGetRelatedDeviceObject的时候,会有这么段代码:

 

if (deviceObject->AttachedDevice != NULL) {

        if (FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION) {

 

            PIOP_FILE_OBJECT_EXTENSION  fileObjectExtension =

                (PIOP_FILE_OBJECT_EXTENSION)(FileObject + 1);

 

            ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));

 

            if (fileObjectExtension->TopDeviceObjectHint != NULL &&

                IopVerifyDeviceObjectOnStack(deviceObject, fileObjectExtension->TopDeviceObjectHint)) {

               return fileObjectExtension->TopDeviceObjectHint;

            }

        }

        deviceObject = IoGetAttachedDevice( deviceObject );

    }

 

居然返回了TopDeviceObjectHint字段的值。这个值在哪填充,又是什么用途呢?跟踪一下,原来在IoCreateFileSpecifyDeviceObjectHint

-》ObOpenObjectByName的时候会openPacket->TopDeviceObjectHint = DeviceObject;原来这个字段表示需要“命中”的对象。

那么又有疑问了,系统中理论上只有我们的过滤驱动调用了IoCreateFileSpecifyDeviceObjectHint,但是我们调用IoCreateFileSpecifyDeviceObjectHint产生的文件对象,已经在ZwReadFile被ZwClose了呀,按理说应该会被销毁。

所以继续跟踪下去,发现果然如此,堆栈如下:

 

f70c9508 804e58f6 nt!ObfReferenceObject+0x28

f70c9548 f700de62 nt!CcInitializeCacheMap+0x1a2

f70c9614 f700869a Fastfat!FatCommonRead+0x561

f70c9684 804eedf9 Fastfat!FatFsdRead+0x13d

f70c9694 80574b42 nt!IopfCallDriver+0x31

f70c96a8 80571b98 nt!IopSynchronousServiceTail+0x60

f70c9750 8053d808 nt!NtReadFile+0x580

f70c9750 804ff0d1 nt!KiFastCallEntry+0xf8

f70c97ec f6ea188d nt!ZwReadFile+0x11

f70c9850 f6ebc09c xxx!IsPeFile+0x119

f70c9864 f6ea0177 xxx!MyFileCreate+0x99

f70c99d8 f6ea0d8f xxx!OnCreate+0x415

f70c99f8 f6e993a7 xxx!OnDispatch+0xb2

f70c9a14 804eedf9 xxx!OnDriverDispatch+0x4b

f70c9a24 805783bc nt!IopfCallDriver+0x31

f70c9b04 805787da nt!IopParseDevice+0xa58

f70c9b3c 805b420d nt!IopParseFile+0x46

f70c9bc4 805b0b3f nt!ObpLookupObjectName+0x119

f70c9c18 8056b133 nt!ObOpenObjectByName+0xeb

f70c9c94 8056baaa nt!IopCreateFile+0x407

也就是说CcInitializeCacheMap里会把这个IoCreateFileSpecifyDeviceObjectHint产生的文件对象缓存起来。由于调用了ObfReferenceObject,所以ZwClose的时候并没被销毁。

所以事情就明朗了,原来我们在过滤驱动的create回调里调用IoCreateFileSpecifyDeviceObjectHint产生的文件对象,会在

ZwReadFile的时候被CcInitializeCacheMap缓存起来,以后再有文件读写,都是走入了缓存管理器,而缓存管理器在真正需要读写磁盘的

时候,会使用第一次捕获并缓存的文件对象来发送IRP。而这个文件对象,由于我们是用IoCreateFileSpecifyDeviceObjectHint产生的,所以带有TopDeviceObjectHint字段,表示直接命中文件系统。所以缓存管理器绕过了我们的过滤驱动。

那么解决方案是什么呢?有几种方案。比如处理好fast io,这样无论什么write方式都会被捕获到。当然,还有种更简单的,就是在IoCreateFileSpecifyDeviceObjectHint的时候,对CreateOptions 加上FILE_NO_INTERMEDIATE_BUFFERING  参数。这个参数会让ZwReadFile组装irp的时候,在Irp->Flags里带上IRP_NOCACHE标志。而这个标志会让文件系统的FatCommonRead不调用CcInitializeCacheMap而是直接走FatNonCachedIo。所以也不会缓存我们的文件对象了。

不过加了FILE_NO_INTERMEDIATE_BUFFERING  参数会有些效率上的问题。不过对我来说足够了。修改驱动后放在虚拟机里测试,发现果然收到write消息了,今天一早上的功夫没白费~~

也许本文有分析的不对的地方,欢迎大家指正

posted @ 2012-06-08 23:41  kkindof  阅读(1197)  评论(0编辑  收藏  举报