Minifilter 拦截FileMapping IO事件

Posted zj510

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Minifilter 拦截FileMapping IO事件相关的知识,希望对你有一定的参考价值。

Minifilter 拦截FileMapping IO事件

IO类型

在Windows上一般常见的两种IO:

  1. 普通IO, 如通过ReadFile,WriteFile等Windows API进行读写
  2. FileMapping,通过CreateFile打开一个文件,然后通过CreateFileMapping创建FileMapping对象,通过MapViewOfFile映射,之后就像操作内存一样(memcpy, strcpy等等),这些数据的变更会被操作系统同步到对应的文件上。

Minifilter拦截

针对IO类型的(1),其实,绝大多数人都会在minifilter中拦截,因为很简单,只需要拦截IRP_MJ_WRITE/IRP_MJ_READ就可以了。如下。

     IRP_MJ_WRITE,
        0,
        PreWrite,
	PostWrite 

每当应用程序调用WriteFile的时候,这两个函数都会进来,然后里面可以做一些过滤,阻断,加密等等。
但是对于(2),FileMapping,可能会有一些迷惑。

FileMapping拦截

比如应用程序这样写文件:

void TestIOFileMapping() 
    HANDLE  hFile;

    hFile = CreateFile(L"testfilemapping.foo",
        GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
       // CREATE_ALWAYS,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile == INVALID_HANDLE_VALUE)
    
        return;
    

    HANDLE hMapping = CreateFileMapping(hFile,
        NULL,
        PAGE_READWRITE,
        0,
        MAX_FILESIZE,
        NULL);

    if (hMapping == NULL)
    
        CloseHandle(hFile);
        return;
    

    char* puchData = (char*)MapViewOfFile(hMapping,
        FILE_MAP_WRITE,
        0,
        0,
        0);

    if (puchData == NULL)
    
        CloseHandle(hMapping);
        CloseHandle(hFile);
        return;
    

    for (int i = 0; i < 10; i++)
    
        memset(puchData + i * 1024, 0x41 + i, 1024);
    

    UnmapViewOfFile(puchData);

//    FlushFileBuffers(hFile);
    CloseHandle(hMapping);
    CloseHandle(hFile);

那么很可能在不同的操作系统上会得到不同的结果。基本分这么几类:

  1. IRP_MJ_WRITE的对应函数(比如PreWrite),直接就被调用,然后进程号就是当前应用。
  2. PreWrite很快被调用,但是发现进程号是System进程的,也就是4号
  3. PreWrite过了好长一会才被调用,进程号是System进程的,也就是4号
  4. PreWrite一直都没有被调用

其实这里有个有趣的问题:
我们在上面的测试代码里面把FlushFileBuffers(hFile);给禁用了。

那么得到的结果就取决于操作系统了。
上面四种情况都有可能发生。对于第四种,其实,操作系统会在关机前把内容写入磁盘(只是你不容易看到)
针对FileMapping,当应用层调用了memset或者strcpy之类的操作内存后,这些内容变更什么时候反映到磁盘(不调用FlushFilebuffers),是由操作系统决定的。
但是有一点是肯定的,就是内容要写入磁盘,一定会经过IRP, 也就是PreWrite一定会被调用。只是时间点有操作系统决定

那么如果我们把 FlushFileBuffers(hFile);打开,那么基本就是第一种情况了,也就是IRP马上被调用,因为应用程序要立即把内容flush到磁盘。

其实,当我们编写FileMapping代码的时候,是需要调用FlushFileBuffers的,不然,假如突然断电,就有可能丢失内容(还没写入磁盘)。只是很多人会遗漏,然后大多数情况,也确实不会有问题。

结论

无论是ReadFile,WriteFile等常规IO手段,还是FileMapping,IRP事件一定有,只是FileMapping可能会有一点点迷惑,因为在没有调用FlushFileBuffers的时候,有时看到的情况不太一样。但是数据要写入磁盘,IRP一定有。

附:

 HANDLE hMapping = CreateFileMapping(hFile,
        NULL,
        PAGE_READWRITE,
        0,
        MAX_FILESIZE,
        NULL);

这个调用会触发一个IRP:

 IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION,
		 0,
		 AcquireForSectionSynchronization,
		 PostAcquireForSectionSynchronization 

这里有时也可以做一些事情,比如查看索要的权限等等。

那么在IRP_MJ_WRITE里面怎么获取当前IRP操作文件的文件名呢?这个对于WriteFile和FileMapping还有一些不一样,后面再讲吧。

以上是关于Minifilter 拦截FileMapping IO事件的主要内容,如果未能解决你的问题,请参考以下文章

如何在Minifilter驱动的IRP中获取操作文件路径?

Minifilter和IRP_MJ_DIRECTORY_CONTROL发布后操作

FileMapping写的内容什么时候会flush到磁盘?

FileMapping写的内容什么时候会flush到磁盘?

文件系统Minifilter驱动

使用 FileMapping 传递 _EXCEPTION_POINTERS * 时遇到问题