All Articles

A PART OF ANTI-VIRUS 3 - 公開サンプルコードで学ぶ Windows Filtering Platform (WFP) - (WEB 版)【2 章 WFP によるアクセス制御を行うサンプル】

本章では、前の章で解説した WFP を使用した通信のフィルタリング機能の動作を実際に確認します。

動作の確認には、指定したレイヤーに様々なフィルターを追加できるユーザーモードプログラムを使用します。

このサンプルのユーザーモードプログラムのソースコードは以下のリポジトリで公開しています。

URL: https://github.com/kash1064/book06-wfp-samples

もくじ

サンプルプログラムの概要

上記のリポジトリからダウンロードしたサンプルプログラム (UserModeWFPFilter) のコードは、Visual Studio 2022 を使用してビルドできます。

このプログラムは、以下の 2 つの機能を持ちます。

  1. プログラムにハードコードされた設定に従い、特定のレイヤーにフィルターを追加する機能
  2. プログラムで登録したフィルターをすべて削除する機能

このプログラムで登録されるフィルターはすべて独自の WFP プロバイダーである「Simple WFP User-Mode Provider」に紐づけられます。 また、このプロバイダーと紐づくすべてのフィルターを削除することで、設定の復元を行います。

以下は、このサンプルプログラムを使用してフィルターの追加と削除を実施した際の画面キャプチャです。

サンプルプログラムによるフィルターの追加と削除

プログラムが追加するフィルターを定義する

このサンプルプログラムでは、グローバル変数として定義されている FILTER_RULES 構造体の配列 rules に要素を追加する形でフィルターを追加することができます。

struct FILTER_RULES
{
	const GUID filterKey;
	const GUID layerKey;
	const GUID subLayerKey;
	const wchar_t* name;
	const wchar_t* description;
	UINT16 port;
	const wchar_t* appPath;
	FWP_ACTION_TYPE action;
	UINT64 weight;
};

const FILTER_RULES rules[] = {
	{ FILTER_KEY_1, FWPM_LAYER_ALE_AUTH_CONNECT_V4, FWPM_SUBLAYER_UNIVERSAL, L"Block HTTPS(443)", L"Block HTTPS(443)", 443, NULL, FWP_ACTION_BLOCK, 0xFFFFFFFFFFFFFFF1 },

	{ FILTER_KEY_2, FWPM_LAYER_ALE_AUTH_CONNECT_V4, FWPM_SUBLAYER_UNIVERSAL, L"Allow HTTPS(443)", L"Allow HTTPS(443)", 443, NULL, FWP_ACTION_PERMIT, 0xFFFFFFFFFFFFFFF2 },
};

FILTER_RULES のメンバーである filterKey や layerKey、subLayerKey などは、 WFP フィルターに関連づけされる情報である FWPM_FILTER0 構造体のメンバーと対応しております。1

FWPM_FILTER0 構造体は以下の通り定義されており、フィルターの登録に必要な情報が含まれます。

typedef struct FWPM_FILTER0_ {
  GUID                   filterKey;
  FWPM_DISPLAY_DATA0     displayData;
  UINT32                 flags;
  GUID                   *providerKey;
  FWP_BYTE_BLOB          providerData;
  GUID                   layerKey;
  GUID                   subLayerKey;
  FWP_VALUE0             weight;
  UINT32                 numFilterConditions;
  FWPM_FILTER_CONDITION0 *filterCondition;
  FWPM_ACTION0           action;
  union {
    UINT64 rawContext;
    GUID   providerContextKey;
  };
  GUID                   *reserved;
  UINT64                 filterId;
  FWP_VALUE0             effectiveWeight;
} FWPM_FILTER0;

上記の例では、プログラムにハードコードされている GUID である FILTER_KEY_1 を filterKey として使用します。 この filterKey は、フィルターを一意に識別するために使用されます。

続くメンバーである layerKey と subLayerKey は、それぞれフィルターを追加するレイヤーとサブレイヤーの GUID を指しています。

WFP でフィルターを追加できるレイヤーと識別子については公開ドキュメント2で整理されており、 上記の例で使用している FWPM_LAYER_ALE_AUTH_CONNECT_V4 は、IPv4 を使用する送信側の TCP 接続要求を監査可能な ALE レイヤーを指定しています。

