CLOSE
Updated on 04 Aug, 202524 mins read 11 views

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 *