Preventing Port Scanning

The following sample code demonstrates how to prevent port scanning using stealth discards and silent drops.

#include <windows.h>
#include <fwpmu.h>
#include <stdio.h>

#pragma comment(lib, "fwpuclnt.lib")

#define EXIT_ON_ERROR(fnName) \
   if (result != ERROR_SUCCESS) \
   { \
      printf(#fnName " = 0x%08X\n", result); \
      goto CLEANUP; \
   }

DWORD BlockPortScanning(
         __in HANDLE engine,
         __in PCWSTR filterName,
         __in_opt const GUID* providerKey,
         __in_opt const GUID* subLayerKey
         )
{
   DWORD result = ERROR_SUCCESS;
   FWPM_FILTER_CONDITION0 conds[1];
   FWPM_FILTER0 filter;
   BOOL txnInProgress = FALSE;

   // Fill in the common fields shared by all our filters.
   memset(&filter, 0, sizeof(filter));
   // For MUI compatibility, object names should be indirect strings. See
   // SHLoadIndirectString for details.
   filter.displayData.name = (PWSTR)filterName;
   // Link all our objects to our provider. When multiple providers are
   // installed on a computer, this makes it easy to determine who added what.
   filter.providerKey = (GUID*)providerKey;
   // Generally, it's best to add filters to our own sublayer, so we don't have
   // to worry about being overridden by filters added by another provider.
   if (subLayerKey != NULL)
   {
      filter.subLayerKey = *subLayerKey;
   }
   filter.numFilterConditions = 1;
   filter.filterCondition = conds;

   // We add all the objects from within a single transaction to make it easy
   // to clean up partial results in error paths.
   result = FwpmTransactionBegin0(engine, 0);
   EXIT_ON_ERROR(FwpmTransactionBegin0);
   txnInProgress = TRUE;

   //////////
   // Block outbound ICMP Destination Unreachable to prevent UDP port scanning.
   //////////

   // ICMP Type == Destination Unreachable.
   conds[0].fieldKey = FWPM_CONDITION_ICMP_TYPE;
   conds[0].matchType = FWP_MATCH_EQUAL;
   conds[0].conditionValue.type = FWP_UINT16;
   conds[0].conditionValue.uint16 = 3;

   filter.action.type = FWP_ACTION_BLOCK;

   // Add the IPv4 filter.
   filter.layerKey = FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4;
   result = FwpmFilterAdd0(engine, &filter, NULL, NULL);
   EXIT_ON_ERROR(FwpmFilterAdd0);

   // Add the IPv6 filter.
   filter.layerKey = FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6;
   result = FwpmFilterAdd0(engine, &filter, NULL, NULL);
   EXIT_ON_ERROR(FwpmFilterAdd0);


   //////////
   // Block outbound TCP RST to prevent TCP port scanning.
   //////////

   // We don't want to block TCP RST for loopback, so use a condition that
   // matches everything but loopback.
   conds[0].fieldKey = FWPM_CONDITION_FLAGS;
   conds[0].matchType = FWP_MATCH_FLAGS_NONE_SET;
   conds[0].conditionValue.type = FWP_UINT32;
   conds[0].conditionValue.uint32 = FWP_CONDITION_FLAG_IS_LOOPBACK;

   filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;

   // Add the IPv4 filter.
   filter.layerKey = FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD;
   filter.action.calloutKey = FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V4_SILENT_DROP;
   result = FwpmFilterAdd0(engine, &filter, NULL, NULL);
   EXIT_ON_ERROR(FwpmFilterAdd0);

   // Add the IPv6 filter.
   filter.layerKey = FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD;
   filter.action.calloutKey = FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V6_SILENT_DROP;
   result = FwpmFilterAdd0(engine, &filter, NULL, NULL);
   EXIT_ON_ERROR(FwpmFilterAdd0);

   // Once all the adds have succeeded, we commit the transaction to atomically
   // add all the new objects.
   result = FwpmTransactionCommit0(engine);
   EXIT_ON_ERROR(FwpmTransactionCommit0);
   txnInProgress = FALSE;

CLEANUP:
   if (txnInProgress)
   {
      // Abort any transaction still in progress to clean up partial results.
      FwpmTransactionAbort0(engine);
   }
   return result;
}

Filtering Conditions Available at Each Filtering Layer