标题 简介 类型 公开时间
关联规则 关联知识 关联工具 关联文档 关联抓包
参考1(官网)
参考2
参考3
详情
[SAFE-ID: JIWO-2024-3105]   作者: 大猪 发表于: [2022-05-21]

本文共 [311] 位读者顶过

MiniFilter驱动加载顺序

每个minifilter驱动程序必须具有称为altitude的唯一标识符。 此值在驱动的inf文件中定义,例如:Instance1.Altitude = "370030"

当加载微过滤器驱动程序时,微过滤器驱动程序的高度定义其相对于I/O堆栈中其他微过滤器驱动程序的位置。高度是一个无限精度的字符串,被解释为十进制数字。具有较低数值高度的微过滤器驱动器被加载到具有较高数值的微过滤器驱动器下方的I/O堆栈中。

高度越低,加载越早。

下图显示了带有过滤器管理器和三个微过滤器驱动程序的简化I/O堆栈:

[出自:jiwo.org]

微过滤器驱动程序可以过滤基于IRP的I/O操作以及快速I/O和文件系统过滤器回调操作。对于它选择过滤的每个I/O操作,minifilter驱动程序可以注册操作前回调例程,后操作回调例程或两者。

假设上图中的所有三个微过滤器驱动程序都注册了相同的I/O操作,过滤器管理器将按照从高到低(A,B,C)的高度顺序调用其操作前回调例程,然后转发I/O请求下一个较低的驱动程序进行进一步处理。当过滤器管理器收到完成的I/O请求时,它会以相反的顺序从最低到最高(C,B,A)调用每个minifilter驱动程序的后操作回调例程。

Minifilter的启动和停止

在系统运行时,可以通过服务启动请求:

1
2
3
sc start
 
net start

或通过显式加载请求

1
fltmc load

加载minifilter驱动程序时会调用minifilter驱动程序的DriverEntry例程,因此minifilter驱动程序可以执行初始化,该初始化将应用于minifilter驱动程序的所有实例。在其DriverEntry例程中,minifilter驱动程序调用FltRegisterFilter向过滤管理器注册回调例程,和FltStartFilter通知过滤管理器minifilter驱动程序已准备好开始附加到卷并过滤I/O请求。

在挂载卷后的第一个创建操作上调用其InstanceSetupCallBack例程,自动通知微过滤器驱动程序有关可用卷的信息。

1
fltmc detach

停止过程如下:

如果minifilter驱动程序注册了InstanceTeardownStartCallback回调例程,则过滤管理器会在卸载过程开始时调用它

在卸载期间,任何当前正在执行的操作前或操作后回调例程继续正常处理,等待后操作回调的任何I/O请求可以被排空或取消,并且由微过滤器驱动程序生成的任何I/O请求继续正常处理直到他们完成。

如果minifilter驱动程序注册了InstanceTeardownCompleteCallback例程,则过滤管理器会在完成所有未完成的I/O操作后调用此例程。在此例程中,minifilter驱动程序将关闭所有仍处于打开状态的文件。

释放所有对实例的未完成引用后,过滤管理器将删除剩余的上下文,并且实例将完全拆除。

在系统运行时,可以通过服务停止请求:

1
2
sc stop
net stop

或 通过显式卸载请求

1
fltmc unload

卸载minifilter请求。

卸载minifilter驱动程序时会调用minifilter驱动程序的FilterUnloadCallback例程。此例程关闭所有打开的通信服务器端口,调用FltUnregisterFilter,并执行任何所需的清理。注册此例程是可选的,但是,如果minifilter驱动程序未注册此例程,则无法卸载minifilter驱动。

MiniFilter注册相关代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
// 此段代码应该放在驱动初始化DriverEntry处。
status = FltRegisterFilter(DriverObject, &FilterRegistration, &g_FilterHandle);     //注册过滤器
 
if (NT_SUCCESS(status))
{
    status = FltStartFiltering(g_FilterHandle);
 
    if (!NT_SUCCESS(status))
    {
        KdPrint(("MiniFilter注册失败\n"));
        FltUnregisterFilter(g_FilterHandle);
    }
}
1
2
3
4
5
6
7
8
9
10
// minifilter反注册放在minifilter卸载回调函数中
NTSTATUS
    MiniFilterUnload(
    _In_ FLT_FILTER_UNLOAD_FLAGS Flags
    )
{
    //卸载回调函数
    FltUnregisterFilter(g_FilterHandle);
    return STATUS_SUCCESS;
}

