0x01 从文件读取,一瞥“缓存管理器与文件系统以及内存管理器”的关系
当某个文件第一次被NtReadFile系统调用读取时,NtReadFile函数会构造一个IRP下发到文件系统驱动FSD.
FSD查看该IRP是否可以被缓存,如果可以的话,会调用缓存管理器的CcInitializeCacheMap()函数建立缓存,它里面会调用内存管理器(VMM)的MmCreateSection函数建立一个Section Object,并把这个Section Object映射到内核空间,如果IRP是可以缓存的,则会调用CcCopyRead()函数进行从缓存中读取文件。
如果此文件还没有在内存中,则会产生页面错误,交给MmAccessFault()函数处理,它会调用IoPageRead()分配一个不缓存 的IRP,但是它会走FSD,不会调用缓存的函数,而是最终调用磁盘驱动进行真实的磁盘读写读入到内存。之后CcCopyRead()再不会产生错误了,会从缓存复制到用户Buffer中。
从xp源码看NtReadFile:
NtReadFile 中判断是否有缓存,如果文件已经被缓存,那么读写操作都通过被称为“快速IO”的高速机制来处理,而不需要“生成一个IRP”这样的开销:
如果没有缓存走文件系统,IopfCallDriver下发IRP到文件对象对应的设备所在的设备堆栈的最顶层设备对象:
最后IRP下发会调用到Ntfs的NtfsFsdRead()函数,该函数通过缓存管理器的CcInitializeCacheMap建立缓存
CcInitializeCacheMap内又会调用内存管理器(VMM)的MmCreateSection函数建立一个Section Object
创建SectionObject 后,缓存管理器调用CcCreateVacbArray创建VACB索引数组.
再之后文件系统驱动的NtfsFsdRead,调用CcCopyRead尝试从缓存中读取数据,如果如果缓存没有这个要读文件的页面,则产生页面异常。
0x02 拿到文件记录缓存数据的方法
获取缓存管理器所用的那个文件对象,就可以读到缓存管理器中的缓存了。
如果文件对象对应的文件是文件目录,那么可以读取到该目录对目录下文件的各个索引INDEX_ENTRY;如果是文件对象对应的文件是文件本身,那么可以读到文件数据的缓存;
要判断这两种缓存读取是否正确,只需要判断NTFS_NTC_SCB_INDEX 和 NTFS_NTC_SCB_DATA标志即可。