。。。
爲什麽你的字體變成了繁體字?因爲按錯了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,比如下圖的代碼。
之後在繼續分析。。。。。