返回
顶部

references:

windows提供了分层的驱动模型,过滤驱动位于IO请求和真正的负责处理该IO请求的驱动之间,可以捕获到IRP,从中获取必要的信息并进行必要的操作之后将IRP转发给真正负责处理的驱动

代码编写

main.c

所有的驱动入口函数都是DriverEntry

我们需要将DriverObject作为全局变量存储起来

PDRIVER_OBJECT g_fsFilterDriverObject =  NULL;

extern "C"
NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
    (RegistryPath);
    NTSTATUS status = STATUS_SUCCESS;
    return status;
}

下面我们需要填充IRP派遣表

NTSTATUS FsFilterDispatchCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    NTSTATUS status = STATUS_SUCCESS;
    return status;
}

NTSTATUS FsFilterDispatchPassThrough(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    NTSTATUS status = STATUS_SUCCESS;
    return status;
}

extern "C"
NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
    (RegistryPath);
    NTSTATUS status = STATUS_SUCCESS;
    for (int i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
        DriverObject->MajorFunction[i] = FsFilterDispatchPassThrough;
    }
    DriverObject->MajorFunction[IRP_MJ_CREATE] = FsFilterDispatchCreate;
    return status;   
}

设置快速IO派遣表

对于普通驱动,是不需要这一步的,但是对于过滤驱动,需要额外初始化一个被称为fast IO dispatch table的东西

FAST_IO_DISPATCH g_fastIoDispatch = {
    sizeof(FAST_IO_DISPATCH),
    FsFilterFastCheckIfPossible,
    ...
}; // 这个结构体的字段超级多

extern "C"
NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
    (RegistryPath);
    NTSTATUS status = STATUS_SUCCESS;

    DriverObject->FastIoDispatch = &g_fastIoDispatch;
    return status;
}

注册关于文件系统变更的通知回调

我们需要这个通知来在发现新的文件系统时挂载到上面,从而进行IO的过滤操作

// 注册文件系统通知回调
status = IoRegisterFsRegistrationChange(DriverObject, FsFilterNotificationCallback);
if (!NT_SUCCESS(status)) {
    return status;
}

设置卸载例程

 DriverObject->DriverUnload = FsFilterUnload;

驱动卸载例程的实现

void FsFilterUnload(_In_ PDRIVER_OBJECT DriverObject) {
    // 注销通知回调
    IoUnregisterFsRegistrationChange(DriverObject, FsFilterNotificationCallback);
}

然后我们需要清除并deattach所有之前创建并attach到文件系统上的设备

void FsFilterUnload(_In_ PDRIVER_OBJECT DriverObject) {
    // 注销通知回调
    IoUnregisterFsRegistrationChange(DriverObject, FsFilterNotificationCallback);
    ULONG numDevices = 0;
    while (1) {
        IoEnumerateDeviceObjectList(
            DriverObject,
            devList,
            sizeof(devList),
            &numDevices
        );
        if (0 == numDevices) 
            break;
        numDevices = min(numDevices, RTL_NUMBER_OF(devList));
        for (int i = 0; i < numDevices; i++) {
            FsFilterDetachFromDevice(devList[i]);
            ObDereferenceObject(devList[i]);
        }

        // 睡眠一段时间等待所有的IRP完成
        KeDelayExecutionThread(KernelMode, FALSE, &interval);
    }
}

IrpDispatch.c

我们的过滤驱动的IRP handler只有一个任务,就是把请求传递给更底层的驱动,下一层驱动存储在device extension中

NTSTATUS FsFilterDispatchPassThrough(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 
    PFSFILTER_DEVICE_EXTENSION pDevExt = (PFSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
    IoSkipCurrentIrpStackLocation(Irp);
    return IoCallDriver(pDevExt->AttachedToDeviceObject, Irp); 
}

来自用户模式的所有CreateFile的操作都会触发我们的IrpCreate函数的调用

我们可以从FILE_OBJECT中获取到文件名,然后打印到Dbg信息中,之后我们再调用PassThrough将IRP传递下去

NTSTATUS FsFilterDispatchCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) {

    PFILE_OBJECT pFileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
    DbgPrint("%wZ\n", &pFileObject->FileName);
    return FsFilterDispatchPassThrough(DeviceObject, Irp);
}

并不是所有的文件系统驱动都实现了fast IO,因此我们需要先检测一下

可以直接定义一个宏来进行检测

#define VALID_FAST_IO_DISPATCH_HANDLER(_FastIoDispatchPtr,_FieldName)\
(((_FastIoDispatchPtr)!=NULL)&& \
(((_FastIoDispatchPtr)->SizeofFastIoDispatch) >= \
(FIELD_OFFSET(FAST_IO_DISPATCH,_FieldName)+sizeof(void*))) && \
((_FastIoDispatchPtr)->_FieldName!=NULL))