FltRegisterFilter函数注册了什么?

以下是API的声明:

1
2
3
4
5
NTSTATUS FLTAPI FltRegisterFilter(
  PDRIVER_OBJECT         Driver,
  const FLT_REGISTRATION *Registration,
  PFLT_FILTER            *RetFilter
);

注意第二个参数FLT_REGISTERATION结构体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct _FLT_REGISTRATION {
  USHORT                                      Size;
  USHORT                                      Version;
  FLT_REGISTRATION_FLAGS                      Flags;
  const FLT_CONTEXT_REGISTRATION              *ContextRegistration;
  const FLT_OPERATION_REGISTRATION            *OperationRegistration;
  PFLT_FILTER_UNLOAD_CALLBACK                 FilterUnloadCallback;
  PFLT_INSTANCE_SETUP_CALLBACK                InstanceSetupCallback;
  PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK       InstanceQueryTeardownCallback;
  PFLT_INSTANCE_TEARDOWN_CALLBACK             InstanceTeardownStartCallback;
  PFLT_INSTANCE_TEARDOWN_CALLBACK             InstanceTeardownCompleteCallback;
  PFLT_GENERATE_FILE_NAME                     GenerateFileNameCallback;
  PFLT_NORMALIZE_NAME_COMPONENT               NormalizeNameComponentCallback;
  PFLT_NORMALIZE_CONTEXT_CLEANUP              NormalizeContextCleanupCallback;
  PFLT_TRANSACTION_NOTIFICATION_CALLBACK      TransactionNotificationCallback;
  PFLT_NORMALIZE_NAME_COMPONENT_EX            NormalizeNameComponentExCallback;
  PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK SectionNotificationCallback;
} FLT_REGISTRATION, *PFLT_REGISTRATION;

此结构体包含了minifilter所用到的回调函数,除了size和version参数不为空且固定以外,其余参数皆可为NULL。

size处固定填sizeof(FLT_REGISTRATION)

version处固定填FLT_REGISTRATION_VERSION

Flags处一般为NULL

ContextRegistration处是一个FLT_CONTEXT_REGISTRATION结构的变长数组,用于标明minifilter使用的上下文类型。数组必须以{FLT_CONTEXT_END}结尾,在分配上下文时会使用到。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CONST FLT_CONTEXT_REGISTRATION ContextNotifications[] = {
 
    { FLT_VOLUME_CONTEXT,
    0,
    CleanupVolumeContext,
    sizeof(VOLUME_CONTEXT),
    FILE_DISK_POOL_TAG },
 
    { FLT_STREAMHANDLE_CONTEXT,
    0,
    NULL,
    sizeof(SCANNER_STREAM_HANDLE_CONTEXT),
    FILE_DISK_POOL_TAG },
 
    { FLT_CONTEXT_END }
};

OperationRegistration是一个FLT_OPERATION_REGISTRATION结构的变长数组,每个IRP请求类型对应一个,minifilter为它注册预操作和后操作。数组中的最后一个元素必须以{IRP_MJ_OPERATION_END}结尾,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
 
#if 0 // TODO - List all of the requests to filter.
    { IRP_MJ_CREATE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_CREATE_NAMED_PIPE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_CLOSE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_READ,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_WRITE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_QUERY_INFORMATION,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_SET_INFORMATION,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_QUERY_EA,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_SET_EA,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_FLUSH_BUFFERS,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_QUERY_VOLUME_INFORMATION,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_SET_VOLUME_INFORMATION,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_DIRECTORY_CONTROL,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_FILE_SYSTEM_CONTROL,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_DEVICE_CONTROL,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_INTERNAL_DEVICE_CONTROL,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_SHUTDOWN,
      0,
      FsFilter1PreOperationNoPostOperation,
      NULL },                               //post operations not supported
 
    { IRP_MJ_LOCK_CONTROL,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_CLEANUP,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_CREATE_MAILSLOT,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_QUERY_SECURITY,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_SET_SECURITY,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_QUERY_QUOTA,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_SET_QUOTA,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_PNP,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_ACQUIRE_FOR_MOD_WRITE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_RELEASE_FOR_MOD_WRITE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_ACQUIRE_FOR_CC_FLUSH,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_RELEASE_FOR_CC_FLUSH,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_NETWORK_QUERY_OPEN,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_MDL_READ,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_MDL_READ_COMPLETE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_PREPARE_MDL_WRITE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_MDL_WRITE_COMPLETE,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_VOLUME_MOUNT,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
    { IRP_MJ_VOLUME_DISMOUNT,
      0,
      FsFilter1PreOperation,
      FsFilter1PostOperation },
 
#endif // TODO
 
    { IRP_MJ_OPERATION_END }
};

