nt文件系统(2)--minifilter
前言
前面总结了 sfilter 相关的内容,接下来就该轮到minifilter了。
minifilter基础
minifilter可以简单理解为微软自己开发了一个相当于sfilter的驱动,并提供了相关接口供其他驱动使用。
由于sfilter本身的复杂性极高,一个是在不同版本系统下的兼容性问题,一个是与核心需要的业务逻辑相比,无关的代码太多,故使用 minifilter是一个很好的选择,但是由于 minifilter本身也是基于传统的设备过滤驱动实现的,故在系统中存在其他 sfilter 的时候优先级会比别人低。
minifilter框架
DriverEntry
微型文件过滤驱动程序的DriverEntry例程必须按顺序执行以下步骤:
- 初始化所需的全局变量
- 通过调用FltRegisterFilter注册一个过滤器。
- 通过调用FltStartFiltering启动过滤器。
- 返回相应的 NTSTATUS 值。
- 在卸载驱动时调用 FltUnregisterFilter 卸载过滤器
相关函数及结构
1 | NTSTATUS FLTAPI FltRegisterFilter( |
共有三个参数,第一个参数传入本驱动对象的指针,第二个参数是一个 FLT_REGISTRATION 结构,其中便存在注册过滤例程的地方了,第三个参数是一个返回值,其作用相当于一个句柄。
1 | NTSTATUS FLTAPI FltStartFiltering( |
一个参数,即注册时返回的句柄。
1 | typedef struct _FLT_REGISTRATION { |
具体可以参考 MSDN 相关
其中最为重要的便是 OperationRegistration 成员。
1 | typedef struct _FLT_OPERATION_REGISTRATION { |
MajorFunction 主功能号,Flags 指定过滤哪种类型的请求,可以为0, Pre IRP完成前执行的回调例程,Post IRP完成后执行的完成例程,Reserved1 保留供系统使用。
例子:
1 | const FLT_OPERATION_REGISTRATION |
PFLT_PRE_OPERATION_CALLBACK
FLT_POSTOP_CALLBACK_STATUS
原型
1 | PFLT_PRE_OPERATION_CALLBACK PfltPreOperationCallback; |
其参数 FLT_CALLBACK_DATA
相当于对原来的 IRP
进行了包装,避免直接与IRP
打交道。
另外可以通过其返回值轻易控制 IRP的状态,完成(FLT_PREOP_COMPLETE),挂起(FLT_PREOP_PENDING),传递(FLT_PREOP_SUCCESS_WITH_CALLBACK) 等等。
1 | typedef struct _FLT_CALLBACK_DATA { |
minifilter 本身也提供了一系列函数用于处理 FLT_CALLBACK_DATA
的相关信息。
例如可以通过 FltGetFileNameInformation
获取文件名信息,再通过 FltParseFileNameInformation
拆分为更加易用的格式,使用完毕之后再通过 FltReleaseFileNameInformation
回收相关资源。
又例如给文件对象绑定上下文信息等等操作。
通过设置 IoStatus 则可以控制请求的结果信息。
在 FLT_IO_PARAMETER_BLOCK
也存放着许多需要关注的信息,如要 得到读写的具体内容等就要从该结构入手。
由于篇幅限制,就不一一展开,在MSDN文档上有对其详细的描述。
minifilter之应用层通信
除了常规的设备通信方式外,minifilter内部封装了一套更为方便的通信机制。
主要函数如下:FltBuildDefaultSecurityDescriptor
创建默认的安全描述符FltCreateCommunicationPort
创建一个服务端口
1 | NTSTATUS status; |
其中 gFilterHandle
及注册minifilter返回的句柄,MiniConnect
MiniDisconnect
MiniMessage
为事件回调,在相应事件到达时触发。
相关函数FltCloseClientPort
在 MiniDisconnect
中关闭用户端口FltSendMessage
向正在等待的用户层发送FltCloseCommunicationPort
在驱动卸载时关闭监听端口
##小拓展
Ps.刚刚在看相关博客时就发现一个他写了个bug
,网上的代码真的不要随便用
实际上之前用内核管道做过测试,这种异步IO的方式本身效率不高,并不适合频繁传输,
对频繁传输,还是推荐使用 IO方式中的其他方式,在同一进程上下文时,直接操作进程,并通过共享事件的方式做互相通知更为高效。
有人肯定会说,别人都是说线程上下文,为什么你说的是进程上下文?
能否直接操作r3进程内存,看的是当前是进程上下文,一个进程中的多个线程的上下文都可以被直接操作,只是说判断某个线程是否属于某个进程要麻烦一丢丢,并非在某个特定的线程上下文才能访问进程空间。
例如调用 同步的DeviceIoControl
的线程会陷入内核调用派遣函数,那么派遣函数就和 DeviceIoControl
在同一线程上下文,也就在同一进程上下文,此时是可以直接操作进程空间的。
一般可以认为在异步中,其上下文是不可控的,这是由于实现异步的交付者是不可控的,你要说是某进程内部实现的协程也是可以的。这里主要指系统级的异步,极有可能会在其他进程的上下文交付。
minifilter 动态安装卸载
网上随便找了一篇,并未测试,按照他的说法是在注册表中创建相关子键和键值
SYSTEM\\CurrentControlSet\\Services\\DriverName\\Instances
SYSTEM\\CurrentControlSet\\Services\\DriverName\\Instances\\DriverName Instance
话说回来,网上大多数流传已久的驱动加载方式本来就有点问题,会出现重启都卸载不干净的情况,编码不规范造成的,这块是个细节。
总结
本文是官方文档和众多参考文章汇集而成,由于 minifilter 并不需要像 sfilter 那样悉知细节,故相较之要简单许多。