방화벽 동적 키워드

방화벽 동적 키워드 API를 사용하여 Microsoft Defender 방화벽에서 동적 키워드(keyword) 주소를 관리합니다. 동적 키워드 주소는 하나 이상의 방화벽 규칙에서 참조할 수 있는 IP 주소 세트를 만드는 데 사용됩니다. 동적 키워드 주소는 IPv4 및 IPv6를 모두 지원합니다.

참고

이 항목에 도입된 API에 대한 API 참조 콘텐츠는 방화벽 동적 키워드 참조를 참조하세요.

동적 키워드(keyword) 주소에 대한 작업

방화벽 동적 키워드 API를 사용하여 다음 작업을 수행할 수 있습니다.

  • 동적 키워드(keyword) 주소 추가
  • 동적 키워드(keyword) 주소 삭제
  • ID 또는 형식별로 동적 키워드(keyword) 주소 열거
  • 동적 키워드(keyword) 주소 업데이트
  • 동적 키워드(keyword) 주소 변경 알림 구독 및 처리

이 항목의 뒷부분에 있는 모든 작업에 대한 코드 예제가 있습니다.

동적 키워드(keyword) 주소를 추가한 후에는 다시 부팅할 때 계속 유지됩니다. 개체를 완료한 후에는 동적 키워드(keyword) 주소를 삭제해야 합니다.

다음 두 섹션에 설명된 대로 동적 키워드(keyword) 주소의 두 클래스가 있습니다.

동적 키워드(keyword) 주소 자동 해결

첫 번째 유형은 AutoResolve입니다. 여기서 키워드(keyword) 필드는 확인 가능한 이름을 나타내며 IP 주소는 생성 시 정의되지 않습니다.

이러한 개체는 해당 IP 주소를 자동으로 확인하도록 하기 위한 것입니다. 즉, 개체를 만들 때 관리자를 통해서가 아닙니다. 운영 체제(OS) 자체를 통해서도 마찬가지입니다. 방화벽 서비스 외부의 구성 요소는 이러한 개체에 대한 IP 주소 확인을 수행하고 적절하게 업데이트해야 합니다. 이러한 구성 요소의 구현은 이 콘텐츠의 scope 외부에 있습니다.

동적 키워드(keyword) 주소는 FWAddDynamicKeywordAddress0 함수를 호출할 때 개체에서 FW_DYNAMIC_KEYWORD_ADDRESS_FLAGS_AUTO_RESOLVE 플래그를 설정하여 AutoResolve로 표시됩니다. 키워드(keyword) 필드는 확인되는 값, 즉 FQDN(정규화된 도메인 이름) 또는 호스트 이름을 나타내는 데 사용해야 합니다. 주소 필드는 처음에 이러한 개체에 대해 NULL이어야 합니다. 이러한 개체는 부팅 주기에 걸쳐 IP 주소를 유지하지 않으며, 다음 부팅 주기 동안 해당 주소를 다시 평가/다시 채워야 합니다.

참고

AutoResolve 동적 키워드(keyword) 주소 개체는 FWAddDynamicKeywordAddress0FWDeleteDynamicKeywordAddress0에서 알림을 트리거하지만 FWUpdateDynamicKeywordAddress0은 트리거하지 않습니다.

비 AutoResolve 동적 키워드(keyword) 주소

두 번째 형식은 autoResolve가 아닌 형식입니다. 여기서 키워드(keyword) 필드는 문자열이고 주소는 생성 시 정의됩니다.

이러한 개체는 IP 주소, 서브넷 또는 범위 집합을 저장하는 데 사용됩니다. 여기서 키워드(keyword) 필드는 관리 편의를 위해 사용되며 모든 문자열로 설정할 수 있습니다. 주소 필드는 만들 때 NULL이 아니어야 합니다. 이러한 개체의 주소는 다시 부팅할 때 유지됩니다.

참고

비 AutoResolve 동적 키워드(keyword) 주소 개체는 FWAddDynamicKeywordAddress0, FWDeleteDynamicKeywordAddress0FWUpdateDynamicKeywordAddress0에 대한 알림을 트리거합니다.

동적 키워드(keyword) 주소에 대한 자세한 정보

