阅读:2021回复:0
sfilter的笔记及讲解
我们知道,要过滤文件系统上的某个卷设备对象,只需要在这个卷设备对象的设备对象
栈上绑定我们写的过滤设备对象,这样, 发送到该卷设备对象的所有请求就会先放到我们的过滤设备对象上。例如,比如我们要检测“C:”,这是一个卷设备对象的符号链接名,有了这个设备对象的符号连接名,我们就可以得到对应的卷设备对象。我们就可以调用 IoCreateDevice 创建一个过滤设备对象,并调用IoAttachDeviceToDeviceStackDevice 将 我们的过滤设备对象上绑定到卷设备对象上。 上边的方法是可行的,FileMon使用的就是这种方法。但是,这种方法的缺陷就是不能 处理动态加载卷。例如,插入一个 U 盘,动态形成一个“J”盘。这种情况下,就无法检测到这个 J 盘了。这时候我们就得动态监控卷了。 动态监控卷的挂载(VolumeMounting)概述: 一个新的存储介质被系统发现并在文件系统中创建一个新的卷 Volume 的过程被称为 Mounting。其过程是: 文件系统的控制设备对象(FS CDO)将得到一个 IRP,其主功能码(Major Function Code)为 IRP_MJ_FILE_SYSTEM_CONTRO。副功能码 (Minor Function Code)为 IRP_MN_MOUNT, 呵呵,仔细想一下,不要死板,如果我们创建一个设备对象,并将其绑定到文件系统控制设 备对象的设备对象栈上,那么我们的设备对象不就能接受到这个 IRP了吗!这样,我们就能时刻的知道新卷的产生了。解决了上面的问题。这就称为: 那么现在,我们需要着手解决一个问题就是: 1、创建一个设备对象 2、绑定文件系统控制对象的设备对象栈上。 OK,让我们进入正题吧。 绑定文件系统: 首先,我们需要知道当前系统中都有那些文件系统,例如:NTFS,FAT32,CDFS。因为, 卷设备对象是由文件系统创建的。 其次,我们要知道什么时候去绑定文件系统。当一个卷设备对象动态产生的时候,其对 应的文件系统就被激活。例如,如果一个FAT32 的U 盘被插入到电脑上,则对应的FAT32 文件系统就会被激活,并创建一个“J:”的卷设备对象。 IoRegisterFsRegistrationChange是一个非常有用的系统调用。 这个调用注册一个回调 函数,当系统中有文件系统被激活或者撤销时,该回调函数就被调用。Oh Good,那么我们 就可以在这个回调函数中去绑定文件系统的控制设备对象。 这里要注意:文件系统的加载和卷的挂载是两码事,卷的挂载是建立在文件系统被激活 的基础上。当一个文件系统被激活后,才能创建卷设备对象。 让我们开始写这个回调函数吧,SFILTER中为SfFsNotification。 代码实现: 先介绍所使用的几个例程: //有关绑定设备对象的例程 //动态绑定设备到设备对象栈外包例程 NTSTATUS SfAttachDeviceToDeviceStack(IN PDEVICE_OBJECTSourceDevice,IN PDEVICE_OBJECT TargetDevice,IN OUT PDEVICE_OBJECT *AttachedToDeviceObject); // 回调例程,当文件系统被激活或者撤销时调用 // 在该例程中,完成对文件系统控制设备对象的绑定. VOID SfFsNotification(IN PDEVICE_OBJECT DeviceObject,INBOOLEAN FsActive); // 该例程创建设备对象,并绑定到指定的文件系统控制设备对象的对象栈上 NTSTATUS SfAttachToFileSystemDevice(IN PDEVICE_OBJECTDeviceObject, IN PUNICODE_STRING DeviceName); SfFsNotification 是我们要注册的回调函数,它调用SfAttachToFileSystemDevice 完 成真正的设备绑定。当然,它还有其他功能,代码说明问题。 SfAttachToFileSystemDevice 创建过滤设备对象,并调用我们设备绑定外包函数: SfAttachDeviceToDeviceStack 来将过滤设备对象绑定到文件系统控制设备对象的设备栈 上。 这样,我们的过滤设备对象就能接受到发送到 FSCDO 的IRP_MJ_FILE_SYSTEM_CONTRO 的请求,动态监控卷的挂载。那么以后的工作就是完成对卷的监控绑定了。 //************************************************************************ //回调例程,当文件系统被激活或者撤销时调用 // 在该例程中,完成对文件系统控制设备对象的绑定. // 例程描述: // 这个例程在文件系统激活或者销毁的时被调用 // // 这个例程创建一个设备对象将它附加到指定的文件系统控制设备对象 // 的对象栈上,这就允许这个设备对象过滤所有发送给文件系统的请求. // 这样,我们就能获得一个挂载卷的请求,就可以附加到这个新的卷设备对象 // 的设备对象栈上 // // 参数: // // DeviceObject: 指向被激活或者撤销的文件系统的控制设备对象 // // FsActive: 激活或者撤销标志 // //************************************************************************ VOID SfFsNotification(IN PDEVICE_OBJECTDeviceObject, IN BOOLEAN FsActive) { //文件控制对象的设备名 UNICODE_STRING nameFSCDO; WCHAR nameBuffer[MAX_DEVNAME_LENGTH]; PAGE_CODE(); //关联并初始化nameFSCDO // RtlInitEmptyUnicodeString(&nameFSCDO,nameBuffer,sizeof(nameBuffer)); //获得FSCDO 的名字 // SfGetObjectName(DeviceObject,&nameFSCDO); //处理激活或者撤销请求 if (FsActive) { // 创建设备对象,绑定到文件系统控制设备对象的设备栈上 // SfAttachToFileSystemDevice(DeviceObject,&nameFSCDO); } else { // // 卸载,先暂且不考虑 // SfDetachFromFileSystemDevice(DeviceObject); } } //************************************************************************ // // 该例程创建设备对象,并绑定到指定的文件系统控制设备对象的对象栈上 // // 例程描述: // 将要附加到指定文件系统的控制设备对象的设备栈上,所以,我们知道 // 什么时候新卷被挂载(由文件系统创建) // // DeviceObject: 指向文件系统的控制设备对象 // // DeviceName: 控制设备对象名 // //************************************************************************ NTSTATUS SfAttachToFileSystemDevice(INPDEVICE_OBJECT DeviceObject, IN PUNICODE_STRING DeviceName) { PDEVICE_OBJECT newDeviceObject; // 我们创建的过滤设备对象 PSFILTER_DEVICE_EXTENSION devExt; // 设备对象扩展 NTSTATUS status; PAGE_CODE(); // // 如果不是文件系统类型,则返回 // if(IS_DESIRED_DEVICE_TYPE(DeviceObject->DeviceType)) { return STATUS_SUCCESS; } // // 创建我们的设备对象,用于附加到文件系统的控制设备对象 // 的设备对象栈上 // status =IoCreateDevice(gSFilterDriverObject, sizeof(SFILTER_DEVICE_EXTENSION), NULL, DeviceObject->DeviceType, 0, FALSE, &newDeviceObject); if (!NT_SUCCESS(status)) { return status; } // // 下面是复制文件系统控制设备对象的标志到我们新的设备对象上 // // 因为我们新的设备对象要附加到文件系统的设备栈上,所以,我们的 // 设备对象必须和文件系统的设备对象的标志一致. // // 这也是设备栈制定的规则吧 // if ( FlagOn( DeviceObject->Flags,DO_BUFFERED_IO )) { SetFlag( newDeviceObject->Flags,DO_BUFFERED_IO ); } if ( FlagOn( DeviceObject->Flags,DO_DIRECT_IO )) { SetFlag( newDeviceObject->Flags,DO_DIRECT_IO ); } if ( FlagOn(DeviceObject->Characteristics, FILE_DEVICE_SECURE_OPEN ) ) { SetFlag(newDeviceObject->Characteristics, FILE_DEVICE_SECURE_OPEN ); } // // 设备扩展 // devExt =newDeviceObject->DeviceExtension; // // 帮定我们的设备对象到文件系统的设备控制对象的设备栈上 // // 注意: 原先设备栈顶端的设备对象,附加后为我们设备对象的下一级设备对象 // 保存在我们设备对象的设备扩展中. // status =SfAttachDeviceToDeviceStack(newDeviceObject, DeviceObject, &devExt->AttachedToDeviceObject); if(!NT_SUCCESS(status)) { goto ErrorCleanupDevice; } // // 保存该文件系统控制设备对象 // RtlInitEmptyUnicodeString(&devExt->DeviceName, devExt->DeviceNameBuffer, sizeof(devExt->DeviceNameBuffer)); RtlCopyUnicodeString(&devExt->DeviceName,DeviceName); // // 清空设备对象的初始化标志 // ClearFlag( newDeviceObject->Flags,DO_DEVICE_INITIALIZING); return status; // // 错误: // ErrorCleanupDevice: IoDeleteDevice( newDeviceObject ); } 注册回调例程: 在 DriverEntry 初始化时,我们注册上面写的回调例程: // // 注册文件系统回调函数,当文件系统创建或者撤销时,该回调函数就会被调用 // status =IoRegisterFsRegistrationChange(DriverObject,SfFsNotification); 这样就完成了吗?oh,NO,NO,NO,上面的代码只是完成绑定文件系统的主要功能, 为了不 妨碍我分析的思路,我把琐碎的东西拿出来,放到下面去学习。 排除文件系统识别器: 文件系统识别器是文件系统驱动的一个很小的替身。 为了避免没有使用到的文件系统驱 动占据内核内存,windows不会加载这些大驱动,而代替为文件识别器。当新的物理存储媒 介进入系统,IO 管理器会依次尝试每个文件系统识别器对其进行识别。识别成功,立即加 载对应的文件系统驱动,对应的文件系统识别器会被卸载掉。SFILTER 不绑定文件系统识别 器,因为它最后会被卸载掉,我们绑定文件系统驱动就可以了。 这里需要注意:IoRegisterFsRegistrationChange注册的回调例程不仅仅是在对应的 文件系统被激活或者撤销时调用。对于 windows2000SP4 和 windowsXP,系统还对已经存在 的文件系统进行遍历时调用,所以,我们要排除掉上面所说的,存在于内存中的文件系统识 别器。 如何分辨文件系统识别器?根据驱动对象名,如果是微软的标准文件系统识别器都为: "\FileSystem\Fs_Rec",那么我们就可以根据对象名来判断是不是文件系统识别器了, 如果 是,则干掉他。当然,还有其他的第三方的文件系统识别器,我们在以后学习如何过滤掉 参考SFILTER 程序添加下列代码: // // 文件驱动名 // UNICODE_STRING fsName; WCHAR fsNameBuffer[MAX_DEVNAME_LENGTH]; UNICODE_STRING fsrecName; // 文件系统识别器名 ......... // // 关联初始化文件驱动名 // RtlInitEmptyUnicodeString(&fsName, fsNameBuffer, sizeof(fsNameBuffer)); // // 获得指定文件系统的驱动名 // SfGetObjectName(DeviceObject->DriverObject, &fsName); // // 初始化文件系统识别器名 // // 注意,这里是微软标准的文件识别器名 // 还有不标准的,在控制操作中过滤 // RtlInitUnicodeString(&fsrecName,L"\\FileSystem\\Fs_Rec"); // // 过滤掉微软标准文件识别器 // if (RtlCompareUnicodeString(&fsName,&fsrecName, TRUE) == 0) { return STATUS_SUCCESS; } 小结: 1、为我们的过滤驱动生成一个控制设备对象 2、设置 IRP 分发例程 3、设置 FAST IO 分发例程 4、编写SfFsNotification 回调例程。 |
|
|