上面的那个宏中的+sizeof(void*)我现在也没看懂,不知道为啥还要加上这个东西,我明白了,这个sizeof(void*)是字段本身的长度,FAST_IO_DISPATCH结构体除了第一个字段之外,其他的字段都是指针

那么这样就可以计算出,Field是否超出了FAST_IO_DISPATCH结构体的真实长度

Fast IO的传递和普通IRP的传递不太一样,前者需要大量的代码,因为每一个Fast IO函数的参数都不太一样

BOOLEAN FsFilterFastIoQueryBasicInfo(
    __in PFILE_OBJECT  FileObject,
    __in BOOLEAN Wait,
    __out PFILE_BASIC_INFORMATION Buffer,
    __out PIO_STATUS_BLOCK  IoStatus,
    __in PDEVICE_OBJECT DeviceObject
) {
    PDEVICE_OBJECT nextDeviceObject = ((PFSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->AttachedToDeviceObject;
    return FALSE;
}

VOID FsFilterFastIoDetachDevice(
    __in PDEVICE_OBJECT SourceDevice,
    __in PDEVICE_OBJECT TargetDevice) {
    IoDetachDevice(TargetDevice);
    IoDeleteDevice(SourceDevice);
}

notification.c

常规的文件系统包含了控制设备和volume设备(这个volume device我不知道要怎么翻译)

volume设备attach到storage device stack上,控制设备将自己注册为文件系统

Common file system devices

每当有文件系统注册或者注销的时候,我们的过滤驱动注册的通知回调都会被调用

通过这个回调我们可以将过滤驱动在正确的时间attach或者detach到文件系统中

当文件系统注册时,我们attach到他的控制设备上,然后枚举该设备上所有的volume设备并attach上去

当文件系统deactivate时,我们就检查他的控制设备stack,从中找到我们的设备,detach掉,这个操作通过上面的FsFilterFastIoDetachDevice函数完成

attachdetach.c

这个文件中包含了一些针对attache和detach的helper函数

这个attach这里我是真的读不懂,需要去看一下书查一下资料,在微软的官方文档中,找到了这份关于文件系统过滤驱动的资料

文件系统过滤驱动需要调用IoCreateDevice函数来创建一个过滤设备对象以attach到volume或者文件系统stack上

status = IoCreateDevice(
          gFileSpyDriverObject,                     //DriverObject
          sizeof(MYLEGACYFILTER_DEVICE_EXTENSION),  //DeviceExtensionSize
          NULL,                                     //DeviceName
          DeviceObject->DeviceType,                 //DeviceType
          0,                                        //DeviceCharacteristics
          FALSE,                                    //Exclusive
          &newDeviceObject);                        //DeviceObject

上面代码中DeviceObject参数是我们将要attach到的目标设备对象,newDeviceObject是过滤驱动设备对象自己

新创建的newDeviceObject的DeviceExtension将会指向一个MYLEGACYFILTER_DEVICE_EXTENSION结构体,这个结构体是由我们自己定义的,但是有一个要求,就是MYLEGACYFILTER_DEVICE_EXTENSION结构体中必须要包含下面这个字段:

PDEVICE_OBJECT AttachedToDeviceObject;

DeviceName参数必须被设置为NULL,因为过滤驱动是attach到文件系统或者volume driver stack上的,他不需要名称

DeviceType参数的类型必须和要attach到的目标设备类型一致

下面这个是我们自定义的device extension结构体:

typedef struct _FSFILTER_DEVICE_EXTENSION
{
    PDEVICE_OBJECT AttachedToDeviceObject;
} FSFILTER_DEVICE_EXTENSION, *PFSFILTER_DEVICE_EXTENSION;

detach:

VOID FsFilterDetachFromDevice(PDEVICE_OBJECT DeviceObject) {
    PFSFILTER_DEVICE_EXTENSION pDevExt = (PFSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
    IoDetachDevice(pDevExt->AttachedToDeviceObject);
    IoDeleteDevice(DeviceObject);
}

检查我们的过滤驱动设备是否已经attach:

BOOLEAN FsFilterIsMyDeviceObject(PDEVICE_OBJECT DeviceObject) {
    return DeviceObject->DriverObject == g_fsFilterDriverObject;
}

安装驱动

sc create FsFilter type= filesys binPath= c:\FSFilter.sys

执行命令sc start FsFilter来启动我们的过滤驱动

之后我们可以使用DeviceTree看到我们的过滤驱动创建了一堆设备,这些设备是在挂在到volume device上的时候创建出来的

image-20240508195234566

我们的驱动也已经趴到了文件系统上面

image-20240508195852877

可以看到我们的过滤驱动已经爬满了各个文件系统

image-20240508200957101

并且在dbgView中可以截获到一堆文件操作:

image-20240508201725717

停止服务后,我们的过滤驱动就自动卸载了,之前attach上的device也全都detach了

image-20240508201958991

项目文件