また、FWPM_SUBLAYER_UNIVERSAL は既定のサブレイヤーの識別子であり、他のどのサブレイヤーにも割り当てられていないすべてのフィルターをホストするサブレイヤーを指定しています。3

name と description メンバーは、どちらもフィルターの displayData に使用する情報です。これは、FWPM_DISPLAY_DATA0 構造体として定義されています。4

typedef struct FWPM_DISPLAY_DATA0_ {
  wchar_t *name;
  wchar_t *description;
} FWPM_DISPLAY_DATA0;

ここで指定した「Block HTTPS(443)」などの名前はフィルター名として登録され、 WFP Explorer などのツールを用いてフィルターの一覧を参照することで確認できます。

登録したフィルター名の参照

port と appPath メンバーは、それぞれフィルターの条件に使用するポート番号やアプリケーションのパス名を指定するために使用します。

前述の例では、ポート番号 443 のみをフィルターの条件として使用しており、アプリケーションパスについては条件に含まないため appPath には NULL を指定しています。

最後のメンバーである action と weight は、それぞれフィルターの条件に合致した場合のアクションとフィルターの重みを指定するための値です。

action はフィルターの条件に合致した場合のアクションを定義した FWPM_ACTION0 に含まれる FWP_ACTION_TYPE 列挙型の値を指定するものであり、FWP_ACTION_BLOCK はトラフィックのブロック、FWP_ACTION_PERMIT はトラフィックの許可を意味します。5

また、本章で使用するサンプルプログラムでは扱いませんが、Callout ドライバーを使用する場合は FWP_ACTION_CALLOUT_TERMINATINGFWP_ACTION_CALLOUT_INSPECTION、または FWP_ACTION_CALLOUT_UNKNOWN などを type として使用する場合があります。

weight メンバーは FWPM_FILTER0 構造体の weight として使用する値であり、サブレイヤー内でのフィルターの優先度 (重み) を指定します。 weight には FWP_UINT64FWP_UINT8 などの型を使用できますが、今回のサンプルプログラムでは FWP_UINT64 を使用しています。

本章で使用するサンプルプログラムにおける FILTER_RULES 構造体の定義は以上の通りです。

本章では、WFP によるネットワークトラフィックのフィルタリングの動作確認のために、 FILTER_RULES 構造体の配列 rules にルールの追加や削除を行った後にプログラムを再ビルドすることで様々なフィルターをシステムに登録します。

例えば、FWPM_LAYER_ALE_AUTH_CONNECT_V4 レイヤーのサブレイヤー FWPM_SUBLAYER_UNIVERSAL に対して、 Microsoft Edge によるアウトバウンドアクセスをすべてブロックするようなフィルターを追加したい場合は、rules 配列に以下の要素を追加します。

