Objective
The goal of this project is to develop a kernel-mode
driver using the Windows Filtering Platform (WFP)
to monitor and log network connections in real time. The driver will:
- Capture outgoing TCP/IPv4 connections
- Extract Process ID (PID) of the application making the connection
- Log remote IP addresses and ports
- Permit traffic while enabling inspection
This can serve as a foundation for network security tools, data leak prevention (DLP), and forensic analysis.
#include <ntddk.h>
#include <fwpsk.h>
#include <fwpmk.h>
#define INITGUID
#include <guiddef.h>
#include <fwpmu.h>
#define NTOHS(x) ((USHORT)((((USHORT)(x)) >> 8) | (((USHORT)(x)) << 8)))
DEFINE_GUID(
HELLO_CALLOUT_GUID,
0xaabbccdd, 0x1122, 0x3344, 0x55, 0x66, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
);
void NTAPI CalloutClassifyFn(
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
) {
UNREFERENCED_PARAMETER(inFixedValues);
//UNREFERENCED_PARAMETER(inMetaValues);
UNREFERENCED_PARAMETER(layerData);
UNREFERENCED_PARAMETER(classifyContext);
UNREFERENCED_PARAMETER(filter);
UNREFERENCED_PARAMETER(flowContext);
if (inMetaValues->currentMetadataValues & FWPS_METADATA_FIELD_PROCESS_ID) {
UINT64 pid = inMetaValues->processId;
DbgPrint("DLP006Network: Connection detected from PID: %llu\n", pid);
}
UINT32 remoteAddr = 0;
UINT16 remotePort = 0;
// Get remote IP
remoteAddr = inFixedValues->incomingValue[
FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS
].value.uint32;
// Get remote port
remotePort = inFixedValues->incomingValue[
FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT
].value.uint16;
DbgPrint("Remote IP: %d.%d.%d.%d, Port: %u\n",
(remoteAddr >> 24) & 0xff,
(remoteAddr >> 16) & 0xff,
(remoteAddr >> 8) & 0xff,
(remoteAddr & 0xff),
NTOHS(remotePort)
);
/*UINT32 hostOrderIP = RtlUlongByteSwap(remoteAddr);
DbgPrint("Remote IP: %d.%d.%d.%d, Port: %u\n",
(hostOrderIP >> 24) & 0xFF,
(hostOrderIP >> 16) & 0xFF,
(hostOrderIP >> 8) & 0xFF,
(hostOrderIP) & 0xFF,
NTOHS(remotePort));*/
/*IN_ADDR ipAddr = { .S_un.S_addr = remoteAddr };
CHAR ipStr[16] = { 0 };
RtlIpv4AddressToStringA(&ipAddr, ipStr);
DbgPrint("Remote IP: %s, Port: %u\n", ipStr, NTOHS(remotePort));
*/
// DbgPrint("DLP006Network: Hello from WFP callout!\n");
classifyOut->actionType = FWP_ACTION_PERMIT;
}
NTSTATUS NTAPI CalloutNotifyFn(
FWPS_CALLOUT_NOTIFY_TYPE notifyType,
const GUID* filterKey,
FWPS_FILTER* filter
) {
UNREFERENCED_PARAMETER(notifyType);
UNREFERENCED_PARAMETER(filterKey);
UNREFERENCED_PARAMETER(filter);
return STATUS_SUCCESS;
}
void NTAPI CalloutFlowDeleteFn(UINT16 layerId, UINT32 calloutId, UINT64 flowContext) {
UNREFERENCED_PARAMETER(layerId);
UNREFERENCED_PARAMETER(calloutId);
UNREFERENCED_PARAMETER(flowContext);
}
void DriverUnload(PDRIVER_OBJECT driverObject) {
UNREFERENCED_PARAMETER(driverObject);
// Unregister from BFE
HANDLE engineHandle = NULL;
if (NT_SUCCESS(FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, NULL, &engineHandle))) {
FwpmCalloutDeleteByKey(engineHandle, &HELLO_CALLOUT_GUID);
FwpmEngineClose(engineHandle);
}
DbgPrint("DLP006Network: Driver Unloaded\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT driverObject, PUNICODE_STRING registryPath) {
UNREFERENCED_PARAMETER(registryPath);
driverObject->DriverUnload = DriverUnload;
NTSTATUS status;
UINT32 calloutId = 0;
HANDLE engineHandle = NULL;
// 1. Register the callout with kernel-mode WFP
FWPS_CALLOUT callout = { 0 };
callout.calloutKey = HELLO_CALLOUT_GUID;
callout.classifyFn = CalloutClassifyFn;
callout.notifyFn = CalloutNotifyFn;
callout.flowDeleteFn = CalloutFlowDeleteFn;
status = FwpsCalloutRegister(driverObject, &callout, &calloutId);
if (!NT_SUCCESS(status)) {
DbgPrint("DLP006Network: FwpsCalloutRegister failed: %08x\n", status);
return status;
}
// 2. Open a session to the filter engine (BFE)
status = FwpmEngineOpen(
NULL,
RPC_C_AUTHN_WINNT,
NULL,
NULL,
&engineHandle
);
if (!NT_SUCCESS(status)) {
DbgPrint("Failed to open WFP engine: %08x\n", status);
FwpsCalloutUnregisterByKey(&HELLO_CALLOUT_GUID);
return status;
}
// 3. Add the callout to the filter engine
FWPM_CALLOUT wfpCallout = { 0 };
wfpCallout.calloutKey = HELLO_CALLOUT_GUID;
wfpCallout.displayData.name = L"DLP006 Callout";
wfpCallout.displayData.description = L"Callout for DLP006 Module";
//wfpCallout.applicableLayer = FWPM_LAYER_STREAM_V4; // Kernel-mode GUID constant
wfpCallout.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
status = FwpmTransactionBegin(engineHandle, 0);
if (!NT_SUCCESS(status)) {
DbgPrint("Failed to begin WFP transaction: %08x\n", status);
FwpmEngineClose(engineHandle);
FwpsCalloutUnregisterByKey(&HELLO_CALLOUT_GUID);
return status;
}
status = FwpmCalloutAdd(engineHandle, &wfpCallout, NULL, NULL);
if (!NT_SUCCESS(status)) {
DbgPrint("Failed to add WFP callout: %08x\n", status);
FwpmTransactionAbort(engineHandle);
FwpmEngineClose(engineHandle);
FwpsCalloutUnregisterByKey(&HELLO_CALLOUT_GUID);
return status;
}
// 4. Add a filter to trigger our callout
FWPM_FILTER filter = { 0 };
filter.displayData.name = L"DLP006 Filter";
filter.displayData.description = L"Filter for DLP006 Module";
filter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;//FWPM_LAYER_STREAM_V4; // Must match the layer in user-mode
filter.action.type = FWP_ACTION_CALLOUT_INSPECTION;
filter.action.calloutKey = HELLO_CALLOUT_GUID;
filter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL;
filter.weight.type = FWP_EMPTY; // Default weight
status = FwpmFilterAdd(engineHandle, &filter, NULL, NULL);
if (!NT_SUCCESS(status)) {
DbgPrint("Failed to add WFP filter: %08x\n", status);
FwpmTransactionAbort(engineHandle);
FwpmEngineClose(engineHandle);
FwpsCalloutUnregisterByKey(&HELLO_CALLOUT_GUID);
return status;
}
// 5. Commit and close
status = FwpmTransactionCommit(engineHandle);
FwpmEngineClose(engineHandle);
if (!NT_SUCCESS(status)) {
DbgPrint("DLP006Network: FwpmTransactionCommit failed: 0x%08x\n", status);
FwpsCalloutUnregisterByKey(&HELLO_CALLOUT_GUID);
return status;
}
DbgPrint("DLP006Network: Driver Loaded. Callout ID: %u\n", calloutId);
return STATUS_SUCCESS;
}
Leave a comment
Your email address will not be published. Required fields are marked *