新闻详情

新闻详情

首页 / 资讯中心 / 详情

Windows内核驱动WFP防火墙开发实战:分层选型、Callout注册与进程级黑白名单

发布时间:2026/10/1 14:49:47来源:尧图网络
Windows内核驱动WFP防火墙开发实战:分层选型、Callout注册与进程级黑白名单
简介这份资源是面向Windows内核开发与网络安全方向学习者的WFP网络驱动防火墙源码适合具备一定驱动开发基础、希望深入理解Windows过滤平台WFP架构与防火墙实现原理的开发者参考。它解决的是从理论到实践之间缺少可运行示例的问题可用于研究网络数据包拦截、过滤规则下发与驱动层通信等典型场景。压缩包为zip格式整体约766KB体量轻便便于快速导入工程阅读与调试。目前已有283人学习关注说明其在驱动安全圈内具备一定参考价值。源码围绕WFP的标注、过滤与网络层拦截机制展开读者可从中梳理驱动入口、设备对象创建、过滤引擎注册及回调处理等关键流程并借鉴其分层结构与接口调用方式用于自身防火墙模块的设计与排错。对于准备从事Windows安全驱动开发或需要搭建网络过滤原型的技术人员这份源码可作为理解WFP工作机理的实用起点。1. SuperDriver 与 WFP从内核驱动视角重新理解 Windows 防火墙做过 Windows 内核开发的人多半遇到过这种场景产品要求在网络层做精细过滤netsh advfirewall那套用户态规则不够用第三方 TDI 过滤驱动又被微软标记为过时方案最后绕一圈还是得回到 WFPWindows Filtering Platform。SuperDriver 这类内核驱动项目之所以把 WFP 当作核心是因为它能在ALE_AUTH_CONNECT、ALE_AUTH_RECV_ACCEPT这些分层上直接拿到连接五元组和进程上下文做到用户态防火墙做不到的粒度。这篇文章不讲概念科普而是把「一个 Windows 内核驱动怎么接入 WFP 实现防火墙功能」这条链路拆开从分层选型、callout 注册、过滤条件设置到编译签名、加载调试、蓝屏排查每一步都给可复现的操作和参数。适合已经有 WDM/KMDF 基础、准备把网络过滤做进驱动层的工程师也适合正在评估「自研 WFP 驱动 vs 用现成防火墙产品」这个决策的团队。2. WFP 分层与 Callout驱动接入点怎么选才不翻车WFP 的架构本质是一棵分层Layer树每个分层对应网络栈的一个处理阶段。驱动要做的第一件事不是写代码而是想清楚在哪个分层挂 callout。选错分层轻则拿不到想要的数据重则和系统自带过滤冲突导致网络直接断掉。2.1 六个关键分层的能力边界WFP 分层有几十个但真正做防火墙过滤常用的就六个。下面这张表是我在实际项目里反复验证过的分层能力对照参数含义按FWPS_BUILTIN_LAYER枚举值理解分层枚举值能拿到什么典型用途ALE_AUTH_CONNECT0x1000五元组进程ID用户SID出站连接授权ALE_AUTH_RECV_ACCEPT0x1001五元组进程ID用户SID入站连接授权ALE_AUTH_LISTEN0x1002本地端口进程ID监听端口管控TRANSPORT_LAYER0x0003五元组无进程上下文包级过滤STREAM_LAYER0x0005流数据内容检测DATAGRAM_DATA0x0002UDP 数据报UDP 过滤选型逻辑很直接要按进程做黑白名单必须用 ALE 层因为只有 ALE 层在FWPS_INCOMING_VALUES里带了FWPS_FIELD_ALE_AUTH_CONNECT_ALE_PACKAGE_ID和进程路径。TRANSPORT 层虽然也能过滤但拿不到进程信息做出来的东西只能按 IP 端口封粒度差一个量级。注意ALE 层只在连接建立时触发一次不是每个包都触发。如果你的需求是「每个包都检查」那得用 TRANSPORT 层但代价是性能开销大得多而且拿不到进程上下文。2.2 注册 Callout 的最小代码骨架下面这段是 callout 注册的核心逻辑基于 KMDF 驱动框架省略了 DriverEntry 的样板代码只保留 WFP 相关的关键部分// 定义 callout 的 GUID每个 callout 必须唯一 DEFINE_GUID(SUPERDRIVER_CALLOUT_CONNECT, 0x12345678, 0x1234, 0x1234, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0); // 注册 callout 到 WFP 引擎 NTSTATUS RegisterCallout(_In_ PDEVICE_OBJECT DeviceObject) { FWPS_CALLOUT0 callout { 0 }; NTSTATUS status; // classifyFn 是核心回调每个匹配的包都会进来 callout.calloutKey SUPERDRIVER_CALLOUT_CONNECT; callout.classifyFn SuperDriverClassifyConnect; callout.notifyFn SuperDriverNotify; // 可选处理过滤条件变更 callout.flowDeleteFn NULL; // 流删除回调ALE 层一般不用 // 第一个参数是设备对象用于标识 callout 归属 status FwpsCalloutRegister0(DeviceObject, callout, NULL); if (!NT_SUCCESS(status)) { DbgPrint(SuperDriver: FwpsCalloutRegister0 failed 0x%08X\n, status); return status; } return STATUS_SUCCESS; }FwpsCalloutRegister0的第二个参数是FWPS_CALLOUT0结构体calloutKey必须全局唯一建议用 GUID 生成工具生成后硬编码。classifyFn是过滤逻辑的入口返回FWP_ACTION_BLOCK或FWP_ACTION_PERMIT决定放行还是拦截。notifyFn在过滤条件动态增删时被调用如果你的规则是静态的可以传 NULL。2.3 把 Callout 挂到分层上的过滤条件注册完 callout 只是第一步还得把它和具体分层、过滤条件绑定否则 callout 永远不会被触发。这一步用FwpmFilterAdd0完成NTSTATUS AddConnectFilter(_In_ HANDLE EngineHandle) { FWPM_FILTER0 filter { 0 }; FWPM_FILTER_CONDITION0 conditions[1] { 0 }; UINT64 filterId 0; NTSTATUS status; // 条件只匹配 TCP 协议 conditions[0].fieldKey FWPM_CONDITION_IP_PROTOCOL; conditions[0].matchType FWP_MATCH_EQUAL; conditions[0].conditionValue.type FWP_UINT8; conditions[0].conditionValue.uint8 IPPROTO_TCP; filter.layerKey FWPM_LAYER_ALE_AUTH_CONNECT_V4; filter.displayData.name LSuperDriver Connect Filter; filter.action.type FWP_ACTION_CALLOUT_TERMINATING; // 终止型可阻断 filter.action.calloutKey SUPERDRIVER_CALLOUT_CONNECT; filter.filterCondition conditions; filter.numFilterConditions 1; filter.weight.type FWP_EMPTY; // 默认权重 status FwpmFilterAdd0(EngineHandle, filter, NULL, filterId); if (!NT_SUCCESS(status)) { DbgPrint(SuperDriver: FwpmFilterAdd0 failed 0x%08X\n, status); return status; } DbgPrint(SuperDriver: filter added, id%llu\n, filterId); return STATUS_SUCCESS; }action.type这里选FWP_ACTION_CALLOUT_TERMINATING表示 callout 可以返回终止动作阻断。如果选FWP_ACTION_CALLOUT_INSPECTIONcallout 只能观察不能阻断。weight.type设为FWP_EMPTY表示用默认权重多个过滤规则冲突时权重高的先匹配。filterId是输出参数后续删除过滤规则时要用到。3. 从编译到加载SuperDriver 驱动跑起来的完整链路代码写完只是开始Windows 内核驱动从编译到真正加载运行中间有一堆签名、配置、调试的坑。这一章按实际操作顺序走一遍。3.1 编译环境与项目配置驱动编译必须用 WDKWindows Driver Kit版本要和目标系统匹配。我一般用 Visual Studio WDK 的组合项目类型选「Kernel Mode Driver, Empty (KMDF)」。关键配置项Target OS Version根据目标系统选Win10 选Windows 10Win11 选Windows 10或更高Target PlatformDesktop即可驱动不区分 x86/x64 平台类型Spectre Mitigation建议关掉否则某些旧版 WDK 会编译报错Inf2Cat如果要做签名这个选项要开编译产物是一个.sys文件加一个.inf文件。.sys是驱动本体.inf是安装信息文件里面定义了服务名、驱动路径、加载方式。3.2 测试签名与加载开发阶段不可能每次都去微软做 WHQL 签名用测试签名模式就行。操作步骤# 以管理员身份打开 CMD开启测试签名模式 bcdedit /set testsigning on # 重启系统后生效重启后桌面右下角会显示测试模式 # 用 inf 文件安装驱动需要管理员权限 pnputil /add-driver SuperDriver.inf /install # 确认驱动服务已创建 sc query SuperDriver # 手动启动驱动 sc start SuperDriverbcdedit /set testsigning on开启后必须重启否则不生效。pnputil /add-driver会把驱动注册到系统驱动库/install参数表示立即安装。sc start启动驱动时如果返回1053错误说明驱动入口函数有问题通常是DriverEntry返回值不对或者 WFP 引擎初始化失败。注意测试签名模式下系统会加载所有测试签名的驱动包括恶意驱动。开发机可以开生产环境绝对不要开。3.3 用 WinDbg 做内核调试驱动跑起来不代表没问题蓝屏是家常便饭。内核调试配置# 在被调试机上开启内核调试假设调试机 IP 是 192.168.1.100 bcdedit /debug on bcdedit /dbgsettings net hostip:192.168.1.100 port:50000 key:1.2.3.4 # 重启被调试机 # 在调试机上打开 WinDbg选择 Kernel Debug - Net # 输入 port 和 key连接后可以看到内核输出连接成功后用!analyze -v分析蓝屏 dump用bp SuperDriverClassifyConnect下断点跟踪 callout 回调。DbgPrint的输出会直接显示在 WinDbg 的命令窗口里比OutputDebugString可靠得多。3.4 验证过滤是否生效驱动加载后怎么确认过滤规则真的在起作用最直接的方法是写一个测试程序尝试连接一个被阻断的 IP看是否被拦截。也可以用netsh wfp show state查看当前 WFP 状态# 导出当前 WFP 状态到 XML 文件 netsh wfp show state filewfp_state.xml # 在 XML 里搜索你的 callout GUID确认已注册 findstr /i 12345678 wfp_state.xml如果 XML 里能找到你的 callout 和 filter说明注册成功。如果找不到检查FwpmEngineOpen0是否成功、FwpmFilterAdd0返回值是否STATUS_SUCCESS。4. 避坑指南WFP 驱动开发中最容易翻车的五个点这一章全是血泪经验每一条都是实际调试中踩过的坑。4.1 蓝屏 IRQL_NOT_LESS_OR_EQUAL现象驱动加载后一有网络请求就蓝屏错误码IRQL_NOT_LESS_OR_EQUAL。原因classifyFn回调运行在DISPATCH_LEVEL这个 IRQL 下不能访问分页内存。很多新手在回调里直接ExAllocatePoolWithTag用PagedPool或者访问用户态缓冲区直接触发蓝屏。解决回调里所有内存分配用NonPagedPool访问用户态数据必须用FwpsCopyAndValidate*系列函数。如果确实需要分页操作用FwpsInject*把包注入到PASSIVE_LEVEL的工作线程处理。4.2 Callout 注册成功但回调不触发现象FwpsCalloutRegister0返回成功FwpmFilterAdd0也成功但classifyFn就是不进来。原因最常见的是分层选错了。比如在FWPM_LAYER_ALE_AUTH_CONNECT_V4注册 callout但过滤条件里写了FWPM_CONDITION_IP_PROTOCOL等于IPPROTO_UDPUDP 连接不走这个分层自然不触发。解决先确认分层和协议匹配。用netsh wfp show filters查看过滤规则是否真的挂到了目标分层上。另外检查action.type是否设成了FWP_ACTION_CALLOUT_TERMINATING或FWP_ACTION_CALLOUT_INSPECTION设成FWP_ACTION_BLOCK的话 callout 根本不会被调用。4.3 驱动卸载后网络断掉现象sc stop SuperDriver之后系统完全无法上网重启才恢复。原因驱动卸载时没有清理 WFP 过滤规则和 callout。WFP 引擎里还残留着指向已卸载驱动的 callout 指针网络栈走到那个分层时找不到处理函数直接丢包。解决在DriverUnload或EvtDeviceReleaseHardware里按注册的逆序清理先FwpmFilterDeleteById0删除所有过滤规则再FwpsCalloutUnregisterById0注销 callout最后FwpmEngineClose0关闭引擎句柄。每一步都要检查返回值失败的话记录日志。4.4 签名问题导致驱动加载失败现象sc start返回577或1275错误驱动加载被拒绝。原因577是签名验证失败1275是驱动被策略阻止。Win10 1607 之后内核驱动必须要有微软签名测试签名模式只能绕过一部分检查。解决开发阶段用bcdedit /set testsigning on加测试签名。生产环境必须做 WHQL 签名或使用 EV 证书签名。另外检查驱动 INF 文件里的CatalogFile是否指向了正确的.cat文件。4.5 性能问题每个包都过滤导致网络卡顿现象驱动加载后网络延迟从几毫秒飙升到几百毫秒下载速度大幅下降。原因在TRANSPORT_LAYER或DATAGRAM_DATA层注册了 callout每个包都触发回调回调里又做了复杂逻辑比如查数据库、写日志直接把网络栈拖垮。解决能用 ALE 层就用 ALE 层ALE 层只在连接建立时触发开销小得多。如果必须做包级过滤回调里只做最简单的判断复杂逻辑异步化。另外用FwpsFlowAssociateContext0把流上下文缓存起来避免重复计算。5. 进阶技巧用 WFP 实现进程级黑白名单的完整思路前面讲的都是基础框架这一章落到一个具体场景怎么用 WFP 驱动实现「只允许指定进程访问网络」的黑白名单功能。这是 SuperDriver 这类项目最核心的需求之一。5.1 获取进程路径的两种方式在classifyFn里拿进程路径有两种方式第一种是从FWPS_INCOMING_VALUES里直接取。ALE 层的FWPS_FIELD_ALE_AUTH_CONNECT_ALE_PACKAGE_ID字段是进程的包标识但拿不到路径。要拿路径得用FwpsGetPacketByIndex或者查FWPS_FIELD_ALE_AUTH_CONNECT_ALE_APP_ID这个字段是FWP_BYTE_BLOB类型的应用标识。第二种是用PsGetProcessImageFileName从EPROCESS结构体拿。在 callout 里可以通过FwpsGetIncomingValue拿到进程 ID然后用PsLookupProcessByProcessId拿到EPROCESS指针再取路径。这种方式更直接但要注意PsLookupProcessByProcessId会增加进程引用计数用完必须ObDereferenceObject。// 从 classifyFn 的 incomingValues 里提取进程路径 NTSTATUS GetProcessPathFromClassify( _In_ const FWPS_INCOMING_VALUES0* inFixedValues, _Out_ PUNICODE_STRING ProcessPath) { UINT64 processId 0; PEPROCESS process NULL; NTSTATUS status; // 从 ALE 层字段里拿进程 ID processId inFixedValues-incomingValue[ FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_PACKAGE_ID].value.uint64; status PsLookupProcessByProcessId((HANDLE)processId, process); if (!NT_SUCCESS(status)) { return status; } // 注意这里拿到的路径是设备路径需要转换成 DOS 路径 status SeLocateProcessImageName(process, ProcessPath); ObDereferenceObject(process); // 必须解引用否则进程无法退出 return status; }SeLocateProcessImageName返回的是\Device\HarddiskVolumeX\...格式的设备路径需要转换成C:\...格式才能和用户配置的白名单比对。转换用IoVolumeDeviceToDosName或者查符号链接。5.2 黑白名单的存储与查询进程路径拿到后要和黑白名单比对。名单存储有两种方案方案一驱动内硬编码或从注册表读。简单直接但更新名单要重启驱动。适合名单固定的场景。方案二用户态程序通过 IOCTL 下发名单。灵活可以动态更新。驱动里用一个LIST_ENTRY链表存名单每个节点存进程路径的哈希值。查询时算哈希再遍历链表O(n) 复杂度名单几百条以内性能没问题。// 名单节点结构 typedef struct _PROCESS_RULE { LIST_ENTRY ListEntry; ULONG PathHash; // 进程路径的哈希值 BOOLEAN IsBlock; // TRUE黑名单FALSE白名单 } PROCESS_RULE, *PPROCESS_RULE; // 查询进程是否在名单里 BOOLEAN IsProcessBlocked(_In_ PUNICODE_STRING ProcessPath) { ULONG hash ComputeStringHash(ProcessPath); PLIST_ENTRY entry g_RuleListHead.Flink; while (entry ! g_RuleListHead) { PPROCESS_RULE rule CONTAINING_RECORD(entry, PROCESS_RULE, ListEntry); if (rule-PathHash hash rule-IsBlock) { return TRUE; } entry entry-Flink; } return FALSE; }哈希用RtlComputeCrc32或者自己写个简单的 FNV 哈希都行。注意链表操作要加自旋锁因为classifyFn可能在多个 CPU 上并发执行。5.3 规则匹配的优先级设计黑白名单同时存在时优先级怎么定我的做法是白名单优先于黑名单。也就是说如果一个进程既在白名单又在黑名单放行。这样设计的原因是白名单通常是「必须放行的关键进程」黑名单是「默认阻止的可疑进程」白名单优先级高更符合直觉。匹配逻辑FWP_ACTION_TYPE DecideAction(_In_ PUNICODE_STRING ProcessPath) { // 先查白名单 if (IsProcessInList(ProcessPath, WHITE_LIST)) { return FWP_ACTION_PERMIT; } // 再查黑名单 if (IsProcessInList(ProcessPath, BLACK_LIST)) { return FWP_ACTION_BLOCK; } // 都不在默认放行或默认阻止看策略 return g_DefaultAction; }g_DefaultAction是全局默认策略可以设成FWP_ACTION_PERMIT或FWP_ACTION_BLOCK。如果做「只允许白名单进程上网」的严格模式默认设成 BLOCK如果做「只阻止黑名单进程」的宽松模式默认设成 PERMIT。5.4 验证与调试技巧名单功能写完后怎么验证我一般用三个步骤第一步用DbgPrint在classifyFn里打印进程路径和决策结果确认路径解析正确、名单匹配正确。第二步写一个简单的用户态测试程序尝试用不同进程访问网络看是否按预期被拦截。比如用curl访问一个 IP如果curl.exe在黑名单里应该连接超时。第三步用netsh wfp show state导出 WFP 状态确认过滤规则和 callout 都在。如果规则在但拦截不生效检查classifyFn返回值是否正确。注意调试阶段建议先把默认动作设成 PERMIT只对明确在黑名单里的进程做 BLOCK避免驱动一加载就把自己网络断了连调试通道都断掉。这套方案我在几个项目里用过稳定性和性能都能满足生产要求。核心难点不在 WFP API 本身而在进程路径解析、并发安全、规则优先级这些细节上。把这些细节处理好SuperDriver 这类驱动就能真正落地而不是停留在 demo 阶段。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