{ FILTER_KEY_3, FWPM_LAYER_ALE_AUTH_CONNECT_V4, FWPM_SUBLAYER_UNIVERSAL, L"Block Edge", L"Block Edge", 0, L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", FWP_ACTION_BLOCK, 0xFFFFFFFFFFFFFFF1 }

BFE を使用してフィルターを登録する

WFP で使用するフィルターの追加や削除は、主にユーザーモードサービスである Base Filtering Engine (BFE) により行われます。

アプリケーションは、プラットフォームが提供している管理機能をインターフェースとして BFE と通信し、 フィルターの追加や削除などの様々な操作を行うことができます。6

以下は、本章で使用するサンプルプログラムがフィルターの追加や削除を行う際の一連の操作をまとめたものです。

  1. FwpmEngineOpen0 関数を使用してフィルターエンジンへのセッションを開きます。
  2. セッションでトランザクションを開始します。
  3. カスタムプロバイダーを登録します。
  4. 登録したカスタムプロバイダーと紐づけてフィルターの登録を行います。
  5. すべてのフィルターを登録したら、トランザクションをコミットして変更を反映します。

以下に、各ステップの詳細を解説します。

フィルターエンジンへのセッションを開く

WFP で通信をフィルタリングするためのフィルターを登録するためにはまず、フィルターエンジンへのセッションを開く必要があります。

WFP のほとんどの関数呼び出しはセッションのコンテキストの中で実行されます。 セッションはクライアントが FwpmEngineClose0 関数を呼び出すか、クライアントのプロセスが終了した際に破棄されます。7

フィルターエンジンへのセッションを開く操作は、サンプルプログラム内では OpenFilterEngine 関数として実装しており、 FwpmEngineOpen0 関数を使用して開いたセッションへのハンドルを engineHandle に保存しています。

DWORD OpenFilterEngine(
    HANDLE* engineHandle
)
{
	if (engineHandle == NULL) {
		return ERROR_INVALID_PARAMETER;
	}

	return FwpmEngineOpen0(
		NULL,
		RPC_C_AUTHN_WINNT,
		NULL,
		NULL,
		engineHandle);
}

FwpmEngineOpen0 関数は、フィルターエンジンへのセッションを開くために使用する関数で、以下の通り定義されています。8

開かれたフィルターエンジンへのセッションのハンドルは engineHandle に返されます。

DWORD FwpmEngineOpen0(
  [in, optional] const wchar_t             *serverName,
  [in]           UINT32                    authnService,
  [in, optional] SEC_WINNT_AUTH_IDENTITY_W *authIdentity,
  [in, optional] const FWPM_SESSION0       *session,
  [out]          HANDLE                    *engineHandle
);

2 番目のパラメーターの authnService は、使用する認証サービスを指定するものです。

また、authIdentity はフィルターエンジンへのアクセスに使用する認証および認可の資格情報を指定可能なパラメーターですが、 NULL を指定することが可能であり、NULL を指定した場合は呼び出し元のスレッドの資格情報が使用されます。

トランザクションを開始する

フィルターエンジンへのセッションを開いたら、FwpmTransactionBegin0 関数を使用して現在のセッション内で明示的にトランザクションを開始します。

このトランザクションは厳格な ACID 特性(原子性、一貫性、独立性、永続性)を強制するものであり、 もしトランザクションの実行中にセッションが終了した場合には、そのトランザクションは自動的に中止されます。9

トランザクションを開始する FwpmTransactionBegin0 関数は以下の通り定義されており、 FwpmEngineOpen0 関数で取得した engineHandle をパラメーターとして受け取って実行します。10

また、第 2 パラメーターの flags は、トランザクションが「読み取り/書き込みトランザクション」か、「読み取り専用トランザクション」かを指定する値です。

サンプルプログラムでは「読み取り/書き込みトランザクション」を開始するため、flags には 0 を使用しています。

DWORD FwpmTransactionBegin0(
  [in] HANDLE engineHandle,
  [in] UINT32 flags
);

カスタムプロバイダーを登録する

このサンプルプログラムでは、「Simple WFP User-Mode Provider」という名前のカスタムプロバイダーを登録します。

プロバイダーの登録には FwpmProviderAdd0 関数を使用します。11 この関数は、FWPM_PROVIDER0 構造体で定義されたプロバイダー情報を引数として受け取ります。12

また、3 つ目のパラメーターである sd では、追加するプロバイダーオブジェクトに対するセキュリティディスクリプタを指定できますが、 今回のサンプルプログラムでは既定のセキュリティディスクリプタを使用する NULL を指定しています。

typedef struct FWPM_PROVIDER0_ {
  GUID               providerKey;
  FWPM_DISPLAY_DATA0 displayData;
  UINT32             flags;
  FWP_BYTE_BLOB      providerData;
  wchar_t            *serviceName;
} FWPM_PROVIDER0;

DWORD FwpmProviderAdd0(
  [in]           HANDLE               engineHandle,
  [in]           const FWPM_PROVIDER0 *provider,
  [in, optional] PSECURITY_DESCRIPTOR sd
);

以下が、サンプルプログラムにおけるカスタムプロバイダーの登録部分の抜粋箇所です。

DefineProvider 関数にて、FWPM_PROVIDER0 構造体のオブジェクト provider に PROVIDER_KEY として定義した GUID やプロバイダー名などの情報を与えています。

void DefineProvider(FWPM_PROVIDER0* provider)
{
	if (provider == NULL) {
		return;
	}

	provider->providerKey = PROVIDER_KEY;
	provider->displayData.name = (wchar_t*)L"Simple WFP User-Mode Provider";
	provider->displayData.description = (wchar_t*)L"Provider for simple WFP user-mode filter example.";
	provider->flags = FWPM_PROVIDER_FLAG_PERSISTENT;
}

/* 省略 */

DefineProvider(&provider);
result = FwpmProviderAdd0(engineHandle, &provider, NULL);
if (result == FWP_E_ALREADY_EXISTS) {
	printf("Provider already exists. Continuing...\n");
	result = ERROR_SUCCESS;
}
else if (result != ERROR_SUCCESS) {
	printf("Error: ProviderAdd failed (0x%x)\n", result);
	CleanupWfp(&engineHandle);
	return result;
}

フィルターを登録する

カスタムプロバイダーの登録が完了したら、前述した FILTER_RULES 構造体の配列 rules の情報を元にフィルターを登録します。

for (const auto& rule : rules) {

   FWPM_FILTER0 filter = { 0 };
   FWPM_FILTER_CONDITION0 condition[9] = { 0 };
   UINT32 conditionCount = 0;

   DefineFilterConditions(
      condition, 
      rule.port, 
      rule.appPath, 
      &conditionCount
   );

   BuildFilter(
      &filter, 
      condition, 
      conditionCount, 
      rule, 
      PROVIDER_KEY
   );

   UINT64 filterId = 0;
   result = AddFilterObject(
      engineHandle, 
      &filter, 
      &filterId
   );

   if (result != ERROR_SUCCESS) {
   printf("Error: FwpmFilterAdd0 failed. Code: 0x%x\n", result);
   FwpmTransactionAbort0(engineHandle);
   CleanupWfp(&engineHandle);
   return result;
   }

   printf("Added filter '%S' (ID: %llu)\n", rule.name, filterId);

}

ループ処理の中で初めに呼び出される DefineFilterConditions 関数では、 フィルターの条件を定義する FWPM_FILTER_CONDITION0 構造体のオブジェクトを作成しています。13

この構造体は以下の通り定義されており、FWP_MATCH_TYPE 列挙型14FWP_CONDITION_VALUE0 構造体15をメンバーに含みます。

typedef struct FWPM_FILTER_CONDITION0_ {
  GUID                 fieldKey;
  FWP_MATCH_TYPE       matchType;
  FWP_CONDITION_VALUE0 conditionValue;
} FWPM_FILTER_CONDITION0;

FWPM_FILTER_CONDITION0 構造体の 1 つ目のメンバーである fieldKey には、 登録するフィルターの条件として使用する GUID を指定します。

ここで使用可能なキーはフィルター条件識別子として定義されています。16

例えば、宛先のポート番号をフィルター条件に使用する場合は FWPM_CONDITION_IP_REMOTE_PORT を、 通信元のアプリケーションのパスを条件に使用する場合は FWPM_CONDITION_ALE_APP_ID を使用できます。

FWP_MATCH_TYPE 列挙型はフィルターの条件に使用される様々なマッチングの種類を定義します。

サンプルプログラムで追加するフィルターでは基本的に FWP_MATCH_EQUAL を使用しており、 シンプルに設定された値が条件と合致するかどうかを判断するフィルターとして機能します。

フィルター条件に使用する FWP_MATCH_TYPE 列挙型によっては、 値が条件として設定されたものより大きい場合や小さい場合を判断するフィルターなどを柔軟に作成できます。

一方で FWP_CONDITION_VALUE0 構造体には、フィルター条件を照合する際に使用される値の情報が含まれます。

もし fieldKey に FWPM_CONDITION_IP_REMOTE_PORT を使用している場合は、 以下のように条件に使用する宛先ポート番号の整数値を登録します。

conditionValue.type = FWP_UINT16;
conditionValue.uint16 = remotePort;

フィルターの条件を定義する FWPM_FILTER_CONDITION0 構造体のオブジェクトを作成したら、 続いて FWPM_FILTER0 構造体のオブジェクトを作成します。

この構造体は、フィルターの状態を含むものであり、以下のように定義されています。17

typedef struct FWPM_FILTER0_ {
  GUID                   filterKey;
  FWPM_DISPLAY_DATA0     displayData;
  UINT32                 flags;
  GUID                   *providerKey;
  FWP_BYTE_BLOB          providerData;
  GUID                   layerKey;
  GUID                   subLayerKey;
  FWP_VALUE0             weight;
  UINT32                 numFilterConditions;
  FWPM_FILTER_CONDITION0 *filterCondition;
  FWPM_ACTION0           action;
  union {
    UINT64 rawContext;
    GUID   providerContextKey;
  };
  GUID                   *reserved;
  UINT64                 filterId;
  FWP_VALUE0             effectiveWeight;
} FWPM_FILTER0;

サンプルプログラムでは、BuildFilter 関数を用いて FWPM_FILTER0 構造体のオブジェクト filter を作成しています。

ここで登録している値は、フィルターの名前や説明、レイヤーやサブレイヤーの GUID やフィルターのアクションなど、 ほとんどが FILTER_RULES のメンバーとしてグローバル変数で定義した値です。

void BuildFilter(
	FWPM_FILTER0* filter,
	FWPM_FILTER_CONDITION0* condition,
	UINT32 conditionCount,
	const FILTER_RULES& rule,
	const GUID& providerKey)
{
	if (filter == NULL || condition == NULL || conditionCount == 0) {
		return;
	}

	filter->displayData.name = (wchar_t*)rule.name;
	filter->displayData.description = (wchar_t*)rule.description;
	filter->filterKey = rule.filterKey;
	filter->providerKey = const_cast<GUID*>(&providerKey);
	filter->layerKey = rule.layerKey;
	filter->subLayerKey = rule.subLayerKey;
	filter->action.type = rule.action;
	filter->weight.type = FWP_UINT64;
	filter->weight.uint64 = const_cast<UINT64*>(&rule.weight);
	filter->numFilterConditions = conditionCount;
	filter->filterCondition = condition;
	filter->flags = FWPM_FILTER_FLAG_PERSISTENT;
}

FWPM_FILTER0 構造体のオブジェクトを作成したら、最後に FwpmFilterAdd0 関数を使用してフィルターの登録を行います。

FwpmFilterAdd0 関数は新しいフィルターをシステムに登録するために用意された関数であり、 セッションへのハンドルと FWPM_FILTER0 構造体オブジェクトをパラメーターとして呼び出されます。18

DWORD FwpmFilterAdd0(
  [in]            HANDLE               engineHandle,
  [in]            const FWPM_FILTER0   *filter,
  [in, optional]  PSECURITY_DESCRIPTOR sd,
  [out, optional] UINT64               *id
);

トランザクションをコミットする

すべてのフィルター登録が完了したら、FwpmTransactionCommit0 関数を呼び出して開始したトランザクションをコミットします。19

これで、ユーザーモードプログラムによるフィルターの登録が完了します。

result = FwpmTransactionCommit0(engineHandle);
if (result != ERROR_SUCCESS) {
	printf("Error: Commit failed (0x%x)\n", result);
	FwpmTransactionAbort0(engineHandle);
}
else {
	printf("SUCCESS: Persistent filters added.\n");
}

フィルター調停の動作を確認する

ユーザーモードで動作するサンプルプログラムによりフィルターを追加する挙動を確認したので、 ここからは実際にこのサンプルプログラムを使用して様々なフィルタリング動作を確認していきます。

特定のポート番号への接続をブロックする

まずはサンプルプログラムで定義している FILTER_RULES にて以下の条件を指定したフィルターを追加します。

{ 
  FILTER_KEY_1, 
  FWPM_LAYER_ALE_AUTH_CONNECT_V4, 
  FWPM_SUBLAYER_UNIVERSAL, 
  L"Block HTTPS(443)", 
  L"Block HTTPS(443)", 
  443, 
  NULL, 
  FWP_ACTION_BLOCK, 
  0xFFFFFFFFFFFFFFF1 
}

このフィルターを登録した際のイベントを wfpdiag で確認してみると、 以下のようにリモートポート番号 443 への通信をすべてブロックする条件を持つフィルターが FWPM_LAYER_ALE_AUTH_CONNECT_V4 レイヤーに追加されたことを確認できます。

フィルターの追加イベント

さらに、このフィルターの filterId である 71431 で処理されたイベントを確認すると、 様々なアプリケーションによる 443 ポート宛ての通信がこのフィルターでドロップされたことを確認できます。

ネットワークトラフィックのドロップイベント

同じサブレイヤー内でのフィルターの Weight の影響を確認する

次に、すべての 443 番ポート宛ての通信をブロックする Block HTTPS(443) のフィルター設定はそのまま、 同じ FWPM_LAYER_ALE_AUTH_CONNECT_V4 レイヤーのサブレイヤー FWPM_SUBLAYER_UNIVERSAL に、 msedge.exe の通信をすべて許可するフィルターを追加します。

この時、Allow Edge フィルターの Weight は Block HTTPS(443) フィルターの Weight よりも 1 小さい値を指定しています。

{ 
  FILTER_KEY_2, 
  FWPM_LAYER_ALE_AUTH_CONNECT_V4, 
  FWPM_SUBLAYER_UNIVERSAL, 
  L"Allow Edge", 
  L"Allow Edge", 
  0, 
  L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", 
  FWP_ACTION_PERMIT, 
  0xFFFFFFFFFFFFFFF0 
}

この 2 つのフィルターを登録した場合、Allow Edge フィルターは機能せず、 msedge.exe による 443 番ポート宛ての通信はすべてブロックされます。

また、この通信をドロップしたことを示す netEvents 内の terminatingFiltersInfo についてもカウントは増えず、 サブレイヤー FWPM_SUBLAYER_UNIVERSAL 内での評価結果は最も Weight の大きいフィルターである FWP_ACTION_BLOCK が適用されたことがわかります。

ネットワークトラフィックのドロップイベント 2

続いて、Allow Edge フィルターの Weight を 0xFFFFFFFFFFFFFFFF に変更後、再度フィルターの登録を行います。

すると、今度は msedge.exe を使用する場合のみ、443 番ポート宛ての通信が可能になります。

この動作により、同じサブレイヤー内に競合するフィルターが登録された場合は、 最も Weight の大きいフィルターのアクションが判定に使用されることがわかります。

異なるサブレイヤーの Weight の影響を確認する

次は、同じレイヤーの異なるサブレイヤーに競合するフィルターを登録した場合の動作を確認します。

まず、サブレイヤーにはフィルターと同様に優先度を表す Weight の値が設定されており、 特定のレイヤーで検査されるネットワークトラフィックは、そのレイヤーのすべてのサブレイヤーをサブレイヤーの Weight の順に通過します。

サブレイヤーの一覧

この時行われる評価は、前項で確認したフィルターの評価と比較して、優先度の高い Weight を持つサブレイヤーの中で条件に合致するフィルターが見つかった場合でも、優先度の小さいサブレイヤーまですべての評価を行う点に大きな違いがあります。

実際に、サンプルプログラム内の FILTER_RULES の設定を以下のように変更してこの動作を確認してみます。

const FILTER_RULES rules[] = {
	{ FILTER_KEY_1, FWPM_LAYER_ALE_AUTH_CONNECT_V4, FWPM_SUBLAYER_MPSSVC_QUARANTINE, L"Block HTTPS(443)", L"Block HTTPS(443)", 443, NULL, FWP_ACTION_BLOCK, 0xFFFFFFFFFFFFFFF1 },
	{ FILTER_KEY_2, FWPM_LAYER_ALE_AUTH_CONNECT_V4, FWPM_SUBLAYER_UNIVERSAL, L"Allow Edge", L"Allow Edge", 0, L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", FWP_ACTION_PERMIT, 0xFFFFFFFFFFFFFFFF },
};

ここでは、すべての 443 ポート宛ての通信をブロックする Block HTTPS(443) と、 msedge.exe による通信を許可する Allow Edge のフィルターをそれぞれ同じレイヤーの異なるサブレイヤーに割り当てています。

Block HTTPS(443) のフィルターが割り当てられている FWPM_SUBLAYER_MPSSVC_QUARANTINE の Weight は 4 です。 そして、Allow Edge のフィルターが割り当てられている FWPM_SUBLAYER_UNIVERSAL の Weight は 32768 ですので、 登録されているサブレイヤーの優先度としては Allow Edge のフィルターが割り当てられている FWPM_SUBLAYER_UNIVERSAL の方が高いです。

しかし、この設定でフィルターを登録した場合には、msedge.exe による通信は許可されず、すべての 443 ポート宛ての通信がブロックされます。

1 章で解説した通り、サブレイヤーの場合は Weight の大きいサブレイヤー内に条件に合致するフィルターが登録されている場合でも、 レイヤー内のすべてのサブレイヤーの評価を行い最終的なアクションを決定します。

また、最終的なアクションは WFP のポリシー規則に基づいて判定されるものであり、基本的には「ブロック」は「許可」のアクションをオーバライドします。

そのため、このテスト結果のように、より Weight の大きいサブレイヤーでトラフィックが「許可」されている場合でも、他のサブレイヤーで「ブロック」された場合には、例外的な状況を除いて最終的な判定は「ブロック」になります。

実際に、netevents の結果からも、terminatingFiltersInfo 内の評価されたフィルターのカウントが 3 つに増加しており、 Allow Edge のフィルター (filterId:71441) と Block HTTPS(443) のフィルター (filterId:71440) の両方のフィルターが評価された結果、 最終的な判定がトラフィックのドロップとなったことを確認できます。

`FWPM_SUBLAYER_MPSSVC_QUARANTINE` のブロック判定が適用されたイベント

まとめ

本章では、ユーザーモードプログラムから BFE を介して WFP フィルターを登録・削除する基本的な操作を確認しました。

また、FWPM_LAYER_ALE_AUTH_CONNECT_V4 レイヤーを例に、 同一サブレイヤー内のフィルターや異なるサブレイヤー内のフィルターによる評価結果の違いを検証し、 WFP フィルタリングの実際の挙動を確認しました。

次章では、Callout ドライバーを用いて分類処理を拡張し、 ユーザーモード連携による動的な許可/ブロック制御を扱います。

本書のもくじ


  1. FWPM_FILTER0 structure (fwpmtypes.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0

  2. Management filtering layer identifiers https://learn.microsoft.com/windows-hardware/drivers/network/management-filtering-layer-identifiers

  3. Filtering sublayer identifiers https://learn.microsoft.com/ja-jp/windows/win32/fwp/management-filtering-sublayer-identifiers

  4. FWPM_DISPLAY_DATA0 structure (fwptypes.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwptypes/ns-fwptypes-fwpmdisplaydata0

  5. FWPM_ACTION0 structure (fwpmtypes.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmtypes/ns-fwpmtypes-fwpm_action0

  6. Management Functions https://learn.microsoft.com/ja-jp/windows/win32/fwp/fwp-mgmt-functions

  7. Sessions https://learn.microsoft.com/ja-jp/windows/win32/fwp/object-management#sessions

  8. FwpmEngineOpen0 function (fwpmu.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmu/nf-fwpmu-fwpmengineopen0

  9. Transactions https://learn.microsoft.com/ja-jp/windows/win32/fwp/object-management#transactions

  10. FwpmTransactionBegin0 function (fwpmu.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmu/nf-fwpmu-fwpmtransactionbegin0

  11. FwpmProviderAdd0 function (fwpmu.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmu/nf-fwpmu-fwpmprovideradd0

  12. FwpmProviderAdd0 function (fwpmu.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmu/nf-fwpmu-fwpmprovideradd0

  13. FWPM_FILTER_CONDITION0 structure (fwpmtypes.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmtypes/ns-fwpmtypes-fwpmfiltercondition0

  14. FWP_MATCH_TYPE enumeration (fwptypes.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwptypes/ne-fwptypes-fwpmatchtype

  15. FWP_CONDITION_VALUE0 structure (fwptypes.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwptypes/ns-fwptypes-fwpconditionvalue0

  16. Filtering condition identifiers https://learn.microsoft.com/ja-jp/windows/win32/fwp/filtering-condition-identifiers-

  17. FWPM_FILTER0 structure (fwpmtypes.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0

  18. FwpmFilterAdd0 function (fwpmu.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmu/nf-fwpmu-fwpmfilteradd0

  19. FwpmFilterAdd0 function (fwpmu.h) https://learn.microsoft.com/ja-jp/windows/win32/api/fwpmu/nf-fwpmu-fwpmfilteradd0