news 2026/10/1 19:51:42

SuperDriver 与 WFP:Windows 内核驱动防火墙过滤实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SuperDriver 与 WFP:Windows 内核驱动防火墙过滤实战

简介:这份资源是面向Windows内核开发与网络安全方向学习者的WFP网络驱动防火墙源码,适合具备一定驱动开发基础、希望深入理解Windows过滤平台(WFP)架构的开发者参考。它可用于研究网络数据包拦截、过滤规则下发与驱动层通信等典型场景,帮助读者在真实代码中理清WFP的调用流程与实现思路。压缩包为zip格式,整体约766KB,体量轻便,便于快速导入工程对照阅读。目前已有283人学习关注,说明其在驱动防火墙这一细分领域具备一定参考价值。源码围绕防火墙与WFP驱动展开,读者可从中获取过滤引擎注册、网络层与传输层拦截、规则匹配及驱动与用户态交互等关键实现细节,并据此搭建自己的实验工程或进行二次开发。对于想从零理解Windows驱动防火墙工作原理、补齐内核网络编程短板的开发者而言,这是一份可直接研读的实践素材。

1. SuperDriver 与 WFP:从内核驱动到防火墙过滤的落地路径

Windows 平台上做网络过滤,绕不开 WFP(Windows Filtering Platform)。很多人第一次听到 SuperDriver 这个词,会以为它是一个现成的防火墙产品,其实它更像一个技术方向:用内核驱动的方式,把网络流量的拦截、放行、审计能力做到系统底层。标题里的 windowsdriver、firewall、wfp 三个词,恰好对应了这条路径的三个关键层——驱动是载体,firewall 是目标,WFP 是官方提供的过滤框架。如果你正在做终端安全、流量管控、或者需要在内核态实现精细化的网络策略,这套组合值得认真研究。它适合有 C/C++ 基础、了解 Windows 驱动模型、并且愿意在虚拟机里反复调试的工程师。新手可以跟着步骤跑通最小过滤驱动,熟手则要关注过滤层选择、回调优先级和性能开销这些边界问题。

2. WFP 架构与驱动选型:为什么不用 NDIS 或 TDI

2.1 WFP 的分层模型与过滤引擎

WFP 不是单一 API,而是一套分层的过滤架构。它把网络栈切成多个层(Layer),每个层对应数据包处理的不同阶段。常见的层包括 FWPM_LAYER_INBOUND_IPPACKET_V4(入站 IP 包)、FWPM_LAYER_OUTBOUND_TRANSPORT_V4(出站传输层)、FWPM_LAYER_ALE_AUTH_CONNECT_V4(连接授权)等。每个层上可以挂多个过滤器(Filter),过滤器由条件(Condition)和动作(Action)组成。条件可以是 IP 地址、端口、协议号、进程 ID 等,动作通常是 permit、block 或 callout。

WFP 的核心优势在于:它在内核态提供统一的过滤点,不需要自己解析 TCP/IP 栈,也不需要 hook 系统调用。相比 NDIS 驱动,WFP 的过滤逻辑更靠近策略层,开发量小、稳定性高。相比已经废弃的 TDI,WFP 有微软官方支持和持续更新。选型时我一般会先确认需求:如果只是做 IP/端口级别的黑白名单,用 WFP 的过滤层直接挂 Filter 就够了;如果要做深度包检测或协议解析,才需要写 callout 驱动。

2.2 驱动类型选择:WDM、KMDF 还是 WFP Callout

Windows 驱动模型有 WDM、KMDF、UMDF 几种。WFP 的 callout 驱动通常用 KMDF 或 WDM 编写,因为需要在内核态注册 callout 函数。KMDF 封装了大部分 PnP 和电源管理逻辑,代码量少,推荐新手从 KMDF 入手。但要注意,WFP 的 callout 注册本身是一套独立的 API,和 KMDF 的框架并不冲突。

一个常见的误区是:以为写了 WFP callout 就能拦截所有流量。实际上,callout 只在你注册的层和条件下被触发。如果只在 ALE_AUTH_CONNECT 层注册,那么已经建立的连接不会再次触发。要覆盖完整生命周期,需要在多个层注册,或者使用 WFP 的 net event 机制。我一般会先画一张流量路径图,标出哪些层需要挂过滤器,再决定 callout 的注册点。

2.3 最小过滤驱动的代码骨架

下面是一个 KMDF 驱动中注册 WFP 过滤器的核心代码片段。它演示了如何打开过滤引擎、添加一个出站阻断过滤器。