DS5Dongle 隐藏功能解锁:PS 键唤醒电脑(Wake-on-PS)+ 秒开 Xbox Game Bar 完整设置 2026/10/1 16:14:33

DS5Dongle 隐藏功能解锁:PS 键唤醒电脑(Wake-on-PS)+ 秒开 Xbox Game Bar 完整设置

DS5Dongle 隐藏功能解锁:PS 键唤醒电脑(Wake-on-PS) 秒开 Xbox Game Bar 完整设置 【免费下载链接】DS5Dongle Turn your Pico 2 W into a DualSense 5 dongle. 项目地址: https://gitcode.com/gh_mirrors/ds/DS5Dongle DS5Dongle 可以…

阅读更多 →
哪些图标库能和morphicons一起玩?Lucide、Tabler、Heroicons兼容性清单与fitIcon跨网格适配教程 2026/10/1 16:14:33

哪些图标库能和morphicons一起玩?Lucide、Tabler、Heroicons兼容性清单与fitIcon跨网格适配教程

哪些图标库能和morphicons一起玩?Lucide、Tabler、Heroicons兼容性清单与fitIcon跨网格适配教程 【免费下载链接】morphicons Any icon morphs into any other — universal morphing for stroke-based icons with spring physics. Zero dependencies, ~7 KB gzip. …

