给 minifilter Driver 实现 FilterUnloadCallback 回调函数
注册 FilterUnloadCallback 很简单,只要在DriverEntry 中,调用 FltRegisterFilter 函数时,把FLT_REGISTRATION 中的成员FilterUnloadCallback 填写成为你自己实现的回调函数即可。
FilterUnloadCallback 什么时候会被调用呢?
1. 非强制性卸载。 这种情况发生在有其他程序调用 FilterUnload 或者调用 FltUnloadFilter。 例如: fltmc unload . 对于非强制性卸载,如果 FilterUnloadCallback 函数中返回错误或者警告,例如 STATUS_FLT_DO_NOT_DETACH ,那么filter manager 将不回卸载 filter driver。
2. 强制卸载。 这种情况发生在service 停止。 例如运行了 sc stop 或者 net stop,或者是用户程序调用了 ControlService 传递了 SERVICE_CONTROL_STOP 作为参数。对于强制卸载,无论 FilterUnloadCallback 函数返回什么值,filter manager 都会卸载 filter driver。 为了能够禁止强制卸载 minifilter driver ,可以在注册driver 的时候 FltRegisterFilter 传递的FLT_REGISTRATION 结构中的flag 成员上加入 FLTFL_REGISTRATION_DO_NOT_SUPPORT_SERVICE_STOP .
在driver 的 DriverEntry 函数中,如果返回失败,那么 FilterUnloadCallback 不会被调用, filter manager 会直接把这个 minifilter driver 卸载。 FilterUnloadCallback 函数在系统关机的时候,并不会被调用。需要自己注册 IRP_MJ_SHUTDOWN 的 preoperation 来处理。
如何写一个FilterUnloadCallback 函数?
函数的原型是 : typedef NTSTATUS (*PFLT_FILTER_UNLOAD_CALLBACK) ( FLT_FILTER_UNLOAD_FLAGS Flags );
函数有一个参数,有可能是 NULL 或者是 FLTFL_FILTER_UNLOAD_MANDATORY , filter manager 会设置 FLTFL_FILTER_UNLOAD_MANDATORY, 当需要强制卸载的时候。
在函数中,需要做以下一些动作:
1. 关闭任何打开的kernel 模式的通讯服务器端口句柄 (communication server port handle). 如果驱动调用过 FltCreateCommunicationPort ,那么需要调用 FltCloseCommunicationPort, 这个动作应该在 FltUnregisterFilter 被调用前完成。如果有用户态的程序打开了 通讯端口,那么客户端端口在调用 FltCloseCommunicationPort 后依然保持打开,不过filter manager 在驱动被卸载之后,就会关闭所有的端口。
2. 调用 FltUnregisterFilter 来注销 minifilter driver 。 当 FltUnRegisterFilter 被调用时会导致发生下面的事情:
- minifilter driver 回调函数会被注销
- minifilter driver 的instance 会被 torn down。 每一个instance 上都会触发函数 InstanceTeardownStartCallback 和 InstanceTeardownCompleteCallback 调用
- 如果minifilter driver 在 volume、instance、stream、stream handle 上设置任何的 context,都会被删除。 如果minifilter driver 在某一个 context type 上注册了CleanupContext 回调函数,那么这个函数会在删除context 之前被调用。
3. 其他的全局资源的清除
- Call ExDeleteResourceLite to delete a global resource variable that was initialized by a previous call to ExInitializeResourceLite.
- Call ExFreePool or ExFreePoolWithTag to free global memory that was allocated by a previous call to a routine such as ExAllocatePoolWithTag.
- Call ExDeleteNPagedLookasideList or ExDeletePagedLookasideList to delete a lookaside list that was allocated by a previous call to ExInitializeNPagedLookasideList or ExInitializePagedLookasideList, respectively.
- Call PsRemoveCreateThreadNotifyRoutine or PsRemoveLoadImageNotifyRoutine to unregister a global callback routine that was registered by a previous call to PsSetCreateThreadNotifyRoutine or PsSetLoadImageNotifyRoutine, respectively
4. 返回适当的 NTSTATUS value
If there are outstanding rundown references on the minifilter driver's opaque filter pointer, FltUnregisterFilter enters a wait state until they are removed. Outstanding rundown references usually happen because the minifilter driver has called FltQueueGenericWorkItem to insert a work item into a system work queue, and the work item has not yet been dequeued and processed. (The filter manager adds the rundown reference when the minifilter driver calls FltQueueGenericWorkItem and removes it when the minifilter driver's work routine returns.)
Outstanding rundown references can also happen if the minifilter driver has called any routines that add a rundown reference to the minifilter driver's opaque filter pointer, such as FltObjectReference or FltGetFilterFromInstance, but did not subsequently call FltObjectDereference.