모든 동적 키워드(keyword) 주소에는 해당 주소를 나타내는 고유한 GUID 식별자가 있어야 합니다.

FwpmDynamicKeywordSubscribe0 API는 동적 키워드(keyword) 주소가 변경되면 클라이언트에 알림을 제공합니다. 시스템에서 변경된 내용을 정확히 설명하는 페이로드는 클라이언트에 전달되지 않습니다. 변경된 개체를 알아야 하는 경우 FWEnumDynamicKeywordAddressById0 또는 FWEnumDynamicKeywordAddressesByType0 API를 사용하여 시스템에서 개체의 현재 상태를 쿼리해야 합니다. 다양한 플래그를 사용하여 개체의 하위 집합에 대해서만 알림을 요청할 수 있습니다. 플래그를 사용하지 않으면 모든 개체에 대해 변경 알림이 전달됩니다.

방화벽 규칙은 원격 주소 조건에 대한 IP 주소를 명시적으로 정의하는 대신 동적 키워드(keyword) 주소를 사용할 수 있습니다. 방화벽 규칙은 동적 키워드(keyword) 주소와 정적으로 정의된 원격 주소 범위를 모두 사용할 수 있습니다. 여러 방화벽 규칙에서 단일 동적 키워드(keyword) 주소 개체를 다시 사용할 수 있습니다. 방화벽 규칙에 구성된 원격 주소(즉, 아직 확인되지 않은 AutoResolve 개체로만 구성됨)가 없는 경우 규칙이 적용되지 않습니다. 또한 규칙이 여러 동적 키워드(keyword) 주소를 사용하는 경우 아직 확인되지 않은 다른 개체가 있더라도 현재 해결된 모든 주소에 규칙이 적용됩니다. 동적 키워드(keyword) 주소가 업데이트되면 연결된 모든 규칙 개체의 원격 주소도 업데이트됩니다.

운영 체제(OS) 자체는 규칙과 동적 키워드(keyword) 주소 간에 종속성을 적용하지 않습니다. 즉, 두 개체 중 하나를 먼저 만들 수 있습니다. 규칙은 아직 존재하지 않는 동적 키워드(keyword) 주소 ID를 참조할 수 있습니다(이 경우 규칙이 적용되지 않음). 또한 방화벽 규칙에서 사용 중인 경우에도 동적 키워드(keyword) 주소를 삭제할 수 있습니다. 이 항목에서는 관리자가 동적 키워드(keyword) 주소를 사용하도록 규칙을 구성하는 방법을 간략하게 설명합니다.

코드 예제

이러한 각 코드 예제를 사용해 보려면 먼저 Visual Studio를 시작하고 콘솔 앱 프로젝트 템플릿을 기반으로 새 프로젝트를 만듭니다. 의 main.cpp 내용을 코드 목록으로 바꿀 수 있습니다.

대부분의 코드 예제에서는 WIL(Windows 구현 라이브러리)을 사용합니다. WIL을 설치하는 편리한 방법은 Visual Studio로 이동하여 프로젝트>NuGet 패키지 관리...를 클릭하는 것입니다.>검색 상자에 Microsoft.Windows.ImplementationLibrary를 찾아보거나 입력하거나 붙여넣고 검색 결과에서 항목을 선택한 다음 설치를 클릭하여 해당 프로젝트에 대한 패키지를 설치합니다.

참고

NetFw 무료 함수에 대한 포인터 형식은 를 통해 NetFw.h게시되지만 정적 링크 라이브러리는 게시되지 않습니다. 이러한 코드 예제와 같이 LoadLibraryExW/GetProcAddress 패턴을 사용하여 이러한 함수를 호출합니다.

동적 키워드(keyword) 주소 추가

이 예제에서는 FWAddDynamicKeywordAddress0 함수를 사용하는 방법을 보여줍니다.

// main.cpp in a Console App project.
#include <windows.h>
#include <wil/resource.h>
#include <netfw.h>

// {26548e4f-d486-4a1d-8a1d-22b0837cd53b}
const GUID DYNAMIC_KEYWORD_ADDRESS_ID_1 =
{
    0x26548e4f,
    0xd486,
    0x4a1d,
    {0x8a,0x1d,0x22,0xb0,0x83,0x7c,0xd5,0x3b}
};