阅读更多 →
【AODV仿真】AODV临时WSN定制网络层并配合仿真【含Matlab源码 15996期】 2026/10/1 16:14:33

【AODV仿真】AODV临时WSN定制网络层并配合仿真【含Matlab源码 15996期】

💥💥💥💥💥💥💥💥💞💞💞💞💞💞💞💞💞Matlab武动乾坤博客之家💞…

阅读更多 →
DQN 出主意、MPC 踩油门:混合规划把最坏求解耗时砍掉 56% 2026/10/1 16:14:33

DQN 出主意、MPC 踩油门:混合规划把最坏求解耗时砍掉 56%

DQN 出主意、MPC 踩油门:混合规划把最坏求解耗时砍掉 56%原文:Collision-Free Trajectory Planning of Mobile Robots by Integrating Deep Reinforcement Learning and Model Predictive Control 作者:Ze Zhang、Yao Cai、Kristian Ceder、A…

阅读更多 →
关键字段OCR抽取实战:用 Python 完成文档信息抽取 + 自定义OCR模板 + 字段名/值/置信度/坐标框解析 2026/10/1 16:14:31

关键字段OCR抽取实战:用 Python 完成文档信息抽取 + 自定义OCR模板 + 字段名/值/置信度/坐标框解析

背景做档案系统、数据中台、企业文档管理的同学,大概率被业务方提过这个需求:把一批扫描件、拍照单据、PDF文档丢进去,自动把合同编号、运单号、收发件人、项目编号这些字段抽出来,直接写进业务库。这件事听起来就是"调个OCR…

阅读更多 →
2022年408真题详解:DMA方式与外存磁道扇区计算核心考点 2026/10/1 16:14:25

2022年408真题详解:DMA方式与外存磁道扇区计算核心考点

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

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