Listen to this Post

Introduction
Every network packet traversing a modern Windows system is subject to inspection before it ever leaves the host. The Windows Filtering Platform (WFP)—the kernel-level arbitration engine introduced in Windows Vista—has become the primary battleground where Endpoint Detection and Response (EDR) products intercept, attribute, and analyze outbound connections. Understanding WFP is no longer optional for offensive security practitioners: it is the difference between an implant that operates silently and one that triggers immediate detection at the kernel level before the first SYN packet is transmitted. This article dissects WFP from both the defender’s and attacker’s perspectives, exploring how EDRs collect telemetry through callout drivers and how offensive operators can manipulate filters, sublayers, and callout registrations to blind network monitoring—all while providing actionable code, commands, and operational checklists.
Learning Objectives
- Objective 1: Understand the WFP architecture—its three planes, shim layers, filter engine, and the critical distinction between BFE (policy broker) and the kernel engine (enforcer).
- Objective 2: Learn exactly what network telemetry EDRs collect at each layer, including process attribution, beacon pattern analysis, DNS correlation, and TLS metadata extraction without decryption.
- Objective 3: Master five offensive WFP manipulation techniques—from adding higher-weight blocking filters to kernel-level callout function pointer patching—with step-by-step implementation guidance and operational risk assessments.
You Should Know
1. WFP Architecture: The Operator’s Map
Before WFP, Windows relied on TDI (Transport Driver Interface) and NDIS LWF (Lightweight Filter) for network interception. TDI offered process context via `FILE_OBJECT` but suffered from undefined filter ordering and undocumented APIs, leading to driver conflicts and instability. NDIS LWF operated below tcpip.sys, seeing raw Ethernet frames but with zero process attribution—making it architecturally wrong for process-correlated monitoring. WFP solved both problems by baking inspection points directly into `tcpip.sys` as shim layers that provide automatic process context at ALE (Application Layer Enforcement) layers.
WFP separates into three planes: the shim layers (small code stubs inside `tcpip.sys` that pause data and assemble classification context), the filter engine (fwpkclnt.sys—the arbitration brain that evaluates filters and callouts), and BFE (bfe.dll running in a `svchost.exe` instance—the policy broker that owns persistent object storage but is not in the data path). This last distinction is critical: killing BFE does not blind EDR callouts already loaded in the kernel engine. Filters and callouts continue running independently.
The ALE layers—particularly ALE_AUTH_CONNECT_V4—are the EDR’s preferred battlefield because they provide both PID and full application path (ALE_APP_ID) simultaneously. At this layer, the EDR receives the connection event before any SYN packet leaves the machine.
Step‑by‑step: Enumerating WFP state with built-in tools
To understand what filters and callouts are active on a system, use the following commands:
List all WFP filters with details (requires admin) netsh wfp show filters Show filters in a verbose format netsh wfp show filters verbose List all sublayers netsh wfp show sublayers Show callouts registered in the system netsh wfp show callouts
For programmatic enumeration via the Windows Filtering Platform API, use `FwpmFilterEnum` and `FwpmCalloutEnum` with templates filtered by `providerKey` to scope a specific EDR product’s objects.
- The Object Model: Providers, Sublayers, Filters, and Callouts
Understanding WFP’s object model is essential for both building defensive callouts and crafting offensive manipulations.
Providers (FWPM_PROVIDER) are identity namespaces that group an EDR’s filters and callouts under a single owner GUID. Once you have the provider GUID—often visible via netsh wfp show providers—you can enumerate all objects belonging to that product. Providers can optionally bind to a Windows service name as a tamper-resistance feature; if the service is killed, WFP may attempt to restart it.
Sublayers organize filters within each layer and have a 16-bit weight (0–65535). Higher-weight sublayers are evaluated first. The default sublayer has weight zero—the lowest possible. Any custom sublayer registered with weight > 0 will be evaluated before the default sublayer. This is the mechanism exploited by EDRSilencer-style attacks: register a higher-weight sublayer, add a BLOCK filter, and the packet is dropped before the EDR’s callout ever fires.
Filters (FWPM_FILTER) are rules attached to a specific layer. They trigger an action when conditions are satisfied. A filter with zero conditions matches every event at its layer. The most useful condition fields for offensive filtering include `FWPM_CONDITION_ALE_APP_ID` (match by executable NT path), FWPM_CONDITION_IP_REMOTE_ADDRESS, FWPM_CONDITION_IP_REMOTE_PORT, and FWPM_CONDITION_IP_PROTOCOL.
Callouts are kernel drivers that register three callback functions (classifyFn, notifyFn, flowDeleteFn) and associate them with a WFP layer. A critical operational fact: if the management registration exists in BFE’s database but the kernel registration does not (the driver is not loaded or the callout was unregistered), the filter engine treats that callout as a BLOCK action. This can be weaponized by injecting a management-layer callout reference without loading the actual driver, creating a phantom blocker.
Step‑by‑step: Registering a custom sublayer with higher weight
// Register a sublayer with weight 100 (higher than default weight 0)
FWPM_SUBLAYER sublayer = {0};
sublayer.subLayerKey = MY_SUBLAYER_GUID;
sublayer.displayData.name = L"Offensive Sublayer";
sublayer.displayData.description = L"Higher-weight sublayer for pre-emptive blocking";
sublayer.weight = 100; // Higher than default
sublayer.flags = 0;
FwpmSubLayerAdd(engineHandle, &sublayer, NULL);
3. EDR Network Telemetry: What They Actually Collect
EDRs collect far more than just IP addresses and ports. At FWPM_LAYER_ALE_AUTH_CONNECT_V4, the `classifyFn` receives a fully attributed connection event including PID, process path, process hash, local/remote addresses and ports, protocol, timestamp, signing status, and reputation.
Beacon pattern analysis goes beyond individual events. EDRs store connection timestamps per process per remote endpoint and perform autocorrelation on time-delta sequences. A beacon firing every 60 seconds produces a strong spike at lag=60. With 50% jitter, modern statistical models can still identify the underlying period through long-observation analysis. Packet size consistency is also suspicious—a process sending identically-sized packets at regular intervals triggers alerts even with timestamp jitter.
DNS process correlation intercepts queries at `FWPM_LAYER_DATAGRAM_DATA_V4` on port 53, extracting the PID, query name, and record type. Even with domain fronting, the DNS resolution event for the front domain is logged and attributed to the implant’s process, and subsequent connections to the resolved IP are correlated.
TLS metadata extraction requires no decryption. During the TLS handshake, the Client Hello transmits SNI (Server Name Indication), cipher suite list, extensions, and elliptic curves in cleartext. The JA3 fingerprint—an MD5 hash of TLS version, ciphers, extensions, elliptic curves, and point formats—identifies the client library. Cobalt Strike, Sliver, Metasploit, and Havoc each have documented default JA3 values that EDRs query. The Server Hello provides the selected cipher suite and the server certificate (DER-encoded, readable without decryption), including issuer, subject, serial number, and validity dates. The EDR knows the domain you connected to (via SNI), the TLS library you used (via JA3), and the certificate served—potentially flagging known C2 certificates or self-signed ones.
Step‑by‑step: Extracting JA3 fingerprints from a pcap
Using tshark to extract JA3 from a pcap
tshark -r capture.pcap -Y "tls.handshake.type == 1" -T fields -e tls.handshake.ja3
Alternatively, using ja3 Python library
pip install ja3
python3 -c "import ja3; print(ja3.generate_from_pcap('capture.pcap'))"
Windows command to view active network connections with process IDs:
netstat -ano | findstr ESTABLISHED
- Building a WFP Callout Driver: The Defender’s View
To understand how to blind an EDR, you must first understand how to build the very mechanism they use. The following is a practical walkthrough of WfpMonitor.sys—a kernel driver that registers callouts at `ALE_AUTH_CONNECT_V4` and ALE_AUTH_RECV_ACCEPT_V4, extracts and logs PID, application path, and remote endpoint on every connection, and demonstrates blocking by dropping connections to a configurable IP range.
Build environment: Windows Driver Kit (WDK) 10, Visual Studio 2019+, test VM with kernel debugging enabled (bcdedit /set debug on, bcdedit /set testsigning on), WinDbg Preview, and linking against `fwpkclnt.lib` and uuid.lib.
Registration sequence (order matters):
1. Register kernel callbacks with `FwpsCalloutRegister`
2. Open the engine with `FwpmEngineOpen`
3. Add sublayer with `FwpmSubLayerAdd`
4. Add callouts to management layer with `FwpmCalloutAdd`
5. Add filters last with `FwpmFilterAdd`
6. Cleanup in exact reverse order
Cleanup transactions are not optional. Without a transaction, a partial failure (e.g., `FwpmFilterAdd` succeeds but the driver fails to load) leaves orphaned WFP objects in BFE’s database that survive reboots.
The `classifyFn` dissection—this function runs in the kernel for every matching network event and must be fast, avoiding blocking calls, paging, and unnecessary allocation:
VOID NTAPI ClassifyFn(
const FWPS_INCOMING_VALUES inFixedValues,
const FWPS_INCOMING_METADATA_VALUES inMetaValues,
VOID layerData,
const VOID classifyContext,
const FWPS_FILTER filter,
UINT64 flowContext,
FWPS_CLASSIFY_OUT classifyOut)
{
// Check if we are allowed to write the action
if (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE)) return;
// Extract network metadata
UINT32 remoteIp = inFixedValues->incomingValue[
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
UINT16 remotePort = inFixedValues->incomingValue[
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;
// Extract PID from metadata (always check validity bitmask)
UINT64 pid = 0;
if (inMetaValues->currentMetadataValues & FWPS_METADATA_FIELD_PROCESS_ID) {
pid = inMetaValues->processId;
}
// Extract application path (ALE_APP_ID)
FWP_BYTE_BLOB appId = inFixedValues->incomingValue[
FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_APP_ID].value.byteBlob;
// Blocking logic: drop all connections to 192.168.100.0/24
UINT32 blockNetwork = 0xC0A86400; // 192.168.100.0
UINT32 blockMask = 0xFFFFFF00;
if ((remoteIp & blockMask) == (blockNetwork & blockMask)) {
classifyOut->actionType = FWP_ACTION_BLOCK;
classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
return;
}
// Inspection-only: defer to lower sublayers
classifyOut->actionType = FWP_ACTION_CONTINUE;
}
Key correctness notes: Always check `FWPS_RIGHT_ACTION_WRITE` before setting any action. `FWP_ACTION_CONTINUE` is only valid when the filter action type is FWP_ACTION_CALLOUT_UNKNOWN. IP bytes in `inFixedValues` are in host byte order for IPv4 but network byte order for ports—this inconsistency is a common bug source. `DbgPrint` is not safe at DISPATCH_LEVEL.
Cleanup sequence (inverse of registration):
VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
// 1. Remove filters first (they reference callouts and sublayers)
FwpmFilterDeleteById(g_State.EngineHandle, g_State.FilterId);
// 2. Remove callout from management layer
FwpmCalloutDeleteById(g_State.EngineHandle, g_State.MgmtCalloutId);
// 3. Remove sublayer
FwpmSubLayerDeleteByKey(g_State.EngineHandle, &SUbLAYER_GUID);
// 4. Close engine handle
FwpmEngineClose(g_State.EngineHandle);
// 5. Unregister kernel callout LAST—must retry on STATUS_DEVICE_BUSY
while (FwpsCalloutUnregisterById(g_State.KernelCalloutId) == STATUS_DEVICE_BUSY) {
KeStallExecutionProcessor(100);
}
// 6. Delete device object
IoDeleteDevice(g_State.DeviceObject);
}
The `STATUS_DEVICE_BUSY` retry loop is necessary because `classifyFn` may still be executing on another processor when `DriverUnload` is called.
5. Offensive WFP Manipulation: Blinding the EDR
Five primary techniques exist for offensive WFP manipulation, ranked by operational noise and risk:
Technique 1: Adding Higher-Weight Blocking Filters (EDRSilencer Model) — Register a sublayer with weight higher than the EDR’s sublayer, then add a BLOCK filter that matches the EDR’s telemetry traffic (or all traffic). Your filter runs first, the packet is dropped, and the EDR’s callout never fires. This is the lowest-1oise technique as it leaves EDR objects intact but preempts them.
Technique 2: Filter Deletion via Management API — Use `FwpmFilterDeleteById` or `FwpmFilterDeleteByKey` to remove the EDR’s filters from BFE. This requires knowing the filter IDs or keys (enumerated via `FwpmFilterEnum` filtered by provider GUID). The kernel engine updates its in-memory table on the next synchronization cycle, after which the EDR’s callout no longer receives events.
Technique 3: Callout Unregistration — Call `FwpsCalloutUnregisterById` to remove the kernel registration of the EDR’s callout. If the management registration remains but the kernel registration is gone, traffic matching that callout will be blocked (phantom blocker). This can cause denial-of-service and is high-1oise.
Technique 4: Kernel-Level classifyFn Pointer Patching — Directly patch the `classifyFn` function pointer in the `FWPS_CALLOUT` structure in kernel memory to redirect to a no-op function. This is the most surgical technique but requires kernel-level privileges and carries high risk of BSOD.
Technique 5: BFE Service Disruption — Kill the BFE service (svchost.exe -k NetworkService hosting bfe.dll). This stops policy management—no new updates go in, and your own changes cannot go in either. Filters and callouts already loaded in the kernel engine keep running, so this does not immediately blind the EDR. It is the least effective technique for immediate evasion.
Comparison Matrix:
| Technique | Stealth | Persistence | Implementation Complexity | Risk of Detection |
|–||-||-|
| Higher-weight blocking filter | High | Until reboot | Low | Low |
| Filter deletion | Medium | Until EDR re-adds | Medium | Medium |
| Callout unregistration | Low | Until reboot | Medium | High |
| classifyFn pointer patching | Very High | Until reboot | Very High | Very High |
| BFE service disruption | Very Low | Until service restart | Low | Very High |
6. Reconnaissance: Mapping the WFP State with WfpAtlas
Before any manipulation, reconnaissance is essential. WfpAtlas is a tool designed to map the complete WFP state—enumerating providers, sublayers, filters, and callouts—to identify exactly which objects belong to which EDR product.
Step‑by‑step: Manual WFP reconnaissance using built-in tools
Enumerate all providers to find EDR GUIDs netsh wfp show providers Enumerate all sublayers with weights netsh wfp show sublayers Enumerate filters with provider key filtering (conceptual) Using PowerShell to call WFP API via C or using FwpmFilterEnum in a C++ tool List all callouts netsh wfp show callouts
Programmatic enumeration using the WFP API:
// Open engine
HANDLE engineHandle;
FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, NULL, &engineHandle);
// Enumerate filters filtered by provider key
FWPM_FILTER_ENUM_TEMPLATE enumTemplate = {0};
enumTemplate.providerKey = &targetProviderGUID;
FwpmFilterEnum(engineHandle, &enumTemplate, INFINITE, &numFilters, &filterArray);
// Enumerate callouts similarly
FWPM_CALLOUT_ENUM_TEMPLATE calloutTemplate = {0};
calloutTemplate.providerKey = &targetProviderGUID;
FwpmCalloutEnum(engineHandle, &calloutTemplate, INFINITE, &numCallouts, &calloutArray);
What Undercode Say
- Key Takeaway 1: WFP’s architecture is fundamentally sound—it provides process-correlated network telemetry at the kernel level with deterministic arbitration. However, the very features that make it powerful for defenders (sublayer weights, filter arbitration, callout registration) are also the features that make it exploitable. The weight system is the Achilles’ heel: a higher-weight blocking filter trumps any lower-weight EDR callout, regardless of the EDR’s intentions.
-
Key Takeaway 2: EDR telemetry extends far beyond simple IP/port logging. Beacon pattern analysis, DNS correlation, and TLS metadata extraction (JA3/JA3S) provide defenders with multiple layers of detection that operate independently of payload signatures. Offensive operators must now contend with behavioral analytics that detect periodic communication patterns, statistical outliers in packet sizes, and cryptographic fingerprints—all collected before the first byte of encrypted payload is transmitted.
Analysis: The cat-and-mouse game between EDR vendors and offensive operators is accelerating. EDRs are moving toward machine learning models that detect behavioral anomalies rather than relying solely on static IoCs. Techniques like higher-weight blocking filters are effective today but are increasingly being countered by EDRs that monitor for new sublayer registrations and filter additions. The future of offensive WFP manipulation lies in more surgical approaches—such as targeted `classifyFn` pointer patching or leveraging the phantom blocker vulnerability—that leave minimal forensic artifacts. However, these techniques require deep kernel expertise and carry significant stability risks. The most sustainable approach for operators is to understand WFP’s data collection thoroughly and design implants that mimic legitimate application behavior—using non-uniform jitter distributions, randomized packet sizes, and common JA3 fingerprints—rather than relying solely on blinding the telemetry source.
Prediction
- +1 EDR vendors will increasingly implement integrity monitoring of WFP objects, alerting on new sublayer registrations, filter additions, and callout unregistrations. This will drive offensive techniques toward lower-level kernel manipulations that bypass management-layer monitoring.
-
-1 The complexity of WFP manipulation will continue to rise as Microsoft introduces additional security mitigations, such as requiring signed drivers for callout registration and enforcing stricter validation of filter weights.
-
+1 Open-source tools like WfpAtlas will become essential components of every red team’s toolkit, enabling rapid reconnaissance and targeted manipulation with minimal manual effort.
-
-1 Behavioral analytics will render simple blinding techniques obsolete. EDRs will correlate the absence of expected telemetry from a process with other indicators (e.g., process injection, anomalous memory patterns) to flag evasion attempts, even if network telemetry is successfully suppressed.
-
+1 The offensive community will develop more sophisticated implants that operate entirely within the constraints of WFP’s data collection—using encrypted tunnels that mimic legitimate TLS traffic, implementing non-periodic beaconing with high jitter, and dynamically altering JA3 fingerprints to avoid signatured detection.
-
-1 Defenders will adopt a defense-in-depth approach that combines WFP telemetry with endpoint behavioral monitoring, making it increasingly difficult for operators to achieve stealth through network-level manipulation alone. The era of single-vector evasion is ending; future tradecraft must be multi-layered and context-aware.
▶️ Related Video (76% Match):
https://www.youtube.com/watch?v=55GaIolVVqI
🎯Let’s Practice For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
IT/Security Reporter URL:
Reported By: Muaaztalaat A – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅


