最近几天学习了下文件过滤驱动, 这方面的资料确实很少, 国内也就是楚狂人教程比较详细.
这篇文章是我这几天学习的总结, 有些内容楚狂人教程中没有提到, 特此贴出来供大家一起学习,
毕竟是初学文件过滤驱动, 错误之处难免, 还请多多见谅.

ps:1.第一次写文章, 如果斑竹大大觉得小弟的文章对大家有点帮助, 那么, 你懂的, 嘿嘿.
    2.文档中的代码可以在我的另一个帖子中下载到.http://bbs.pediy.com/showthread.php?p=958810


下面进入正题
1.DriverEntry例程
(1)创建过滤驱动的控制设备, 以后我们的IO控制码就是发到这个设备上面

//这里的设备名与普通设备有所不同.
//当然, 最简单的可以直接写成 L"\\Device\\Filemontor";(FileMon中是这么写的, 调试过可行)
RtlInitUnicodeString( &nameString, L"\\FileSystem\\Filters\\FileMonitor" );
status = IoCreateDevice( DriverObject,
             0,                     //has no device extension  
                        //这是与其他Attach到别的设备上的设备的不同之处 
             &nameString,
             FILE_DEVICE_DISK_FILE_SYSTEM,
             FILE_DEVICE_SECURE_OPEN,
             FALSE,
             &gSFilterControlDeviceObject );

if (status == STATUS_OBJECT_PATH_NOT_FOUND) {

  RtlInitUnicodeString( &nameString, L"\\FileSystem\\FileMonitor" );
  status = IoCreateDevice( DriverObject,
               0,                      
               &nameString,
               FILE_DEVICE_DISK_FILE_SYSTEM,
               FILE_DEVICE_SECURE_OPEN,
               FALSE,
               &gSFilterControlDeviceObject );


//创建符号链接
RtlInitUnicodeString(&syblnkString, L"\\DosDevices\\FileMonitor");
status = IoCreateSymbolicLink( &syblnkString, &nameString );

if (!NT_SUCCESS(status)) {

  IoDeleteSymbolicLink( &syblnkString );
  status = IoCreateSymbolicLink( &syblnkString, &nameString );
  
  if (!NT_SUCCESS(status)) {

    KdPrint(("创建符号链接失败~\n"));
    IoDeleteDevice(gSFilterControlDeviceObject);
    return status;
  }
}

(2)设置例程
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {

  DriverObject->MajorFunction[i] = SfDispatch;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] = SfCreate;

DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = SfFsControl;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SfCleanupClose;

(3)调用IoRegisterFsRegistrationChange函数来通知我们文件系统的加载和卷的mount.


2.SfCreate 例程
(1)sfilter的原版中是这么写的
if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {

  Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
  Irp->IoStatus.Information = 0;
  IoCompleteRequest( Irp, IO_NO_INCREMENT );
  return STATUS_INVALID_DEVICE_REQUEST;
}

这样写的后果是我们用CreateFile函数在R3下打开此控制设备符号链接的时候失败
我刚开始学习文件过滤驱动的时候对此不是很了解, CreateFile老是失败, 
起初还以为是符号链接名写错了, 后来参看了FileMon的代码才反应过来

于是修改如下:
if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {
    
  Irp->IoStatus.Status = STATUS_SUCCESS;
  Irp->IoStatus.Information = FILE_OPENED;
  IoCompleteRequest( Irp, IO_NO_INCREMENT );
  return STATUS_SUCCESS;
}

(2)根据不同软件的需要, 编写此函数的接下来部分
[1]比如我们要阻止病毒在Windows目录下创建文件, 那么我们就要在此文件还没创建的时候得到此文件的全路径.
要在这个时候得到文件的路径, 楚狂人也说了有点麻烦. 下面提供一个函数给大家, 用于在文件创建前得到路径.
BOOLEAN MzfGetFileFullPathPreCreate(PFILE_OBJECT pFile, PUNICODE_STRING path )
{
   NTSTATUS status;
   POBJECT_NAME_INFORMATION pObjName = NULL;
   WCHAR buf[256] = {0};
   void *obj_ptr = NULL;
   ULONG ulRet = 0;
   BOOLEAN bSplit = FALSE;

   if (pFile == NULL) return FALSE;
   if (pFile->FileName.Buffer == NULL) return FALSE;

   pObjName = (POBJECT_NAME_INFORMATION)buf;

   if (pFile->RelatedFileObject != NULL)
      obj_ptr = (void *)pFile->RelatedFileObject;
   else
      obj_ptr = (void *)pFile->DeviceObject;

   status = ObQueryNameString(obj_ptr, pObjName, 256*sizeof(WCHAR), &ulRet);
   if (status == STATUS_INFO_LENGTH_MISMATCH)
   {
      pObjName = (POBJECT_NAME_INFORMATION)ExAllocatePool(NonPagedPool, ulRet);

      if (pObjName == NULL)  return FALSE;

      RtlZeroMemory(pObjName, ulRet);

      status = ObQueryNameString(obj_ptr, pObjName, ulRet, &ulRet);
      if (!NT_SUCCESS(status)) return FALSE;
   }

 //拼接的时候, 判断是否需要加 '\\'
 if (pFile->FileName.Length > 2 && 
  pFile->FileName.Buffer[0] != L'\\' &&
  pObjName->Name.Buffer[pObjName->Name.Length/sizeof(WCHAR) -1] != L'\\')
    bSplit = TRUE;
 
 ulRet = pObjName->Name.Length + pFile->FileName.Length;

 if (path->MaximumLength < ulRet)  return FALSE;

 RtlCopyUnicodeString(path, &pObjName->Name);
 if (bSplit)
   RtlAppendUnicodeToString(path, L"\\");

 RtlAppendUnicodeStringToString(path, &pFile->FileName);

 if ((void*)pObjName != (void*)buf)
    ExFreePool(pObjName);

 return TRUE;
}

至此, 得到了文件的路径以后, 我们就可以做出判断了, 如果要阻止文件创建, 
那么直接用IoCompleteRequest函数结束此IRP即可, 否则下发
IoSkipCurrentIrpStackLocation( Irp );
return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp );

[2]要是像FileMon那样只是记录系统中创建了哪些文件的话, 我们可以设置此函数的完成例程.
然后等文件创建完成了之后, 只要调用 IoQueryFileDosDeviceName 函数即可知道文件的全路径了.

设置完成例程如下:
{
  KEVENT waitEvent;
  KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );

  IoCopyCurrentIrpStackLocationToNext( Irp );
  IoSetCompletionRoutine(Irp, SfCreateCompletion, &waitEvent, TRUE, TRUE, TRUE );
  status = IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp );

  if (STATUS_PENDING == status) {

    NTSTATUS localStatus = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL);
    ASSERT(STATUS_SUCCESS == localStatus);
  }
  //此处文件已经创建完成了, 我们可以调用IoQueryFileDosDeviceName函数得到文件的全路径

  //最后结束此IRP
  status = Irp->IoStatus.Status;
  IoCompleteRequest( Irp, IO_NO_INCREMENT );
  return status;
}

3.SfDispatch例程
在此例程中要判断是不是我们的控制设备, 如果使我们的控制设备, 则要处理相应的IO控制码.
否则, 下发此IRP
NTSTATUS SfDispatch ( IN PDEVICE_OBJECT DeviceObject,  IN PIRP Irp )
{
  NTSTATUS status = STATUS_SUCCESS;
    PIO_STACK_LOCATION irpStack;
    
    if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {

        Irp->IoStatus.Information = 0;
        irpStack = IoGetCurrentIrpStackLocation( Irp );
    
        switch (irpStack->MajorFunction) {
      
    case IRP_MJ_DEVICE_CONTROL:
      // 此函数用来执行相应的控制码
      status = SpyCommonDeviceIoControl( Irp->AssociatedIrp.SystemBuffer,
                                                   irpStack->Parameters.DeviceIoControl.InputBufferLength,
                                                   Irp->AssociatedIrp.SystemBuffer,
                                                   irpStack->Parameters.DeviceIoControl.OutputBufferLength,
                                                   irpStack->Parameters.DeviceIoControl.IoControlCode,
                                                   &Irp->IoStatus );
      break;
      
    case IRP_MJ_CLEANUP
      status = STATUS_SUCCESS;
      break;
      
    default:
      status = STATUS_INVALID_DEVICE_REQUEST;
        }
    
        Irp->IoStatus.Status = status;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
        return status;
    }
  
  //不是我们的控制设备则下发此IRP
    return SfPassThrough( DeviceObject, Irp );
}


IO控制码也可以在FASTIO例程的SfFastIoDeviceControl函数中处理,如下:
当然, 最简单的还是在FASTIO例程中返回FALSE. 这样, 系统便会调用我们上面的SfDispatch函数.
if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {
    
        SpyCommonDeviceIoControl( InputBuffer,
      InputBufferLength,
      OutputBuffer,
      OutputBufferLength,
      IoControlCode,
      IoStatus );
    
        return TRUE;
}