65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Minifilter 驱动程序方法限制存储设备

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (5投票s)

2022年9月12日

CPOL

6分钟阅读

viewsIcon

8361

downloadIcon

251

完全可用的示例代码解释了创建 Minifilter 驱动程序以阻止通过接口连接的设备所需的一切。

引言

本文需要基本的 Windows 驱动程序开发和 C/C++ 知识。但是,对于没有 Windows 驱动程序开发经验的人来说,它也可能很有趣。本文介绍了如何开发一个简单的文件系统 Mini-filter 驱动程序。

演示驱动程序会阻止连接到系统的所有 USB 类型设备的写入操作。但是,用户可以修改驱动程序以阻止/允许设备连接到任何总线。

什么是文件系统 Mini-filter 驱动程序?

文件系统是用于访问文件的 I/O 操作的目标。Windows 支持多种文件系统。新技术文件系统 (NTFS) 是其原生文件系统。还存在其他类型的文件系统,例如物理文件系统 (UDFS, CDFS, FAT)、网络重定向器 (RDR, NWRDR, WebDAV) 和特殊文件系统 (Npfs, Msfs)。Minifilter 驱动程序会拦截发送在 I/O 管理器和文件系统驱动程序之间的 I/O 操作。

背景

Windows 提供了一个名为 Filter manager 的传统筛选器驱动程序,该驱动程序实现为一个传统筛选器驱动程序。每个 Mini-filter 都有自己的 Altitude,这决定了它在设备堆栈中的相对位置。Filter manager 接收 IRP,然后 Filter manager 按 Altitude 降序调用它正在管理的 mini-filter。Altitude 是一个无限精度的字符串,被解释为十进制数。数值 Altitude 较低的筛选器驱动程序会加载到 I/O 堆栈中,位于数值较高的筛选器驱动程序下方。

简单的 Mini-Filter 驱动程序

先决条件

在 Visual Studio 2019 中,找到“创建新项目”并找到要使用的模板“空 WDM 驱动程序”。您可以从 GitHub 下载 passthrough 示例,删除不相关的代码,然后开始编写。作为参考,请遵循附带的源代码。

DriverEntry

当驱动程序加载时,I/O 管理器会调用驱动程序的 DriverEntry 例程。在 NT 中,无论驱动程序将控制多少设备,都只会加载一个驱动程序实例。因此,DriverEntry 会被调用一次。它在 IRQL PASSIVE_LEVEL 和系统进程上下文中被调用。

extern "C"
NTSTATUS
DriverEntry (
    __in PDRIVER_OBJECT DriverObject,
    __in PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS status;

    UNREFERENCED_PARAMETER( RegistryPath );

    KdPrint(("PocMiniFilter!DriverEntry: Entered\n") );
    MiniFilterData.DriverObject = DriverObject;

    //
    //  Register with FltMgr to tell it our callback routines
    //

    status = FltRegisterFilter( DriverObject,
                                &FilterRegistration,
                                &MiniFilterData.Filter );

    FLT_ASSERT( NT_SUCCESS( status ) );

    if (NT_SUCCESS( status )) {

        //
        //  Start filtering i/o
        //

        status = FltStartFiltering( MiniFilterData.Filter );

        if (!NT_SUCCESS( status )) {

            FltUnregisterFilter( MiniFilterData.Filter );
        }
    }

    return status;
}

Mini-filter 驱动程序必须通过指定各种设置(例如它希望拦截的操作)来向 filter manager 注册为 mini-filter。驱动程序设置适当的结构,然后调用 FltRegisterFilter 例程进行注册。如果成功,驱动程序可以根据需要执行进一步的初始化,最后调用 FltStartFiltering 来开始过滤操作。

注意:驱动程序不需要独立设置分派例程,因为驱动程序不在 I/O 路径中;filter manager 才是。

FltRegisterFilter 具有以下原型

NTSTATUS
FltRegisterFilter (
    _In_ PDRIVER_OBJECT Driver,
    _In_ CONST FLT_REGISTRATION *Registration,
    _Outptr_ PFLT_FILTER *RetFilter
    );

必需的 FLT_REGISTRATION 结构提供了注册所需的所有信息。此结构中封装了大量信息。

