Development/Windows

#3 WFP - Filter와 Condition을 이용한 통신 차단

geunyeong 2021. 12. 23. 22:39

Table of Contents

    Abstract

    WFP의 필터와 조건을 이용하면 콜아웃을 호출하는 오버헤드를 줄이면서도 강력한 방화벽 기능을 제공할 수 있다. 필터의 조건에는 어플리케이션이나 원격지/로컬 IP 주소, 포트, 프로토콜, 인바운드/아웃바운드 등 다양한 유형이 존재하며 내가 원하는 조건의 트래픽만 차단하거나 허용할 수 있다.

    본 글에서는 8.8.8.8과의 모든 통신을 차단하는 예제를 만들고 설명함으로써 필터 오브젝트를 사용한 방화벽 기능을 어떻게 만드는 지 알아볼 것이다. 예제 코드는 깃헙에 공개했으며, 본문 가장 하단에 링크가 있다.

    Blocking a communication using Filter & Condition

    본 글에서는 8.8.8.8과의 모든 통신을 차단하는 필터 오브젝트를 만들어 볼 것이다. WFP의 개요와 주요 구성요소, 기본적인 만드는 법은 이전 글들을 참고하길 바란다. 본 글에서는 간략하게만 설명하고 넘어갈 것이다.

    • #2 WFP - WFP Callout Driver 만들기
    https://geun-yeong.tistory.com/57
    • #1 WFP - 개요
    https://geun-yeong.tistory.com/55

    Condition

    조건(Condition)은 필터 오브젝트의 행위(Action)을 수행할 네트워크 이벤트를 정의한다. 필터 오브젝트가 "원격지 주소가 10.10.10.10인 트래픽은 차단하라"나 "원격지 포트가 4000인 트래픽은 콜아웃을 호출하라"를 정의한다면 이 중 "원격지 주소가 10.10.10.10인"과 "원격지 포트가 4000인" 같은 말 그대로 '조건'을 정의하는 것이  Condition이다. 조건은 FWPM_FILTER_CONDITION이라는 구조체로 표현된다.

    우리의 목표인 "원격지 주소가 8.8.8.8인"을 만드려면 아래와 같이 조건 구조체를 만들면 된다.

    FWPM_FILTER_CONDITION fwpm_condition = { 0, };
    
    fwpm_condition.fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
    fwpm_condition.matchType = FWP_MATCH_EQUAL;
    fwpm_condition.conditionValue.type = FWP_V4_ADDR_MASK;
    fwpm_condition.conditionValue.v4AddrMask = ipv4;

    FWPM_FILTER_CONDITION 구조체의 각 멤버 설명은 아래와 같다.

    • fieldKey: 조건의 의미를 정의한다. 즉 해당 조건이 원격지 IP 주소에 대한 것인지, 로컬 IP 주소에 대한 것인지, 원격지 포트에 대한 것인지 등을 의미한다.
    • matchType: 조건을 비교할 연산자를 정의한다. FWP_MATCH_EQUAL은 conditionValue에 정의된 값과 완전히 일치해야 함을 의미한다. 이 외에도 이상, 이하, 범위에 포함됨, 플래그가 세팅됨 등 다양한 연산이 존재한다.
    • conditionValue.type = 조건을 비교할 값의 타입을 정의한다. UINT8 ~ UINT64, IPv4/v6, BLOB 등 다양한 데이터 타입이 존재한다. 위 코드에서는 IPv4 타입을 의미하며 conditionValue 멤버에 저장된 값이 FWP_V4_ADDR_AND_MASK 구조체의 포인터 타입이라는 걸 필터 엔진에게 알려주는 역할을 한다.
    • conditionValue.v4AddrMask: type이 FWP_V4_ADDR_MASK일 때 의미를 갖는다. FWP_V4_ADDR_AND_MASK 구조체의 포인터를 저장한다.

    conditionValue.v4AddrMask에 저장되는 값은 아래와 같은 형태다. 참고로 FWP_V4_ADDR_AND_MASK에는 Byte Order를 신경쓰지 않아도 된다. 필터 엔진이 알아서 Network Byte Order로 변환해 비교하기 때문에 조건을 만들 때부터 네트워크 바이트 오더로 집어 넣으면 원하는대로 동작하지 않는다.

    //
    // block all communication from/to 8.8.8.8 in transport layer
    //
    FWP_V4_ADDR_AND_MASK blocked = { 0x08'08'08'08 /*8.8.8.8*/, 
                                     0xFF'FF'FF'FF /*255.255.255.255*/};
    for (size_t i = 0; i < ARRAYSIZE(layer_keys); i++) {
        status = AddFilterIpv4(layer_keys[i], &blocked);
        IF_ERROR(AddFilterIpv4, EXIT_OF_DRIVER_ENTRY);
    }

    Filter

    조건을 만들었으면 필터 오브젝트에 위 조건을 할당해야 한다. 필터 오브젝트를 나타내는 구조체인 FWPM_FILTER에는 filterCondition과 numFilterConditions라는 멤버가 있다. filterCondition은 조건 구조체의 배열을 저장하고, numFilterConditions에는 조건 구조체 배열의 개수를 저장하면 된다. 또한 우리의 목표대로 "차단하라"를 정의하려면 action.type 멤버를 FWP_ACTION_BLOCK으로 세팅하면 된다.

    // fwpm filter key was automatically created by FwpmFilterAdd
    fwpm_filter.displayData.name        = (wchar_t*)L"wfpflt_example_filter";
    fwpm_filter.displayData.description = (wchar_t*)L"The filter object for wfp-filter-example";
    fwpm_filter.layerKey                = *layer_key;
    fwpm_filter.weight.type             = FWP_EMPTY;
    fwpm_filter.action.type             = FWP_ACTION_BLOCK;
    fwpm_filter.filterCondition         = &fwpm_condition;
    fwpm_filter.numFilterConditions     = 1;
    
    status = FwpmFilterAdd(kmfe_handle, 
                           &fwpm_filter, 
                           NULL, 
                           &fwpm_filter_id);
    IF_ERROR(FwpmFilterAdd, CLEANUP_OF_ADD_FILTER_IPV4);
    KdPrint(("[wfpkm] " __FUNCTION__ " - FwpmFilterAdd success (filter id = %llu)",
             fwpm_filter_id));

    Run

    원격지 IPv4 주소가 8.8.8.8인 트래픽은 차단하도록 하는 필터를 등록하고 실행하면 분명 통신이 되던 8.8.8.8이 Ping도 막히는 것을 볼 수 있다.

    Close

    이상으로 필터 오브젝트를 이용한 네트워크 통신 차단 방법에 대해 알아봤다. 이전 글인 콜아웃 드라이버 작성법과 함께 기본적인 WFP 사용법에 대해 어느 정도 정리가 된 느낌이다. 

    Windows 기반의 엔드 포인트 보안 프로그램을 생각하는 이라면 WFP는 분명 필수 요소일 것이다. 본 글들이 큰 도움이 되길 바란다.

    Github

    https://github.com/geun-yeong/wfp-example