NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath){ PFAST_IO_DISPATCH fastIoDispatch; UNICODE_STRING nameString; UNICODE_STRING path2K; UNICODE_STRING pathXP; WCHAR nameBuffer[MY_DEV_MAX_PATH] = { 0 }; UNICODE_STRING userNameString; WCHAR userNameBuffer[MY_DEV_MAX_NAME] = { 0 }; UNICODE_STRING syblnkString; WCHAR syblnkBuffer[MY_DEV_MAX_NAME] = { 0 }; UNICODE_STRING dosDevicePrefix; UNICODE_STRING dosDevice; WCHAR dosDeviceBuffer[MY_DEV_MAX_NAME] = { 0 }; NTSTATUS status; ULONG i; #if WINVER >= 0x0501 // Try to load the dynamic functions that may be available for our use. SfLoadDynamicFunctions(); // Now get the current OS version that we will use to determine what logic paths to take when this driver is built to run on various OS version. SfGetCurrentVersion(); #endif // Get Registry values SfReadDriverParameters(RegistryPath); // Save our Driver Object, set our UNLOAD routine gSFilterDriverObject = DriverObject; #if WINVER >= 0x0501 // MULTIVERSION NOTE: // We can only support unload for testing environments if we can enumerate the outstanding device objects that our driver has. // Unload is useful for development purposes. It is not recommended for production versions if (NULL != gSfDynamicFunctions.EnumerateDeviceObjectList) gSFilterDriverObject->DriverUnload = DriverUnload; #endif // Setup other global variables ExInitializeFastMutex(&gSfilterAttachLock); RtlInitEmptyUnicodeString(&nameString, nameBuffer, MY_DEV_MAX_PATH); RtlInitEmptyUnicodeString(&userNameString, userNameBuffer, MY_DEV_MAX_NAME); RtlInitEmptyUnicodeString(&syblnkString, syblnkBuffer, MY_DEV_MAX_NAME); RtlInitUnicodeString(&pathXP, L"\\FileSystem\\Filters\\"); RtlInitUnicodeString(&path2K, L"\\FileSystem\\"); RtlInitUnicodeString(&dosDevicePrefix, L"\\DosDevices\\"); RtlInitEmptyUnicodeString(&dosDevice, dosDeviceBuffer, MY_DEV_MAX_NAME); RtlCopyUnicodeString(&dosDevice, &dosDevicePrefix); status = OnSfilterDriverEntry(DriverObject, RegistryPath, &userNameString, &syblnkString, &gUserExtensionSize); if (!NT_SUCCESS(status)) return status; RtlCopyUnicodeString(&nameString, &pathXP); RtlAppendUnicodeStringToString(&nameString, &userNameString); // Create the Control Device Object (CDO). This object represents this driver. Note that it does not have a device extension. // 这是生成控制设备。 if (g_cdo_for_all_users) status = IoCreateDevice(DriverObject, 0, &nameString, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &gSFilterControlDeviceObject); //没有设备扩展 else { // 以下生成一个可以被任何用户打开读写的设备。但是这个guid是我手写固定的。不清楚如果在有多个基于本sfilter模块的驱动同时安装的时候,会不会导致出现guid重复的问题。 UNICODE_STRING sddlString; RtlInitUnicodeString(&sddlString, L"D:P(A;;GA;;;WD)"); status = IoCreateDeviceSecure(DriverObject, 0, &nameString, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &sddlString, (LPCGUID)&SFGUID_CLASS_MYCDO, &gSFilterControlDeviceObject); }; if (status == STATUS_OBJECT_PATH_NOT_FOUND) { //路径没找到 生成失败 RtlInitEmptyUnicodeString(&nameString, nameBuffer, MY_DEV_MAX_PATH); RtlCopyUnicodeString(&nameString, &path2K); RtlAppendUnicodeStringToString(&nameString, &userNameString); // 这是再次生成控制设备。 if (g_cdo_for_all_users) status = IoCreateDevice(DriverObject, 0, &nameString, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &gSFilterControlDeviceObject); //has no device extension else { // 以下生成一个可以被任何用户打开读写的设备。但是这个guid是我手写固定的。不清楚如果在有多个基于本sfilter模块的驱动同时安装的时候,会不会导致出现guid重复的问题。 UNICODE_STRING sddlString; RtlInitUnicodeString(&sddlString, L"D:P(A;;GA;;;WD)"); status = IoCreateDeviceSecure(DriverObject, 0, &nameString, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &sddlString, (LPCGUID)&SFGUID_CLASS_MYCDO, &gSFilterControlDeviceObject); }; if (!NT_SUCCESS(status)) { KdPrint(("SFilter!DriverEntry: Error creating control device object \"%wZ\", status=%08x\n", &nameString, status)); return status; }; } elseif (!NT_SUCCESS(status)) { KdPrint(("SFilter!DriverEntry: Error creating control device object \"%wZ\", status=%08x\n", &nameString, status)); return status; }; RtlAppendUnicodeStringToString(&dosDevice, &syblnkString); IoDeleteSymbolicLink(&dosDevice); status = IoCreateSymbolicLink(&dosDevice, &nameString); if (!NT_SUCCESS(status)) { KdPrint(("SFilter!DriverEntry: Error creating syblnk object \"%wZ\", status=%08x\n", &syblnkString, status)); IoDeleteDevice(DriverObject->DeviceObject); return status; }; // Initialize the driver object with this device driver's entry points. for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) DriverObject->MajorFunction[i] = SfPassThrough; // We will use SfCreate for all the create operations DriverObject->MajorFunction[IRP_MJ_CREATE] = SfCreate; DriverObject->MajorFunction[IRP_MJ_CREATE_NAMED_PIPE] = SfCreate; DriverObject->MajorFunction[IRP_MJ_CREATE_MAILSLOT] = SfCreate; DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = SfFsControl; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = SfCleanupClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = SfCleanupClose; // Allocate fast I/O data structure and fill it in. // NOTE: The following FastIo Routines are not supported: // AcquireFileForNtCreateSection // ReleaseFileForNtCreateSection // AcquireForModWrite // ReleaseForModWrite // AcquireForCcFlush // ReleaseForCcFlush // For historical reasons these FastIO's have never been sent to filtersby the NT I/O system. Instead, they are sent directly to the base file system. On Windows XP and later OS releases, you can use the new system routine "FsRtlRegisterFileSystemFilterCallbacks" if you need to intercept these callbacks (see below). fastIoDispatch = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_IO_DISPATCH), SFLT_POOL_TAG); if (!fastIoDispatch) { IoDeleteDevice(gSFilterControlDeviceObject); return STATUS_INSUFFICIENT_RESOURCES; }; RtlZeroMemory(fastIoDispatch, sizeof(FAST_IO_DISPATCH)); fastIoDispatch->SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH); fastIoDispatch->FastIoCheckIfPossible = SfFastIoCheckIfPossible; fastIoDispatch->FastIoRead = SfFastIoRead; fastIoDispatch->FastIoWrite = SfFastIoWrite; fastIoDispatch->FastIoQueryBasicInfo = SfFastIoQueryBasicInfo; fastIoDispatch->FastIoQueryStandardInfo = SfFastIoQueryStandardInfo; fastIoDispatch->FastIoLock = SfFastIoLock; fastIoDispatch->FastIoUnlockSingle = SfFastIoUnlockSingle; fastIoDispatch->FastIoUnlockAll = SfFastIoUnlockAll; fastIoDispatch->FastIoUnlockAllByKey = SfFastIoUnlockAllByKey; fastIoDispatch->FastIoDeviceControl = SfFastIoDeviceControl; fastIoDispatch->FastIoDetachDevice = SfFastIoDetachDevice; fastIoDispatch->FastIoQueryNetworkOpenInfo = SfFastIoQueryNetworkOpenInfo; fastIoDispatch->MdlRead = SfFastIoMdlRead; fastIoDispatch->MdlReadComplete = SfFastIoMdlReadComplete; fastIoDispatch->PrepareMdlWrite = SfFastIoPrepareMdlWrite; fastIoDispatch->MdlWriteComplete = SfFastIoMdlWriteComplete; fastIoDispatch->FastIoReadCompressed = SfFastIoReadCompressed; fastIoDispatch->FastIoWriteCompressed = SfFastIoWriteCompressed; fastIoDispatch->MdlReadCompleteCompressed = SfFastIoMdlReadCompleteCompressed; fastIoDispatch->MdlWriteCompleteCompressed = SfFastIoMdlWriteCompleteCompressed; fastIoDispatch->FastIoQueryOpen = SfFastIoQueryOpen; DriverObject->FastIoDispatch = fastIoDispatch; // VERSION NOTE: // There are 6 FastIO routines for which file system filters are bypassed as the requests are passed directly to the base file system. These 6 routines are AcquireFileForNtCreateSection, ReleaseFileForNtCreateSection, AcquireForModWrite, ReleaseForModWrite, AcquireForCcFlush, and ReleaseForCcFlush. // In Windows XP and later, the FsFilter callbacks were introduced to allow filters to safely hook these operations. See the IFS Kit documentation for more details on how these new interfaces work. // MULTIVERSION NOTE: // If built for Windows XP or later, this driver is built to run on multiple versions. When this is the case, we will test for the presence of FsFilter callbacks registration API. If we have it, then we will register for those callbacks, otherwise, we will not. #if WINVER >= 0x0501 { FS_FILTER_CALLBACKS fsFilterCallbacks; if (NULL != gSfDynamicFunctions.RegisterFileSystemFilterCallbacks) { // Setup the callbacks for the operations we receive through the FsFilter interface. // NOTE: You only need to register for those routines you really need to handle. SFilter is registering for all routines simply to give an example of how it is done. fsFilterCallbacks.SizeOfFsFilterCallbacks = sizeof(FS_FILTER_CALLBACKS); fsFilterCallbacks.PreAcquireForSectionSynchronization = SfPreFsFilterPassThrough; fsFilterCallbacks.PostAcquireForSectionSynchronization = SfPostFsFilterPassThrough; fsFilterCallbacks.PreReleaseForSectionSynchronization = SfPreFsFilterPassThrough; fsFilterCallbacks.PostReleaseForSectionSynchronization = SfPostFsFilterPassThrough; fsFilterCallbacks.PreAcquireForCcFlush = SfPreFsFilterPassThrough; fsFilterCallbacks.PostAcquireForCcFlush = SfPostFsFilterPassThrough; fsFilterCallbacks.PreReleaseForCcFlush = SfPreFsFilterPassThrough; fsFilterCallbacks.PostReleaseForCcFlush = SfPostFsFilterPassThrough; fsFilterCallbacks.PreAcquireForModifiedPageWriter = SfPreFsFilterPassThrough; fsFilterCallbacks.PostAcquireForModifiedPageWriter = SfPostFsFilterPassThrough; fsFilterCallbacks.PreReleaseForModifiedPageWriter = SfPreFsFilterPassThrough; fsFilterCallbacks.PostReleaseForModifiedPageWriter = SfPostFsFilterPassThrough; status = (gSfDynamicFunctions.RegisterFileSystemFilterCallbacks)(DriverObject, &fsFilterCallbacks); if (!NT_SUCCESS(status)) { DriverObject->FastIoDispatch = NULL; ExFreePool(fastIoDispatch); IoDeleteDevice(gSFilterControlDeviceObject); return status; }; }; }; #endif // The registered callback routine "SfFsNotification" will be called whenever a new file systems is loaded or when any file system is unloaded. // VERSION NOTE: // On Windows XP and later this will also enumerate all existing file systems (except the RAW file systems). On Windows 2000 this does notenumerate the file systems that were loaded before this filter was loaded. status = IoRegisterFsRegistrationChange(DriverObject, SfFsNotification); //注册一个文件系统变动回调函数 当文件系统被激活或卸载时使用 if (!NT_SUCCESS(status)) { KdPrint(("SFilter!DriverEntry: Error registering FS change notification, status=%08x\n", status)); DriverObject->FastIoDispatch = NULL; ExFreePool(fastIoDispatch); IoDeleteDevice(gSFilterControlDeviceObject); return status; }; // Attempt to attach to the appropriate RAW file system device objects since they are not enumerated by IoRegisterFsRegistrationChange. { PDEVICE_OBJECT rawDeviceObject; PFILE_OBJECT fileObject; // Attach to RawDisk device RtlInitUnicodeString(&nameString, L"\\Device\\RawDisk"); status = IoGetDeviceObjectPointer(&nameString, FILE_READ_ATTRIBUTES, &fileObject, &rawDeviceObject); if (NT_SUCCESS(status)) { SfFsNotification(rawDeviceObject, TRUE); ObDereferenceObject(fileObject); }; // Attach to the RawCdRom device RtlInitUnicodeString(&nameString, L"\\Device\\RawCdRom"); status = IoGetDeviceObjectPointer(&nameString, FILE_READ_ATTRIBUTES, &fileObject, &rawDeviceObject); if (NT_SUCCESS(status)) { SfFsNotification(rawDeviceObject, TRUE); ObDereferenceObject(fileObject); }; }; // Clear the initializing flag on the control device object since we have now successfully initialized everything. ClearFlag(gSFilterControlDeviceObject->Flags, DO_DEVICE_INITIALIZING); return STATUS_SUCCESS; };
BOOLEAN SfFastIoCheckIfPossible(IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN BOOLEAN Wait, IN ULONG LockKey, IN BOOLEAN CheckForReadOperation, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject){ PDEVICE_OBJECT nextDeviceObject; PFAST_IO_DISPATCH fastIoDispatch; PAGED_CODE(); // 如果是控制设备,不允许 if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) return FALSE; // 如果不是我的设备(影子设备可能发生这种情况) if (!IS_MY_DEVICE_OBJECT(DeviceObject)) return FALSE; if (DeviceObject->DeviceExtension) { ASSERT(IS_MY_DEVICE_OBJECT(DeviceObject)); // Pass through logic for this type of Fast I/O nextDeviceObject = ((PSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->AttachedToDeviceObject; //得到本驱动绑定的设备 ASSERT(nextDeviceObject); fastIoDispatch = nextDeviceObject->DriverObject->FastIoDispatch; //得到目标设备FastIo分发函数接口 if (VALID_FAST_IO_DISPATCH_HANDLER(fastIoDispatch, FastIoCheckIfPossible)) return (fastIoDispatch->FastIoCheckIfPossible)(FileObject, FileOffset, Length, Wait, LockKey, CheckForReadOperation, IoStatus, nextDeviceObject); //直接调用 }; return FALSE; };
VOID SfFsNotification(IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN FsActive){ UNICODE_STRING name; WCHAR nameBuffer[MAX_DEVNAME_LENGTH]; PAGED_CODE(); // Init local name buffer RtlInitEmptyUnicodeString(&name, nameBuffer, sizeof(nameBuffer)); SfGetObjectName(DeviceObject, &name); // Display the names of all the file system we are notified of SF_LOG_PRINT(SFDEBUG_DISPLAY_ATTACHMENT_NAMES, ("SFilter!SfFsNotification: %s %p \"%wZ\" (%s)\n", (FsActive) ? "Activating file system " : "Deactivating file system", DeviceObject, &name, GET_DEVICE_TYPE_NAME(DeviceObject->DeviceType))); // Handle attaching/detaching from the given file system. if (FsActive) SfAttachToFileSystemDevice(DeviceObject, &name); //绑定文件系统控制设备 else SfDetachFromFileSystemDevice(DeviceObject); //注销 解除绑定 return; };
文件系统识别器
只关心磁盘文件系统、光盘、网络文件系统:
1 2
// Macro to test for device types we want to attach to #define IS_DESIRED_DEVICE_TYPE(_type) (((_type) == FILE_DEVICE_DISK_FILE_SYSTEM) || ((_type) == FILE_DEVICE_CD_ROM_FILE_SYSTEM) || ((_type) == FILE_DEVICE_NETWORK_FILE_SYSTEM))
NTSTATUS SfAttachToFileSystemDevice(IN PDEVICE_OBJECT DeviceObject, IN PUNICODE_STRING DeviceName){ PDEVICE_OBJECT newDeviceObject; PSFILTER_DEVICE_EXTENSION devExt; UNICODE_STRING fsrecName; NTSTATUS status; UNICODE_STRING fsName; WCHAR tempNameBuffer[MAX_DEVNAME_LENGTH]; PAGED_CODE(); // See if this is a file system type we care about. If not, return. if (!IS_DESIRED_DEVICE_TYPE(DeviceObject->DeviceType)) //检查设备类型 return STATUS_SUCCESS; // always init NAME buffer RtlInitEmptyUnicodeString(&fsName, tempNameBuffer, sizeof(tempNameBuffer)); // See if we should attach to the standard file system recognizer device or not RtlInitUnicodeString(&fsrecName, L"\\FileSystem\\Fs_Rec"); SfGetObjectName(DeviceObject->DriverObject, &fsName); if (!FlagOn(SfDebug, SFDEBUG_ATTACH_TO_FSRECOGNIZER)) //根据我们是否要绑定识别器来决定是否跳过文件系统识别器 // See if this is one of the standard Microsoft file system recognizer devices (see if this device is in the FS_REC driver). If so skip it. We no longer attach to file system recognizer devices, we simply wait for the real file system driver to load. if (RtlCompareUnicodeString(&fsName, &fsrecName, TRUE) == 0) return STATUS_SUCCESS; //放弃绑定 直接返回成功即可 // We want to attach to this file system. Create a new device object we can attach with. status = IoCreateDevice(gSFilterDriverObject, sizeof(SFILTER_DEVICE_EXTENSION) + gUserExtensionSize, NULL, DeviceObject->DeviceType, 0, FALSE, &newDeviceObject); //生成新的设备 准备绑定目标设备 if (!NT_SUCCESS(status)) return status; if (!OnSfilterAttachPre(newDeviceObject, DeviceObject, &fsName, (PVOID)(((PSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->UserExtension))) //最后一节讲 询问用户是否要绑定 return STATUS_UNSUCCESSFUL; // Propagate flags from Device Object we are trying to attach to. Note that we do this before the actual attachment to make sure the flags are properly set once we are attached (since an IRP can come in immediately after attachment but before the flags would be set). 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); // Do the attachment devExt = newDeviceObject->DeviceExtension; status = SfAttachDeviceToDeviceStack(newDeviceObject, DeviceObject, &devExt->AttachedToDeviceObject); //绑定 if (!NT_SUCCESS(status)) goto ErrorCleanupDevice; // Set the name devExt->TypeFlag = SFLT_POOL_TAG; RtlInitEmptyUnicodeString(&devExt->DeviceName, devExt->DeviceNameBuffer, sizeof(devExt->DeviceNameBuffer)); //设备名字记录到设备扩展中 RtlCopyUnicodeString(&devExt->DeviceName, DeviceName); //Save Name // Mark we are done initializing ClearFlag(newDeviceObject->Flags, DO_DEVICE_INITIALIZING); // Display who we have attached to SF_LOG_PRINT(SFDEBUG_DISPLAY_ATTACHMENT_NAMES, ("SFilter!SfAttachToFileSystemDevice: Attaching to file system %p \"%wZ\" (%s)\n", DeviceObject, &devExt->DeviceName, GET_DEVICE_TYPE_NAME(newDeviceObject->DeviceType))); // VERSION NOTE: // In Windows XP, the IO Manager provided APIs to safely enumerate all the device objects for a given driver. This allows filters to attach to all mounted volumes for a given file system at some time after the volume has been mounted. There is no support for this functionality in Windows 2000. // MULTIVERSION NOTE: // If built for Windows XP or later, this driver is built to run on multiple versions. When this is the case, we will test for the presence of the new IO Manager routines that allow for volume enumeration. If they are not present, we will not enumerate the volumes when we attach to a new file system. #if WINVER >= 0x0501 if (IS_WINDOWSXP_OR_LATER()) { ASSERT(NULL != gSfDynamicFunctions.EnumerateDeviceObjectList && NULL != gSfDynamicFunctions.GetDiskDeviceObject && NULL != gSfDynamicFunctions.GetDeviceAttachmentBaseRef && NULL != gSfDynamicFunctions.GetLowerDeviceObject); // Enumerate all the mounted devices that currently exist for this file system and attach to them. status = SfEnumerateFileSystemVolumes(DeviceObject, &fsName); //枚举所有的卷 逐个绑定 if (!NT_SUCCESS(status)) { IoDetachDevice(devExt->AttachedToDeviceObject); goto ErrorCleanupDevice; }; }; #endif OnSfilterAttachPost(newDeviceObject, DeviceObject, devExt->AttachedToDeviceObject, (PVOID)(((PSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->UserExtension), STATUS_SUCCESS); //最后一节讲 绑定后处理回调函数 return STATUS_SUCCESS; // Cleanup error handling ErrorCleanupDevice: //错误处理 SfCleanupMountedDevice(newDeviceObject); IoDeleteDevice(newDeviceObject); OnSfilterAttachPost(newDeviceObject, DeviceObject, NULL, (PVOID)(((PSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->UserExtension), status); return status; };
FS-CDO绑定
FS-CDO绑定
sfilter过滤设备的设备扩展定义:
1 2 3 4 5 6 7 8 9
typedefstruct_SFILTER_DEVICE_EXTENSION { //文件过滤系统驱动的设备扩展 ULONG TypeFlag; PDEVICE_OBJECT AttachedToDeviceObject; //绑定的文件系统设备(真实设备) PDEVICE_OBJECT StorageStackDeviceObject; //与文件系统设备相关的真实设备(磁盘) 在绑定时用 UNICODE_STRING DeviceName; //如果绑定了一个卷 那么这是物理磁盘卷名 否则是绑定的CDO名 WCHAR DeviceNameBuffer[MAX_DEVNAME_LENGTH]; //保存名字的缓冲区 // The extension used by other user. UCHAR UserExtension[1]; } SFILTER_DEVICE_EXTENSION, * PSFILTER_DEVICE_EXTENSION;
为了让系统看起来过滤驱动和文件系统一样的话,有些标志位需要复制:
1 2 3 4 5 6
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);
NTSTATUS SfFsControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PAGED_CODE(); // Sfilter doesn't allow handles to its control device object to be created, therefore, no other operation should be able to come through. ASSERT(!IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); if (!IS_MY_DEVICE_OBJECT(DeviceObject)) { PVOID context = NULL; NTSTATUS status; SF_RET ret = OnSfilterIrpPre(DeviceObject, NULL, NULL, Irp, &status, &context); ASSERT(context == NULL); ASSERT(ret == SF_IRP_COMPLETED); return status; }; // Process the minor function code. switch (irpSp->MinorFunction) { case IRP_MN_MOUNT_VOLUME: returnSfFsControlMountVolume(DeviceObject, Irp); case IRP_MN_LOAD_FILE_SYSTEM: returnSfFsControlLoadFileSystem(DeviceObject, Irp); case IRP_MN_USER_FS_REQUEST: { switch (irpSp->Parameters.FileSystemControl.FsControlCode) { case FSCTL_DISMOUNT_VOLUME: { //事实证明 不知道为啥这个请求根本不会出现 所以随便写写就行了 PSFILTER_DEVICE_EXTENSION devExt = DeviceObject->DeviceExtension; SF_LOG_PRINT(SFDEBUG_DISPLAY_ATTACHMENT_NAMES, ("SFilter!SfFsControl: Dismounting volume %p \"%wZ\"\n", devExt->AttachedToDeviceObject, &devExt->DeviceName)); break; }; }; break; }; }; // Pass all other file system control requests through. IoSkipCurrentIrpStackLocation(Irp); returnIoCallDriver(((PSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp); };
NTSTATUS SfFsControlLoadFileSystem(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ PSFILTER_DEVICE_EXTENSION devExt = DeviceObject->DeviceExtension; NTSTATUS status; PFSCTRL_COMPLETION_CONTEXT completionContext; PAGED_CODE(); // This is a "load file system" request being sent to a file system recognizer device object. This IRP_MN code is only sent to file system recognizers. // NOTE: Since we no longer are attaching to the standard Microsoft file system recognizers we will normally never execute this code. However, there might be 3rd party file systems which have their own recognizer which may still trigger this IRP. SF_LOG_PRINT(SFDEBUG_DISPLAY_ATTACHMENT_NAMES, ("SFilter!SfFscontrolLoadFileSystem: Loading File System, Detaching from \"%wZ\"\n", &devExt->DeviceName)); // VERSION NOTE: // On Windows 2000, we cannot simply synchronize back to the dispatch routine to do our post-load filesystem processing. We need to do this work at passive level, so we will queue that work to a worker thread from the completion routine. // For Windows XP and later, we can safely synchronize back to the dispatch routine. The code below shows both methods. Admittedly, the code would be simplified if you chose to only use one method or the other, but you should be able to easily adapt this for your needs. #if WINVER >= 0x0501 if (IS_WINDOWSXP_OR_LATER()) { KEVENT waitEvent; KeInitializeEvent(&waitEvent, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, SfFsControlCompletion, &waitEvent, TRUE, TRUE, TRUE); //context parameter status = IoCallDriver(devExt->AttachedToDeviceObject, Irp); // Wait for the operation to complete if (STATUS_PENDING == status) { status = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL); ASSERT(STATUS_SUCCESS == status); }; // Verify the IoCompleteRequest was called ASSERT(KeReadStateEvent(&waitEvent) || !NT_SUCCESS(Irp->IoStatus.Status)); status = SfFsControlLoadFileSystemComplete(DeviceObject, Irp); } else { #endif // Set a completion routine so we can delete the device object when the load is complete. completionContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(FSCTRL_COMPLETION_CONTEXT), SFLT_POOL_TAG); if (completionContext == NULL) { // If we cannot allocate our completion context, we will just pass through the operation. If your filter must be present for data access to this volume, you should consider failing the operation if memory cannot be allocated here. IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(devExt->AttachedToDeviceObject, Irp); } else { ExInitializeWorkItem(&completionContext->WorkItem, SfFsControlLoadFileSystemCompleteWorker, completionContext); completionContext->DeviceObject = DeviceObject; completionContext->Irp = Irp; completionContext->NewDeviceObject = NULL; IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, SfFsControlCompletion, completionContext, TRUE, TRUE, TRUE); // Detach from the file system recognizer device object. IoDetachDevice(devExt->AttachedToDeviceObject); // Call the driver status = IoCallDriver(devExt->AttachedToDeviceObject, Irp); }; #if WINVER >= 0x0501 }; #endif return status; };
NTSTATUS SfFsControlMountVolume(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ PSFILTER_DEVICE_EXTENSION devExt = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PDEVICE_OBJECT newDeviceObject; PDEVICE_OBJECT storageStackDeviceObject; PSFILTER_DEVICE_EXTENSION newDevExt; NTSTATUS status; BOOLEAN isShadowCopyVolume; PFSCTRL_COMPLETION_CONTEXT completionContext; PAGED_CODE(); ASSERT(IS_MY_DEVICE_OBJECT(DeviceObject)); ASSERT(IS_DESIRED_DEVICE_TYPE(DeviceObject->DeviceType)); // Get the real device object (also known as the storage stack device object or the disk device object) pointed to by the vpb parameter because this vpb may be changed by the underlying file system. Both FAT and CDFS may change the VPB address if the volume being mounted is one they recognize from a previous mount. storageStackDeviceObject = irpSp->Parameters.MountVolume.Vpb->RealDevice; //保存下来RealDevice // Determine if this is a shadow copy volume. If so don't attach to it. // NOTE: There is no reason sfilter shouldn't attach to these volumes, this is simply a sample of how to not attach if you don't want to status = SfIsShadowCopyVolume(storageStackDeviceObject, &isShadowCopyVolume); //判断是否是卷影 if (NT_SUCCESS(status) && isShadowCopyVolume && !FlagOn(SfDebug, SFDEBUG_ATTACH_TO_SHADOW_COPIES)) { //不打算绑定卷影就跳过去 UNICODE_STRING shadowDeviceName; WCHAR shadowNameBuffer[MAX_DEVNAME_LENGTH]; // Get the name for the debug display RtlInitEmptyUnicodeString(&shadowDeviceName, shadowNameBuffer, sizeof(shadowNameBuffer)); SfGetObjectName(storageStackDeviceObject, &shadowDeviceName); SF_LOG_PRINT(SFDEBUG_DISPLAY_ATTACHMENT_NAMES, ("SFilter!SfFsControlMountVolume Not attaching to Volume %p \"%wZ\", shadow copy volume\n", storageStackDeviceObject, &shadowDeviceName)); // Go to the next driver IoSkipCurrentIrpStackLocation(Irp); returnIoCallDriver(devExt->AttachedToDeviceObject, Irp); }; // This is a mount request. Create a device object that can be attached to the file system's volume device object if this request is successful. We allocate this memory now since we can not return an error in the completion routine. // Since the device object we are going to attach to has not yet been created (it is created by the base file system) we are going to use the type of the file system control device object. We are assuming that the file system control device object will have the same type as the volume device objects associated with it. status = IoCreateDevice(gSFilterDriverObject, sizeof(SFILTER_DEVICE_EXTENSION) + gUserExtensionSize, NULL, DeviceObject->DeviceType, 0, FALSE, &newDeviceObject); //预先生成过滤设备 虽然没到绑定的时候 if (!NT_SUCCESS(status)) { // If we can not attach to the volume, then don't allow the volume to be mounted. KdPrint(("SFilter!SfFsControlMountVolume: Error creating volume device object, status=%08x\n", status)); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; }; // We need to save the RealDevice object pointed to by the vpb parameter because this vpb may be changed by the underlying file system. Both FAT and CDFS may change the VPB address if the volume being mounted is one they recognize from a previous mount. newDevExt = newDeviceObject->DeviceExtension; //填写设备扩展 RealDevice被保存在这里 newDevExt->StorageStackDeviceObject = storageStackDeviceObject; newDevExt->TypeFlag = SFLT_POOL_TAG; // Get the name of this device RtlInitEmptyUnicodeString(&newDevExt->DeviceName, newDevExt->DeviceNameBuffer, sizeof(newDevExt->DeviceNameBuffer)); SfGetObjectName(storageStackDeviceObject, &newDevExt->DeviceName); // VERSION NOTE: // On Windows 2000, we cannot simply synchronize back to the dispatch routine to do our post-mount processing. We need to do this work at passive level, so we will queue that work to a worker thread from the completion routine. // For Windows XP and later, we can safely synchronize back to the dispatch routine. The code below shows both methods. Admittedly, the code would be simplified if you chose to only use one method or the other, but you should be able to easily adapt this for your needs. #if WINVER >= 0x0501 if (IS_WINDOWSXP_OR_LATER()) { KEVENT waitEvent; KeInitializeEvent(&waitEvent, NotificationEvent, FALSE); //初始化事件 IoCopyCurrentIrpStackLocationToNext(Irp); //因为要等待完成 所以拷贝当前调用栈 IoSetCompletionRoutine(Irp, SfFsControlCompletion, &waitEvent, TRUE, TRUE, TRUE); //设置完成函数 waitEvent当作上下文传入 status = IoCallDriver(devExt->AttachedToDeviceObject, Irp); //发送IRP并等待事件完成 // Wait for the operation to complete if (STATUS_PENDING == status) { status = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL); ASSERT(STATUS_SUCCESS == status); }; // Verify the IoCompleteRequest was called ASSERT(KeReadStateEvent(&waitEvent) || !NT_SUCCESS(Irp->IoStatus.Status)); status = SfFsControlMountVolumeComplete(DeviceObject, Irp, newDeviceObject); //请求完成 调用函数绑定卷 } else { #endif // Initialize our completion routine completionContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(FSCTRL_COMPLETION_CONTEXT), SFLT_POOL_TAG); if (completionContext == NULL) { // If we cannot allocate our completion context, we will just pass through the operation. If your filter must be present for data access to this volume, you should consider failing the operation if memory cannot be allocated here. IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(devExt->AttachedToDeviceObject, Irp); } else { ExInitializeWorkItem(&completionContext->WorkItem, SfFsControlMountVolumeCompleteWorker, completionContext); //初始化工作任务 completionContext->DeviceObject = DeviceObject; //写入上下文 completionContext->Irp = Irp; completionContext->NewDeviceObject = newDeviceObject; IoCopyCurrentIrpStackLocationToNext(Irp); //拷贝调用栈 IoSetCompletionRoutine(Irp, SfFsControlCompletion, &completionContext->WorkItem, TRUE, TRUE, TRUE); //context parameter // Call the driver status = IoCallDriver(devExt->AttachedToDeviceObject, Irp); }; #if WINVER >= 0x0501 }; #endif return status; };
NTSTATUS SfFsControlCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context){ UNREFERENCED_PARAMETER(DeviceObject); UNREFERENCED_PARAMETER(Irp); ASSERT(IS_MY_DEVICE_OBJECT(DeviceObject)); ASSERT(Context != NULL); #if WINVER >= 0x0501 if (IS_WINDOWSXP_OR_LATER()) // On Windows XP or later, the context passed in will be an event to signal. KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE); else { #endif // For Windows 2000, if we are not at passive level, we should queue this work to a worker thread using the workitem that is in Context. if (KeGetCurrentIrql() > PASSIVE_LEVEL) //中断级别过高 工作任务放到DelayedWorkQueue队列中执行 // We are not at passive level, but we need to be to do our work, so queue off to the worker thread. ExQueueWorkItem((PWORK_QUEUE_ITEM)Context, DelayedWorkQueue); else { //否则直接执行 PWORK_QUEUE_ITEM workItem = Context; // We are already at passive level, so we will just call our worker routine directly. (workItem->WorkerRoutine)(workItem->Parameter); }; #if WINVER >= 0x0501 }; #endif return STATUS_MORE_PROCESSING_REQUIRED; };
NTSTATUS SfFsControlMountVolumeComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PDEVICE_OBJECT NewDeviceObject){ PVPB vpb; PSFILTER_DEVICE_EXTENSION newDevExt; PIO_STACK_LOCATION irpSp; PDEVICE_OBJECT attachedDeviceObject; NTSTATUS status; PAGED_CODE(); newDevExt = NewDeviceObject->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation(Irp); // Get the correct VPB from the real device object saved in our device extension. We do this because the VPB in the IRP stack may not be the correct VPB when we get here. The underlying file system may change VPBs if it detects a volume it has mounted previously. vpb = newDevExt->StorageStackDeviceObject->Vpb; //获取之前保存的VPB // Display a message when we detect that the VPB for the given device object has changed. if (vpb != irpSp->Parameters.MountVolume.Vpb) SF_LOG_PRINT(SFDEBUG_DISPLAY_ATTACHMENT_NAMES, ("SFilter!SfFsControlMountVolume: VPB in IRP stack changed %p IRPVPB=%p VPB=%p\n", vpb->DeviceObject, irpSp->Parameters.MountVolume.Vpb, vpb)); // See if the mount was successful. if (NT_SUCCESS(Irp->IoStatus.Status)) { // Acquire lock so we can atomically test if we area already attached and if not, then attach. This prevents a double attach race condition. ExAcquireFastMutex(&gSfilterAttachLock); //获得互斥体 判断是否绑定过一个卷设备 防止重复绑定 // The mount succeeded. If we are not already attached, attach to the device object. Note: one reason we could already be attached is if the underlying file system revived a previous mount. if (!SfIsAttachedToDevice(vpb->DeviceObject, &attachedDeviceObject)) { //判断是否绑定过了 // Attach to the new mounted volume. The file system device object that was just mounted is pointed to by the VPB. status = SfAttachToMountedDevice(vpb->DeviceObject, NewDeviceObject); //绑定 if (!NT_SUCCESS(status)) { // The attachment failed, cleanup. Since we are in the post-mount phase, we can not fail this operation. We simply won't be attached. The only reason this should ever happen at this point is if somebody already started dismounting the volume therefore not attaching should not be a problem. SfCleanupMountedDevice(NewDeviceObject); IoDeleteDevice(NewDeviceObject); }; ASSERT(NULL == attachedDeviceObject); } else { // We were already attached, handle it SF_LOG_PRINT(SFDEBUG_DISPLAY_ATTACHMENT_NAMES, ("SFilter!SfFsControlMountVolume Mount volume failure for %p \"%wZ\", already attached\n", ((PSFILTER_DEVICE_EXTENSION)attachedDeviceObject->DeviceExtension)->AttachedToDeviceObject, &newDevExt->DeviceName)); // Cleanup and delete the device object we created SfCleanupMountedDevice(NewDeviceObject); //已绑定过 放弃 IoDeleteDevice(NewDeviceObject); // Dereference the returned attached device object ObDereferenceObject(attachedDeviceObject); }; // Release the lock ExReleaseFastMutex(&gSfilterAttachLock); } else { // The mount request failed, handle it. SF_LOG_PRINT(SFDEBUG_DISPLAY_ATTACHMENT_NAMES, ("SFilter!SfFsControlMountVolume: Mount volume failure for %p \"%wZ\", status=%08x\n", DeviceObject, &newDevExt->DeviceName, Irp->IoStatus.Status)); // Cleanup and delete the device object we created SfCleanupMountedDevice(NewDeviceObject); IoDeleteDevice(NewDeviceObject); }; // Complete the request. // NOTE: We must save the status before completing because after completing the IRP we can not longer access it (it might be freed). status = Irp->IoStatus.Status; //完成请求 IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; };
NTSTATUS SfAttachToMountedDevice(IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT SFilterDeviceObject){ PSFILTER_DEVICE_EXTENSION newDevExt = SFilterDeviceObject->DeviceExtension; NTSTATUS status; ULONG i; PAGED_CODE(); ASSERT(IS_MY_DEVICE_OBJECT(SFilterDeviceObject)); #if WINVER >= 0x0501 ASSERT(!SfIsAttachedToDevice(DeviceObject, NULL)); #endif if (!OnSfilterAttachPre(SFilterDeviceObject, DeviceObject, NULL, (PVOID)(((PSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->UserExtension))) //最后一节讲 询问用户是否要绑定 return STATUS_UNSUCCESSFUL; // Propagate flags from Device Object we are trying to attach to. Note that we do this before the actual attachment to make sure the flags are properly set once we are attached (since an IRP can come in immediately after attachment but before the flags would be set). if (FlagOn(DeviceObject->Flags, DO_BUFFERED_IO)) //复制设备标记 SetFlag(SFilterDeviceObject->Flags, DO_BUFFERED_IO); if (FlagOn(DeviceObject->Flags, DO_DIRECT_IO)) SetFlag(SFilterDeviceObject->Flags, DO_DIRECT_IO); // It is possible for this attachment request to fail because this device object has not finished initializing. This can occur if this filter loaded just as this volume was being mounted. for (i = 0; i < 8; i++) { //反复8次尝试绑定 LARGE_INTEGER interval; // Attach our device object to the given device object The only reason this can fail is if someone is trying to dismount this volume while we are attaching to it. status = SfAttachDeviceToDeviceStack(SFilterDeviceObject, DeviceObject, &newDevExt->AttachedToDeviceObject); if (NT_SUCCESS(status)) { OnSfilterAttachPost(SFilterDeviceObject, DeviceObject, newDevExt->AttachedToDeviceObject, (PVOID)(((PSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->UserExtension), status); // Finished all initialization of the new device object, so clear the initializing flag now. This allows other filters to now attach to our device object. ClearFlag(SFilterDeviceObject->Flags, DO_DEVICE_INITIALIZING); // Display the name SF_LOG_PRINT(SFDEBUG_DISPLAY_ATTACHMENT_NAMES, ("SFilter!SfAttachToMountedDevice: Attaching to volume %p \"%wZ\"\n", newDevExt->AttachedToDeviceObject, &newDevExt->DeviceName)); return STATUS_SUCCESS; }; // Delay, giving the device object a chance to finish its initialization so we can try again interval.QuadPart = (500 * DELAY_ONE_MILLISECOND); //delay 1/2 second KeDelayExecutionThread(KernelMode, FALSE, &interval); //这个线程延迟500ms后再继续 }; OnSfilterAttachPost(SFilterDeviceObject, DeviceObject, NULL, (PVOID)(((PSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->UserExtension), status); //最后一节讲 绑定后处理回调函数 return status; };
NTSTATUS ObQueryNameString( IN PVOID Object, //文件对象 OUT POBJECT_NAME_INFORMATION ObjectNameInfo, //接收返回名字信息 IN ULONG Length, OUT PULONG ReturnLength );
extern NTSTATUS OnSfilterDriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, OUT PUNICODE_STRING userNameString, //控制设备的名字 OUT PUNICODE_STRING syblnkString, //控制设备符号链接名 OUT PULONG extensionSize //每个设备的设备扩展长度 );
这个回调函数的插入在上文最开始DriverEntry中实现了。
绑定回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14
extern BOOLEAN OnSfilterAttachPre( //每当有设备要被绑定时调用 用户返回TRUE表示同意绑定 反之不同意绑定 IN PDEVICE_OBJECT ourDevice, //已生成好的过滤设备 IN PDEVICE_OBJECT theDeviceToAttach, //要被绑定的真实设备 IN PUNICODE_STRING DeviceName, //设备名 IN PVOID extension //设备扩展指针 设备扩展大小在OnSfilterDriverEntry中决定 );
extern VOID OnSfilterAttachPost( //用户决定绑定 绑定完成后无论成功与否都调用这个函数 IN PDEVICE_OBJECT ourDevice, //参数都同上 IN PDEVICE_OBJECT theDeviceToAttach, IN PDEVICE_OBJECT theDeviceToAttached, IN PVOID extension, IN NTSTATUS status //STATUS_SUCCESS绑定成功 );
extern SF_RET OnSfilterIrpPre( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT NextObject, IN PVOID extension, IN PIRP Irp, OUT PNTSTATUS status, PVOID* context //上下文指针 会被传递到后处理函数中 );
请求后处理:
1 2 3 4 5 6 7 8
extern VOID OnSfilterIrpPost( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT NextObject, IN PVOID extension, IN PIRP Irp, IN NTSTATUS status, PVOID context );
NTSTATUS OnSfilterCDODispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ return STATUS_UNSUCCESSFUL; };
BOOLEAN OnSfilterAttachPre(IN PDEVICE_OBJECT ourDevice, IN PDEVICE_OBJECT theDeviceToAttach, IN PUNICODE_STRING DeviceName, IN PVOID extension){ // 直接返回TRUE,所有设备都绑定 return TRUE; };
VOID OnSfilterAttachPost(IN PDEVICE_OBJECT ourDevice, IN PDEVICE_OBJECT theDeviceToAttach, IN PDEVICE_OBJECT theDeviceToAttached, IN PVOID extension, IN NTSTATUS status){ // 不需要做什么。 return; };