Minifilter Minifilter简介 MiniFilter是微软为我们开发的一个新的驱动,称为过滤管理器.(Filter Manager或者 fltmgr).这个驱动主要作用就是如果有文件操作可以通知我们.
Minifilter
与legacy filter
的区别:
1.比sfilter加载顺序更易控制,因为Minifilter可以设置Altitude
对于Minifilter 来说,Altitude 是一个非常重要的概念,因为 Minifilter 结构允许多个过滤驱动共存,而 Altitude 数值用于规定每个驱动的层级位置,高度越低的驱动越靠近文件系统核心,优先处理文件操作。
而Sfilter要自己手动Attach,如果多个过滤设备的话,加载顺序难免会较难控制
2.Minifilter具有可卸载能力 而legacy filter
卸载可能会蓝屏,原因是可能存在仍在处理的IRP请求
3.CallBack模型,只需要处理必要操作的能力
提供了Pre和Post两个类型的回调
4.兼容性更好
5.名字更好处理:提供了函数一键获取文件路径
6.同样遵循IRQL,锁等内核机制
总体框架
以上是用Filter管理器简化后的I/O栈和minifilter驱动
流程是这样的
首先由用户模式发送的文件I/O发送给 I/O 管理器,由I/O管理器去将用户层发来的数据和命令封装为IRP
原来的模式是,I/O管理器将这个IRP一次发给文件系统驱动,再发给磁盘系统驱动,最后发给硬件
现在多了一个FilterManager,Filter Manager 位于 I/O 管理器 和 文件系统驱动 之间的中间层。它是一个内核组件,用于管理文件系统的 Minifilter 驱动。Filter Manager 通过拦截并协调所有 I/O 请求,为所有文件系统 I/O 操作提供了一个必经之路,以便调用注册的 Minifilter 驱动。
现在I/O管理器发IRP的时候,要先发给Filter Manager,如果注册了Minifilter,这个Filter Manager会把IRP封装为 CALLBACK_DATA
这个结构,然后发给Minifilter进行处理
minifilter传递顺序是 A->B->C,任何一层都可以决定是否将IRP继续往下发或者到此为止。
稍微复杂点:
这幅图就告诉我们,可以既安装Legacy Filter,也可以安装Minifilter,两者不冲突,另外, Windows 系统将 Legacy Filter 固定在 Frame 1 和 Frame 0 之间是为了确保系统的兼容性和稳定性。 如果希望在更低的层次拦截请求,建议使用 Minifilter 驱动程序代替 Legacy Filter Driver。Minifilter 驱动通过 Altitude 值进行排序,可以放置在 Frame 0 以达到较低的优先级。
Altitude值:20000–429999 每一个minifilter驱动必须有一个叫做altitude的唯一标识符,每一个minifilter驱动的altitude定义了它加载时在I/O栈中相对于其他minifilter驱动的位置,值越小,栈中的位置就越低
FSFilter Anti-Virus 320000-329999 此组包括在文件I/O期间探测并杀毒的过滤驱动
FSFilter Encryption 140000-149999此组包括在文件I/O期间加密和解密数据的过滤驱动
320000-329999 和 140000-149999 之间的范围可能属于其他用途,或者被其他特定类型的驱动(如备份、文件同步、系统监控等)使用。
它们是由 微软 预设的 常用用途范围 。
Minifilter架构 FLT_REGISTRATION
结构体
FLT_REGISTRATION
结构体是 Minifilter 驱动 注册过程中的关键数据结构,用于定义和管理 Minifilter 驱动的属性和行为。它在 Windows 操作系统中扮演着至关重要的角色,负责告诉操作系统如何加载和配置 Minifilter 驱动。通过 FLT_REGISTRATION
,Minifilter 驱动能够注册其处理的文件系统操作、过滤的路径、需要的操作模式等信息。
结构体定义:
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;
字段详细解释
Size
类型:USHORT
说明:该字段指定结构体的大小,通常设置为 sizeof(FLT_REGISTRATION)
。操作系统使用该字段来确保正确解析结构体。
Version
类型:USHORT
说明:此字段指定 FLT_REGISTRATION
结构体的版本,通常为 1。
Flags
类型:FLT_REGISTRATION_FLAGS
说明:此字段包含注册标志,用于定义 Minifilter 驱动的行为。例如,FLT_REGISTRATION_FLAG_SUPPORTS_DYNAMIC_FILTERING
标志表明该驱动支持动态启用或禁用过滤操作。
ContextRegistration
类型:const FLT_CONTEXT_REGISTRATION*
说明:该字段用于注册上下文类型。上下文允许 Minifilter 驱动在操作期间存储与文件或文件系统实例相关的特定数据。此字段指向 FLT_CONTEXT_REGISTRATION
结构体,定义了上下文类型及其生命周期。
OperationRegistration
类型:const FLT_OPERATION_REGISTRATION*
说明: 是 FLT_REGISTRATION
结构体中的一个关键字段,它定义了 Minifilter 驱动支持的所有文件操作及其对应的回调函数。这个字段包含 FLT_OPERATION_REGISTRATION
数组,列出驱动可以处理的所有操作,如文件创建、读取、写入等。
FilterUnloadCallback
类型:PFLT_FILTER_UNLOAD_CALLBACK
说明:指向回调函数的指针,负责在 Minifilter 驱动卸载时执行清理操作。当驱动被卸载时,该回调会被调用。
InstanceSetupCallback
类型:PFLT_INSTANCE_SETUP_CALLBACK
说明:在加载 Minifilter 驱动时,操作系统会遍历系统中的所有卷设备,并为每个卷设备创建一个实例(实例与卷设备绑定)。每当为卷设备创建一个新实例时,InstanceSetupCallback
就会被调用。此回调函数用于初始化该实例,设置与实例相关的上下文或资源,记录下卷设备的名称,扇区大小等信息
InstanceQueryTeardownCallback
类型:PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK
说明:指向回调函数的指针,负责在 Minifilter 驱动实例销毁前进行查询操作。该回调允许在销毁实例之前进行清理或状态检查。
InstanceTeardownStartCallback
类型:PFLT_INSTANCE_TEARDOWN_CALLBACK
说明:指向回调函数的指针,负责在 Minifilter 驱动实例开始销毁时执行清理工作。当驱动实例准备销毁时,该回调会被调用。
InstanceTeardownCompleteCallback
类型:PFLT_INSTANCE_TEARDOWN_CALLBACK
说明:指向回调函数的指针,负责在 Minifilter 驱动实例销毁完成后执行清理工作。此回调会在实例完全销毁后调用。
GenerateFileNameCallback
类型:PFLT_GENERATE_FILE_NAME
说明:指向回调函数的指针,负责生成文件路径名。此回调可以用于将文件路径从内部格式转换为外部格式或进行其他路径转换。
NormalizeNameComponentCallback
类型:PFLT_NORMALIZE_NAME_COMPONENT
说明:指向回调函数的指针,用于处理文件路径组件的规范化。它可以确保文件路径按照标准的格式进行处理。
NormalizeContextCleanupCallback
类型:PFLT_NORMALIZE_CONTEXT_CLEANUP
说明:指向回调函数的指针,负责清理与文件路径规范化过程相关的上下文数据。
TransactionNotificationCallback
类型:PFLT_TRANSACTION_NOTIFICATION_CALLBACK
说明:指向回调函数的指针,用于处理文件 I/O 操作中的事务通知。该回调函数可用于在文件操作发生时执行某些事务控制操作。
NormalizeNameComponentExCallback
类型:PFLT_NORMALIZE_NAME_COMPONENT_EX
说明:指向回调函数的指针,扩展的名称组件规范化回调,通常用于处理更复杂的路径和组件规范化操作。
SectionNotificationCallback
类型:PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK
说明:指向回调函数的指针,用于处理文件系统中的分区冲突。这种情况通常出现在文件 I/O 操作跨越了文件系统或分区的边界时。
注册监控回调示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const FLT_OPERATION_REGISTRATION fileMonitorCallBacks[]={ { IRP_MJ_CREATE, FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, HOOK_PreNtCreateFile, HOOK_PostNtCreateFile, }, { IRP_MJ_CLEANUP, 0 , HOOK_PreNtCleanup, 0 }, { IRP_MJ_WRITE, 0 , HOOK_PreNtWriteFile, 0 } }
这里要说一下为什么IRP_MJ_CREATE
这里,flag要设置为FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO
微软介绍为:
微筛选器为读取或写入操作设置此标志,以指定不应为分页 I/O 操作调用其操作前和操作后回调例程。 此标志仅适用于基于 IRP 的 I/O 操作。 对于不基于 IRP 的 I/O 操作,将忽略它。
首先我们要先介绍下什么是IRP_PAGING_IO
这是由系统MM发起的
IRP_PAGING_IO
主要是由系统缓存管理器发起的IO操作,所以对于系统的操作,我们一般不去管
通常IO请求的过程是
IRP_NOCACHE
和 IRP_CACHE
是指 I/O 请求是否通过缓存管理器进行数据缓存操作:
情况1:不开启缓存管理器,App -> IO -> FSD -> DISK
情况2: 开启缓存管理器 , App(应用程序) -> IO( I/O 管理器) -> FSD(文件系统驱动) -> CC(缓存管理) -> MM(内存管理)( -> FSD -> DISK(磁盘))
即应用程序发起的读操作会首先经过 I/O 管理器,然后由文件系统驱动尝试直接从缓存中读取。缓存未命中时(比如就读写一个字节,那么不会立刻被写入),请求会通过内存管理器加载数据至缓存,最终从磁盘获取数据。
IRP_PAGING_IO
:在情况2中,MM会发起一个IRP并标记为 IRP_XXX_PAGING_IO
,因为是由MM发起的,是系统干的活,所以我们一般去忽略
minifilter里面没有IRP了,变为PFLT_CALLBACK_DATA
,这个就是从IRP来的
Pre回调: 1 2 3 4 5 6 7 8 PFLT_PRE_OPERATION_CALLBACK PfltPreOperationCallback; FLT_PREOP_CALLBACK_STATUS PfltPreOperationCallback ( [in, out] PFLT_CALLBACK_DATA Data, [in] PCFLT_RELATED_OBJECTS FltObjects, [out] PVOID *CompletionContext ) {...}
**PFLT_CALLBACK_DATA Data
**(输入/输出):
Data
是指向 FLT_CALLBACK_DATA
结构体的指针,其中包含了关于即将执行的 I/O 请求的详细信息,包括操作的类型、缓冲区、目标对象等。
该结构体中的字段可以帮助你获取操作类型(如读取、写入)、请求缓冲区、偏移量等信息。
常用字段:
**Iopb
**:包含请求参数(如偏移量、目标文件、操作缓冲区、长度等)。
**IoStatus
**:可以在回调中设置操作的完成状态,比如可以设置为失败状态来阻止操作。
在 PreOperation
回调中,开发者可以使用此结构来修改请求的参数,记录日志,或阻止操作。
**PCFLT_RELATED_OBJECTS FltObjects
**(输入):
FltObjects
是指向 FLT_RELATED_OBJECTS
结构体的指针,包含了与当前 I/O 操作有关的对象(例如文件、实例、卷等)。
常用字段包括:
**Instance
**:当前过滤器的实例,可以用来判断在哪个卷上处理该请求。
**FileObject
**:与当前操作相关的文件对象,表示目标文件。
**Filter
**:当前操作所在的过滤器对象。
FltObjects
提供了当前操作的上下文信息,便于判断目标文件、卷或过滤器实例的信息,并对特定的文件或卷执行条件处理。
**PVOID \*CompletionContext
**(输出):
CompletionContext
是一个指向指针的输出参数,用于在 PreOperation
和 PostOperation
之间传递自定义的上下文信息。
你可以在 PreOperation
中分配一个上下文结构,将其地址赋给 CompletionContext
,后续会将这个上下文指针传递给 PostOperation
回调,以便在操作完成时执行进一步处理或清理工作。
若不需要传递上下文,设置为 NULL
即可。
返回值: FLT_PREOP_SUCCESS_WITH_CALLBACK`
表示预操作处理成功,并且希望在该操作完成后调用 PostOperation
回调。
适用于希望在操作结束后执行进一步处理的情况。
会往下传递
在返回 FLT_PREOP_SUCCESS_WITH_CALLBACK
后,操作会继续往下传递 ,传递到文件系统驱动等下层组件后再执行实际的 I/O 操作。等到这个操作完成,并且返回到 minifilter 驱动时,才会调用 PostOperation
回调。因此,PostOperation
回调是在 I/O 操作传递到下层并执行完成之后 才会执行的。
FLT_PREOP_SUCCESS_NO_CALLBACK
表示预操作处理成功,但不需要调用 PostOperation
回调。
适用于不需要进一步处理操作完成情况的场景,返回该值可以减少性能开销。
会往下传递
**FLT_PREOP_PENDING **
表示该操作尚未完成,需要异步处理。
返回此值时,开发者需自行完成 I/O 操作,并调用 FltCompletePendedPreOperation
来通知过滤器框架操作已完成。
适用于异步处理或复杂操作场景。
FLT_PREOP_DISALLOW_FASTIO
表示不允许此操作通过快速 I/O (Fast I/O) 通道执行,操作将转至标准 I/O 路径。
适用于需要绕过快速路径以保证一致性或进行特定处理的场景。
FLT_PREOP_COMPLETE
表示立即完成此操作,不再传递给下层文件系统。
可用于阻止操作,或自行完成处理(例如返回一个特定错误状态)。
IoStatus.Status
和 IoStatus.Information
应在 FLT_CALLBACK_DATA
中设置,以指示操作的结果。
一般我们在Pre做 SandBox,主防,加解密,杀毒引擎等
Pre函数一般通常在 PASSIVE LEVEL(低级别) 或 APC LEVEL 执行。大部分情况下,它是在 PASSIVE LEVEL 下调用的
Post回调: 1 2 3 4 5 6 7 8 9 PFLT_POST_OPERATION_CALLBACK PfltPostOperationCallback; FLT_POSTOP_CALLBACK_STATUS PfltPostOperationCallback ( [in, out] PFLT_CALLBACK_DATA Data, [in] PCFLT_RELATED_OBJECTS FltObjects, [in, optional] PVOID CompletionContext, [in] FLT_POST_OPERATION_FLAGS Flags ) {...}
参数说明
**PFLT_CALLBACK_DATA Data
**(输入/输出):
Data
是指向 FLT_CALLBACK_DATA
结构体的指针,包含了有关正在处理的 I/O 操作的详细信息,例如请求的类型、目标对象、缓冲区信息等。
FLT_CALLBACK_DATA
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 中常用字段包括: - **`Iopb`**:请求参数块,包含当前操作的详细信息,如操作的文件对象、文件偏移量、缓冲区地址、长度等。 - **`IoStatus`**:用于表示操作的状态,如成功、失败等。 - **`RequestorMode`**:指定了请求的模式,用户模式或内核模式。 - 通过这个结构体,开发者可以读取、修改 I/O 请求的数据或状态。 2. **`PCFLT_RELATED_OBJECTS FltObjects`**(输入): - `FltObjects` 是指向 `FLT_RELATED_OBJECTS` 结构体的指针,该结构体包含了与当前 I/O 操作相关的对象指针。 - 其中包括: - **`Instance`**:当前过滤器的实例,表示当前操作在哪个卷上执行。 - **`FileObject`**:文件对象指针,代表当前操作的目标文件。 - **`Filter`**:当前调用的过滤器对象。 - **`Volume`**:表示此 I/O 操作所在的卷对象。 - 通过这些对象,开发者可以获取到有关当前操作目标的上下文,从而实现对卷、文件的操作和判断。 3. **`PVOID CompletionContext`**(可选): - `CompletionContext` 用于在前后操作回调之间传递自定义的上下文信息。通常由前置回调函数 (`PreOperation`) 提供,用来在后置回调函数中完成某些清理工作或共享信息。 - 该值的类型是 `PVOID`,因此可以指向任何类型的数据结构,比如某种操作的状态信息、标志位等。 4. **`FLT_POST_OPERATION_FLAGS Flags`**(输入): - `Flags` 是 `FLT_POST_OPERATION_FLAGS` 枚举,指示了操作完成时的一些附加信息或标志。 - 主要标志: - **`FLTFL_POST_OPERATION_DRAINING`**:表示驱动栈正在关闭,回调可能在清理过程中被调用。 - **`FLTFL_POST_OPERATION_SYNCHRONIZE`**:指示后置回调是同步的,即与原始操作同一线程执行。 #### **返回值:** **FLT_POSTOP_FINISHED_PROCESSING** 表示后操作处理已完成,不需要进一步处理。 适用于无需延迟操作或后续异步处理的情况。 微筛选器驱动程序已完成 I/O 操作的处理,并将操作的控制权返回给筛选器管理器。 **FLT_POSTOP_MORE_PROCESSING_REQUIRED** 表示需要进一步的处理,通常用于异步操作。 在 `PostOperation` 回调函数中返回 `FLT_POSTOP_MORE_PROCESSING_REQUIRED` 后,Minifilter 框架会等待 minifilter 驱动通过调用特定的 API 来指示何时完成进一步的处理。通常情况下,你需要在另一个上下文中(例如在工作线程或其他延迟执行的环境中)完成进一步的处理,最后通过调用 `FltCompletePendedPostOperation` 来通知框架该操作处理已结束。 ```c++ FLT_POSTOP_CALLBACK_STATUS PostOperationCallback( PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID CompletionContext, FLT_POST_OPERATION_FLAGS Flags ) { // 判断是否需要延迟处理 if (需要延迟处理) { // 启动异步处理(例如将工作项加入系统队列) FltQueueDeferredIoWorkItem(...); // 返回 MORE_PROCESSING_REQUIRED,表明稍后会完成操作 return FLT_POSTOP_MORE_PROCESSING_REQUIRED; } // 立即完成操作的情况 return FLT_POSTOP_FINISHED_PROCESSING; } // 在工作线程中完成延迟处理 VOID DeferredPostOperationWorkItemRoutine(PFLT_CALLBACK_DATA Data) { // 进行延迟处理... // 处理完成后调用 FltCompletePendedPostOperation 完成操作 FltCompletePendedPostOperation(Data); }
判断Data是什么操作的宏: FLT_IS_IRP_OPERATION
: 判断操作是否为 I/O 请求包(IRP)。 IRP 操作涉及标准文件系统操作,例如文件创建、读写、关闭等。
FLT_IS_FASTIO_OPERATION
: 判断操作是否为快速 I/O(Fast I/O)请求。 Fast I/O 是一种优化方式,绕过常规 IRP 通道执行文件操作,适用于较简单的读写操作。
``FLT_IS_FS_FILTER_OPERATION`: 判断操作是否为文件系统过滤操作(FS Filter)。 FS Filter 操作通常用于文件系统驱动间的特殊通信,处理缓存、加密、压缩等请求。
Minifilter的启动 简化代码如下
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 NTSTATUS initFileMonitor (PDRIVER_OBJECT DriverObject) { return FltRegisterFilter ( DriverObject, &fileMonitorRegistration, &g_pFilter ); } NTSTATUS startFileMonitor (PDRIVER_OBJECT DriverObject) { if (g_pFilter) { return FltStartFiltering (g_pFilter); } return STATUS_INSUFFICIENT_RESOURCES; } NTSTATUS stopFileMonitor (PDRIVER_OBJECT DriverObject) { if (g_pFilter) { FltUnregisterFilter (g_pFilter); g_pFilter = NULL ; } }
Nullfilter安装 inf文件安装 首先我们要知道什么叫inf
文件
在Windows文件系统的Minifilter驱动程序中,.inf
文件是安装信息文件,它用来描述驱动的安装方式,包括注册表设置、文件拷贝、设备驱动安装等。总之就是.inf
文件会告诉系统如何安装和配置Minifilter驱动。它可以包含以下信息:
服务注册 :定义服务的名称、类型、启动方式等。
拷贝文件 :指定驱动文件(.sys
文件)和其他必要的文件应该复制到什么位置。
注册表设置 :配置与驱动相关的注册表键值,如用于Minifilter驱动的实例化和加载的配置信息。
安装顺序 :定义在系统加载时如何初始化和加载Minifilter。
.inf
文件的核心作用就是简化和自动化Minifilter驱动的安装过程,避免手动配置注册表或移动文件。
我们不必自己写,了解下原理拿别人的稍微改改即可
首先我们来分析一下nullfilter的inf文件
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 ;;; ;;; NullFilter ;;; ;;; ;;; Copyright (c) 1999 - 2002, Microsoft Corporation ;;; [Version] Signature = "$Windows NT$" ;标识安装文件格式适用于 Windows NT。 Class = "ActivityMonitor" ;指定驱动的类别名称为 "ActivityMonitor",用于表示这是一个监控类的文件系统驱动。 ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;为该类指定唯一的 GUID 标识符。 Provider = %ProviderString% ;提供商的字符串信息,此处定义为 TODO-Set-Provider。 DriverVer = 06/16/2007,1.0.0.0;驱动的版本信息,包含日期和版本号。 CatalogFile = nullfilter.cat;安装时需要的 .cat 文件(nullfilter.cat),用于驱动签名和验证。 PnpLockdown = 1;指定即插即用设备的限制级别。 ;定义源文件和安装盘的信息,nullfilter.sys 是主驱动文件。 [SourceDisksFiles] nullfilter.sys = 1,, [SourceDisksNames] 1 = %DiskId1%,,, [DestinationDirs] ;12 代表 system32\drivers,13 代表驱动程序存储目录。 NullFilterDownlevel.CopyDriverFiles = 12 ;%windir%\system32\drivers NullFilterDownlevel.DelDriverFiles = 12 ;%windir%\system32\drivers NullFilter.DriverFiles = 13 ;driver store ;; ;; Default install sections ;; [DefaultInstall.NTamd64.10.0...25952] OptionDesc = %ServiceDescription% CopyFiles = NullFilter.DriverFiles [DefaultInstall.NTamd64.10.0...25952.Services] AddService = %ServiceName%,,NullFilter.Service ; ; ; Support sections ; [NullFilter.Service] DisplayName = %ServiceName% Description = %ServiceDescription% ServiceBinary = %13%\%DriverName%.sys ;%windir%\system32\drivers\ 指定驱动文件的位置。 Dependencies = "FltMgr" ;表示依赖的服务,此处是 "FltMgr"(文件系统过滤管理器)。 ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER 表示文件系统驱动。 StartType = 3 ;SERVICE_DEMAND_START 表示按需启动(即手动启动)。 ErrorControl = 1 ;SERVICE_ERROR_NORMAL LoadOrderGroup = "FSFilter Activity Monitor" ;设置加载顺序组为 "FSFilter Activity Monitor"。 AddReg = NullFilter.AddRegistry ;指定与服务相关的注册表键(由 NullFilter.AddRegistry 定义) [NullFilter.AddRegistry] ;配置该驱动实例的注册表信息。 HKR,"Parameters","SupportedFeatures",0x00010001,0x3 ;设置驱动支持的功能。 HKR,"Parameters\Instances","DefaultInstance",0x00000000,%DefaultInstance% ;设置默认实例名称。 HKR,"Parameters\Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% ;配置实例的 "Altitude" 和 "Flags" 属性,用于控制Minifilter驱动的加载顺序和自动附加行为。 HKR,"Parameters\Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% [NullFilter.DriverFiles] %DriverName%.sys ;定义复制到目标系统的驱动文件(nullfilter.sys)。 ;; ;; Downlevel default install sections ;; ;下层支持安装部分(Downlevel Sections) ;这些部分用于向旧版 Windows 提供兼容支持,定义了下层系统的驱动文件复制、服务配置等。 [DefaultInstall.NTamd64] OptionDesc = %ServiceDescription% CopyFiles = NullFilterDownlevel.CopyDriverFiles [DefaultInstall.NTamd64.Services] AddService = %ServiceName%,,NullFilterDownlevel.Service ; 下层支持卸载部分 [DefaultUninstall.NTamd64] LegacyUninstall = 1 DelFiles = NullFilterDownlevel.DelDriverFiles [DefaultUninstall.NTamd64.Services] DelService = %ServiceName%,0x200 ;确保在删除之前停止服务 ; ; Downlevel support sections ; [NullFilterDownlevel.Service] DisplayName = %ServiceName% Description = %ServiceDescription% ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ Dependencies = "FltMgr" ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER StartType = 3 ;SERVICE_DEMAND_START ErrorControl = 1 ;SERVICE_ERROR_NORMAL LoadOrderGroup = "FSFilter Activity Monitor" AddReg = NullFilterDownlevel.AddRegistry [NullFilterDownlevel.AddRegistry] HKR,,"SupportedFeatures",0x00010001,0x3 HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% [NullFilterDownlevel.CopyDriverFiles] %DriverName%.sys [NullFilterDownlevel.DelDriverFiles] %DriverName%.sys ;; ;; String Section ;; [Strings] ;该部分定义了 .inf 文件中使用的字符串变量。 ProviderString = "TODO-Set-Provider" ServiceDescription = "NullFilter mini-filter driver" ServiceName = "NullFilter" DriverName = "NullFilter" DiskId1 = "NullFilter Device Installation Disk" ;Instances specific information. DefaultInstance = "Null Instance" Instance1.Name = "Null Instance" Instance1.Altitude = "370020" Instance1.Flags = 0x1 ; Suppress automatic attachments
以上有一个很坑的点,将所有 NT$ARCH$ 替换为 NTamd64(表示 x64 架构)。从Github脱下来的代码都是ARCH的,这一点要自己手动改改。
[Version]中的class
和GUID
查看以下链接
为供应商提供的系统定义的设备安装程序类 - Windows drivers | Microsoft Learn
如果是自己写的minifilter
,直接在[String]
这里改一改就好了
必须修改的有
**ServiceName
**: 每个 Minifilter 在系统中需要唯一的服务名称,这样才能在服务控制器中分别管理它们。
**DriverName
**不同的 Minifilter 需要有各自唯一的驱动文件名,以防止文件名冲突。
Instance1.Name
和 **DefaultInstance
**: 每个 Minifilter 实例在系统中也需要唯一的名称。
**Instance1.Altitude
**: Minifilter 驱动的 Altitude
值必须是唯一的,以确定它在文件过滤栈中的相对位置。
然后把编译好的驱动,和Inf文件放在同一个文件夹
在inf文件右键,即可安装
具体有没有成功,可以看/Windows/System32/Driver目录里面有没有我们的nullfilter.sys,这里可以看到已经进入目录了
然后再看看注册表
.inf
文件中没有明确指定 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\
这一完整路径,因为它是安装服务的默认注册表路径。Windows 系统会自动将所有在 [Service]
和 [AddService]
部分指定的服务,注册到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\
下。
都做好之后,就是安装这个nullfilter了
用管理员权限打开cmd,安装的命令是
卸载的命令是:
可执行程序安装 后续补….
参数数据的获取 从PFLT_CALLBACK_DATA获取 从PFLT_CALLBACK_DATA
这个结构我们能拿到什么?
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 #include <fltKernel.h> PFLT_CALLBACK_DATA Data; PEPROCESS processObject = nullptr ; if (Data->Thread){ processObject = IoThreadToProcess (Data->Thread); } else { processObject = PsGetCurrentProcess (); } ULONG pid = HandleToUlong (PsGetProcessId (processObject)); Data->IoStatus.Status = STATUS_ACCESS_DENIED; Data->IoStatus.Information = 0 ; PFLT_VOLUME Volume = FltObjects->Volume; PFLT_INSTANCE Instance = FltObjects->Instance; PFILE_OBJECT FileObject = FltObjects->FileObject; PDEVICE_OBJECT DeviceObject = FileObject->DeviceObject; PMDL pReadMdl = Data->Iopb->Parameters.Read.MdlAddress; PVOID pReadBuffer = Data->Iopb->Parameters.Read.ReadBuffer; ULONG uReadLength = Data->Iopb->Parameters.Read.Length; ACCESS_MASK DesiredAccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess; PVOID pQueryBuffer = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer; ULONG uQueryBufferSize = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length; return FLT_PREOP_COMPLETE;
当前进程信息 , I/O 状态信息 , 卷、实例、文件对象及设备对象 , 读取操作相关信息 , 安全上下文 , 查询操作相关信息 , 返回状态
获取操作的文件路径 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 PFLT_FILE_NAME_INFORMATION pNameInfo = NULL ; ntStatus = FltGetFileNameInformation ( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pNameInfo ); FltParseFileNameInformation (pNameInfo);pNameInfo->Name; pNameInfo->Volume; FltReleaseFileNameInformation (pNameInfo);PFILE_RENAME_INFORMATION pFileRenameInformation = (PFILE_RENAME_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer; FltGetDestinationFileNameInformation
FltGetFileNameInformation
:此函数通过 Data
(即 PFLT_CALLBACK_DATA
类型)来提取文件名信息,并将信息存储在 pNameInfo
指针指向的结构中。参数 FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT
指示返回标准化的路径。 FltParseFileNameInformation
:进一步解析文件名信息,将提取的内容填入 pNameInfo
的 Name
和 Volume
字段。
FltReleaseFileNameInformation
:在使用完 pNameInfo
后,必须调用此函数来释放分配的内存,避免内存泄漏。
重命名路径的获取 :当文件被重命名时,需要获取新的文件名信息。代码通过 PFILE_RENAME_INFORMATION
类型的指针 pFileRenameInformation
来访问新文件名信息。
Minifilter上下文 上下文的介绍和分类 Context上下文:其实就是附着在某个对象上的一段内存,内存缓存相关数据,这段数据由自己定义
举个例子,对象就是一个人,上下文就是对应这个人的口袋,里面有钱包,手机啥的,这是一个人几乎必须要带在身上的,而不是需要的时候再冲回家拿
Stream Context :
这是绑定在FILE CONTROL BLOCK
的上下文,文件和FCB是一对一的关系,FCB主要用于文件系统内部, 存储文件的物理信息、文件属性、大小、路径等 。用于处理与磁盘存储相关的操作,像文件的读写、位置定位等。
FCB 主要与文件系统相关,存储文件的磁盘元数据,关注文件的物理存储和属性,管理文件的全局状态。它与文件系统的底层操作紧密相关。
Stream Handle Context
绑定在FO(File Object)的上下文,一个文件可以对应多个FO,属于一对多的关系。File Object存储文件的访问信息、当前读写位置、文件句柄、权限等
FO 是文件在内存中的表示,关注文件在进程中的使用和访问。每个打开的文件在每个进程中都会有一个 FO
,它存储了文件的状态、读写偏移、句柄等。
Instance Context
与 Minifilter 实例相关的上下文,允许您存储有关 Minifilter 驱动程序实例的全局状态信息。
Volume Context
在一个与磁盘卷相关的过滤操作中,Volume Context
可以用于存储与该卷的 I/O 操作、缓存状态、挂载信息等相关的上下文。
用于与磁盘卷相关的上下文,通常在与卷的操作(例如卷的挂载或卸载)相关时使用。
FltAllocateContext
用于分配一个新的上下文对象。 这是一块内存,存着某个对象的具体信息
1 2 3 4 5 6 7 8 9 PFLT_CONTEXT streamContext; NTSTATUS status; status = FltAllocateContext ( gFilterHandle, FLT_STREAM_CONTEXT, sizeof (MY_STREAM_CONTEXT), NonPagedPool, &streamContext );
注册上下文的过程: 1.定义上下文类型:
可以通过 FLT_CONTEXT_REGISTRATION 数组来定义您的上下文类型,并将其传递给 FltRegisterContext。还可以创建上下文回调函数 (可选)
1 2 3 4 5 6 7 8 9 10 11 FLT_CONTEXT_REGISTRATION ContextRegistration[] = { { sizeof (FLT_CONTEXT_REGISTRATION), FLT_STREAM_CONTEXT, StreamContextCleanup, StreamContextAllocate, 0 , NULL }, {0 } };
这里的Cleanup是用来清理申请到的指针,不是用来清理自己上下文的
2.注册上下文
1 2 3 4 5 6 7 8 9 NTSTATUS Status; Status = FltRegisterContext ( Filter, &ContextRegistration[0 ], &gStreamContextRegHandle ); if (!NT_SUCCESS (Status)) { }
分配和释放上下文
1 2 3 4 5 6 7 8 9 10 11 12 13 PFLT_CONTEXT pContext = NULL ; Status = FltAllocateContext ( Filter, FLT_STREAM_CONTEXT, &pContext ); if (NT_SUCCESS (Status)) { } FltReleaseContext (pContext);
Context的使用示例 代码:
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 typedef struct _INSTANCE_CONTEXT { ... } INSTANCE_CONTEXT, *PINSTANCE_CONTEXT; PINSTANCE_CONTEXT pContext = NULL ; ntStatus = FltGetInstanceContext (FltObjects->Instance, &pContext); if (NT_SUCCESS (ntStatus) == FALSE) { ntStatus = FltAllocateContext (g_Filter, FLT_INSTANCE_CONTEXT, sizeof (INSTANCE_CONTEXT), PagedPool, &pContext); if (NT_SUCCESS (Status) == FALSE) { return ntStatus; } RtlZeroMemory (pContext, sizeof (INSTANCE_CONTEXT)); } pContext->m_DeviceType = VolumeDeviceType; pContext->m_FSType = VolumeFilesystemType; FltSetInstanceContext (FltObjects->Instance, FLT_SET_CONTEXT_REPLACE_IF_EXISTS, pContext, NULL ); if (pContext) { FltReleaseContext (pContext); } PINSTANCE_CONTEXT pContext2 = NULL ; Status = FltGetInstanceContext (FltObjects->Instance, &pContext2); pContext2->xxx = xxx;
拿到上下文记得要Release掉
R3和R0的通讯 R3给R0发信息 驱动代码:
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 #include <fltKernel.h> #include <dontuse.h> PFLT_PORT g_ServerPort = NULL ; PFLT_PORT g_ClientPort = NULL ; NTSTATUS MinifilterMessageCallback ( PVOID ConnectionCookie, PVOID InputBuffer, ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, PULONG ReturnOutputBufferLength ) { UNREFERENCED_PARAMETER (ConnectionCookie); DbgPrint ("Received message from user mode\n" ); if (OutputBuffer && OutputBufferSize >= sizeof ("Hello from Kernel" )) { RtlCopyMemory (OutputBuffer, "Hello from Kernel" , sizeof ("Hello from Kernel" )); *ReturnOutputBufferLength = sizeof ("Hello from Kernel" ); } else { return STATUS_BUFFER_TOO_SMALL; } return STATUS_SUCCESS; } VOID MinifilterConnectCallback ( PVOID ConnectionCookie, PFLT_PORT ClientPort ) { UNREFERENCED_PARAMETER (ConnectionCookie); g_ClientPort = ClientPort; DbgPrint ("User mode connected\n" ); } VOID MinifilterDisconnectCallback ( PVOID ConnectionCookie ) { UNREFERENCED_PARAMETER (ConnectionCookie); if (g_ClientPort) { FltCloseClientPort (g_FilterHandle, &g_ClientPort); g_ClientPort = NULL ; } DbgPrint ("User mode disconnected\n" ); } NTSTATUS DriverEntry ( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath ) { UNICODE_STRING portName = RTL_CONSTANT_STRING (L"\\MinifilterPort" ); FLT_REGISTRATION FilterRegistration = { sizeof (FLT_REGISTRATION), FLT_REGISTRATION_VERSION }; NTSTATUS status = FltRegisterFilter (DriverObject, &FilterRegistration, &g_FilterHandle); if (!NT_SUCCESS (status)) { return status; } status = FltCreateCommunicationPort ( g_FilterHandle, &g_ServerPort, &portName, NULL , MinifilterConnectCallback, MinifilterDisconnectCallback, MinifilterMessageCallback, 1 ); if (NT_SUCCESS (status)) { FltStartFiltering (g_FilterHandle); } else { FltUnregisterFilter (g_FilterHandle); } return status; } VOID DriverUnload ( PDRIVER_OBJECT DriverObject ) { if (g_ServerPort) { FltCloseCommunicationPort (g_ServerPort); } FltUnregisterFilter (g_FilterHandle); }
全局变量 :
g_ServerPort
:保存服务器端通信端口的句柄。
g_ClientPort
:保存客户端连接的端口句柄。
MinifilterMessageCallback :
此回调函数处理从用户模式发送的消息。
当用户模式程序发送消息到此端口时,MinifilterMessageCallback
会被调用。
该函数检查输出缓冲区是否足够大,如果足够大,就将 “Hello from Kernel” 这条消息复制到输出缓冲区中返回给用户模式程序。
MinifilterConnectCallback :
此回调函数在用户模式程序连接到通信端口时被调用。
g_ClientPort
被设置为当前的客户端端口句柄,以便后续通信使用。
打印调试信息表示连接已建立。
MinifilterDisconnectCallback :
此回调函数在用户模式程序断开连接时被调用。
如果 g_ClientPort
存在(即有一个连接),则关闭该连接端口并将其指针设置为 NULL。
打印调试信息表示连接已断开。
DriverEntry :
驱动的入口函数,初始化 Minifilter 并创建通信端口。
通过调用 FltRegisterFilter
注册过滤器。
使用 FltCreateCommunicationPort
创建通信端口,并提供连接、断开连接和消息处理的回调函数。
如果通信端口创建成功,则调用 FltStartFiltering
启动过滤器。
如果通信端口创建失败,则注销过滤器。
三环代码:
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 #include <windows.h> #include <fltUser.h> #include <iostream> int main () { HANDLE hPort = NULL ; HRESULT hr = FilterConnectCommunicationPort ( L"\\MinifilterPort" , 0 , NULL , 0 , NULL , &hPort ); if (FAILED (hr)) { std::cerr << "Failed to connect to port. Error: " << hr << std::endl; return 1 ; } char outputBuffer[100 ] = {0 }; DWORD bytesReturned; hr = FilterSendMessage ( hPort, "Hello from User Mode" , (DWORD)strlen ("Hello from User Mode" ) + 1 , outputBuffer, sizeof (outputBuffer), &bytesReturned ); if (SUCCEEDED (hr)) { std::cout << "Received from kernel: " << outputBuffer << std::endl; } else { std::cerr << "Failed to send message. Error: " << hr << std::endl; } CloseHandle (hPort); return 0 ; }
R0给R3发信息 三环代码:
用户模式程序首先需要使用 FilterCreateCommunicationPort
创建一个通信端口,并等待来自内核模式的消息。通过 FilterGetMessage
来获取内核模式发送的消息。
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 #include <windows.h> #include <fltUser.h> #include <iostream> #define BUFFER_SIZE 256 int main () { HRESULT hr; HANDLE hPort; HANDLE hCompletionPort; DWORD bytesReturned; ULONG_PTR completionKey; LPOVERLAPPED pOvlp; char receiveBuffer[BUFFER_SIZE] = { 0 }; hr = FilterConnectCommunicationPort ( L"\\MinifilterPort" , 0 , NULL , 0 , NULL , &hPort ); if (FAILED (hr)) { std::cerr << "Failed to connect to port. Error: " << hr << std::endl; return 1 ; } hCompletionPort = CreateIoCompletionPort (hPort, NULL , 0 , 1 ); if (hCompletionPort == NULL ) { std::cerr << "Failed to create I/O completion port. Error: " << GetLastError () << std::endl; CloseHandle (hPort); return 1 ; } std::cout << "Waiting for messages from kernel..." << std::endl; while (true ) { if (GetQueuedCompletionStatus (hCompletionPort, &bytesReturned, &completionKey, &pOvlp, INFINITE)) { std::cout << "Received message from kernel: " << receiveBuffer << std::endl; } else { std::cerr << "Failed to receive message. Error: " << GetLastError () << std::endl; break ; } } CloseHandle (hPort); CloseHandle (hCompletionPort); return 0 ; }
内核代码:
内核模式 Minifilter 代码使用 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 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 #include <fltKernel.h> #include <dontuse.h> PFLT_PORT g_ServerPort = NULL ; PFLT_PORT g_ClientPort = NULL ; NTSTATUS MinifilterConnectCallback ( PVOID ConnectionCookie, PFLT_PORT ClientPort ) { UNREFERENCED_PARAMETER (ConnectionCookie); g_ClientPort = ClientPort; DbgPrint ("User mode connected\n" ); return STATUS_SUCCESS; } VOID MinifilterDisconnectCallback ( PVOID ConnectionCookie ) { UNREFERENCED_PARAMETER (ConnectionCookie); if (g_ClientPort) { FltCloseClientPort (g_FilterHandle, &g_ClientPort); g_ClientPort = NULL ; } DbgPrint ("User mode disconnected\n" ); } VOID SendMessageToUserMode ( const char * message ) { if (g_ClientPort) { ULONG bytesWritten; NTSTATUS status = FltSendMessage ( g_FilterHandle, &g_ClientPort, (PVOID)message, (ULONG)strlen (message) + 1 , NULL , 0 , &bytesWritten ); if (NT_SUCCESS (status)) { DbgPrint ("Message sent to user mode: %s\n" , message); } else { DbgPrint ("Failed to send message. Status: 0x%X\n" , status); } } else { DbgPrint ("No user mode client connected.\n" ); } } NTSTATUS DriverEntry ( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath ) { UNICODE_STRING portName = RTL_CONSTANT_STRING (L"\\MinifilterPort" ); FLT_REGISTRATION FilterRegistration = { sizeof (FLT_REGISTRATION), FLT_REGISTRATION_VERSION }; NTSTATUS status = FltRegisterFilter (DriverObject, &FilterRegistration, &g_FilterHandle); if (!NT_SUCCESS (status)) { return status; } status = FltCreateCommunicationPort ( g_FilterHandle, &g_ServerPort, &portName, NULL , MinifilterConnectCallback, MinifilterDisconnectCallback, NULL , 1 ); if (NT_SUCCESS (status)) { FltStartFiltering (g_FilterHandle); } else { FltUnregisterFilter (g_FilterHandle); } SendMessageToUserMode ("Hello from Kernel" ); return status; } VOID DriverUnload ( PDRIVER_OBJECT DriverObject ) { if (g_ServerPort) { FltCloseCommunicationPort (g_ServerPort); } FltUnregisterFilter (g_FilterHandle); }
FltSendMessage是一个同步的API
当 FltSendMessage
发送消息给用户模式进程时,用户模式进程需要通过 FilterGetMessage
来接收这个消息并进行处理。如果用户模式进程没有及时接收到消息或没有回复(例如,用户模式进程被挂起、退出、或处理速度较慢),FltSendMessage
就会等待用户模式的响应,直到超时设定的时间到达。
同步:不拿到数据不返回 同步也分阻塞和非阻塞,阻塞就是进入休眠,直到被唤醒,非阻塞就是轮询
scanner分析 微软的官方示例MInifilter-Scanner 是实现了一个文件扫描器的功能,实时监控文件并检测文件中是否含有预设特征。
一共三次扫描,具体分别是在 创建后扫描,写之前扫描,写关闭扫描
驱动代码分析 函数作用预览 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 PFN_IoOpenDriverRegistryKey ScannerGetIoOpenDriverRegistryKey ( VOID ) ;NTSTATUS ScannerOpenServiceParametersKey ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING ServiceRegistryPath, _Out_ PHANDLE ServiceParametersKey ) ;NTSTATUS ScannerInitializeScannedExtensions ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) ;VOID ScannerFreeExtensions ( ) ;NTSTATUS ScannerAllocateUnicodeString ( _Inout_ PUNICODE_STRING String ) ;VOID ScannerFreeUnicodeString ( _Inout_ PUNICODE_STRING String ) ;NTSTATUS ScannerPortConnect ( _In_ PFLT_PORT ClientPort, _In_opt_ PVOID ServerPortCookie, _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext, _In_ ULONG SizeOfContext, _Outptr_result_maybenull_ PVOID *ConnectionCookie ) ;VOID ScannerPortDisconnect ( _In_opt_ PVOID ConnectionCookie ) ;NTSTATUS ScannerpScanFileInUserMode ( _In_ PFLT_INSTANCE Instance, _In_ PFILE_OBJECT FileObject, _Out_ PBOOLEAN SafeToOpen ) ;BOOLEAN ScannerpCheckExtension ( _In_ PUNICODE_STRING Extension ) ;
具体分析 以下分析的都写在注释上
ScannerInitializeScannedExtensions
函数
这里的参数_In_ PUNICODE_STRING RegistryPath
是来自DriverEntry的RegisterPath
,注册表路径一般默认就是HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\xxYourDriver
我们可以从inf文件中读取到注册表的内容
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 NTSTATUS ScannerInitializeScannedExtensions ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { NTSTATUS status; HANDLE driverRegKey = NULL ; UNICODE_STRING valueName; PKEY_VALUE_PARTIAL_INFORMATION valueBuffer = NULL ; ULONG valueLength = 0 ; PWCHAR ch; SIZE_T length; ULONG count; PUNICODE_STRING ext; PAGED_CODE (); ScannedExtensions = NULL ; ScannedExtensionCount = 0 ; status = ScannerOpenServiceParametersKey ( DriverObject, RegistryPath, &driverRegKey ); if (!NT_SUCCESS ( status )) { driverRegKey = NULL ; goto ScannerInitializeScannedExtensionsCleanup; } RtlInitUnicodeString ( &valueName, L"Extensions" ); status = ZwQueryValueKey ( driverRegKey, &valueName, KeyValuePartialInformation, NULL , 0 , &valueLength ); if (status!=STATUS_BUFFER_TOO_SMALL && status!=STATUS_BUFFER_OVERFLOW) { status = STATUS_INVALID_PARAMETER; goto ScannerInitializeScannedExtensionsCleanup; } valueBuffer = ExAllocatePoolZero ( NonPagedPool, valueLength, SCANNER_REG_TAG ); if (valueBuffer == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; goto ScannerInitializeScannedExtensionsCleanup; } status = ZwQueryValueKey ( driverRegKey, &valueName, KeyValuePartialInformation, valueBuffer, valueLength, &valueLength ); if (!NT_SUCCESS ( status )) { goto ScannerInitializeScannedExtensionsCleanup; } ch = (PWCHAR)(valueBuffer->Data); count = 0 ; while (*ch != '\0' ) { ch = ch + wcslen ( ch ) + 1 ; count++; } ScannedExtensions = ExAllocatePoolZero ( PagedPool, count * sizeof (UNICODE_STRING), SCANNER_STRING_TAG ); if (ScannedExtensions == NULL ) { goto ScannerInitializeScannedExtensionsCleanup; } ch = (PWCHAR)((PKEY_VALUE_PARTIAL_INFORMATION)valueBuffer->Data); ext = ScannedExtensions; while (ScannedExtensionCount < count) { length = wcslen ( ch ) * sizeof (WCHAR); ext->MaximumLength = (USHORT) length; status = ScannerAllocateUnicodeString ( ext ); if (!NT_SUCCESS ( status )) { goto ScannerInitializeScannedExtensionsCleanup; } ext->Length = (USHORT)length; RtlCopyMemory ( ext->Buffer, ch, length ); ch = ch + length/sizeof (WCHAR) + 1 ; ScannedExtensionCount++; ext++; } ScannerInitializeScannedExtensionsCleanup: if (valueBuffer != NULL ) { ExFreePoolWithTag ( valueBuffer, SCANNER_REG_TAG ); valueBuffer = NULL ; } if (driverRegKey != NULL ) { ZwClose ( driverRegKey ); } if (!NT_SUCCESS ( status )) { ScannerFreeExtensions (); } return status; }
DriverEntry
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 NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { OBJECT_ATTRIBUTES oa; UNICODE_STRING uniString; PSECURITY_DESCRIPTOR sd; NTSTATUS status; ExInitializeDriverRuntime ( DrvRtPoolNxOptIn ); status = FltRegisterFilter ( DriverObject, &FilterRegistration, &ScannerData.Filter ); if (!NT_SUCCESS ( status )) { return status; } status = ScannerInitializeScannedExtensions ( DriverObject, RegistryPath ); if (!NT_SUCCESS ( status )) { status = STATUS_SUCCESS; ScannedExtensions = &ScannedExtensionDefault; ScannedExtensionCount = 1 ; } RtlInitUnicodeString ( &uniString, ScannerPortName ); status = FltBuildDefaultSecurityDescriptor ( &sd, FLT_PORT_ALL_ACCESS ); if (NT_SUCCESS ( status )) { InitializeObjectAttributes ( &oa, &uniString, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL , sd ); status = FltCreateCommunicationPort ( ScannerData.Filter, &ScannerData.ServerPort, &oa, NULL , ScannerPortConnect, ScannerPortDisconnect, NULL , 1 ); FltFreeSecurityDescriptor ( sd ); if (NT_SUCCESS ( status )) { status = FltStartFiltering ( ScannerData.Filter ); if (NT_SUCCESS ( status )) { return STATUS_SUCCESS; } FltCloseCommunicationPort ( ScannerData.ServerPort ); } } ScannerFreeExtensions (); FltUnregisterFilter ( ScannerData.Filter ); return status; }
建立连接回调ScannerPortConnect
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 NTSTATUS ScannerPortConnect ( _In_ PFLT_PORT ClientPort, _In_opt_ PVOID ServerPortCookie, _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext, _In_ ULONG SizeOfContext, _Outptr_result_maybenull_ PVOID *ConnectionCookie ) { PAGED_CODE (); UNREFERENCED_PARAMETER ( ServerPortCookie ); UNREFERENCED_PARAMETER ( ConnectionContext ); UNREFERENCED_PARAMETER ( SizeOfContext); UNREFERENCED_PARAMETER ( ConnectionCookie = NULL ); FLT_ASSERT ( ScannerData.ClientPort == NULL ); FLT_ASSERT ( ScannerData.UserProcess == NULL ); ScannerData.UserProcess = PsGetCurrentProcess (); ScannerData.ClientPort = ClientPort; DbgPrint ( "!!! scanner.sys --- connected, port=0x%p\n" , ClientPort ); return STATUS_SUCCESS; }
断开连接回调ScannerPortDisconnect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 VOID ScannerPortDisconnect ( _In_opt_ PVOID ConnectionCookie ) { UNREFERENCED_PARAMETER ( ConnectionCookie ); PAGED_CODE (); DbgPrint ( "!!! scanner.sys --- disconnected, port=0x%p\n" , ScannerData.ClientPort ); FltCloseClientPort ( ScannerData.Filter, &ScannerData.ClientPort ); ScannerData.UserProcess = NULL ; }
接下来分析Minifilter里面具体注册的IRP回调
IRP_MJ_CREATE
的Pre回调:
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 FLT_PREOP_CALLBACK_STATUS ScannerPreCreate ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) { UNREFERENCED_PARAMETER ( FltObjects ); UNREFERENCED_PARAMETER ( CompletionContext = NULL ); PAGED_CODE (); if (IoThreadToProcess ( Data->Thread ) == ScannerData.UserProcess) { DbgPrint ( "!!! scanner.sys -- allowing create for trusted process \n" ); return FLT_PREOP_SUCCESS_NO_CALLBACK; } return FLT_PREOP_SUCCESS_WITH_CALLBACK; }
IRP_MJ_CREATE
的Post回调:
前面提到过了,在IRP_MJ_CREATE
的Post回调我们会开始我们的第一次扫描验证,让我们看看怎么个
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 FLT_POSTOP_CALLBACK_STATUS ScannerPostCreate ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags ) { PSCANNER_STREAM_HANDLE_CONTEXT scannerContext; FLT_POSTOP_CALLBACK_STATUS returnStatus = FLT_POSTOP_FINISHED_PROCESSING; PFLT_FILE_NAME_INFORMATION nameInfo; NTSTATUS status; BOOLEAN safeToOpen, scanFile; UNREFERENCED_PARAMETER ( CompletionContext ); UNREFERENCED_PARAMETER ( Flags ); if (!NT_SUCCESS ( Data->IoStatus.Status ) || (STATUS_REPARSE == Data->IoStatus.Status)) { return FLT_POSTOP_FINISHED_PROCESSING; } status = FltGetFileNameInformation ( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo ); if (!NT_SUCCESS ( status )) { return FLT_POSTOP_FINISHED_PROCESSING; } FltParseFileNameInformation ( nameInfo ); scanFile = ScannerpCheckExtension ( &nameInfo->Extension ); FltReleaseFileNameInformation ( nameInfo ); if (!scanFile) { return FLT_POSTOP_FINISHED_PROCESSING; } (VOID) ScannerpScanFileInUserMode ( FltObjects->Instance, FltObjects->FileObject, &safeToOpen ); if (!safeToOpen) { DbgPrint ( "!!! scanner.sys -- foul language detected in postcreate !!!\n" ); DbgPrint ( "!!! scanner.sys -- undoing create \n" ); FltCancelFileOpen ( FltObjects->Instance, FltObjects->FileObject ); Data->IoStatus.Status = STATUS_ACCESS_DENIED; Data->IoStatus.Information = 0 ; returnStatus = FLT_POSTOP_FINISHED_PROCESSING; } else if (FltObjects->FileObject->WriteAccess) { status = FltAllocateContext ( ScannerData.Filter, FLT_STREAMHANDLE_CONTEXT, sizeof (SCANNER_STREAM_HANDLE_CONTEXT), PagedPool, &scannerContext ); if (NT_SUCCESS (status)) { scannerContext->RescanRequired = TRUE; (VOID) FltSetStreamHandleContext ( FltObjects->Instance, FltObjects->FileObject, FLT_SET_CONTEXT_REPLACE_IF_EXISTS, scannerContext, NULL ); FltReleaseContext ( scannerContext ); } } return returnStatus; }
将扫描数据发送给三环的函数分析
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 NTSTATUS ScannerpScanFileInUserMode ( _In_ PFLT_INSTANCE Instance, _In_ PFILE_OBJECT FileObject, _Out_ PBOOLEAN SafeToOpen ) { NTSTATUS status = STATUS_SUCCESS; PVOID buffer = NULL ; ULONG bytesRead; PSCANNER_NOTIFICATION notification = NULL ; FLT_VOLUME_PROPERTIES volumeProps; LARGE_INTEGER offset; ULONG replyLength, length; PFLT_VOLUME volume = NULL ; *SafeToOpen = TRUE; if (ScannerData.ClientPort == NULL ) { return STATUS_SUCCESS; } try { status = FltGetVolumeFromInstance ( Instance, &volume ); if (!NT_SUCCESS ( status )) { leave; } status = FltGetVolumeProperties ( volume, &volumeProps, sizeof ( volumeProps ), &length ); if (NT_ERROR ( status )) { leave; } length = max ( SCANNER_READ_BUFFER_SIZE, volumeProps.SectorSize ); buffer = FltAllocatePoolAlignedWithTag ( Instance, NonPagedPool, length, 'nacS' ); if (NULL == buffer) { status = STATUS_INSUFFICIENT_RESOURCES; leave; } notification = ExAllocatePoolZero ( NonPagedPool, sizeof ( SCANNER_NOTIFICATION ), 'nacS' ); if (NULL == notification) { status = STATUS_INSUFFICIENT_RESOURCES; leave; } offset.QuadPart = bytesRead = 0 ; status = FltReadFile ( Instance, FileObject, &offset, length, buffer, FLTFL_IO_OPERATION_NON_CACHED | FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, &bytesRead, NULL , NULL ); if (NT_SUCCESS ( status ) && (0 != bytesRead)) { notification->BytesToScan = (ULONG) bytesRead; RtlCopyMemory ( ¬ification->Contents, buffer, min ( notification->BytesToScan, SCANNER_READ_BUFFER_SIZE ) ); replyLength = sizeof ( SCANNER_REPLY ); status = FltSendMessage ( ScannerData.Filter, &ScannerData.ClientPort, notification, sizeof (SCANNER_NOTIFICATION), notification, &replyLength, NULL ); if (STATUS_SUCCESS == status) { *SafeToOpen = ((PSCANNER_REPLY) notification)->SafeToOpen; } else { DbgPrint ( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n" , status ); } } } finally { if (NULL != buffer) { FltFreePoolAlignedWithTag ( Instance, buffer, 'nacS' ); } if (NULL != notification) { ExFreePoolWithTag ( notification, 'nacS' ); } if (NULL != volume) { FltObjectDereference ( volume ); } } return status; }
接下来是关于IRP_MJ_WRITE
的pre,这将会是进行第二次安全扫描
之所以是Pre而不是Post,是因为如果是Post就代表已经写入了,所以我们需要在Pre,也就是写之前进行对写入数据检测
写拦截,假如我们去拦截可疑特征码:“foul”,但是有个局限性,如果攻击者分开写,依次写入’f’,’o’,’u’,’l’,这下去拦截写就不太好
所以我们要去拦截写关闭,在cleanup的时候进行拦截,对文件进行扫描,具体来说,在Create的时候,可以知道是读还是写,如果是写,那么在Cleanup就会去扫描
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 FLT_PREOP_CALLBACK_STATUS ScannerPreWrite ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) { FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; NTSTATUS status; PSCANNER_NOTIFICATION notification = NULL ; PSCANNER_STREAM_HANDLE_CONTEXT context = NULL ; ULONG replyLength; BOOLEAN safe = TRUE; PUCHAR buffer; UNREFERENCED_PARAMETER ( CompletionContext ); if (ScannerData.ClientPort == NULL ) { return FLT_PREOP_SUCCESS_NO_CALLBACK; } status = FltGetStreamHandleContext ( FltObjects->Instance, FltObjects->FileObject, &context ); if (!NT_SUCCESS ( status )) { return FLT_PREOP_SUCCESS_NO_CALLBACK; } try { if (Data->Iopb->Parameters.Write.Length != 0 ) { if (Data->Iopb->Parameters.Write.MdlAddress != NULL ) { buffer = MmGetSystemAddressForMdlSafe ( Data->Iopb->Parameters.Write.MdlAddress, NormalPagePriority | MdlMappingNoExecute ); if (buffer == NULL ) { Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; Data->IoStatus.Information = 0 ; returnStatus = FLT_PREOP_COMPLETE; leave; } } else { buffer = Data->Iopb->Parameters.Write.WriteBuffer; } notification = ExAllocatePoolZero ( NonPagedPool, sizeof ( SCANNER_NOTIFICATION ), 'nacS' ); if (notification == NULL ) { Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; Data->IoStatus.Information = 0 ; returnStatus = FLT_PREOP_COMPLETE; leave; } notification->BytesToScan = min ( Data->Iopb->Parameters.Write.Length, SCANNER_READ_BUFFER_SIZE ); try { RtlCopyMemory ( ¬ification->Contents, buffer, notification->BytesToScan ); } except ( EXCEPTION_EXECUTE_HANDLER ) { Data->IoStatus.Status = GetExceptionCode () ; Data->IoStatus.Information = 0 ; returnStatus = FLT_PREOP_COMPLETE; leave; } replyLength = sizeof ( SCANNER_REPLY ); status = FltSendMessage ( ScannerData.Filter, &ScannerData.ClientPort, notification, sizeof ( SCANNER_NOTIFICATION ), notification, &replyLength, NULL ); if (STATUS_SUCCESS == status) { safe = ((PSCANNER_REPLY) notification)->SafeToOpen; } else { DbgPrint ( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n" , status ); } } if (!safe) { DbgPrint ( "!!! scanner.sys -- foul language detected in write !!!\n" ); if (!FlagOn ( Data->Iopb->IrpFlags, IRP_PAGING_IO )) { DbgPrint ( "!!! scanner.sys -- blocking the write !!!\n" ); Data->IoStatus.Status = STATUS_ACCESS_DENIED; Data->IoStatus.Information = 0 ; returnStatus = FLT_PREOP_COMPLETE; } } } finally { if (notification != NULL ) { ExFreePoolWithTag ( notification, 'nacS' ); } if (context) { FltReleaseContext ( context ); } } return returnStatus; }
最后,还有一次检查,就是在IRP_MJ_CLEANUP
这个IRP请求
关于什么时候会发送IRP_MJ_CLEANUP
:
文件句柄关闭: 用户程序调用 CloseHandle
或在文件描述符生命周期结束时,Windows 内核会生成一个 IRP_MJ_CLEANUP
请求。这通常发生在文件对象不再需要时,句柄关闭或文件流结束时。
关闭文件对象时的清理 , 当文件系统或文件对象的句柄引用计数降到零时,操作系统会调用驱动程序的 Cleanup
例程来清理资源。
关闭时的流清理:在文件 I/O 操作(如 Read
、Write
)完成后,文件句柄被关闭时,IRP_MJ_CLEANUP
用于指示文件的清理。
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 FLT_PREOP_CALLBACK_STATUS ScannerPreCleanup ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) { NTSTATUS status; PSCANNER_STREAM_HANDLE_CONTEXT context; BOOLEAN safe; UNREFERENCED_PARAMETER ( Data ); UNREFERENCED_PARAMETER ( CompletionContext ); status = FltGetStreamHandleContext ( FltObjects->Instance, FltObjects->FileObject, &context ); if (NT_SUCCESS ( status )) { if (context->RescanRequired) { (VOID) ScannerpScanFileInUserMode ( FltObjects->Instance, FltObjects->FileObject, &safe ); if (!safe) { DbgPrint ( "!!! scanner.sys -- foul language detected in precleanup !!!\n" ); } } FltReleaseContext ( context ); } return FLT_PREOP_SUCCESS_NO_CALLBACK; }
用户层代码分析 scanuser.c
代码实现了一个用户模式程序,用于与过滤驱动进行通信并扫描文件内容。它通过与内核模式的过滤器进行交互来扫描数据,并将扫描结果返回给过滤器。
完整代码如下
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 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 #include <windows.h> #include <stdlib.h> #include <stdio.h> #include <winioctl.h> #include <string.h> #include <crtdbg.h> #include <assert.h> #include <fltuser.h> #include "scanuk.h" #include "scanuser.h" #include <dontuse.h> #define SCANNER_DEFAULT_REQUEST_COUNT 5 #define SCANNER_DEFAULT_THREAD_COUNT 2 #define SCANNER_MAX_THREAD_COUNT 64 UCHAR FoulString[] = "foul" ; typedef struct _SCANNER_THREAD_CONTEXT { HANDLE Port; HANDLE Completion; } SCANNER_THREAD_CONTEXT, *PSCANNER_THREAD_CONTEXT; VOID Usage ( VOID ) { printf ( "Connects to the scanner filter and scans buffers \n" ); printf ( "Usage: scanuser [requests per thread] [number of threads(1-64)]\n" ); } BOOL ScanBuffer ( _In_reads_bytes_(BufferSize) PUCHAR Buffer, _In_ ULONG BufferSize ) { PUCHAR p; ULONG searchStringLength = sizeof (FoulString) - sizeof (UCHAR); for (p = Buffer;p <= (Buffer + BufferSize - searchStringLength);p++) { if (RtlEqualMemory ( p, FoulString, searchStringLength )) { printf ( "Found a string\n" ); return TRUE; } } return FALSE; } DWORD ScannerWorker ( _In_ PSCANNER_THREAD_CONTEXT Context ) { PSCANNER_NOTIFICATION notification; SCANNER_REPLY_MESSAGE replyMessage; PSCANNER_MESSAGE message; LPOVERLAPPED pOvlp; BOOL result; DWORD outSize; HRESULT hr; ULONG_PTR key; #pragma warning (push) #pragma warning (disable:4127) while (TRUE) { #pragma warning (pop) result = GetQueuedCompletionStatus ( Context->Completion, &outSize, &key, &pOvlp, INFINITE ); message = CONTAINING_RECORD ( pOvlp, SCANNER_MESSAGE, Ovlp ); if (!result) { hr = HRESULT_FROM_WIN32 ( GetLastError () ); break ; } printf ( "Received message, size %Id\n" , pOvlp->InternalHigh ); notification = &message->Notification; assert (notification->BytesToScan <= SCANNER_READ_BUFFER_SIZE); _Analysis_assume_(notification->BytesToScan <= SCANNER_READ_BUFFER_SIZE); result = ScanBuffer ( notification->Contents, notification->BytesToScan ); replyMessage.ReplyHeader.Status = 0 ; replyMessage.ReplyHeader.MessageId = message->MessageHeader.MessageId; replyMessage.Reply.SafeToOpen = !result; printf ( "Replying message, SafeToOpen: %d\n" , replyMessage.Reply.SafeToOpen ); hr = FilterReplyMessage ( Context->Port, (PFILTER_REPLY_HEADER) &replyMessage, sizeof ( replyMessage ) ); if (SUCCEEDED ( hr )) { printf ( "Replied message\n" ); } else { printf ( "Scanner: Error replying message. Error = 0x%X\n" , hr ); break ; } memset ( &message->Ovlp, 0 , sizeof ( OVERLAPPED ) ); hr = FilterGetMessage ( Context->Port, &message->MessageHeader, FIELD_OFFSET ( SCANNER_MESSAGE, Ovlp ), &message->Ovlp ); if (hr != HRESULT_FROM_WIN32 ( ERROR_IO_PENDING )) { break ; } } if (!SUCCEEDED ( hr )) { if (hr == HRESULT_FROM_WIN32 ( ERROR_INVALID_HANDLE )) { printf ( "Scanner: Port is disconnected, probably due to scanner filter unloading.\n" ); } else { printf ( "Scanner: Unknown error occured. Error = 0x%X\n" , hr ); } } return hr; } int _cdeclmain ( _In_ int argc, _In_reads_(argc) char *argv[] ) { DWORD requestCount = SCANNER_DEFAULT_REQUEST_COUNT; DWORD threadCount = SCANNER_DEFAULT_THREAD_COUNT; HANDLE threads[SCANNER_MAX_THREAD_COUNT] = { NULL }; SCANNER_THREAD_CONTEXT context; HANDLE port, completion; PSCANNER_MESSAGE messages; DWORD threadId; HRESULT hr; if (argc > 1 ) { requestCount = atoi ( argv[1 ] ); if (requestCount <= 0 ) { Usage (); return 1 ; } if (argc > 2 ) { threadCount = atoi ( argv[2 ] ); } if (threadCount <= 0 || threadCount > 64 ) { Usage (); return 1 ; } } printf ( "Scanner: Connecting to the filter ...\n" ); hr = FilterConnectCommunicationPort ( ScannerPortName, 0 , NULL , 0 , NULL , &port ); if (IS_ERROR ( hr )) { printf ( "ERROR: Connecting to filter port: 0x%08x\n" , hr ); return 2 ; } completion = CreateIoCompletionPort ( port, NULL , 0 , threadCount ); if (completion == NULL ) { printf ( "ERROR: Creating completion port: %d\n" , GetLastError () ); CloseHandle ( port ); return 3 ; } printf ( "Scanner: Port = 0x%p Completion = 0x%p\n" , port, completion ); context.Port = port; context.Completion = completion; messages = calloc (((size_t ) threadCount) * requestCount, sizeof (SCANNER_MESSAGE)); if (messages == NULL ) { hr = ERROR_NOT_ENOUGH_MEMORY; goto main_cleanup; } for (DWORD i = 0 ; i < threadCount; i++) { threads[i] = CreateThread ( NULL , 0 , (LPTHREAD_START_ROUTINE) ScannerWorker, &context, 0 , &threadId ); if (threads[i] == NULL ) { hr = GetLastError (); printf ( "ERROR: Couldn't create thread: %d\n" , hr ); goto main_cleanup; } for (DWORD j = 0 ; j < requestCount; j++) { PSCANNER_MESSAGE msg = &(messages[i * requestCount + j]); memset ( &msg->Ovlp, 0 , sizeof ( OVERLAPPED ) ); hr = FilterGetMessage ( port, &msg->MessageHeader, FIELD_OFFSET ( SCANNER_MESSAGE, Ovlp ), &msg->Ovlp ); if (hr != HRESULT_FROM_WIN32 ( ERROR_IO_PENDING )) { goto main_cleanup; } } } hr = S_OK; main_cleanup: for (INT i = 0 ; threads[i] != NULL ; ++i) { WaitForSingleObjectEx (threads[i], INFINITE, FALSE); } printf ( "Scanner: All done. Result = 0x%08x\n" , hr ); CloseHandle ( port ); CloseHandle ( completion ); free (messages); return hr; }
这里注意一下就是说:
驱动发送的数据是SCANNER_NOTIFICATION
结构体
1 2 3 4 5 6 7 8 9 10 status = FltSendMessage ( ScannerData.Filter, &ScannerData.ClientPort, notification, sizeof ( SCANNER_NOTIFICATION ), notification, &replyLength, NULL );
但是在三环是这样提取的 , message指向实际的消息内容,minifliter把SCANNER_NOTIFICATION封装在SCANNER_MESSAGE里面了,需要提取出来。
1 2 3 4 5 6 7 8 9 result = GetQueuedCompletionStatus ( Context->Completion, &outSize, &key, &pOvlp, INFINITE ); message = CONTAINING_RECORD ( pOvlp, SCANNER_MESSAGE, Ovlp );
注册表回调整体框架 在64位系统下,我们不用Hook去监控注册表了,我们选择用回调
利用CmRegisterCallbackEx
我们可以注册监控注册表的回调例程,实现一系列Pre Post操作
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 NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { CmRegisterCallbackEx ( MyRegCallback, &uAltitude, DriverObject, NULL , &g_RegCookie, NULL ); return STATUS_SUCCESS; } NTSTATUS MyRegCallback ( _In_ PVOID CallbackContext, _In_opt PVOID Argument1, _In_opt PVOID Argument2 ) { switch ((REG_NOTIFY_CLASS)Argument1) { case RegNtPreDeleteKey: return HOOK_PreNtDeleteKey ((PREG_DELETE_KEY_INFORMATION)Argument2); case RegNtPreRenameKey: return HOOK_PreNtRenameKey ((PREG_RENAME_KEY_INFORMATION)Argument2); case RegNtPreCreateKeyEx: return HOOK_PreNtCreateKeyEx ((PREG_CREATE_KEY_INFORMATION)Argument2); case RegNtPostCreateKeyEx: return HOOK_PostNtCreateKeyEx ((PREG_POST_OPERATION_INFORMATION)Argument2); default : return STATUS_SUCCESS; } } NTSTATUS HOOK_PreNtDeleteKey (PREG_DELETE_KEY_INFORMATION Data) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING keyName; UNICODE_STRING uTarget; return status; }
SandBox工作原理 路径的概念 沙盒根目录:\device\harddiskvolume1\sandbox
想操作路径:\device\harddiskvolume2\doc\hi.txt
当我们想要把一个应用程序放入沙盒中以后,如果我们想要操作\device\harddiskvolume2\doc\hi.txt
,沙盒会自动将这个重定向,转变为操作\device\harddiskvolume1\sandbox\device\harddiskvolume2\doc\hi.txt
,这样恶意程序就无法改动原来想要改动的目标,进而攻击失败
而且一旦中毒,我们也只需要删除掉 sandbox 这个文件夹即可,如果写得好,病毒是不会突破沙盒的
文件重定向 Windows的I/O管理器提供了一个方便的方法来重定向一个文件对象,通常会使用文件过滤驱动(在文件打开和文件创建的操作中)实现该方法,操作方法如下:
1.在IRP_MJ_CREATE
的分发函数中,获得FILE_OBJECT
的FileName
属性
2.用目标文件的完整路径替换掉原有的文件名字
3.设置IoStatus的status字段为STATUS_REPARSE
,然后设置Information
字段为IO_REPARSE
4.完成该IRP请求
5.返回STATUS_REPARSE
I/O管理器在接收到该返回以后,便会触发另一个文件打开操作,并发送一个IRP_MJ_CREATE
的请 求
拦截IRP_MJ_CREATE
示例代码,注意这是Minifilter
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 NTSTATUS SbRedirectFile ( IN PFLT_CALLBACK_DATA Data, IN PCFLT_RELATED_OBJECTS FltObjects, IN PUNICODE_STRING pUstrDstFileName ) { PFILE_OBJECT pFileObject; pFileObject = Data->Iopb->TargetFileObject; if (pFileObject == NULL ) return STATUS_INVALID_PARAMETER; if (pFileObject->FileName.Length > 0 && pFileObject->FileName.Buffer != NULL ) { ExFreePool (pFileObject->FileName.Buffer); pFileObject->FileName.Buffer = NULL ; } pFileObject->FileName = *pUstrDstFileName; pFileObject->RelatedFileObject = NULL ; Data->IoStatus.Status = STATUS_REPARSE; Data->IoStatus.Information = IO_REPARSE; FltSetCallbackDataDirty (Data); return STATUS_REPARSE; }
主要逻辑 :
检查目标文件对象是否为空 :
从 Data->Iopb->TargetFileObject
获取目标文件对象。
如果为空,则直接返回 STATUS_INVALID_PARAMETER
。
释放旧的文件名内存 :
如果目标文件对象的 FileName
字段存在有效的内存缓冲区,则释放该内存并清空指针。
设置新的文件名 :
将目标文件对象的 FileName
字段设置为新的文件名(pUstrDstFileName
)。
将 RelatedFileObject
字段清空。
标记为重解析操作 :
修改 IoStatus.Status
为 STATUS_REPARSE
,表示当前请求需要重新解析。
设置 IoStatus.Information
为 IO_REPARSE
,进一步说明是重解析请求。
通知 MiniFilter 处理的数据已被修改 :
返回重解析状态 :
返回 STATUS_REPARSE
,让系统重新解析文件路径。
CreatePre需要做的事 一.首先判断该进程是否需要SANDBOX
二.获取被沙盒的文件操作的文件名(通过PID,文件名等判断是否该进程需要在沙盒运行)在这里需要获取两个文件名,一个是沙盒内部的,一个是沙盒外部的
三.判断沙盒里是否含有该文件的.del文件,如果有,则按照是否来自沙盒内的请求和是否改变文件进行处理
分以下情形:
1.如果说是要在沙箱外面操作创建或改变文件,那么就重定向到内部
2.如果说是要在沙箱里面操作创建或改变文件,那么就删除该标志文件,交给文件系统去创建
3.只读操作该文件,含有删除标志,访问失败
四:如果沙盒里面存在该文件,且请求来自沙盒,就交给文件系统进行处理。请求来自外面,重解析到里面
五:如果操作不改变文件,但是沙盒里面没有这个文件,并且请求来自沙盒,则重定向到外面,让它访问也无妨;如果沙盒有这个文件,那就交给文件系统去处理,我们不必处理
六:如果操作改变文件,且沙盒里面没有这个文件,那么就准备路径,如果沙盒外面有这个文件,就拷贝进来
DeleteFile需要做的事 删除文件时会发送 IRP_MJ_SET_INFORMATION
这个 IRP。
所以我们要监控这个IRP
一:首先要获取一下文件的路径
二:判断是来自沙盒的外面还是里面
三:如果是外面的路径,就转为里面的路径,也就是不删除外面的文件,而是在里面设置一个删除的标志
四:如果是里面的路径,就先获取外面的路径, 如果外面这个路径不存在,就直接删除里面的文件, 如果外面存在,就在里面建立一个删除的标志,并删除。
QueryDir需要做的事 查询文件夹发起的IRP请求是:IRP_MJ_DIRECTORY_CONTROL
为了确保删除掉的文件不会再被恶意程序检测到,我们要做的是
一:首先确定是否需要沙盒
二:获取文件名(沙盒内和沙盒外都要)
三:在沙箱中去掉含删除标志的文件,在外面的路径去掉含删除文件的标志,然后合并这两个部分
关于IRP_MJ_WRITE是否要重定向 其实是不需要重定向的,因为前面在CreatePre的时候已经重定向过了
对示例SandBox分析 分析项目: minifilter/Sandbox at master · haidragon/minifilter
分析写在注释上
首先是入口函数DriverEntry
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 NTSTATUS DriverEntry ( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath) { NTSTATUS ntStatus; PDEVICE_OBJECT pDevObj; UNICODE_STRING uDevName; UNICODE_STRING uLinkName; PROCESS_LIST_ENTRY *pPROCESS_LIST_ENTRYEntry; DbgPrint ("Driver Load begin!\n" ); ExInitializeFastMutex (&g_PROCESS_LIST_ENTRYListLock); InitializeListHead (&g_PROCESS_LIST_ENTRYList); pPROCESS_LIST_ENTRYEntry = ExAllocatePoolWithTag (NonPagedPool, sizeof (PROCESS_LIST_ENTRY), 'XBBS' ); if (pPROCESS_LIST_ENTRYEntry == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory (pPROCESS_LIST_ENTRYEntry, sizeof (PROCESS_LIST_ENTRY)); wcscpy (pPROCESS_LIST_ENTRYEntry->NameBuffer, L"notepad.exe" ); InsertHeadList (&g_PROCESS_LIST_ENTRYList, &pPROCESS_LIST_ENTRYEntry->Entry); pPROCESS_LIST_ENTRYEntry = ExAllocatePoolWithTag (NonPagedPool, sizeof (PROCESS_LIST_ENTRY), 'XBBS' ); if (pPROCESS_LIST_ENTRYEntry == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory (pPROCESS_LIST_ENTRYEntry, sizeof (PROCESS_LIST_ENTRY)); wcscpy (pPROCESS_LIST_ENTRYEntry->NameBuffer, L"iexplore.exe" ); InsertHeadList (&g_PROCESS_LIST_ENTRYList, &pPROCESS_LIST_ENTRYEntry->Entry); ntStatus = initFileMonitor (pDriverObject); if (! NT_SUCCESS (ntStatus)) return ntStatus; ntStatus = initIPC ( ); if (! NT_SUCCESS (ntStatus)) { stopMiniMonitor ( ); return ntStatus; } InitSb (); ntStatus = startMiniMonitor ( ); if (! NT_SUCCESS (ntStatus)) { stopMiniMonitor ( ); closeIPC ( ); return STATUS_SUCCESS; } pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWrite; pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchControl; pDriverObject->DriverUnload = DriverUnload; RtlInitUnicodeString (&uDevName, DEVICE_NAME); ntStatus = IoCreateDevice ( pDriverObject, 0 , &uDevName, FILE_DEVICE_SANDBOX, 0 , TRUE, &pDevObj ); if (!NT_SUCCESS (ntStatus)) { DbgPrint ("IoCreateDevice Failed:%x\n" , ntStatus); return ntStatus; } pDevObj->Flags |= DO_BUFFERED_IO; RtlInitUnicodeString (&uLinkName, LINK_NAME); ntStatus = IoCreateSymbolicLink ( &uLinkName, &uDevName ); if (!NT_SUCCESS (ntStatus)) { DbgPrint ("IoCreateSymbolicLink Failed:%x\n" , ntStatus); IoDeleteDevice ( pDevObj ); return ntStatus; } DbgPrint ("Driver Load success!\n" ); return ntStatus; }
DriverEntry
主要就是注册了Minifilter
然后是minifilter的连接,断连,通讯三个回调,但是由于过于简单,这里就不分析了
CreatePre 接下来就是重头戏,也就是CreatePre的分析,写在注释上了
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 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 NTSTATUS sbPreCreateFile ( IN OUT PFLT_CALLBACK_DATA Data, IN PCFLT_RELATED_OBJECTS FltObjects ) { NTSTATUS ntStatus = STATUS_SUCCESS; ULONG ulDisposition = 0 ; BOOLEAN bNeedFree = FALSE; BOOLEAN bDir = FALSE; BOOLEAN bIsRename = FALSE; BOOLEAN bIsHardLink = TRUE; BOOLEAN bCreateFile = FALSE; PWCHAR pFileName = NULL ; PEPROCESS pEprocess = PsGetCurrentProcess (); PACCESS_STATE accessState; PFILE_OBJECT OutFileObject = NULL ; PFLT_INSTANCE pOutVolumeInstance = NULL ; UNICODE_STRING ustrDstFile = { 0 }; UNICODE_STRING ustrSrcFile = {0 }; UNICODE_STRING ustrDeledName = {0 }; PUNICODE_STRING pInName = NULL ; PUNICODE_STRING pOutName = NULL ; BOOLEAN bReparsed = FALSE; BOOLEAN bReqInSandbox = FALSE; ACCESS_MASK AccessMask = 0 ; PFLT_FILE_NAME_INFORMATION pNameInfo = NULL ; Data->IoStatus.Status = STATUS_UNSUCCESSFUL; accessState = Data->Iopb->Parameters.Create.SecurityContext->AccessState; __try { if ((ExGetPreviousMode () == KernelMode) || (KeGetCurrentIrql () > APC_LEVEL) || (pEprocess == NULL ) || (pEprocess == g_pProcessObject)) { return STATUS_SUCCESS; } if (!SbShouldBeSandBoxed (PsGetProcessId (pEprocess))) { return STATUS_SUCCESS; } if (g_SbVolInstance == NULL ) { g_SbVolInstance = SbGetVolumeInstance (gp_Filter, &g_ustrVolumeDeviceName); if (g_SbVolInstance == NULL ) { ntStatus = STATUS_SUCCESS; __leave; } } if (FltObjects->FileObject->Flags & FO_VOLUME_OPEN) { ntStatus = STATUS_SUCCESS; __leave; } AccessMask = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess; bIsRename = (AccessMask == (SYNCHRONIZE | FILE_READ_ATTRIBUTES | DELETE)); bIsHardLink = (AccessMask == (SYNCHRONIZE | FILE_WRITE_DATA)); if (!bIsRename && !bIsHardLink) { ntStatus = FltGetFileNameInformation (Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pNameInfo); if (NT_SUCCESS (ntStatus)) { FltParseFileNameInformation (pNameInfo); } } if ( bIsRename || bIsHardLink || !NT_SUCCESS (ntStatus )) { ntStatus = SbGetFileNameInformation (FltObjects->Volume, FltObjects->Instance, FltObjects->FileObject, FALSE, &pNameInfo); if (NT_SUCCESS (ntStatus)) { bNeedFree = TRUE; } else { Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } } if (!RtlCompareUnicodeString (&pNameInfo->Name, &pNameInfo->Volume, TRUE)) { ntStatus = STATUS_SUCCESS; __leave; } if (pNameInfo->Name.Length >= sizeof (WCHAR)*DEL_LENGTH && !_wcsnicmp(pNameInfo->Name.Buffer+pNameInfo->Name.Length/sizeof (WCHAR)-DEL_LENGTH, DEL_MARK, DEL_LENGTH)) { Data->IoStatus.Status = STATUS_OBJECT_NAME_NOT_FOUND; Data->IoStatus.Information = 0 ; ntStatus = STATUS_OBJECT_NAME_NOT_FOUND; __leave; } if (!RtlPrefixUnicodeString (&g_SandboxPath, &pNameInfo->Name, TRUE)) { ntStatus = SbConvertToSbName (&g_SandboxPath, &pNameInfo->Name, &ustrDstFile, NULL ); if (!NT_SUCCESS (ntStatus)) { Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } pInName = &ustrDstFile; pOutName = &pNameInfo->Name; pOutVolumeInstance = FltObjects->Instance; } else { UNICODE_STRING ustrVolName = {0 }; ustrVolName.Buffer = (PWCHAR)MyNew (BYTE, sizeof (WCHAR)*MAX_PATH); if (ustrVolName.Buffer == NULL ) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } ustrVolName.MaximumLength = sizeof (WCHAR)*MAX_PATH; ntStatus = SbConvertInSbNameToOutName (gp_Filter, &pNameInfo->Name, &g_SandboxPath, &ustrSrcFile, &ustrVolName); if (!NT_SUCCESS (ntStatus)) { MyDelete (ustrVolName.Buffer); Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } ustrDstFile.Buffer = MyNew (WCHAR, pNameInfo->Name.Length/sizeof (WCHAR)); if (ustrDstFile.Buffer == NULL ) { MyDelete (ustrVolName.Buffer); ntStatus = STATUS_INSUFFICIENT_RESOURCES; Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } ustrDstFile.Length = 0 ; ustrDstFile.MaximumLength = pNameInfo->Name.Length; RtlCopyUnicodeString (&ustrDstFile, &pNameInfo->Name); pInName = &ustrDstFile; pOutName = &ustrSrcFile; pOutVolumeInstance = SbGetVolumeInstance (gp_Filter, &ustrVolName); MyDelete (ustrVolName.Buffer); if (pOutVolumeInstance == NULL ) { ntStatus = STATUS_UNSUCCESSFUL; Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } bReqInSandbox = TRUE; } ustrDeledName.MaximumLength = pInName->Length + DEL_LENGTH*sizeof (WCHAR); ustrDeledName.Buffer = MyNew (WCHAR, ustrDeledName.MaximumLength/sizeof (WCHAR)); if (ustrDeledName.Buffer == NULL ) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } ulDisposition = (Data->Iopb->Parameters.Create.Options >> 24 ) & 0x000000ff ; bCreateFile = (BOOLEAN)((ulDisposition == FILE_CREATE) || (ulDisposition == FILE_OPEN_IF) || (ulDisposition == FILE_OVERWRITE_IF) || (ulDisposition == FILE_SUPERSEDE)); RtlCopyUnicodeString (&ustrDeledName, pInName); RtlAppendUnicodeToString (&ustrDeledName, DEL_MARK); if (SbFileExist (gp_Filter, g_SbVolInstance, &ustrDeledName)) { if (bCreateFile ||SbOperWillModifyFile (AccessMask)) { if (!SbFileExist (gp_Filter,g_SbVolInstance, pInName)) { ntStatus = SbPrepareSandboxPath ( gp_Filter, g_SbVolInstance, &g_SandboxPath, pInName, AccessMask); if (! NT_SUCCESS (ntStatus)) { Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } if (! bReqInSandbox) { ntStatus = SbRedirectFile (Data, FltObjects, pInName); if (NT_SUCCESS (ntStatus)) { ntStatus = STATUS_SB_TRY_REPARSE; __leave; } } else { if (!SBDeleteOneFile (g_SbVolInstance, gp_Filter, NULL , &ustrDeledName)) { ntStatus = STATUS_UNSUCCESSFUL; Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } ntStatus = STATUS_SUCCESS; __leave; } } } else { Data->IoStatus.Status = STATUS_OBJECT_NAME_NOT_FOUND; Data->IoStatus.Information = 0 ; ntStatus = STATUS_OBJECT_NAME_NOT_FOUND; __leave; } } else { if (bReqInSandbox) { if (!SbFileExist (FltObjects->Filter, pOutVolumeInstance, pOutName) ) { ntStatus = STATUS_SUCCESS; __leave; } } else { } } if (SbFileExist (gp_Filter, g_SbVolInstance, pInName) ) { if (bReqInSandbox) { ntStatus = STATUS_SUCCESS; __leave; } ntStatus = SbRedirectFile (Data, FltObjects, pInName); if (NT_SUCCESS (ntStatus)) { ntStatus = STATUS_SB_TRY_REPARSE; } else { Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; } __leave; } if (!bCreateFile && !SbOperWillModifyFile (AccessMask)) { if (bReqInSandbox) { ntStatus = SbRedirectFile (Data, FltObjects, pOutName); if (NT_SUCCESS (ntStatus)) { ntStatus = STATUS_SB_TRY_REPARSE; } else { Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; } __leave; } else { } ntStatus = STATUS_SUCCESS; __leave; } ntStatus = SbPrepareSandboxPath ( gp_Filter, g_SbVolInstance, &g_SandboxPath, pInName, Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess); if (! NT_SUCCESS (ntStatus)) { Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } if (bReqInSandbox || SbFileExist ( FltObjects->Filter, pOutVolumeInstance, pOutName) ) { ntStatus = SbIsDirectory (NULL , pOutName, FltObjects->Filter, pOutVolumeInstance, &bDir); if (!NT_SUCCESS (ntStatus)) { Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } ntStatus = SbCopyFile (gp_Filter, pOutVolumeInstance, NULL , pOutName, g_SbVolInstance, pInName, bDir); if (!NT_SUCCESS (ntStatus)) { if (ntStatus != STATUS_SB_DIR_CREATED) { Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; __leave; } } if (bReqInSandbox) { ntStatus = STATUS_SUCCESS; __leave; } } ntStatus = SbRedirectFile (Data, FltObjects, pInName); if (NT_SUCCESS (ntStatus)) { ntStatus = STATUS_SB_TRY_REPARSE; } else { Data->IoStatus.Status = ntStatus; Data->IoStatus.Information = 0 ; } } __except(EXCEPTION_EXECUTE_HANDLER) { } if (pNameInfo != NULL ) { if (bNeedFree) MyDelete (pNameInfo); else FltReleaseFileNameInformation (pNameInfo); } if (ustrDeledName.Buffer != NULL ) MyDelete (ustrDeledName.Buffer); if (ntStatus == STATUS_SB_TRY_REPARSE) { Data->IoStatus.Status = STATUS_REPARSE; Data->IoStatus.Information = 0 ; } if (ustrDstFile.Buffer != NULL && ntStatus != STATUS_SB_TRY_REPARSE) { ExFreePool (ustrDstFile.Buffer); ustrDstFile.Buffer = NULL ; } if (OutFileObject) ObDereferenceObject (OutFileObject); return ntStatus; }
WritePre 注释被我手贱删了….
下次有分析再说吧
无非就是分那几种情况
注意事项: 好像卸载再安装容易蓝屏…..
另外每个电脑的的盘符信息不一样,例如我的C盘是HardDiskVolume3,全局变量记得改一下