MiniFilter file filtering lesson 1 file filtering framework and installation method
I. MiniFilter file filtering framework
1.1 INTRODUCTION
MiniFilter is a new driver developed by Microsoft for us, called Filter Manager (Filter Manager or fltmgr) The main function of this driver is to notify us if there are file operations
The advantages and disadvantages of MiniFilter are as follows:
advantage:
1. Increase development speed
2. You don't need to worry about IRP processing. You can just hand it over to the Filter Manager
Insufficient:
MiniFilter development is simple, but it hides a lot of details Such as device object, etc If you use the previous way of development, it is like C language embedded assembly, which has poor compatibility and loses the meaning of MiniFilter
1.2 MiniFilter framework
The framework is as follows:
In the IO manager, our MiniFilter will register As shown in the figure above There are three a, B and C
The most important thing in MiniFilter is that altitude not only has height value, but also has grouping
For example, group A is in fsfilter activity monitor, and group B is in fsfilter anti virus, that is, anti-virus level The higher the height, the more it will be executed first Suppose you block file access, you can not send it to the next layer So B and C can't accept it So we need to apply to Microsoft for this height (but it seems that it can be used without application. As long as it does not affect it)
The altitude value is from 20000 to 429999 The height values are grouped Therefore, the height value can not be scribbled Generally, each group has a height value range
The inquiry address is as follows: Loading order group and height of the minifilter driver - Windows drivers | Microsoft Docs
II. MiniFilter programming framework
2.1 introduction
Corresponding to the program, MiniFilter is very simple Only three kernel API s are needed to use MiniFilter
The parameters required in the API are structures So we can figure out the parameters in the structure In fact, it is just to fill things in the structure
The kernel API is as follows:
NTSTATUS FltRegisterFilter( IN PDRIVER_OBJECT Driver, IN CONST FLT_REGISTRATION *Registration, OUT PFLT_FILTER *RetFilter ); NTSTATUS FltStartFiltering( IN PFLT_FILTER Filter); VOID FltUnregisterFilter( IN PFLT_FILTER Filter );
There are only three API s It is divided into registration, startup and uninstall, both of which are parameters This is the Filter handle This handle is passed out from the third parameter of FltRegisterFilter So the main learning is the first
This function has three arguments
-
Parameter 1 Driver object in DriverEntry of DDK driver
-
Parameter 2 a structure, which is the structure we want to know Let's talk about it
-
Parameter 3 outgoing handle Handle to the file manager After successful registration, the handle will be sent out to the startup and uninstall functions
2.2 FLT_REGISTRATION structure
There is a substructure in our registration function This structure is as follows:
typedef struct _FLT_REGISTRATION { USHORT Size; @1 Points to its own size sizeof(FLT_REGISTRATION). USHORT Version; Version must be set to FLT_REGISTRATION_VERSION FLT_REGISTRATION_FLAGS Flags; sign @1 CONST FLT_CONTEXT_REGISTRATION *ContextRegistration; context@2 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; #if FLT_MGR_LONGHORN PFLT_TRANSACTION_NOTIFICATION_CALLBACK TransactionNotificationCallback; PFLT_NORMALIZE_NAME_COMPONENT_EX NormalizeNameComponentExCallback; #endif // FLT_MGR_LONGHORN } FLT_REGISTRATION, *PFLT_REGISTRATION;
The meaning is as follows:
member | meaning | explain | Key ⚪ Understand the key points of √ × Little or no use |
---|---|---|---|
Size | size | Size of pointing to itself # sizeof(FLT_REGISTRATION) | ⚪ |
Version | edition | Must be set to FLT_REGISTRATION_VERSION | ⚪ |
Flags | sign | Two settings, NULL or fltfl_ REGISTRATION_ DO_ NOT_ SUPPORT_ SERVICE_ When STOP is set to STOP, MinniFilter will not uninstall when it stops the service, whether your uninstall function is set or not | ⚪ |
ContextRegistration | context | If the function that registers the processing context is registered, the last item of the structure array must be set to FLT_CONTEXT_END | ⚪ |
OperationRegistration | Callback Suites | The key point in the key point is to learn how to set this field Is an array of structures that can set callbacks of interest to us The last item is set to IRP_MJ_OPERATION_END | √ |
FilterUnloadCallback | Unload function | Uninstall MiniFilter callback If flags = xx_STOP then it will not be uninstalled whether you set it or not | √ |
InstanceSetupCallback | Volume instance load callback | When a volume is loaded, MiniFilter will generate an instance and bind it. For example, when a mobile hard disk is accessed, an instance will be generated Can be set to NULL | ⚪ |
InstanceQueryTeardownCallback | Control instance destruction function | This instance will only come when you unbind it manually | ⚪ |
InstanceTeardownStartCallback | Instance destruction function | When called, it means that the binding has been unbound and can be set to NULL | ⚪ |
InstanceTeardownCompleteCallback | Instance unbinding completion function | When it is determined, call the unbound completion function, which can be set to NULL | ⚪ |
GenerateFileNameCallback | File name callback | The generated file name can be set to callback or NULL | ⚪ |
NormalizeNameComponentCallback | Query WDK | ⚪× | |
NormalizeContextCleanupCallback | Query WDK | ⚪× | |
TransactionNotificationCallback | Query WDK | ⚪× | |
NormalizeNameComponentExCallback | Query WDK | ⚪× |
In fact, the essence is to learn the callback function set, which is an object array Let's take a look at its structure
typedef struct _FLT_OPERATION_REGISTRATION { UCHAR MajorFunction; FLT_OPERATION_REGISTRATION_FLAGS Flags; PFLT_PRE_OPERATION_CALLBACK PreOperation; PFLT_POST_OPERATION_CALLBACK PostOperation; PVOID Reserved1; } FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION;
-
Parameter 1 indicates the IRP operation you want to monitor
-
Parameter 2 is a flag
-
Parameter 3 is the monitoring callback you executed. pre means the previous callback For example, before file creation has been created, you are called.
-
Parameter 4 callback after monitoring Callback called after file creation
-
Parameter 5: leave the parameter NULL
IRP can monitor many WDK documents for this query
Here's the sign
The signs are as follows:
sign | meaning |
---|---|
FLTFL_OPERATION_REGISTRATION_SKIP_CACHED_IO | Using this flag means that pre and post function operations are not performed for cached IO processing. It is applicable to fast IO because all fast IO has been cached |
FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO | Specifies that callback operation should not be performed for IO of paging operation All IO operations that are not based on IRP will be skipped Our function will not be called |
It looks rather misty, doesn't it Let me talk about it In fact, when we write a file, we don't write it directly to disk
It is written to the cache first The cache is written to memory
The call chain is as follows:
APP->IO->FSD->Cache->MM->IO->FSD->DISk First kind APP->IO->FSD->DISk Second
The first is to write to the cache first, and then MM initiates IO request when 1024 bytes are met Then write the notification file system to disk
The second way is to write directly to the file system through IO and then to the disk
If you read and write frequently, it will affect efficiency Therefore, we can ignore the first request not initiated by IRP
So the meaning of these two signs is almost the same
2.3 pre callback and post callback
The prototype of the pre callback function is as follows:
typedef FLT_PREOP_CALLBACK_STATUS (*PFLT_PRE_OPERATION_CALLBACK) ( __inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __deref_out_opt PVOID *CompletionContext );
The post callback function is as follows:
typedef FLT_POSTOP_CALLBACK_STATUS (FLTAPI *PFLT_POST_OPERATION_CALLBACK) ( __inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __in_opt PVOID CompletionContext, __in FLT_POST_OPERATION_FLAGS Flags );
2.3.1 pre return value and post return value
First, let's talk about the return value
The return value of pre is as follows
Return value | meaning | Is that the point |
---|---|---|
FLT_PREOP_SUCCESS_WITH_CALLBACK | Complete the call of callback and send callbackdata down. Callbackdata can be used in post | √ |
FLT_PREOP_SUCCESS_NO_CALLBACK | Complete the callback and send it down without parameters | √ |
FLT_PREOP_PENDING | Hang | |
FLT_PREOP_DISALLOW_FASTIO | Disable Fastio | |
FLT_PREOP_COMPLETE | The callback is completed and will not be sent down | √ |
FLT_PREOP_SYNCHRONIZE | synchronization |
In fact, there are three common ones, FLT_PREOP_SUCCESS_WITH_CALLBACK and FLT_PREOP_COMPLETE
POST callback
Return value | meaning | Is it commonly used |
---|---|---|
FLT_POSTOP_FINISHED_PROCESSING | When complete, the filter manager will continue to complete the processing of I/O operations. | √ |
FLT_POSTOP_MORE_PROCESSING_REQUIRED | The minifilter driver has stopped the completion processing of I/O operations, but it will not return control of the operation to the filter manager. | √ |
FLT_POSTOP_DISALLOW_FSFILTER_IO | The minifilter driver does not allow a fast QueryOpen operation and forces it down a slow path. Doing so causes the I/O Manager to service the request by opening / querying / closing the file. The minifilter driver should only return this state of QueryOpen. |
2.3.2 PFLT_CALLBACK_DATA acquisition
This parameter is parameter 1, which is a very important parameter Its structure is as follows
typedef struct _FLT_CALLBACK_DATA { FLT_CALLBACK_DATA_FLAGS Flags; PETHREAD CONST Thread; PFLT_IO_PARAMETER_BLOCK CONST Iopb; IO_STATUS_BLOCK IoStatus; struct _FLT_TAG_DATA_BUFFER *TagData; union { struct { LIST_ENTRY QueueLinks; PVOID QueueContext[2]; }; PVOID FilterContext[4]; }; KPROCESSOR_MODE RequestorMode; } FLT_CALLBACK_DATA, *PFLT_CALLBACK_DATA;
The thread is recorded Flags iopb and IoStatus
A thread can determine which process it belongs to
PFLT_CALLBACK_DATA Data; PEPROCESS processObject = Data->Thread ? IoThreadToProcess(Data->Thread) : PsGetCurrentProcess();
The Iopb field records the Buffer value we want to get from the IRP stack
Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess PVOID pQueryBuffer = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer; ULONG uQueryBufferSize = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length PMDL pReadMdl = Data->Iopb->Parameters.Read. MdlAddress; PVOID pReadBuffer = Data->Iopb->Parameters.Read. ReadBuffer; ULONG uReadLength = Data->Iopb->Parameters.Read.Length;
IoStatus records the length value of read and write operations
You can also determine what operation Data is There are also macros
FLT_IS_IRP_OPERATION FLT_IS_FASTIO_OPERATION FLT_IS_FS_FILTER_OPERATION example if (FLT_IS_FASTIO_OPERATION(DATA)) { status = STATUS_FLT_DISALLOW_FAST_IO; Data->IoStatus.Status = status; Data->IoStatus.information = 0; return FLT_PREOP_DISALLOW_FASTIO; }
2.3.3 object acquisition FltObjects
It is also a structure that records all the objects you can use
typedef struct _FLT_RELATED_OBJECTS { USHORT CONST Size; USHORT CONST TransactionContext; PFLT_FILTER CONST Filter; PFLT_VOLUME CONST Volume; PFLT_INSTANCE CONST Instance; PFILE_OBJECT CONST FileObject; PKTRANSACTION CONST Transaction; } FLT_RELATED_OBJECTS, *PFLT_RELATED_OBJECTS; typedef CONST struct _FLT_RELATED_OBJECTS *PCFLT_RELATED_OBJECTS;
Common are as follows:
FltObjects->Volume, FltObjects->Instance, FltObjects->FileObject, FltObjects->FileObject->DeviceObject
2.3.4 example learning
There is a MiniFilter framework under the Src directory of WDK7600 You can learn If you use VS or later, you need to select your own template for generation
Example Directory: x:\WinDDK00.16385.1\src\filesys\miniFilter
-
nullFilter
A basic framework is useless for filtering You can see how it is written
-
passThrough
A complete framework, so filtering has been set, but it is not used It's the best way to learn
-
scanner
An interception framework can take a look at an example
III. use of MiniFilter
MiniFilter needs to be installed in two inf installation modes and dynamic loading mode
3.1 the Inf installation mode is explained in the example of passThrough
Inf understand When using, you can copy an inf Just change your own things
Inf is as follows:
;;; ;;; PassThrough ;;; ;;; ;;; Copyright (c) 1999 - 2001, Microsoft Corporation ;;; [Version] Signature = "$Windows NT$" Class = "ActivityMonitor" ;Indicates the grouping of drivers,Must specify. ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;GUID Each group has a fixed GUID Provider = %Msft% ;Variable value from STRING You can see the name of the driver provider in this section DriverVer = 06/16/2007,1.0.0.1 ;Version number CatalogFile = passthrough.cat ;inf Corresponding cat Files may not be required [DestinationDirs] DefaultDestDir = 12 ;Tell us where to copy the driver. 13 means copy to%windir% MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers ;; ;; Default install sections ;; [DefaultInstall] OptionDesc = %ServiceDescription% CopyFiles = MiniFilter.DriverFiles [DefaultInstall.Services] AddService = %ServiceName%,,MiniFilter.Service ;; ;; Default uninstall sections ;; [DefaultUninstall] DelFiles = MiniFilter.DriverFiles [DefaultUninstall.Services] DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting ; ; Services Section ; [MiniFilter.Service] ;Some information about the service DisplayName = %ServiceName% Description = %ServiceDescription% ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ Dependencies = "FltMgr" ;Service dependency ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER StartType = 3 ;SERVICE_DEMAND_START ErrorControl = 1 ;SERVICE_ERROR_NORMAL LoadOrderGroup = "FSFilter Activity Monitor" ;File filter grouping AddReg = MiniFilter.AddRegistry ;File filtering registry needs to add height value and other information ; ; Registry Modifications ; [MiniFilter.AddRegistry] HKR,,"DebugFlags",0x00010001 ,0x0 HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% ; ; Copy Files ; [MiniFilter.DriverFiles] %DriverName%.sys [SourceDisksFiles] passthrough.sys = 1,, [SourceDisksNames] 1 = %DiskId1%,,, ;; ;; String Section ;; [Strings] Msft = "Microsoft Corporation" ServiceDescription = "PassThrough Mini-Filter Driver" ServiceName = "PassThrough" DriverName = "PassThrough" DiskId1 = "PassThrough Device Installation Disk" ;Instances specific information. DefaultInstance = "PassThrough Instance" Instance1.Name = "PassThrough Instance" Instance1.Altitude = "370030" Instance1.Flags = 0x0 ; Allow all attachments
In fact, the essence of INF file is the same as our normal DDK installation service It's just that he adds two more registry keys and instances to the registry
After INF installation, the driver will be copied to C:\windows\System32\driver first
Then register information under the registry
As follows:
Therefore, if we need to create two more keys when loading the driver dynamically, namely instances xxxinstance, and then add the height value
During dynamic loading, our dependency driver should be modified to FltMgr, and the packet should be written as FSFilter Activity Monitor
3.2 using drive
After inf is installed, it just registers a key to the registry Copy the driver to the system directory
We can start in two ways
net start PassThrough fltmc load PassThrough
Either way is OK