ContextRegistration - 指向 FLT_CONTEXT_REGISTRATION 结构数组的可选指针。它引用了一些驱动程序定义的可以附加到文件系统实体(如文件和卷)的数据。

OperationRegistration - 最重要的字段。这是一个指向 FLT_OPERATION_REGISTRATION 结构数组的指针,每个结构都指定了感兴趣的操作以及驱动程序希望在其上被调用的 pre 和 post 回调。

InstanceCallback - 此回调允许在实例即将附加到新卷时通知驱动程序。驱动程序可以返回 STATUS_SUCCESS 以附加,或者返回 STATUS_FLT_DO_NOT_ATTACH 以表示驱动程序不知道应链接到此卷。

InstanceSetup 回调

每当在卷上创建新实例时,都会调用 InstanceSetup 例程。这使我们有机会决定是否需要附加到卷。如果注册结构中未定义此例程,则会持续创建自动实例。

本文主要实现过滤具有 USB 总线类型的外部媒体卷的阻止功能。在该例程中,首先使用 FltOpenVolume 获取给定 mini-filter 实例附加到的文件系统卷的句柄和文件对象指针。每次成功调用 FltOpenVolume 后必须调用 FltClose 进行匹配。如果在 VolumeFileObject 参数中返回了文件对象指针,则调用者必须在不再需要时通过调用 ObDereferenceObject 来释放它。FltOpenVolume 如果成功,将返回卷根目录的文件对象指针,该指针将在调用 FltDeviceIoControlFile 时用于 IOCTL_STORAGE_QUERY_PROPERTY 查询,以通过存储适配器获取当前设备的磁盘信息。

NTSTATUS
PocMiniFilterInstanceSetup(
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in FLT_INSTANCE_SETUP_FLAGS Flags,
    __in DEVICE_TYPE VolumeDeviceType,
    __in FLT_FILESYSTEM_TYPE VolumeFilesystemType )

