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,比如下圖的代碼。

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值