// 打开 WFP 过滤引擎 HANDLE engineHandle = NULL; FWPM_SESSION0 session = {0}; session.flags = FWPM_SESSION_FLAG_DYNAMIC; // 动态会话,驱动卸载时自动清理 NTSTATUS status = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engineHandle); if (!NT_SUCCESS(status)) { DbgPrint("FwpmEngineOpen0 failed: 0x%X\n", status); return status; } // 添加过滤器:阻断出站到 192.168.1.100 的 TCP 流量 FWPM_FILTER0 filter = {0}; FWPM_FILTER_CONDITION0 conditions[2] = {0}; // 条件1:远程 IP 地址 conditions[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS; conditions[0].matchType = FWP_MATCH_EQUAL; conditions[0].conditionValue.type = FWP_UINT32; conditions[0].conditionValue.uint32 = 0xC0A80164; // 192.168.1.100 // 条件2:协议为 TCP conditions[1].fieldKey = FWPM_CONDITION_IP_PROTOCOL; conditions[1].matchType = FWP_MATCH_EQUAL; conditions[1].conditionValue.type = FWP_UINT8; conditions[1].conditionValue.uint8 = IPPROTO_TCP; filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; filter.action.type = FWP_ACTION_BLOCK; filter.filterCondition = conditions; filter.numFilterConditions = 2; filter.displayData.name = L"Block 192.168.1.100 TCP"; filter.weight.type = FWP_EMPTY; // 自动权重 UINT64 filterId = 0; status = FwpmFilterAdd0(engineHandle, &filter, NULL, &filterId); if (!NT_SUCCESS(status)) { DbgPrint("FwpmFilterAdd0 failed: 0x%X\n", status); FwpmEngineClose0(engineHandle); return status; }

这段代码的逻辑是:先打开一个动态 WFP 会话,然后构造一个过滤器,指定层为 ALE_AUTH_CONNECT_V4,条件为远程地址等于 192.168.1.100 且协议为 TCP,动作为阻断。最后调用 FwpmFilterAdd0 把过滤器加入引擎。参数说明:FWPM_SESSION_FLAG_DYNAMIC 表示会话是动态的,驱动卸载或进程退出时 WFP 会自动清理该会话下的所有过滤器,避免残留规则。filter.weight.type 设为 FWP_EMPTY 表示由系统自动分配权重,权重高的过滤器优先匹配。如果多条规则冲突,需要手动设置权重。

提示:在开发阶段,建议先用 FWP_ACTION_BLOCK 配合具体条件做小范围测试,确认过滤生效后再扩大规则范围。直接阻断所有流量可能导致调试通道中断。

3. 从驱动加载到规则生效:完整落地步骤

3.1 环境准备与驱动签名

在 Windows 上加载内核驱动,签名是绕不过去的坎。开发阶段可以开启测试签名模式,用 bcdedit 命令设置。具体操作:以管理员身份打开命令提示符,执行bcdedit /set testsigning on,然后重启。重启后桌面右下角会显示“测试模式”水印。接着用 Visual Studio 的 WDK 工具链编译驱动,生成 .sys 文件。测试签名可以用 makecert 和 signtool 生成自签名证书并签名。

# 开启测试签名模式(管理员权限) bcdedit /set testsigning on # 重启后验证 bcdedit /enum | findstr testsigning # 生成自签名证书(在 WDK 命令行中) makecert -r -pe -ss PrivateCertStore -n "CN=TestDriver" testcert.cer # 签名驱动 signtool sign /v /s PrivateCertStore /n TestDriver /t http://timestamp.digicert.com SuperDriver.sys

参数说明:/r表示创建自签名证书,/pe表示私钥可导出,/ss指定证书存储位置。signtool 的/s指定存储名,/n指定证书主题名,/t指定时间戳服务器。注意时间戳服务器地址可能因网络环境不同而需要调整,如果时间戳失败可以去掉/t参数先本地签名。

3.2 驱动加载与 WFP 会话初始化

驱动编译签名后,可以用sc create注册为系统服务,然后sc start启动。更常用的调试方式是用 OSR Loader 或 WinDbg 直接加载。驱动入口函数 DriverEntry 中,除了常规的 KMDF 初始化,还要调用 WFP 的引擎打开和过滤器注册。

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { WDF_DRIVER_CONFIG config; WDF_DRIVER_CONFIG_INIT(&config, EvtDeviceAdd); NTSTATUS status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE); if (!NT_SUCCESS(status)) { return status; } // 初始化 WFP status = InitWfpFilters(); if (!NT_SUCCESS(status)) { DbgPrint("InitWfpFilters failed: 0x%X\n", status); return status; } return STATUS_SUCCESS; }

InitWfpFilters 函数内部就是上一节展示的 FwpmEngineOpen0 和 FwpmFilterAdd0 调用。注意驱动卸载时要调用 FwpmEngineClose0 关闭会话,动态会话会自动清理过滤器,但显式关闭更稳妥。

3.3 验证过滤效果:用 netsh 和日志确认

驱动加载后,怎么确认过滤器真的生效?最直接的方法是用 netsh 查看 WFP 状态。netsh wfp show state可以导出当前 WFP 的完整状态到 XML 文件,里面包含所有过滤器和层的信息。搜索你设置的过滤器名称,如果能找到且状态为 active,说明注册成功。

# 导出 WFP 状态 netsh wfp show state # 查看生成的 XML 文件(默认在当前目录) # 搜索过滤器名称 findstr /C:"Block 192.168.1.100 TCP" wfpstate.xml

另一个验证方法是实际发起连接。用 ping 或 telnet 测试目标地址,如果被阻断,连接会超时或立即失败。同时可以在驱动里加 DbgPrint 输出,用 DebugView 查看 callout 是否被触发。注意 DbgPrint 在正式发布版本中应移除或改为 ETW 日志,避免性能影响。

注意:netsh wfp show state导出的 XML 文件可能很大,建议用 findstr 或 PowerShell 的 Select-String 过滤关键字,不要直接打开。

4. 避坑与排查:WFP 驱动开发中的五个血泪教训

4.1 过滤器不生效:层选错了

现象:驱动加载成功,netsh 也能看到过滤器,但目标流量依然能通。原因:过滤器挂在了不匹配的层。比如想阻断出站连接,却挂在了 FWPM_LAYER_INBOUND_IPPACKET_V4。解决:确认流量方向,出站连接授权用 ALE_AUTH_CONNECT_V4,入站用 ALE_AUTH_RECV_ACCEPT_V4。如果不确定,先用netsh wfp show layers列出所有层,找到对应方向的层再注册。

4.2 驱动加载失败:签名或依赖问题

现象:sc start报错 577 或 1275。原因:577 是签名验证失败,1275 是驱动被阻止加载。解决:确认测试签名模式已开启,证书已导入受信任的根证书颁发机构。如果是 1275,检查驱动是否被组策略或安全软件拦截。在虚拟机里开发时,关闭安全软件的驱动拦截功能。

4.3 系统蓝屏:callout 函数里做了分页操作

现象:驱动运行一段时间后蓝屏,错误码 IRQL_NOT_LESS_OR_EQUAL。原因:WFP callout 函数可能在 DISPATCH_LEVEL 被调用,此时不能访问分页内存或调用可能引起页错误的函数。解决:callout 里只做轻量判断,复杂逻辑放到工作项或系统线程中处理。所有在 callout 中访问的内存确保是非分页的,用 ExAllocatePoolWithTag 时指定 NonPagedPool。

4.4 规则残留:动态会话没清理

现象:驱动卸载后,之前设置的阻断规则依然生效,导致目标地址无法访问。原因:WFP 会话不是动态的,或者驱动异常退出没有关闭会话。解决:始终使用 FWPM_SESSION_FLAG_DYNAMIC 标志,并在 DriverUnload 中显式调用 FwpmEngineClose0。如果已经残留,可以用netsh wfp show state找到过滤器 ID,再用netsh wfp delete filter删除。

4.5 性能下降:过滤器条件过于宽泛

现象:开启驱动后,网络吞吐明显下降,CPU 占用升高。原因:过滤器条件太少,导致每个包都要经过 callout 判断。解决:尽量在过滤器中添加精确条件,减少 callout 触发频率。比如用 IP 地址和端口组合条件,而不是只匹配协议。另外,把多个条件合并到一个过滤器,而不是注册多个过滤器分别匹配。

5. 进阶技巧:用 WFP 实现动态黑白名单与进程级管控

5.1 基于进程 ID 的过滤

WFP 支持按进程 ID 过滤,这在做应用级防火墙时非常有用。条件字段是 FWPM_CONDITION_ALE_APP_ID,类型是 FWP_BYTE_BLOB,需要把进程路径转换成 WFP 的内部标识。可以用 FwpmGetAppIdFromFileName0 函数把文件路径转成 App ID。

