读源码笔记--文件过滤驱动FileSpy第2篇 -- 绑定CDO

第一篇中,已经解读了DriverEntry这个函数,函数里面就是做了一些初始化工作,创建用于通信的设备,设置各种回调,注册文件系统变动回调等等。

今天接着看filespy.c中的SpyFsNotification函数。该函数是IoRegisterFsRegistrationChange注册的文件系统变动回调函数。先看这个的理由如下:

在没看任何代码前,我的思维是:首先绑定系统中的所有物理卷;其次过滤文件操作;再其次,卸载过滤驱动的时候,我解除绑定的物理卷。代码做的话,我会在DriverEntry里面直接枚举所有的物理卷设备,然后绑定他们。这只是我的思路,文件过滤驱动里木有我这样的思路,因为我这个思路是不懂的前提下设想的。

 

文件过滤驱动的一个理论:先绑定文件系统控制设备对象CDO,然后绑定卷设备对象。

为什么要这么做,第4篇中说。

 

然而很遗憾,filespy里面没有直接绑定CDO,而是调用IoRegisterFsRegistrationChange注册了一个文件系统变动回调函数。该函数会在文件系统(例如:NTFS,FAT32等)激活或在注销的时候调用该回调函数。在2K SP4和XP以后的OS,即使在过滤驱动加载之前,已经被激活的文件系统,依然会被重新激活一下。2K SP4以前的OS,已经加载过的文件系统是不会被重新激活的。

 

IoRegisterFsRegistrationChange注册的文件系统变动回调函数,会绑定或解除绑定文件卷设备(这里的卷设备,指的是文件系统为每个物理卷对应生成的文件系统卷设备,文件系统卷设备是在文件驱动中生成的。例如:NTFS是个文件系统,我的C盘,D盘是NTFS,E盘是FAT32。那个物理卷C盘,D盘,在文件系统NTFS中,有文件系统卷设备对之各个相对应)。

 

VOID
SpyFsNotification (__in PDEVICE_OBJECT DeviceObject, __in BOOLEAN FsActive)

{

//

// PNAME_CONTROL 结构体NAME_CONTROL指针,位于:namelookup.h中

//

PNAME_CONTROL devName;

//

// NLGetAndAllocateObjectName从参数2中开辟空间存放参数1对应的名称。

// NLGetAndAllocateObjectName函数中调用函数NLAllocateNameControl(namelookup.c中)从函数NLGetAndAllocateObjectName的参数

// 2中开辟内存空间,调用函数NLGetObjectName(namelookup.c中)来获取NLGetAndAllocateObjectName参数1DeviceObject

// 所对应的文件名,而这个DeviceObject的文件名是通过:ObQueryNameString来获得的。这里需要注意下:ObQueryNameString在文件

// 过滤驱动中,直接询问设备的名称是非常容易引起重入的(这是其他高手的文章里面提到的,没有深究)。为什么FileSpy要直接使用

// ObQueryNameString来直接询问呢?理由如下:文件系统控制设备CDO,是SpyFsNotification的第一个参数DeviceObject,

// 它是文件系统的控制设备对象,是底层设备对象。所以可以直接询问这个设备对象的名称。

//

//

// 这里说下重入的问题:这个问题不大懂,只知道一点概念,举例更可能明白。

// 文件过滤驱动中使用Zw系列函数来读写文件的时候,最终会发出一个IRP包。这个包有I/O管理器丢出来又会进入我们的文件过滤驱动中,

// 我们自己要读写文件,IRP结果又回到我们的过滤驱动中了。

//

devName = NLGetAndAllocateObjectName( DeviceObject, &gFileSpyNameBufferLookasideList );

if (devName == NULL) {

         SPY_LOG_PRINT( SPYDEBUG_DISPLAY_ATTACHMENT_NAMES,
                       ("FileSpy!SpyFsNotification:                   Not attaching to %p, insufficient resources.\n",
                        DeviceObject) );
        return;

     }


    SPY_LOG_PRINT( SPYDEBUG_DISPLAY_ATTACHMENT_NAMES,
                   ("FileSpy!SpyFsNotification:                   %s   %p \"%wZ\" (%s)\n",
                    (FsActive) ? "Activating file system  " : "Deactivating file system",
                    DeviceObject,
                    &devName->Name,
                    GET_DEVICE_TYPE_NAME(DeviceObject->DeviceType)) );

//

// 根据SpyFsNotification函数的第2个参数,确定是绑定文件系统控制设备对象CDO,还是解除绑定。

//

    if (FsActive) {

//

// 绑定文件系统控制设备对象CDO

//

        SpyAttachToFileSystemDevice( DeviceObject, devName );

    } else {

//

// 解除绑定文件系统控制设备对象CDO

//

        SpyDetachFromFileSystemDevice( DeviceObject );
    }

    //
    //  释放设备名称的空间,因为SpyAttachToFileSystemDevice已经将名称拷贝到了设备拓展里面。
    //

    NLFreeNameControl( devName, &gFileSpyNameBufferLookasideList );

}

 

 

 

 

 

 

 

上面已经看到绑定文件系统控制设备对象CDO,是在函数:SpyAttachToFileSystemDevice中进行的,那么现在就来读读这个函数,到底怎么绑定的文件系统控制设备对象CDO以及卷设备VDO的。

 

//
//  SpyAttachToFileSystemDevice绑定文件控制设备对象。

//

NTSTATUS
SpyAttachToFileSystemDevice (
    __in PDEVICE_OBJECT DeviceObject,
    __in PNAME_CONTROL DeviceName
    )

{

    PDEVICE_OBJECT filespyDeviceObject;
    PFILESPY_DEVICE_EXTENSION devExt;
    UNICODE_STRING fsrecName;
    NTSTATUS status;
    PNAME_CONTROL fsName;

    PAGED_CODE();

    //
    //  检测这个文件系统设备类型,是否是我们所要关心的。磁盘文件系统,CDROM文件系统和Network文件系统。

    //  #define IS_SUPPORTED_DEVICE_TYPE(_type)  \

    // (\

    // ((_type) == FILE_DEVICE_DISK_FILE_SYSTEM)   ||   \

    // ((_type) == FILE_DEVICE_CD_ROM_FILE_SYSTEM)   ||   \

    // ((_type) == FILE_DEVICE_NETWORK_FILE_SYSTEM)  \

    // )

    //

    if (!IS_SUPPORTED_DEVICE_TYPE(DeviceObject->DeviceType)) {

        return STATUS_SUCCESS;
    }

    //
    //  检测是否是微软的文件系统识别器设备(查看这个驱动的名称是否是FS_REC,因为Windows的标准文件系统识别器都是由驱动

   //  "\\FileSystem\\Fs_Rec"生成的)。如果是文件系统识别器,跳过,不需要绑定。我们需要绑定真正的文件系统。
   //

    RtlInitUnicodeString( &fsrecName, L"\\FileSystem\\Fs_Rec" );

 

    //
    //  这个函数前面已经提到过,就是从参数2开辟空间,询问参数1的名称,并通过返回值返回。
    //
    fsName = NLGetAndAllocateObjectName( DeviceObject->DriverObject,
                                         &gFileSpyNameBufferLookasideList );

    if (fsName == NULL) {

        //
        //  获取名称失败,不绑定。
        //

        SPY_LOG_PRINT( SPYDEBUG_ERROR,
                       ("FileSpy!SpyAttachToFileSystemDevice: Error retrieving name, may attach to FS recognizer, status=%08x\n",
                        STATUS_INSUFFICIENT_RESOURCES) );

    } else if (RtlCompareUnicodeString( &fsName->Name,
                                        &fsrecName, TRUE ) == 0) {

        //
        //  通过驱动名称的比较,是文件系统识别器,不绑定。

        //

        NLFreeNameControl( fsName, &gFileSpyNameBufferLookasideList );
        return STATUS_SUCCESS;
    }

    NLFreeNameControl( fsName, &gFileSpyNameBufferLookasideList );

    //
    //  是我们关心的文件系统,且不是微软的文件系统识别器的设备,创建一个设备绑定这个设备对象。
    //

    status = IoCreateDevice( gFileSpyDriverObject,
                             sizeof( FILESPY_DEVICE_EXTENSION ),
                             (PUNICODE_STRING) NULL,
                             DeviceObject->DeviceType,
                             0,
                             FALSE,
                             &filespyDeviceObject );

    if (!NT_SUCCESS( status )) {

        SPY_LOG_PRINT( SPYDEBUG_ERROR,
                       ("FileSpy!SpyAttachToFileSystemDevice: Error creating volume device object for \"%wZ\", status=%08x\n",
                        DeviceName ? &DeviceName->Name : &gEmptyUnicode,
                        status) );
        return status;
    }

//

// #define FlagOn(_F,_SF) ((_F) & (_SF))

// #define SetFlag(_F,_SF) ((_F) |= (_SF))

// 设置filespyDeviceObject->Flags为DO_BUFFERED_IO、DO_DIRECT_IO、DO_SUPPORTS_TRANSACTIONS

// FlagOn第2个参数,是3个值进行or操作获得3个操作的组合值,FlagOn再进行&,则与这3个操作组合值相等的保留下。

//

    SetFlag( filespyDeviceObject->Flags,
             FlagOn( DeviceObject->Flags,
                     (DO_BUFFERED_IO |
                      DO_DIRECT_IO |
                      DO_SUPPORTS_TRANSACTIONS) ));

    SetFlag( filespyDeviceObject->Characteristics,
             FlagOn( DeviceObject->Characteristics,
                     (FILE_DEVICE_SECURE_OPEN) ));

//

// 获得DeviceObject的设备拓展,初始化设备拓展里面的NLExtHeader头。函数:NLInitDeviceExtensionHeader(namelookup.c中)

// SpyInitDeviceNamingEnvironment(fspyCtx.c中)初始化设备拓展里面的CtxList和CtxLock,Flags成员

//

    devExt = filespyDeviceObject->DeviceExtension;

    NLInitDeviceExtensionHeader( &devExt->NLExtHeader,
                                 filespyDeviceObject,
                                 NULL );

    SpyInitDeviceNamingEnvironment( filespyDeviceObject );

    devExt->Flags = 0;

 

#if WINVER >= 0x0600
    InitializeListHead( &devExt->TxListHead );
    ExInitializeFastMutex( &devExt->TxListLock );
#endif

//

// 设置设备拓展成员NLExtHeader力的DeviceName,通过函数:NLAllocateAndCopyUnicodeString(namelookup.c中)

// 该函数作用:从非分页内存中,开辟内存,将参数2中的值拷入参数1。

//

    status = NLAllocateAndCopyUnicodeString( &devExt->NLExtHeader.DeviceName,
                                             &DeviceName->Name,
                                             FILESPY_DEVNAME_TAG );

    if (!NT_SUCCESS(status)) {

        goto ErrorCleanupDevice;
    }

//

// 绑定设备,函数:SpyAttachDeviceToDeviceStack(fspyLib.c中),参数1绑定到参数2,绑定函数返回的设备存储在参数3中。

// 这个函数就是调用DriverEntry.c里面动态获取的函数SpyLoadDynamicFunctions里面获取的函数地址。

//

    status = SpyAttachDeviceToDeviceStack( filespyDeviceObject,
                                           DeviceObject,
                                           &devExt->NLExtHeader.AttachedToDeviceObject );

    if (!NT_SUCCESS( status )) {

        SPY_LOG_PRINT( SPYDEBUG_ERROR,
                       ("FileSpy!SpyAttachToFileSystemDevice: Could not attach FileSpy to the filesystem control device object \"%wZ\".\n",
                        &DeviceName->Name) );

        goto ErrorCleanupDevice;
    }

 

//

// 网络设备对象的功能既是控制设备对象CDO,又是卷设备对象VDO,所有将网络文件系统的CDO设备也插入到被绑定的

// 设备链表里面,方便以后我们枚举它。

//

    if (FILE_DEVICE_NETWORK_FILE_SYSTEM == DeviceObject->DeviceType) {

        ExAcquireFastMutex( &gSpyDeviceExtensionListLock );
        InsertTailList( &gSpyDeviceExtensionList, &devExt->NextFileSpyDeviceLink );
        ExReleaseFastMutex( &gSpyDeviceExtensionListLock );

        SetFlag(devExt->Flags,ExtensionIsLinked);
    }

//

//  清除设备的初始化Flag

// #define ClearFlag(_F, _SF)  ((_F) &= ~(_SF))

//

    ClearFlag( filespyDeviceObject->Flags, DO_DEVICE_INITIALIZING );

//

//  显示我们绑定了哪个设备对象。 

//

    SPY_LOG_PRINT( SPYDEBUG_DISPLAY_ATTACHMENT_NAMES,
                   ("FileSpy!SpyAttachToFileSystemDevice:         Attaching to file system   %p \"%wZ\" (%s)\n",
                    DeviceObject,
                    &devExt->NLExtHeader.DeviceName,
                    GET_DEVICE_TYPE_NAME(filespyDeviceObject->DeviceType)) );

//

//  在Windows XP中,I/O管理器提供了API可以安全地枚举给定的驱动DriverObject下的所有设备对象DeviceObject。

//  在卷设备被挂载后的某个时间,允许过滤驱动绑定给定的文件系统下的所有的已经挂载了的卷设备,这个功能函数在2K不支持。

//

//  多OS版本编译选项。

//

#if WINVER >= 0x0501

    if (IS_WINDOWSXP_OR_LATER()) {

#       define FSDEnumErrorMsg "FileSpy!SpyAttachToFileSystemDevice: Error attaching to existing volumes for \"%wZ\", status=%08x\n"
        ASSERT( NULL != gSpyDynamicFunctions.EnumerateDeviceObjectList &&
                NULL != gSpyDynamicFunctions.GetStorageStackDeviceObject &&
                NULL != gSpyDynamicFunctions.GetDeviceAttachmentBaseRef &&
                NULL != gSpyDynamicFunctions.GetLowerDeviceObject );

//

//  函数SpyEnumerateFileSystemVolumes(fileLib.c中),枚举给定的文件系统下的当前存在的所有挂载了的设备,并且绑定他们,

//  这样做的目的,是因为过滤驱动可能随时被加载,但是加载过滤驱动的时候,文件系统已经挂载了卷设备。既是:让过滤驱动加

//  后,随时都能绑定已经存在或刚刚挂载上来的文件系统卷设备。

//

        status = SpyEnumerateFileSystemVolumes( DeviceObject );

        if (!NT_SUCCESS( status )) {

            SPY_LOG_PRINT( SPYDEBUG_ERROR,
                           (FSDEnumErrorMsg,
                            DeviceName ? &DeviceName->Name : &gEmptyUnicode,
                            status) );

            IoDetachDevice( devExt->NLExtHeader.AttachedToDeviceObject );
            goto ErrorCleanupDevice;
        }
    }

#endif

    return STATUS_SUCCESS;

    /
    //                  调用失败进行清除一些操作。

   // SpyCleanupMountedDevice(fileLib.c中),调用SpyCleanupDeviceNamingEnvironment和NLCleanupDeviceExtensionHeader

   // 释放空间,清除链表中的特定数据。主要的操作,就是在SpyAttachToFileSystemDevice中设置的数据,开辟的空间进行清理。

    /

ErrorCleanupDevice:

    SpyCleanupMountedDevice( filespyDeviceObject );

    IoDeleteDevice( filespyDeviceObject );

    return status;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值