关于PsSetCreateProcessNotifyRoutineEx与MiniFilter的技术问询
我来帮你梳理这两个内核开发里的常见问题,都是踩过坑的经验之谈,咱们一个个说:
1. MiniFilter中使用PsSetCreateProcessNotifyRoutineEx的相关问题
可行性:完全可以在MiniFilter中使用
在DriverEntry里注册PsSetCreateProcessNotifyRoutineEx是完全合法的操作,回调函数里调用FltSendMessage向用户态发送进程路径也是监控类驱动的常规做法——只要注意回调的IRQL:PsSetCreateProcessNotifyRoutineEx的回调运行在PASSIVE_LEVEL,刚好符合FltSendMessage的IRQL要求,不会有IRQL不匹配的问题。
等待用户态回复时的进程阻塞风险
你测试中遇到的“进程B等待回复后执行”是因为进程创建回调是同步执行的:系统在创建进程的流程中会直接调用你的回调,直到回调返回才会继续后续的进程初始化操作。如果你在回调里同步等待用户态的回复(比如用事件对象阻塞),那所有后续的进程创建请求都会被卡住——包括系统关键进程(比如smss、csrss这些维持系统运行的进程)。一旦系统关键进程的创建被长时间阻塞,很大概率会导致系统挂起甚至蓝屏,这是极高风险的操作。
同步对象的选择:绝对不能用自旋锁,不推荐在回调里同步等待
- 自旋锁:绝对不能用,因为自旋锁要求持有期间IRQL不低于DISPATCH_LEVEL,且不能进入睡眠状态,而等待用户态回复必然会触发线程睡眠,直接违反自旋锁的使用规则,会导致严重的系统崩溃。
- 互斥体(KMUTEX)/事件(KEVENT):虽然在PASSIVE_LEVEL下可以使用,但问题的核心不是同步,而是不能在进程创建回调里做任何可能长时间阻塞的操作。正确的做法是把请求异步化:在回调里收集进程信息,然后提交一个工作项(Work Item),在工作项里异步调用
FltSendMessage并等待用户态回复,这样就不会阻塞进程创建的同步路径。
2. 避免PreCreate回调重复触发同一文件的方案
为什么会重复触发?
同一文件多次触发PreCreate是正常现象:系统后台会有很多操作访问文件,比如预读取、安全扫描、资源管理器的文件预览、系统服务的校验等等,这些都会触发MiniFilter的PreCreate回调。
流上下文 vs 流句柄上下文的选择
首先得明确你的需求是“过滤同一文件的所有重复打开”还是“过滤同一用户操作的重复上报”,对应不同的上下文选择:
如果是过滤同一文件的所有重复打开(不管哪个进程):用流上下文
流上下文是绑定到文件流(包括主文件和交替数据流)的,同一个文件流只会对应一个上下文,不管有多少个进程或句柄打开它。你可以在第一次触发PreCreate时:
- 调用
FltGetStreamContext检查该文件流是否已经有上下文 - 如果没有,创建上下文并向用户态上报
- 后续再触发PreCreate时,检测到上下文存在就直接跳过
注意:流上下文的生命周期和文件流绑定,当文件流被完全关闭(所有句柄关闭且系统缓存清理),你可以通过设置上下文的销毁回调来清理资源,不会有残留问题。
如果是过滤同一用户操作的重复触发:结合多条件+流句柄上下文(或去重逻辑)
如果你的需求是只上报用户主动打开的操作,过滤系统后台访问,那流上下文可能不够用(因为用户打开和系统后台访问用的是同一个文件流)。这时候可以:
- 结合
CreateOptions参数过滤:比如跳过带有FILE_OPEN_FOR_BACKUP_INTENT(备份操作)、FILE_FLAG_OPEN_REPARSE_POINT(重解析点操作)等标记的请求 - 用流句柄上下文标记该句柄已经上报过:每个打开的句柄对应一个上下文,第一次上报时创建上下文,后续如果是同一个句柄的操作(比如后续的PreWrite等)可以跳过,但PreCreate本身每个句柄只会触发一次,所以这个更适合后续回调的过滤
- 短时间去重:记录最近上报过的“文件路径+进程ID”组合,短时间内重复的就过滤掉,避免用户双击打开时的多次系统访问触发上报
内容的提问来源于stack exchange,提问作者Anfield

