references:
过滤驱动的实现
大打开一个文件的时候,IO管理器会分配一个IRP结构体用于描述IO请求,这其中包含了操作类型和与该操作相关的各种各样的参数
IRP会被发送到与该操作相关的设备栈的顶部

过滤驱动会注册一个或者多个IRP对应的操作类型的回调函数,当有目标IRP请求被放到设备栈上的时候,过滤驱动的回调函数就会被调用
过滤驱动可以对该IRP进行以下操作:
- 不作任何修改,直接发送给设备栈的下一层
- 修改之后发送给设备栈的下一层
- 修改IRP响应(post回调)
- 完成IO请求,返回成功(完成IO请求就意味着设备栈下面的驱动不会接收到此IRP请求)
- 完成IO请求,返回失败
- 将IRP发送给别的设备栈进行处理
一般情况下过滤驱动会被插入到设备栈顶部,但是理论上来讲,过滤驱动可以被插入到任意位置
windows自带的bitlocker full disk encryption就是一个插在volume block设备上面的过滤驱动,他会加密写入IRP的sector数据,在读取IRP的回调中解密读出来的sector
过滤管理器和mini filter
一句话概括,过滤管理器和mini filter就是用来让过滤驱动的编写变得更简单的,程序员只需要专注于业务代码,也就是在过滤驱动的回调函数中要做什么

从上图中可以看到顾虑驱动不会往设备栈中插入设备,而是插入了Filter Manager的设备,filter manager负责拦截IO请求,并调用对应的minifilter中的回调函数
filter manager通过minifilter的altitude属性来管理过滤驱动的顺序,这个属性是在驱动开发的过程中由开发者设置的
在驱动的inf安装文件中定义

altitude的值越高,驱动的优先级就越高,对于响应而言就是反过来的
minifilter可以通过InstanceSetupCallback回调函数来设置他是否想要attach到给定的volume类型
下面这个函数就指定了他只会attach到ntfs volume

将IO请求转发给不同的设备栈或者不同的file
filter manager为了避免在minifilter驱动代码中打开文件时造成重入,导出了专门的API来用于在minifilter代码中打开文件,使用FltCreateFileEx打开文件,filter manager会直接将此操作产生的IRP请求发送给当前minifilter的下一层级,从而避免死循环
另外一种方法是使用一个叫做Extra Create Parameters(ECP)的机制,ECP包含了一个GUID用于唯一标识,以及其他的附加数据,这些数据通过FltInsertExtraCreateParameter函数进行插入到文件中,数据的提取可以通过FltFindExtraCreateParameter函数完成,minifilter的代码通过检查ECP的存在来选择是否忽略IRP,这样也可以避免重入
luafv.sys的LuafvReparse函数就使用了这个API


文件重定向,通过修改PreOperation回调中的FLT_IO_PARAMETER_BLOCK结构体中的TargetFileObject为另一个文件即可
PREDIRECT_CONTEXT context = // Get driver’s allocated context.
if (context->FileObject) {
Data->Iopb->TargetFileObject = context->FileObject;
FltSetCallbackDataDirty(Data);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
minifilter通信
漏洞的产生通常源自于对不受信任的输入数据的处理,之所以对minifilter感兴趣就是因为有很多不受信任的输入可以被输入到minifilter的代码中
设备对象
minifilter驱动并不需要创建设备对象,但这并不意味着所有的minifilter都没有设备对象,比如杀软的minifilter同时也会创建一个设备对象