// {e9d5c993-9369-4a96-8228-9c5c37aac51a}
const GUID DYNAMIC_KEYWORD_ADDRESS_ID_2 =
{
    0xe9d5c993,
    0x9369,
    0x4a96,
    {0x82,0x28,0x9c,0x5c,0x37,0xaa,0xc5,0x1a}
};

int main()
{
    DWORD error = ERROR_SUCCESS;
    PFN_FWADDDYNAMICKEYWORDADDRESS0 addDynamicKeywordAddressFn = NULL;
    HMODULE moduleHandle = NULL;
    FW_DYNAMIC_KEYWORD_ADDRESS0 autoResolveKeywordAddress = { 0 };
    FW_DYNAMIC_KEYWORD_ADDRESS0 nonAutoResolveKeywordAddress = { 0 };

    // Use LoadLibrary/GetProcAddress to invoke this function
    moduleHandle = LoadLibraryExW(L"firewallapi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
    auto onExitFreeModuleHandle = wil::scope_exit([&]
        {
            if (moduleHandle)
            {
                FreeLibrary(moduleHandle);
            }
        });

    if (moduleHandle != NULL)
    {
        addDynamicKeywordAddressFn = (PFN_FWADDDYNAMICKEYWORDADDRESS0)GetProcAddress(
            moduleHandle,
            "FWAddDynamicKeywordAddress0"
        );
    }

    if (addDynamicKeywordAddressFn == NULL)
    {
        error = GetLastError();
        return error;
    }

    // Ensure the ID is unique. If not, the add operation will fail with ERROR_ALREADY_EXISTS
    // and you should invoke the API with a new ID.

    // Initialize and add an auto-resolve dynamic keyword address
    autoResolveKeywordAddress.id = DYNAMIC_KEYWORD_ADDRESS_ID_1;
    autoResolveKeywordAddress.keyword = L"bing.com";
    autoResolveKeywordAddress.flags = FW_DYNAMIC_KEYWORD_ADDRESS_FLAGS_AUTO_RESOLVE;
    // must be NULL as we have set the auto resolve flag
    autoResolveKeywordAddress.addresses = NULL;

    error = addDynamicKeywordAddressFn(&autoResolveKeywordAddress);
    if (error != ERROR_SUCCESS)
    {
        return error;
    }

    // Initialize and add a non auto-resolve dynamic keyword address
    nonAutoResolveKeywordAddress.id = DYNAMIC_KEYWORD_ADDRESS_ID_2;
    nonAutoResolveKeywordAddress.keyword = L"myServerIPs";
    nonAutoResolveKeywordAddress.flags = 0;
    nonAutoResolveKeywordAddress.addresses = L"10.0.0.5,20.0.0.0/24,30.0.0.0-40.0.0.0";

    error = addDynamicKeywordAddressFn(&nonAutoResolveKeywordAddress);
    if (error != ERROR_SUCCESS)
    {
        return error;
    }
    return error;
}

동적 키워드(keyword) 주소 삭제

이 예제에서는 FWDeleteDynamicKeywordAddress0 함수를 사용하는 방법을 보여줍니다.

// main.cpp in a Console App project.
#include <windows.h>
#include <wil/resource.h>
#include <netfw.h>

// {26548e4f-d486-4a1d-8a1d-22b0837cd53b}
const GUID DYNAMIC_KEYWORD_ADDRESS_ID_1 =
{
    0x26548e4f,
    0xd486,
    0x4a1d,
    {0x8a,0x1d,0x22,0xb0,0x83,0x7c,0xd5,0x3b}
};


// {e9d5c993-9369-4a96-8228-9c5c37aac51a}
const GUID DYNAMIC_KEYWORD_ADDRESS_ID_2 =
{
    0xe9d5c993,
    0x9369,
    0x4a96,
    {0x82,0x28,0x9c,0x5c,0x37,0xaa,0xc5,0x1a}
};

int main()
{
    DWORD error = ERROR_SUCCESS;
    PFN_FWDELETEDYNAMICKEYWORDADDRESS0 deleteDynamicKeywordAddressFn = NULL;
    HMODULE moduleHandle = NULL;

    // Use LoadLibrary/GetProcAddress to invoke this function
    moduleHandle = LoadLibraryExW(L"firewallapi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
    auto onExitFreeModuleHandle = wil::scope_exit([&]
        {
            if (moduleHandle)
            {
                FreeLibrary(moduleHandle);
            }
        });


    if (moduleHandle != NULL)
    {
        deleteDynamicKeywordAddressFn = (PFN_FWDELETEDYNAMICKEYWORDADDRESS0)GetProcAddress(
            moduleHandle,
            "FWDeleteDynamicKeywordAddress0"
        );
    }

    if (deleteDynamicKeywordAddressFn == NULL)
    {
        error = GetLastError();
        return error;
    }

    // Invoke the functions
    error = deleteDynamicKeywordAddressFn(DYNAMIC_KEYWORD_ADDRESS_ID_1);
    if (error != ERROR_SUCCESS)
    {
        wprintf(L"Failed to delete object with ID 1, err=[%d]", error);
    }

    error = deleteDynamicKeywordAddressFn(DYNAMIC_KEYWORD_ADDRESS_ID_2);
    if (error != ERROR_SUCCESS)
    {
        wprintf(L"Failed to delete object with ID 2, err=[%d]", error);
    }

    return error;
}

ID별로 동적 키워드(keyword) 주소 열거 및 해제

이 예제에서는 FWEnumDynamicKeywordAddressById0FWFreeDynamicKeywordAddressData0 함수를 사용하는 방법을 보여 줍니다.

// main.cpp in a Console App project.
#include <windows.h>
#include <wil/resource.h>
#include <netfw.h>

// {26548e4f-d486-4a1d-8a1d-22b0837cd53b}
const GUID DYNAMIC_KEYWORD_ADDRESS_ID_1 =
{
    0x26548e4f,
    0xd486,
    0x4a1d,
    {0x8a,0x1d,0x22,0xb0,0x83,0x7c,0xd5,0x3b}
};

// {e9d5c993-9369-4a96-8228-9c5c37aac51a}
const GUID DYNAMIC_KEYWORD_ADDRESS_ID_2 =
{
    0xe9d5c993,
    0x9369,
    0x4a96,
    {0x82,0x28,0x9c,0x5c,0x37,0xaa,0xc5,0x1a}
};

int main()
{
    DWORD error = ERROR_SUCCESS;
    PFN_FWENUMDYNAMICKEYWORDADDRESSBYID0 enumDynamicKeywordAddressByIdFn = NULL;
    PFN_FWFREEDYNAMICKEYWORDADDRESSDATA0 freeDynamicKeywordAddressDataFn = NULL;
    HMODULE moduleHandle = NULL;
    PFW_DYNAMIC_KEYWORD_ADDRESS_DATA0 dynamicKeywordAddressData = NULL;

    // Use LoadLibrary/GetProcAddress to invoke this function
    moduleHandle = LoadLibraryExW(L"firewallapi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
    auto onExitFreeModuleHandle = wil::scope_exit([&]
        {
            if (moduleHandle)
            {
                FreeLibrary(moduleHandle);
            }
        });

    if (moduleHandle != NULL)
    {
        enumDynamicKeywordAddressByIdFn = (PFN_FWENUMDYNAMICKEYWORDADDRESSBYID0)GetProcAddress(
            moduleHandle,
            "FWEnumDynamicKeywordAddressById0"
        );
        freeDynamicKeywordAddressDataFn = (PFN_FWFREEDYNAMICKEYWORDADDRESSDATA0)GetProcAddress(
            moduleHandle,
            "FWFreeDynamicKeywordAddressData0"
        );
    }

    if (enumDynamicKeywordAddressByIdFn == NULL ||
        freeDynamicKeywordAddressDataFn == NULL)
    {
        error = GetLastError();
        return error;
    }

    error = enumDynamicKeywordAddressByIdFn(
        DYNAMIC_KEYWORD_ADDRESS_ID_1,
        &dynamicKeywordAddressData
    );
    if (error != ERROR_SUCCESS)
    {
        return error;
    }

    if (dynamicKeywordAddressData != NULL)
    {
        // Process this dynamic keyword address
    }

    // Free the dynamic keyword address
    freeDynamicKeywordAddressDataFn(dynamicKeywordAddressData);
    return error;
}

형식별로 동적 키워드(keyword) 주소 열거 및 해제

이 예제에서는 FWEnumDynamicKeywordAddressesByType0FWFreeDynamicKeywordAddressData0 함수를 사용하는 방법을 보여 줍니다.

// main.cpp in a Console App project.
#include <windows.h>
#include <wil/resource.h>
#include <netfw.h>

int main()
{
    DWORD error = ERROR_SUCCESS;
    PFN_FWENUMDYNAMICKEYWORDADDRESSESBYTYPE0 enumDynamicKeywordAddressesByTypeFn = NULL;
    PFN_FWFREEDYNAMICKEYWORDADDRESSDATA0 freeDynamicKeywordAddressDataFn = NULL;
    HMODULE moduleHandle = NULL;

    PFW_DYNAMIC_KEYWORD_ADDRESS_DATA0 dynamicKeywordAddressData = NULL;
    PFW_DYNAMIC_KEYWORD_ADDRESS_DATA0 currDynamicKeywordAddressData = NULL;

    // Use LoadLibrary/GetProcAddress to invoke this function
    moduleHandle = LoadLibraryExW(L"firewallapi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
    auto onExitFreeModuleHandle = wil::scope_exit([&]
        {
            if (moduleHandle)
            {
                FreeLibrary(moduleHandle);
            }
        });

    if (moduleHandle != NULL)
    {
        enumDynamicKeywordAddressesByTypeFn = (PFN_FWENUMDYNAMICKEYWORDADDRESSESBYTYPE0)GetProcAddress(
            moduleHandle,
            "FWEnumDynamicKeywordAddressesByType0"
        );
        freeDynamicKeywordAddressDataFn = (PFN_FWFREEDYNAMICKEYWORDADDRESSDATA0)GetProcAddress(
            moduleHandle,
            "FWFreeDynamicKeywordAddressData0"
        );
    }

    if (enumDynamicKeywordAddressesByTypeFn == NULL ||
        freeDynamicKeywordAddressDataFn == NULL)
    {
        error = GetLastError();
        return error;
    }

    // Invoke enum for ALL dynamic keyword addresses
    error = enumDynamicKeywordAddressesByTypeFn(
        FW_DYNAMIC_KEYWORD_ADDRESS_ENUM_FLAGS_ALL,
        &dynamicKeywordAddressData
    );
    if (error != ERROR_SUCCESS)
    {
        return error;
    }

    currDynamicKeywordAddressData = dynamicKeywordAddressData;
    while (currDynamicKeywordAddressData != NULL)
    {
        // Process this dynamic keyword address

        // iterate to the next one in the list
        currDynamicKeywordAddressData = currDynamicKeywordAddressData->next;
    }

    // Free the dynamic keyword addresses
    freeDynamicKeywordAddressDataFn(dynamicKeywordAddressData);

    return error;
}

동적 키워드(keyword) 주소 업데이트

이 예제에서는 FWUpdateDynamicKeywordAddress0 함수를 사용하는 방법을 보여 줍니다.

// main.cpp in a Console App project.
#include <windows.h>
#include <wil/resource.h>
#include <netfw.h>

// {26548e4f-d486-4a1d-8a1d-22b0837cd53b}
const GUID DYNAMIC_KEYWORD_ADDRESS_ID_1 =
{
    0x26548e4f,
    0xd486,
    0x4a1d,
    {0x8a,0x1d,0x22,0xb0,0x83,0x7c,0xd5,0x3b}
};

int main()
{
    DWORD error = ERROR_SUCCESS;
    PFN_FWUPDATEDYNAMICKEYWORDADDRESS0 updateDynamicKeywordAddressFn = NULL;
    HMODULE moduleHandle = NULL;
    BOOL appendToCurrentAddresses = TRUE;

    // Use LoadLibrary/GetProcAddress to invoke this function
    moduleHandle = LoadLibraryExW(L"firewallapi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
    auto onExitFreeModuleHandle = wil::scope_exit([&]
        {
            if (moduleHandle)
            {
                FreeLibrary(moduleHandle);
            }
        });

    if (moduleHandle != NULL)
    {
        updateDynamicKeywordAddressFn = (PFN_FWUPDATEDYNAMICKEYWORDADDRESS0)GetProcAddress(
            moduleHandle,
            "FWUpdateDynamicKeywordAddress0"
        );
    }

    if (updateDynamicKeywordAddressFn == NULL)
    {
        error = GetLastError();
        return error;
    }

    // Invoke the function
    error = updateDynamicKeywordAddressFn(
        DYNAMIC_KEYWORD_ADDRESS_ID_1,
        L"20.0.0.5",
        appendToCurrentAddresses);
    return error;
}

동적 키워드(keyword) 주소 변경 알림 구독 및 처리

이 예제에서는 FwpmDynamicKeywordSubscribe0FwpmDynamicKeywordUnsubscribe0 함수 및 FWPM_DYNAMIC_KEYWORD_CALLBACK0 콜백을 사용하는 방법을 보여 줍니다.

// main.cpp in a Console App project.
#include <windows.h>
#include <netfw.h>
#include <fwpmu.h>
#pragma comment(lib, "Fwpuclnt")

void CALLBACK TestCallback(_Inout_ VOID* /*pNotification*/, _Inout_ VOID* pContext)
{
    DWORD error = ERROR_SUCCESS;
    PFN_FWENUMDYNAMICKEYWORDADDRESSESBYTYPE0 enumDynamicKeywordAddressesByTypeFn = NULL;
    PFN_FWFREEDYNAMICKEYWORDADDRESSDATA0 freeDynamicKeywordAddressDataFn = NULL;
    HMODULE moduleHandle = NULL;

    PFW_DYNAMIC_KEYWORD_ADDRESS_DATA0 dynamicKeywordAddressData = NULL;
    PFW_DYNAMIC_KEYWORD_ADDRESS_DATA0 currDynamicKeywordAddressData = NULL;
    HANDLE* waitHandle = (HANDLE*)pContext;

    // Use LoadLibrary/GetProcAddress to invoke this function
    moduleHandle = LoadLibraryW(L"firewallapi.dll");
    if (moduleHandle != NULL)
    {
        enumDynamicKeywordAddressesByTypeFn = (PFN_FWENUMDYNAMICKEYWORDADDRESSESBYTYPE0)GetProcAddress(
            moduleHandle,
            "FWEnumDynamicKeywordAddressesByType0"
        );
        freeDynamicKeywordAddressDataFn = (PFN_FWFREEDYNAMICKEYWORDADDRESSDATA0)GetProcAddress(
            moduleHandle,
            "FWFreeDynamicKeywordAddressData0"
        );
    }

    if (enumDynamicKeywordAddressesByTypeFn == NULL ||
        freeDynamicKeywordAddressDataFn == NULL)
    {
        return;
    }

    // Invoke enum for ALL AutoResolve dynamic keyword addresses
    error = enumDynamicKeywordAddressesByTypeFn(
        FW_DYNAMIC_KEYWORD_ADDRESS_ENUM_FLAGS_AUTO_RESOLVE,
        &dynamicKeywordAddressData
    );
    if (error != ERROR_SUCCESS)
    {
        return;
    }

    currDynamicKeywordAddressData = dynamicKeywordAddressData;
    while (currDynamicKeywordAddressData != NULL)
    {
        // Process this dynamic keyword address

        currDynamicKeywordAddressData = currDynamicKeywordAddressData->next;
    }

    // Free the dynamic keyword addresses
    freeDynamicKeywordAddressDataFn(dynamicKeywordAddressData);

    SetEvent(*waitHandle);
}

int main()
{
    DWORD error = ERROR_SUCCESS;
    HANDLE notifyHandle;
    HANDLE waitHandle;

    waitHandle = CreateEventW(
        NULL,
        TRUE,
        FALSE,
        L"subscriptionWaitEvent"
    );


    // Subscribe for change notifications
    error = FwpmDynamicKeywordSubscribe0(
        FWPM_NOTIFY_ADDRESSES_AUTO_RESOLVE,
        TestCallback,
        &waitHandle,
        &notifyHandle);
    if (error != ERROR_SUCCESS)
    {
        return error;
    }

    WaitForSingleObject(waitHandle, INFINITE);

    // When client is ready to unsubscribe
    error = FwpmDynamicKeywordUnsubscribe0(notifyHandle);

    return error;
}