{
    UNREFERENCED_PARAMETER(Flags);
    UNREFERENCED_PARAMETER(VolumeDeviceType);
    
    PAGED_CODE();

    NTSTATUS                    status = STATUS_UNSUCCESSFUL;
    HANDLE                      FsVolumeHandle {};
    PFILE_OBJECT                FsFileObject = NULL;
    STORAGE_PROPERTY_ID         PropertyId = StorageAdapterProperty;
    STORAGE_DESCRIPTOR_HEADER   HeaderDescriptor;
    PSTORAGE_PROPERTY_QUERY     Query = NULL, buffer = NULL, 
                                pQuery = NULL, OutputBuffer = NULL;
    ULONG                       RetLength, OutputLength, Sizeneeded, SizeRequired;
    PSTORAGE_ADAPTER_DESCRIPTOR pStorageDescriptor = NULL;

    if (VolumeFilesystemType != FLT_FSTYPE_NTFS) {
        KdPrint(("Unknown File system is not supported\n"));
        status = STATUS_FLT_DO_NOT_ATTACH;
        goto end;
    }
    else {
        KdPrint(("InstanceSetup Attched to  (Volume = %p, Instance = %p)\n", 
                  FltObjects->Volume,
                  FltObjects->Instance));

        // Involve an internal IOCTL to Disk filter and get the 
        // desired target disk information
        // If it is of interest, then attach to it 
        // and start filtering else do not attach.
        status = FltOpenVolume(FltObjects->Instance, &FsVolumeHandle, &FsFileObject);
        if (!NT_SUCCESS(status)) {
            KdPrint(("FltOpenVolume failed with status = 0x%x\n", status));
            goto end;
        }

        Sizeneeded = max (sizeof(STORAGE_DESCRIPTOR_HEADER), 
                          sizeof(STORAGE_PROPERTY_QUERY));
        OutputBuffer = (PSTORAGE_PROPERTY_QUERY) 
                        ExAllocatePoolWithTag(NonPagedPool, Sizeneeded, 'VedR');
        ASSERT(OutputBuffer != NULL);
        RtlZeroMemory(OutputBuffer, Sizeneeded);

        Query = (PSTORAGE_PROPERTY_QUERY)OutputBuffer;
        Query->PropertyId = PropertyId;
        Query->QueryType = PropertyStandardQuery;

        status = FltDeviceIoControlFile(FltObjects->Instance, 
                                        FsFileObject,
                                        IOCTL_STORAGE_QUERY_PROPERTY,
                                        Query,
                                        sizeof(STORAGE_PROPERTY_QUERY),
                                        &HeaderDescriptor,
                                        sizeof(STORAGE_DESCRIPTOR_HEADER),
                                        &RetLength);
        if (!NT_SUCCESS(status)) {
            KdPrint(("FltDeviceIoControlFile failed with status = 0x%x\n", status));
            goto end;
        }
        
        // Found the enough size now query property with updated size.
        OutputLength = HeaderDescriptor.Size;

        ASSERT(OutputLength >= sizeof(STORAGE_DESCRIPTOR_HEADER));

        SizeRequired = max(OutputLength, sizeof(STORAGE_PROPERTY_QUERY));
        buffer = (PSTORAGE_PROPERTY_QUERY)ExAllocatePoolWithTag
                 (NonPagedPool, SizeRequired, 'VedR');
        
        ASSERT(buffer != NULL);
        RtlZeroMemory(buffer, SizeRequired);

        pQuery = (PSTORAGE_PROPERTY_QUERY) buffer;
        pQuery->PropertyId = PropertyId;
        pQuery->QueryType = PropertyStandardQuery;

        status = FltDeviceIoControlFile(FltObjects->Instance,
                                        FsFileObject,
                                        IOCTL_STORAGE_QUERY_PROPERTY,
                                        pQuery, 
                                        sizeof(STORAGE_PROPERTY_QUERY),
                                        buffer, 
                                        OutputLength, 
                                        &RetLength);
        if (!NT_SUCCESS(status)) {
            KdPrint(("FltDeviceIoControlFile failed with status = 0x%x\n", status));
            goto end;
        }

        pStorageDescriptor = (PSTORAGE_ADAPTER_DESCRIPTOR)buffer;
        if (pStorageDescriptor->BusType == BusTypeUsb) {
            MiniFilterData.IsTargetDisk = TRUE;
            status = STATUS_FLT_DO_NOT_ATTACH;
        }
    }

end:
    if (OutputBuffer != NULL) {
        ExFreePoolWithTag(OutputBuffer, 'VedR');
        OutputBuffer = NULL;
    }

    if (buffer != NULL) {
        ExFreePoolWithTag(buffer, 'VedR');
        buffer = NULL;
    }

    if (FsVolumeHandle) {
        FltClose(FsVolumeHandle);
    }

    if (FsFileObject != NULL) {
        ObDereferenceObject(FsFileObject);
        FsFileObject = NULL;
    }

    return status;
}

驱动程序不会将任何实例附加到外部媒体 USB 设备。除此之外,还有一些与手动从实例分离相关的代码,可以在 teardown 回调中停止。最后,需要驱动程序卸载例程来注销驱动程序,这也可以由一个标志控制。

NTSTATUS
PocMiniFilterUnload (
    __in FLT_FILTER_UNLOAD_FLAGS Flags
    )

{
    UNREFERENCED_PARAMETER( Flags );

    PAGED_CODE();

    KdPrint(("PocMiniFilter!PtUnload: Entered\n") );

    FltUnregisterFilter( MiniFilterData.Filter );

    return STATUS_SUCCESS;
}

安装

安装文件系统 mini-filter 驱动程序的正确方法是使用 INF 文件。INF 文件用于安装基于硬件的设备驱动程序。但也可用于在 Windows 系统上安装任何驱动程序。对 INF 文件的完整处理超出了本文的范围。

;
; Copyright (c) Raahul
; PocMiniFilter.inf

[Version]
Signature   = "$Windows NT$"
Class       = "ActivityMonitor"                         ;This is determined by the work 
                                                        ;this filter driver does
ClassGuid   = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}    ;This value is determined by 
                                                        ;the Class
Provider    = %ProviderString%
DriverVer   = 06/16/2007,1.0.0.1
CatalogFile = PocMiniFilter.cat

[DestinationDirs]
DefaultDestDir          = 12
MiniFilter.DriverFiles  = 12            ;%windir%\system32\drivers

;;
;; Default install sections
;;

[DefaultInstall]
OptionDesc          = %ServiceDescription%
CopyFiles           = MiniFilter.DriverFiles

[DefaultInstall.Services]
AddService          = %ServiceName%,,MiniFilter.Service