FilterUnloadCallback注册过滤器的卸载例程,如果为NULL的话,则minifilter不能卸载。

InstanceSetupCallback是minifilter安装的回调例程,每个卷要加载都会经过此例程的处理。

其余参数用的不多,自己也不太了解

MiniFilter通信

minifilter通过通信端口进行用户模式和内核模式之间的通信。

内核模式

FltCloseClientPort

FltCloseCommunicationPort

FltCreateCommunicationPort

FltSendMessage

创建通信端口

拿下面代码作讲解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
status = RtlCreateSecurityDescriptor(&miniFltSd, SECURITY_DESCRIPTOR_REVISION);
 
if (!NT_SUCCESS(status)) {
    return status;
}
 
RtlSetDaclSecurityDescriptor(&miniFltSd, TRUE, NULL, FALSE);
 
RtlInitUnicodeString(&uniString, MINISPY_PORT_NAME);
 
InitializeObjectAttributes(&miniFltOa,
    &uniString,
    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
    NULL,
    &miniFltSd);
 
 
 
status = FltCreateCommunicationPort(g_FilterHandle,
    &g_ServerPort,
    &miniFltOa,
    NULL,
    FDMiniConnect,
    FDMiniDisconnect,
    FDMiniMessage,
    50);            //修改客户端最大连接数
 
if (!NT_SUCCESS(status)) {
 
    if (NULL != g_ServerPort) {
        FltCloseCommunicationPort(g_ServerPort);
    }
 
    if (NULL != g_FilterHandle) {
        FltUnregisterFilter(g_FilterHandle);
    }
}

这一套就是一个固定的流程,着重看一下FltCreateCommunicationPort这个函数

1
2
3
4
5
6
7
8
9
10
NTSTATUS FLTAPI FltCreateCommunicationPort(
  PFLT_FILTER            Filter,
  PFLT_PORT              *ServerPort,
  POBJECT_ATTRIBUTES     ObjectAttributes,
  PVOID                  ServerPortCookie,
  PFLT_CONNECT_NOTIFY    ConnectNotifyCallback,
  PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,
  PFLT_MESSAGE_NOTIFY    MessageNotifyCallback,
  LONG                   MaxConnections
);

Filter 这里填FltRegisterFilter返回的过滤器的句柄

ServerPort 这里生成一个用于通信的端口

ObjectAttributes 指向OBJECT_ATTRIBUTES结构的指针

ConnectNotifyCallback 当用户模式应用程序向驱动发送连接请求时,minifilter就会调用此例程

DisconnectNotifyCallback 当用户态与内核态连接结束时,minifilter会调用此例程

MessageNotifyCallback 当用户态与内核态传送数据时,用户模式程序调用FilterSendMessage通过客户端端口向minifilter驱动程序发送消息,minifilter会调用此例程

MaxConnections 此端口允许的最大并发客户端连接数,此参数必需,且必须大于0

详细了解一下接收消息,该函数定义如下:

1
2
3
4
5
6
7
8
9
typedef NTSTATUS
(*PFLT_MESSAGE_NOTIFY) (
      IN PVOID PortCookie,
      IN PVOID InputBuffer OPTIONAL,
      IN ULONG InputBufferLength,
      OUT PVOID OutputBuffer OPTIONAL,
      IN ULONG OutputBufferLength,
      OUT PULONG ReturnOutputBufferLength
      );

InputBuffer 是接收到的缓冲区

InputBufferLength 接收到的缓冲区长度

OutputBuffer 如果minifilter需要回复数据,注意,OutputBuffer是指向用户模式缓冲区的指针,此指针仅在用户模式过程的上下文中有效,并且只能从try/except块中访问。minifilter调用ProbeForWrite来验证这个指针,但是他不能确保缓冲区正确对齐。如果缓冲区包含具有对齐要求的结构,minifilter驱动程序负责执行任何必要的对齐检查。为此,minifilter驱动程序可以使用IS_ALIGNED宏。

OutputBufferLength OutputBuffer指向缓冲区的大小

ReturnOutputBufferLength 接收OutputBuffer指向的缓冲区中返回的字节数

发送消息

minifilter驱动程序使用FltSendMessage例程向等待的用户模式应用程序发送消息