// 把进程路径转为 App ID FWP_BYTE_BLOB* appId = NULL; NTSTATUS status = FwpmGetAppIdFromFileName0(L"\\Device\\HarddiskVolume3\\Program Files\\MyApp\\app.exe", &appId); if (NT_SUCCESS(status)) { conditions[0].fieldKey = FWPM_CONDITION_ALE_APP_ID; conditions[0].matchType = FWP_MATCH_EQUAL; conditions[0].conditionValue.type = FWP_BYTE_BLOB_TYPE; conditions[0].conditionValue.byteBlob = appId; // 后续添加到 filter 中 }

参数说明:FwpmGetAppIdFromFileName0 接受 NT 路径格式的文件名,返回的 FWP_BYTE_BLOB 需要在使用后调用 FwpmFreeMemory0 释放。注意路径必须是设备路径,不能用 Win32 路径。可以用 QueryDosDevice 把盘符转成设备路径。

5.2 动态更新规则:运行时增删过滤器

实际产品中,规则需要动态更新。WFP 提供了 FwpmFilterAdd0 和 FwpmFilterDeleteById0 两个 API,可以在驱动运行期间增删过滤器。我一般会维护一个规则列表,每条规则对应一个 filterId,更新时先删旧再加新。注意删除和添加之间可能有短暂的空窗期,如果要求无缝切换,可以先用高权重的新规则覆盖,再删除旧规则。

5.3 验证与监控:用 ETW 记录过滤事件

DbgPrint 只适合开发阶段,正式版本建议用 ETW(Event Tracing for Windows)记录过滤事件。WFP 本身支持审计事件,可以在注册过滤器时设置 FWP_ACTION_FLAG_CALLOUT 并关联一个 callout 函数,在 callout 中调用 EtwWrite 写入自定义事件。这样可以用 Windows 事件查看器或 tracelog 工具实时监控过滤情况。

监控方式适用阶段性能开销持久化
DbgPrint + DebugView开发调试高否
ETW 自定义事件测试与生产低可配置
WFP 内置审计生产低是
netsh wfp show state排查无否

5.4 一个我踩过的坑:App ID 路径格式

第一次用 FwpmGetAppIdFromFileName0 时,我传了C:\Program Files\MyApp\app.exe,结果返回 STATUS_INVALID_PARAMETER。后来查文档才知道必须用 NT 设备路径,比如\Device\HarddiskVolume3\Program Files\MyApp\app.exe。获取设备路径可以用QueryDosDevice或者用GetFinalPathNameByHandle再转换。这个坑卡了我半天,希望后来者直接绕过。

做 WFP 驱动这些年,最大的习惯就是:每次改完过滤器条件,先在虚拟机里用netsh wfp show state确认规则状态,再用实际流量验证,最后才上真机。驱动开发没有后悔药,蓝屏一次可能就要重装系统。把测试签名、动态会话、非分页内存这三件事刻在脑子里,能省下大量排查时间。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 19:51:36

人工智能杂谈(十五)最大化利用 OpenClaw 与 TaoToken 统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 19:51:35

收藏!小白程序员必看:揭秘AI Agent的“记忆”如何决定其能否持续工作——TaoToken统一Key/API通道下的记忆架构拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 19:51:35

RabbitMQ 安装部署与插件配置实战:从入门到高频问题排查

先说句实在话,RabbitMQ 是我在生产环境里用得最久的消息队列,没有之一。很多后端同学第一次接触它,被 Erlang、管理插件、MQTT 这些名词一搅和,很容易在安装阶段就栽跟头。这篇文章我就把 RabbitMQ 及其插件的安装从头捋一遍&…

作者头像 李华
网站建设 2026/10/1 19:50:42

考研党C语言环境配置指南:VSCode与MinGW-W64最佳实践

很多考研党第一次接触C语言,卡住他们的往往不是语法本身,而是“怎么让自己的电脑跑起第一行代码”。网上的教程东一篇西一篇,今天装这个明天卸那个,折腾一天还在跟环境变量搏斗。这篇文章就是专门写给C语言初学者的,特…

作者头像 李华
网站建设 2026/10/1 19:49:57

WorkBuddy一键部署35B大模型:Intel算力引擎加速端侧AI落地

1. 端侧大模型部署的现状与WorkBuddy的切入点1.1 为什么35B模型开始往本地跑过去两年,大模型的部署方式基本是两条路:要么调用云端API,要么在本地跑7B、13B这种小参数模型。云端API的好处是省心,坏处是数据要出门、按量计费、网络…

作者头像 李华