;;
;; Default uninstall sections
;;

[DefaultUninstall]
DelFiles   = MiniFilter.DriverFiles

[DefaultUninstall.Services]
DelService = %ServiceName%,0x200      ;Ensure service is stopped before deleting

;
; Services Section
;

[MiniFilter.Service]
DisplayName      = %ServiceName%
Description      = %ServiceDescription%
ServiceBinary    = %12%\%DriverName%.sys        ;%windir%\system32\drivers\
Dependencies     = "FltMgr"
ServiceType      = 2                            ;SERVICE_FILE_SYSTEM_DRIVER
StartType        = 3                            ;SERVICE_DEMAND_START
ErrorControl     = 1                            ;SERVICE_ERROR_NORMAL
LoadOrderGroup   = "FSFilter Activity Monitor"
AddReg           = MiniFilter.AddRegistry

;
; Registry Modifications
;

[MiniFilter.AddRegistry]
HKR,,"SupportedFeatures",0x00010001,0x3
HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance%
HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude%
HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags%

;
; Copy Files
;

[MiniFilter.DriverFiles]
%DriverName%.sys

[SourceDisksFiles]
PocMiniFilter.sys = 1,,

[SourceDisksNames]
1 = %DiskId1%,,,

;;
;; String Section
;;

[Strings]
ProviderString          = "Raahul"
ServiceDescription      = "PocMiniFilter Mini-Filter Driver"
ServiceName             = "PocMiniFilter"
DriverName              = "PocMiniFilter"
DiskId1                 = "PocMiniFilter Device Installation Disk"

;Instances specific information.
DefaultInstance         = "PocMiniFilter Instance"
Instance1.Name          = "PocMiniFilter Instance"
Instance1.Altitude      = "370030"
Instance1.Flags         =  0x0              ; Allow all attachments 

让我们开始检查 INF 文件的关键方面。

[Version] 部分在 INF 中是强制性的。

signature 指令必须设置为“$Windows NT$”。callclassguid 指令是强制性的,它们指定了此驱动程序所属的类。类的完整列表可以存储在注册表中键“Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class”下,其中 GUID 唯一标识每个类型。

DefaultInstall 部分:指示在“运行”此 INF 时应执行的操作。默认安装部分也可用于安装文件系统 mini-filter 驱动程序。

有关如何修改 INF 的更多信息,请参阅 https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/creating-an-inf-file-for-a-file-system-driver

安装驱动程序

一旦 INF 文件被充分修改并且驱动程序代码被编译,它就可以安装了。最简单的安装方法是将驱动程序包(SYSINFCAT 文件)复制到目标系统,然后在文件资源管理器中右键单击 INF 文件并选择“安装”。这将运行 INF。

注意:如果在 INF 中,驱动程序的启动类型为 0,则重新启动系统,驱动程序将在启动时加载。对于此示例,驱动程序的启动类型为按需启动,因此无需重启。

此时,POC mini-filter 驱动程序已安装,可以使用 fltmc 命令行工具(使用管理员命令提示符)加载和运行。

C:\> fltmc load pocminifilter

一旦驱动程序加载到系统中,InstanceSetup 回调将识别设备总线类型并采取相应措施。

要从系统中卸载驱动程序,请使用以下命令

c:\> fltmc unload pocminifilter.sys

测试环境准备

您可以暂时禁用数字驱动程序签名检查以开始 Windows 驱动程序测试。在 Windows 11 中,您可以按以下方式执行此操作

  1. bcdedit /set nointegritychecks on
  2. bcdedit /set debug on
  3. bcdedit /set testsigning on
  4. 重新启动计算机,然后:
  5. 按住 Shift 键,然后在主 Windows 菜单中选择“重启”选项。
  6. 选择“疑难解答”->“高级选项”->“启动设置”->“重启
  7. 在“启动设置”中,按 F7 选择“禁用驱动程序签名强制”选项。

就这样 & RFC

就是这样!查看本文,并就文章中的任何内容提出问题或评论。我希望您觉得它很有价值。请随时指出我犯的错误。我将在未来版本中尝试纠正它们。

关注点

在我的一项项目中,任务是阻止某些媒体类型附加筛选器驱动程序实例。

历史

  • 2022 年 9 月 12 日:首次发布
© . All rights reserved.