1
2
3
4
5
6
7
8
9
NTSTATUS FLTAPI FltSendMessage(
  PFLT_FILTER    Filter,
  PFLT_PORT      *ClientPort,
  PVOID          SenderBuffer,
  ULONG          SenderBufferLength,
  PVOID          ReplyBuffer,
  PULONG         ReplyLength,
  PLARGE_INTEGER Timeout
);

ClientPort 指向连接端口的指针,此参数详情看FltCreateCommunicationPort函数的ConnectNotifyCallback项,该函数指针中的有一个ClientPort参数。

SenderBuffer 指向缓冲区的指针,该缓冲区包含要发送到用户模式程序的消息。

SenderBufferLength SenderBuffer指向缓冲区的大小

ReplyBuffer 指向缓冲区的指针,该缓冲区从用户模式程序接收回复。

ReplyBufferLength ReplyBuffer指向缓冲区的大小

Timeout 指向超时时间的指针,时间单位为100纳秒,在应用程序接收到消息之前,驱动程序会进入等待状态,直到在给定的时间内收到回复,如果设置为NULL的话,则minifilter会进入无限等待状态

当minifilter驱动程序向用户模式应用程序使用FltSendMessage发送消息后,如果应用程序调用FilterGetMessage来获取消息,则minifilter驱动程序会立即传递消息。在传递消息后,如果ReplyBuffer为NULL,则FltSendMessage返回STATUS_SUCCESS。否则,如果Timeout非零,则minifilter会进入等待状态:

如果应用程序在超时间隔到期之前调用FilterReplyMessage,则minifilter驱动程序将收到回复,并且FltSendMessage将返回STATUS_SUCCESS

否则,minifilter驱动程序不会收到回复,FltSendMessage将返回STATUS_TIMEOUT

其中用户模式的函数详见用户模式

用户模式

FilterConnectCommunicationPort

FilterGetMessage

1
2
3
4
5
6
HRESULT FilterGetMessage(
  HANDLE                 hPort,
  PFILTER_MESSAGE_HEADER lpMessageBuffer,
  DWORD                  dwMessageBufferSize,
  LPOVERLAPPED           lpOverlapped
);

hPort 调用FilterConnectCommunicationPort返回的通信端口句柄。

lpMessageBuffer 指向缓冲区的指针,该缓冲区接收来自minifilter的消息。消息必须包含FILTER_MESSAGE_HEADER结构。

FilterReplyMessage

1
2
3
4
5
HRESULT FilterReplyMessage(
  HANDLE               hPort,
  PFILTER_REPLY_HEADER lpReplyBuffer,
  DWORD                dwReplyBufferSize
);

lpReplyBuffer 指向缓冲区的指针,该缓冲区包含要发送给minifilter的回复。消息必须包含FILTER_REPLY_HEADER结构

FilterSendMessage

1
2
3
4
5
6
7
8
HRESULT FilterSendMessage(
  HANDLE  hPort,
  LPVOID  lpInBuffer,
  DWORD   dwInBufferSize,
  LPVOID  lpOutBuffer,
  DWORD   dwOutBufferSize,
  LPDWORD lpBytesReturned
);

重要函数

FltGetDiskDeviceObject 返回一个与给定的卷相关联的磁盘设备对象

1
2
3
4
NTSTATUS FLTAPI FltGetDiskDeviceObject(
  PFLT_VOLUME    Volume,
  PDEVICE_OBJECT *DiskDeviceObject
);

此函数增加了*DiskDeviceObject中返回的设备对象指针的引用计数。当不再需要此指针时,调用者必须通过调用ObDereferenceObject来减少此引用计数。如果不这样做,则会导致系统释放或删除设备对象失败。

FltGetDeviceObject 返回一个指向给定卷的过滤管理器的卷设备对象(The FltGetDeviceObject routine returns a pointer to the Filter Manager's volume device object (VDO) for a given volume.)

1
2
3
4
NTSTATUS FLTAPI FltGetDeviceObject(
  PFLT_VOLUME    Volume,
  PDEVICE_OBJECT *DeviceObject
);

此函数在使用过后,如不再使用DeviceObject,也需要调用ObDereferenceObject来减少引用计数。

FltFlushBuffers minifilter给指定的文件刷新缓存

FltQueryInformationFile 获取指定文件的信息


这几个我所认为比较重要的函数是我在开发中哉过很大跟头的,可能并不适用所有人

评论

暂无
发表评论
 返回顶部 
热度(311)
 关注微信