返回
顶部

references:

过滤驱动的实现

大打开一个文件的时候,IO管理器会分配一个IRP结构体用于描述IO请求,这其中包含了操作类型和与该操作相关的各种各样的参数

IRP会被发送到与该操作相关的设备栈的顶部

image-20250812100647213

过滤驱动会注册一个或者多个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就是用来让过滤驱动的编写变得更简单的,程序员只需要专注于业务代码,也就是在过滤驱动的回调函数中要做什么

image-20250812102337446

从上图中可以看到顾虑驱动不会往设备栈中插入设备,而是插入了Filter Manager的设备,filter manager负责拦截IO请求,并调用对应的minifilter中的回调函数

filter manager通过minifilter的altitude属性来管理过滤驱动的顺序,这个属性是在驱动开发的过程中由开发者设置的

在驱动的inf安装文件中定义

image-20250812102827930

altitude的值越高,驱动的优先级就越高,对于响应而言就是反过来的

minifilter可以通过InstanceSetupCallback回调函数来设置他是否想要attach到给定的volume类型

下面这个函数就指定了他只会attach到ntfs volume

image-20250812103804646

将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

image-20250812140444782

image-20250812140605997

文件重定向,通过修改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同时也会创建一个设备对象

minifilter port