references:
- Windows File System Filter Driver Development
- https://learn.microsoft.com/en-us/windows-hardware/drivers/ifs/creating-the-filter-device-object
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上,控制设备将自己注册为文件系统
每当有文件系统注册或者注销的时候,我们的过滤驱动注册的通知回调都会被调用
通过这个回调我们可以将过滤驱动在正确的时间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上的时候创建出来的
我们的驱动也已经趴到了文件系统上面
可以看到我们的过滤驱动已经爬满了各个文件系统
并且在dbgView中可以截获到一堆文件操作:
停止服务后,我们的过滤驱动就自动卸载了,之前attach上的device也全都detach了