⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wdf源代码1.txt

📁 文件系统驱动开发的文档资料(IFS DDK)
💻 TXT
字号:
文件过滤系统设计(1) (附全部源代码)

本文作者为上海楚狂人,有意见和建议者请联系(QQ16191935,MSN walled_river@hotmail.com)

文件过滤系统设计(1) 友好外部接口的lib

首先说明一下,此文所附的代码,也就是<<Windows文件系统过滤驱动开发教程>>的代码。阅读此文需要预先了解文件过滤驱动,最好读过<<Windows文件系统过滤驱动开发教程>>。我计划再发一系列文章总结这方面的最新积累的经验,当然我不敢取名“教程”,因为我没有资格教育任何人。

我不幸被卷入数个与文件过滤有关的项目中,以致为此耗费了数年的时光.我期望我的设计趋于理想,把我的编码工作引入美好的状态.

从理想的角度来说,文件过滤是与操作系统无关的.因为首先我做文件过滤的时候并未关心文件系统的类型.FAT32还是NTFS? 如果机器上有其他文件系统,我也打算过滤它.然后,操作系统并未与文件系统相关.Windows上也可以安装ext2(听闻已经有人开发了相关的驱动),linux上安装FAT32与NTFS(NTFS的驱动可能尚不完美)已经是事实.

提炼与操作系统无关的文件操作是一个比较复杂的接口设计工作.能碰到千奇百怪的困难.所以我认为,应该反过来入手,首先精简Windows的文件操作的接口,再在这个基础上追求操作系统无关化.当然你也可以认为操作系统无关是完全不可能的,这个目标是错误的.但是我已经精简了文件系统过滤操作的接口,使之后的编程工作变得简单.

我把这个项目称为FSFE(File System Filter Engine),这是一个非商业化的项目,所有代码都是本人利用工作业余时间编写.你可以下载这些代码并修改使用,但是不能用于商业开发中.

在Windows下研究文件过滤一般都从SFilter入手.SFilter可以看作一个文件过滤和Windows的文件系统之间的一个"适配器",但绝对不是你要开发的文件过滤系统本身.既然Windows的文件系统操作以IRP作为单元,并在dispatch functions中处理IRP,那么你的文件过滤只需要考虑过滤IRP就可以了.又何必去考虑哪些文件系统何时激活,又合适加载了卷,何时卷被卸载这些无关的问题呢?

我希望我的编码不需要考虑那些问题,而是直接如此编码(为了避免更多的板砖我使用尽量DDK原装的数据类型):

// 这是我的主函数
void fsfe_main()
{
  // 我为create irp的处理设置一个回调函数
  fsfe_set_create(my_create);

  // 我为close 和 clean up的irp处理设置一个回调函数
  fsfe_set_close_clean_up(my_close_clean_up);
}

以上是我打算过滤create和close的情况.create的过滤一般用来阻止某些文件被访问,或者"重新定向"对某些文件的访问(比如把文件操作映射到另外一个磁盘或目录中).

我相信计算机中所有的文件的create irp都会发到我这里.这时我就可以编写我的过滤了:

ULONG 
my_create(
  IN PDEVICE_OBJECT cur,     // 当前设备
  IN PDEVICE_OBJECT next,     // 下一个设备   
  IN PIRP irp,         // irp
  OUT NTSTATUS status,     // 如果irp被完成,状态返回到这里
  OUT PVOID *context)     // 可以为完成函数指定一个上下文
{
  // create传入的参数我并不一定都要使用.如果我要打印请求生成文件
  // 的路径,只要这样这就可以了:
  PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp); 
  PFILE_OBJECT file = irpsp->FileObject;

  if(file != NULL)
  {
    DbgPrint("CREATE %wZ\r\n", &file->FileName);
  }
    
  // 自己定义的一个常量.意义为"继续完成",对IRP我不干涉。
  return WDFF_CB_GO_ON;
}

  irpsp->FileObject->FileName只是一个请求路径。直接用这个路径用来做路径过滤(比如限制对某个目录下的文件访问)是不现实的。首先这个路径可能不是绝对路径,可能只是某个目录的相对路径,而且不含有盘符。更糟的是可能出现短名,如mypath~1之类,让你的过滤落空。后面我们专门研究如何做路径过滤。但是这个演示还是不错的。

  假设你手头有Sfilter的代码,那么你可以按如下的解释实现以上的功能.

  首先Sfilter将被编译成一个Lib.使用Lib使你或者以你的成果为基础的开发者不必再关心Sfilter的代码.

  以上带来的一个担心是调试BUG的问题.但是当然需要调试的时候,你会发现有代码的lib调试起来和你常用的全代码的情况一样的方便.

  令一个容易被误会的问题常常是lib的大小的问题.一般的lib编译出来(尤其是功能非常丰富的lib)编译出来都非常的大.其实连接一个lib绝对不等于把这个lib的全部大小放到你的驱动中.连接器只连接必要的部分.你未使用到的部分会被剔除.因此你会发现你连接的结果往往比lib本身要小很多.

  修改SFilter目录下的SOURCE文件.把

  TARGETTYPE=DRIVER
  
  修改为

  TARGETTYPE=LIBRARY

  然后我可以在SFilter.c中,DriverEntry的实现之前,加入

  extern void fsfe_main();   // 外部函数声明
    
  然后在DriverEntry中,合适的位置调用:

  NT_STATUS DriverEntry(...)
  {
    ...
    // 可以把fsfe_main设计成接受一些参数,但是我没有
    // 用这样的设计.
    fsfe_main( ... );

    ... // 继续初始化
  }
  
  这样你的代码中不必再有DriverEntry,但是必须有fsfe_main.当然你必须连接这个库.在你的SOURCE文件中,加上:

  TARGETLIBS = sfilter.lib   

  check版本和free版本需要不同的库.你可以像这样:

  !IF "$(DDKBUILDENV)"=="fre"
  TARGETLIBS = sfilter.lib
  !else
  TARGETLIBS = sfilterd.lib
  !ENDIF   

  以上是编译问题.下面考虑分发函数.实际上与fsfe_main的嵌入相同.但是为了这些函数不是固定名字,而是可以设置的,我必须在SFilter中定义一组函数指针:

  一个IRP处理的过滤函数类型:

  typedef LONG
  (*wdff_callback_pre_func)(
    IN PDEVICE_OBJECT cur,     // 当前设备
    IN PDEVICE_OBJECT next,     // 下一个设备   
    IN PIRP irp,         // irp
    OUT NTSTATUS status,     // 如果irp被完成,状态返回到这里
    OUT PVOID *context);
    
  一个IRP完成的过滤函数:

  typedef LONG 
  (*wdff_callback_post_func)(
            IN PDEVICE_OBJECT cur,
            IN PDEVICE_OBJECT dev,
            IN PIRP irp,
            IN PVOID context);   

  // 一个含有一组回调指针的结构,有点类似linux驱动开发的分发函数指针数组。
  typedef struct _wdff_callback
  {
    wdff_callback_pre_func read_write;
    wdff_callback_post_func read_write_comp;
    wdff_callback_pre_func create;
    wdff_callback_post_func create_comp;
    wdff_callback_pre_func close_clean_up;
    wdff_callback_post_func close_clean_up_comp;
    wdff_callback_pre_func device_io_ctrl;
    wdff_callback_post_func device_io_ctrl_comp;
    wdff_callback_pre_func other;
    wdff_callback_post_func other_comp;
  } wdff_callback;

  我在这中间做了一些简化的处理.比如常用的IRP被单独提出来作为函数指针.而"其他"的IRP处理则被集合到一个叫做other的处理函数中.

  现在在SFilter中定义一个如下的变量:

  wdff_callback g_callback;

  我很容易提供接口来设置它.我可以在库中导出一个函数:

  wd_void fsfe_set_create(wdff_callback_pre_func create)
  {
    g_callback.create = create;
  };
  
  其他的接口都是类似的.下面的问题,就是在SFilter中何时调用这个回调函数了.相信这个难不到对SFilter了解的诸位:

  NT_STATUS SfCreate(...)
  {
    ...
    if(g_callback.create != NULL)
    {
        (g_callback.create)( ...)
    }
  }

  但是具体到我给出的代码中,情况有一些不同.但是都是细节问题,有兴趣的读者可以自己研究.我提供的代码并不是SFilter修改而来,而是我自己编写的过滤框架。比SFilter的优点在于,提供了路径过滤,影设备读写,2000下动态加载卸载等一些新功能。

  所附代码有如下的说明:

  0.使用这些代码到你的计算机上,一切后果自负。

  1.编译必须安装DDK 3790或者以上的版本,用WNET编译。必须定义环境变量DDKROOT或者BASEDIR.

  2.得到代码后解压。用VC6.0或者VC7.0打开工作空间后点Build,Rebuild All或批构建可直接编译之。编译方法与编译应用程序同。不要试图用ddk或者ds提供的工具去编译它,那样会碰到一些麻烦。如果失败请检查环境变量。

  3.编译后得到的lib在lib目录下,主要有wdf.lib和fsfe.lib.需要的头文件在inc目录下。

  4.使用这些lib的例子在Sample目录下。这些目录下的工程也会在工作空间全部编译的时候被编译。得到的sys每一个都可以在2k-2003中所有的系统上动态加载和卸载。加载后打开DbgView察看输出即可。目前只有一个例子 create_filter.我会在后面的系列文章中编写更多的例子。create_filter.sys加载后会打印出系统当前打开的文件路径(带盘符)。

  5.如果出现了兰屏,死机或者其他意外,请不要过于愤怒。如果你能修改程序的bug,希望你发一份给我指出我的错误,我非常感谢。

  值得指出的是,有时DbgPrint似乎会导致蓝屏。在正式发布的商业软件中不应该包含DbgPrint调用。我目前包含了许多。这是为了作为演示。

  其他方面的问题将在后续的文章中继续说明。


描述:Wdf源代码

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -