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

发布时间:2014-10-23 23:30:30
来源:分享查询网

第一篇中,已经解读了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; }

返回顶部
查看电脑版