• WinDivert驅動的簡單分析(1)


    。。。

    爲什麽你的字體變成了繁體字?因爲按錯了Ctrl+shift+F,於是先將就用幾天看好不好用。不知道會不會持續更新這個,先寫寫看,因爲我比較懶,説不定就不想寫了!

    背景

    WinDivert是一個開源的基於WFP的網絡驅動,主要用在嗅探、抓包、防火墻,這裏就簡單的分析一下該驅動,因爲自己也沒怎麽接觸過WFP和WDF的驅動編程,文中一定會出現理解或者解釋有誤的地方,我也控制不了!

    WFP簡介

    WFP是windows提供的用來做網絡過濾的一種組件,當然還有tdi、ndis這類傳統的網絡過濾,一般windows提供的接口都不怎麽友善,感覺學習起來亂七八糟的。要不是爲了研究WinDivert,順便在學習和復習一下網絡的基本知識,真不想看WFP。

    看到windows給的WFP的'簡單'例子,給人的感覺就是這麽這麽多的參數,注冊的都是些什麽東西?其實最需要注意的是callout(呼出)、filter(過濾器)、layer(分層)這幾個參數怎麽填差不多就可以代碼抄起來了。

    這裏提一下當時不怎麽理解的幾個點

    1、layer那麽多層,我怎麽知道這些層的各個classify的回調的數據是什麽?

    現在也不知道,因爲分的曾確實很多,但是只需要在意關心的那幾層就行了,其它的用到的時候在說。管理筛选层标识符 - Windows drivers | Microsoft Docs

    2、爲什麽要子層、callout、filter都有一個uid?

    不用管,反正就是用來標記它的,方便找到它而已

    3、FwpmSubLayerAdd0需要注意的是weight,也就是權重,越大優先級越高,其他的都是自己指定,也就是説可以亂填。

    static NTSTATUS windivert_install_sublayer(layer_t layer)
    {
        FWPM_SUBLAYER0 sublayer;
        NTSTATUS status;
    
        RtlZeroMemory(&sublayer, sizeof(sublayer));
        sublayer.subLayerKey             = *(layer->sublayer_guid);
        sublayer.displayData.name        = layer->sublayer_name;
        sublayer.displayData.description = layer->sublayer_desc;
        sublayer.weight                  = layer->sublayer_weight;
    
        status = FwpmSubLayerAdd0(engine_handle, &sublayer, NULL);
        if (!NT_SUCCESS(status))
        {
            DEBUG_ERROR("failed to add WFP sub-layer", status);
        }
        
        return status;
    }

    4、FwpsCalloutRegister0主要是注冊一個callout,其中最主要的是classifyFn,就是在這個回調裏面寫bug,calloutKey自己填,notifyFn空實現,flowDeleteFn一般用來回收資源(沒有需要回收的就直接null,連空函數都懶得定義)

    scallout.calloutKey              = callout_guid;//用来标记这个callout的uid
    scallout.classifyFn              = layer->classify;//对应的过滤函数
    scallout.notifyFn                = windivert_notify;//空实现
    scallout.flowDeleteFn            = layer->flow_delete;//这压根没实现
    status = FwpsCalloutRegister0(WdfDeviceWdmGetDeviceObject(device),

    5、FwpmCalloutAdd0這個就是把上面注冊的callout和對應的wfp給你提供的各種層關聯起來,其中applicableLayer就需要用WFP提供的uid,而不是自己瞎制定了

    mcallout.calloutKey              = callout_guid;//用来标记这个callout的uid
    mcallout.displayData.name        = layer->callout_name;//随便写点
    mcallout.displayData.description = layer->callout_desc;//随便写点
    mcallout.applicableLayer         = *(layer->layer_guid);//对应的WFP过滤的UID
    status = FwpmCalloutAdd0(engine, &mcallout, NULL, NULL);

    6、FwpmFilterAdd0,就是將callout、子層關聯起來的,calloutKey寫的是你自己的callout的uid,subLayerKey寫的是你指定的子層的uid,layerKey寫的是你需要在哪一層過濾,是WFP提供給你的,rawContext是在classifyFn的倒數第三個參數。filter還有一個filterCondition字段用來指定需要過濾的一些情況,比如下面。但WinDivert不是這樣實現的,之後再説。

    #include <windows.h>
    #include <fwpmu.h>
    #include <stdio.h>
    
    #pragma comment(lib, "Fwpuclnt.lib")
    
    // Some application to use for filter testing.
    #define FILE0_PATH L"C:\\Program Files\\AppDirectory\\SomeApplication.exe"
    
    void main()
    {
        FWP_BYTE_BLOB *fwpApplicationByteBlob;
        FWPM_FILTER0 fwpFilter;
        FWPM_FILTER_CONDITION0 fwpConditions[4];
        int conCount = 0;
        DWORD result = ERROR_SUCCESS; 
    
        fwpApplicationByteBlob = (FWP_BYTE_BLOB*) malloc(sizeof(FWP_BYTE_BLOB));
        
        printf("Retrieving application identifier for filter testing.\n"); 
        result = FwpmGetAppIdFromFileName0(FILE0_PATH, &fwpApplicationByteBlob);
        if (result != ERROR_SUCCESS)
        {
            printf("FwpmGetAppIdFromFileName failed (%d).\n", result);
            return;
        }
    
          // Application identifier filter condition.
          fwpConditions[conCount].fieldKey = FWPM_CONDITION_ALE_APP_ID;
          fwpConditions[conCount].matchType = FWP_MATCH_EQUAL;
          fwpConditions[conCount].conditionValue.type = FWP_BYTE_BLOB_TYPE;
          fwpConditions[conCount].conditionValue.byteBlob = fwpApplicationByteBlob;
                
          ++conCount;
    
          // TCP protocol filter condition
          fwpConditions[conCount].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
          fwpConditions[conCount].matchType = FWP_MATCH_EQUAL;
          fwpConditions[conCount].conditionValue.type = FWP_UINT8;
          fwpConditions[conCount].conditionValue.uint8 = IPPROTO_TCP;
    
          ++conCount;
    
          // Add conditions and condition count to a filter.
          memset(&fwpFilter, 0, sizeof(FWPM_FILTER0));
    
          fwpFilter.numFilterConditions = conCount;
          if (conCount > 0)
            fwpFilter.filterCondition = fwpConditions;
    
          // Finish initializing filter...
    
        return;
    }
    filter.filterKey                 = filter_guid;//过滤器的uid
    filter.layerKey                  = *(layer->layer_guid);//对应的WFP过滤的UID
    filter.displayData.name          = layer->filter_name;//随便写点
    filter.displayData.description   = layer->filter_desc;//随便写点
    filter.action.type               = FWP_ACTION_CALLOUT_UNKNOWN;
    filter.action.calloutKey         = callout_guid;//用来标记这个callout的uid
    filter.subLayerKey               = *(layer->sublayer_guid);//子层的uid,子层主要是用来控制呼出的权重
    filter.weight.type               = FWP_UINT64;
    filter.weight.uint64             = &weight;
    filter.rawContext                = (UINT64)context;//把这个context也传递到呼出回调里面
    status = FwpmFilterAdd0(engine, &filter, NULL, NULL);
    
    static void windivert_outbound_network_v4_classify(
        IN const FWPS_INCOMING_VALUES0 *fixed_vals,
        IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data,
        const FWPS_FILTER0 *filter, IN UINT64 flow_context,
        OUT FWPS_CLASSIFY_OUT0 *result)
    {
        WINDIVERT_DATA_NETWORK network_data;
        BOOL loopback;
        context_t context = (context_t)(ULONG_PTR)filter->context;

    7、classifyFn有那麽多的參數,怎麽搞?目前只關心第一個和最後一個,以及data,第一個中可以取出該層特有的數據,比如下面的FWPS_FIELD_OUTBOUND_IPPACKET_V4_INTERFACE_INDEX,其他層有其他的東西,宏不一樣;result這裏會首先判斷數據傳遞到我這來,我還能不能操作,用FWPS_RIGHT_ACTION_WRITE判斷一下,函數返回時需要指定result,FWP_ACTION_BLOCK是禁止,也就是到我這你發不出去了,FWP_ACTION_CONTINUE是給下一個過濾器看看,FWP_ACTION_PERMIT在我理解就是不給下一個過濾器了,在我這就把你放行了;data就是該層捕獲到的數據,之後在分析。

    static void windivert_outbound_network_v4_classify(
        IN const FWPS_INCOMING_VALUES0 *fixed_vals,
        IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data,
        const FWPS_FILTER0 *filter, IN UINT64 flow_context,
        OUT FWPS_CLASSIFY_OUT0 *result)
    {
        WINDIVERT_DATA_NETWORK network_data;
        BOOL loopback;
        context_t context = (context_t)(ULONG_PTR)filter->context;
    
        UNREFERENCED_PARAMETER(meta_vals);
        UNREFERENCED_PARAMETER(data);
        UNREFERENCED_PARAMETER(flow_context);
    
        if ((result->rights & FWPS_RIGHT_ACTION_WRITE) == 0 || data == NULL)//FWPS_RIGHT_ACTION_WRITE该标志指示了本callout是否有权限修改过滤action
        {
            return;
        }
    
        network_data.IfIdx = windivert_get_val32(fixed_vals,
            FWPS_FIELD_OUTBOUND_IPPACKET_V4_INTERFACE_INDEX);//网络接口的索引,如网络堆栈所列举的
        network_data.SubIfIdx = windivert_get_val32(fixed_vals,
            FWPS_FIELD_OUTBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX);//逻辑网络界面的索引,由网络堆栈列举
        loopback = ((windivert_get_val32(fixed_vals,
            FWPS_FIELD_OUTBOUND_IPPACKET_V4_FLAGS) &
                FWP_CONDITION_FLAG_IS_LOOPBACK) != 0);//判断数据包是否是回环的
    
        windivert_network_classify(context, &network_data, /*ipv4=*/TRUE,
            /*outbound=*/TRUE, loopback, /*reassembled=*/FALSE, /*advance=*/0,
            data, result);
    }

    8、大約知道這麽多就能進行端口和ip的禁用了,直接通過FWP_ACTION_BLOCK,比如下圖的代碼。

     之後在繼續分析。。。。。

  • 相关阅读:
    Decker ce版社区(个人、免费)版安装
    修改SA登录限制
    vue eslint配置
    win10 搭建FMS流媒体服务 nginx rtmp
    直播推流软件
    常用直播拉流地址
    vue 父组件异步给子组件传递参数
    go int、int32、int6、float64、float32、bool、interface{}、string类型转换
    go如何往数据库中插入null
    go项目中日志的打印
  • 原文地址:https://www.cnblogs.com/csnd/p/16675583.html
Copyright © 2020-2023  润新知