主頁 > 軟體設計 > 驅動開發:內核封裝WFP防火墻入門

驅動開發:內核封裝WFP防火墻入門

2023-06-08 09:34:53 軟體設計

WFP框架是微軟推出來替代TDIHOOK傳輸層驅動介面網路通信的方案,其默認被設計為分層結構,該框架分別提供了用戶態與內核態相同的AIP函式,在兩種模式下均可以開發防火墻產品,以下代碼我實作了一個簡單的驅動過濾防火墻,

WFP 框架分為兩大層次模塊,用戶態基礎過濾引擎BFE (BaseFilteringEngine) ,以及內核態過濾引擎 KMFE (KMFilteringEngine),基礎過濾引擎對上提供C語言呼叫方式的API以及RPC介面,這些介面都被封裝在FWPUCLNT.dll模塊中,開發時可以呼叫該模塊中的匯出函式.

  • WFP程式作業流程:
  • 使用 FwpmEngineOpen() 開啟 WFP 引擎,獲得WFP使用句柄
  • 使用 FwpmTransactionBegin() 設定對網路通信內容的過濾權限 (只讀/允許修改)
  • 使用 FwpsCalloutRegister(),FwpmCalloutAdd(),FwpmFilterAdd() 選擇要過濾的內容,并添加過濾器物件和回呼函式.
  • 使用 FwpmTransactionCommit() 確認剛才的內容,讓剛才添加的回呼函式開始生效.
  • 使用 FwpmFilterDeleteById(),FwpmCalloutDeleteById(),FwpsCalloutUnregisterById()函式撤銷物件和回呼函式.
  • 使用 FwpmEngineClose() 關閉WFP引擎類句柄.

默認情況下WFP一次需要注冊3個回呼函式,只有一個是事前回呼,另外兩個是事后回呼,通常情況下我們只關注事前回呼即可,此外WFP能過濾很對內容,我們需要指定過濾條件標志來輸出我們所需要的資料.

  • 一般可設定為FWPM_LAYER_ALE_AUTH_CONNECT_V4意思是設定IPV4過濾.
  • 還需要設定一個GUID值,該值可隨意設定,名稱為GUID_ALE_AUTH_CONNECT_CALLOUT_V4宏.

首先我們通過上方的流程實作一個簡單的網路控制驅動,該驅動運行后可對自身機器訪問指定地址埠進行控制,例如實作指定應用斷網,禁止指定頁面被訪問等,在配置WFP開發環境時需要在聯結器選項卡中的附加依賴項中增加fwpkclnt.lib,uuid.lib這兩個庫檔案,并且需要使用WDM開發模板,否則編譯將不通過,

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

#define NDIS_SUPPORT_NDIS6 1
#define DEV_NAME L"\\Device\\MY_WFP_DEV_NAME"
#define SYM_NAME L"\\DosDevices\\MY_WFP_SYM_NAME"

#include <ntifs.h>
#include <fwpsk.h>
#include <fwpmk.h>
#include <stdio.h>

// 過濾器引擎句柄
HANDLE g_hEngine;

// 過濾器引擎中的callout的運行時識別符號
ULONG32 g_AleConnectCalloutId;

// 過濾器的運行時識別符號
ULONG64 g_AleConnectFilterId;

// 指定唯一UUID值(只要不沖突即可,內容可隨意)
GUID GUID_ALE_AUTH_CONNECT_CALLOUT_V4 = { 0x6812fc83, 0x7d3e, 0x499a, 0xa0, 0x12, 0x55, 0xe0, 0xd8, 0x5f, 0x34, 0x8b };

// ------------------------------------------------------------------------------
// 頭部函式宣告
// ------------------------------------------------------------------------------

// 注冊Callout并設定過濾點
NTSTATUS RegisterCalloutForLayer(
	IN PDEVICE_OBJECT pDevObj,
	IN const GUID *layerKey,
	IN const GUID *calloutKey,
	IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
	IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
	IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
	OUT ULONG32 *calloutId,
	OUT ULONG64 *filterId,
	OUT HANDLE *engine);

// 注冊Callout
NTSTATUS RegisterCallout(
	PDEVICE_OBJECT pDevObj,
	IN const GUID *calloutKey,
	IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
	IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
	IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
	OUT ULONG32 *calloutId);

// 設定過濾點
NTSTATUS SetFilter(
	IN const GUID *layerKey,
	IN const GUID *calloutKey,
	OUT ULONG64 *filterId,
	OUT HANDLE *engine);

// Callout函式 flowDeleteFn
VOID NTAPI flowDeleteFn(
	_In_ UINT16 layerId,
	_In_ UINT32 calloutId,
	_In_ UINT64 flowContext
	);

// Callout函式 classifyFn
#if (NTDDI_VERSION >= NTDDI_WIN8)
VOID NTAPI classifyFn(
	_In_ const FWPS_INCOMING_VALUES0* inFixedValues,
	_In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
	_Inout_opt_ void* layerData,
	_In_opt_ const void* classifyContext,
	_In_ const FWPS_FILTER2* filter,
	_In_ UINT64 flowContext,
	_Inout_ FWPS_CLASSIFY_OUT0* classifyOut
	);
#elif (NTDDI_VERSION >= NTDDI_WIN7)                       
VOID NTAPI classifyFn(
	_In_ const FWPS_INCOMING_VALUES0* inFixedValues,
	_In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
	_Inout_opt_ void* layerData,
	_In_opt_ const void* classifyContext,
	_In_ const FWPS_FILTER1* filter,
	_In_ UINT64 flowContext,
	_Inout_ FWPS_CLASSIFY_OUT0* classifyOut
	);
#else
VOID NTAPI classifyFn(
	_In_ const FWPS_INCOMING_VALUES0* inFixedValues,
	_In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
	_Inout_opt_ void* layerData,
	_In_ const FWPS_FILTER0* filter,
	_In_ UINT64 flowContext,
	_Inout_ FWPS_CLASSIFY_OUT0* classifyOut
	);
#endif

// Callout函式 notifyFn
#if (NTDDI_VERSION >= NTDDI_WIN8)
NTSTATUS NTAPI notifyFn(
	_In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
	_In_ const GUID* filterKey,
	_Inout_ FWPS_FILTER2* filter
	);
#elif (NTDDI_VERSION >= NTDDI_WIN7)
NTSTATUS NTAPI notifyFn(
	_In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
	_In_ const GUID* filterKey,
	_Inout_ FWPS_FILTER1* filter
	);
#else
NTSTATUS NTAPI notifyFn(
	_In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
	_In_ const GUID* filterKey,
	_Inout_ FWPS_FILTER0* filter
	);
#endif

// ------------------------------------------------------------------------------
// 函式實作部分
// ------------------------------------------------------------------------------

// 協議判斷
NTSTATUS ProtocalIdToName(UINT16 protocalId, PCHAR lpszProtocalName)
{
	NTSTATUS status = STATUS_SUCCESS;

	switch (protocalId)
	{
	case 1:
	{
		// ICMP
		RtlCopyMemory(lpszProtocalName, "ICMP", 5);
		break;
	}
	case 2:
	{
		// IGMP
		RtlCopyMemory(lpszProtocalName, "IGMP", 5);
		break;
	}
	case 6:
	{
		// TCP
		RtlCopyMemory(lpszProtocalName, "TCP", 4);
		break;
	}
	case 17:
	{
		// UDP
		RtlCopyMemory(lpszProtocalName, "UDP", 4);
		break;
	}
	case 27:
	{
		// RDP
		RtlCopyMemory(lpszProtocalName, "RDP", 6);
		break;
	}
	default:
	{
		// UNKNOW
		RtlCopyMemory(lpszProtocalName, "UNKNOWN", 8);
		break;
	}
	}

	return status;
}

// 啟動WFP
NTSTATUS WfpLoad(PDEVICE_OBJECT pDevObj)
{
	NTSTATUS status = STATUS_SUCCESS;

	// 注冊Callout并設定過濾點
	// classifyFn, notifyFn, flowDeleteFn 注冊三個回呼函式,一個事前回呼,兩個事后回呼
	status = RegisterCalloutForLayer(pDevObj, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, &GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
		classifyFn, notifyFn, flowDeleteFn, &g_AleConnectCalloutId, &g_AleConnectFilterId, &g_hEngine);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("注冊回呼失敗 \n");
		return status;
	}

	return status;
}

// 卸載WFP
NTSTATUS WfpUnload()
{
	if (NULL != g_hEngine)
	{
		// 洗掉FilterId
		FwpmFilterDeleteById(g_hEngine, g_AleConnectFilterId);
		// 洗掉CalloutId
		FwpmCalloutDeleteById(g_hEngine, g_AleConnectCalloutId);
		// 清空Filter
		g_AleConnectFilterId = 0;
		// 反注冊CalloutId
		FwpsCalloutUnregisterById(g_AleConnectCalloutId);
		// 清空CalloutId
		g_AleConnectCalloutId = 0;
		// 關閉引擎
		FwpmEngineClose(g_hEngine);
		g_hEngine = NULL;
	}

	return STATUS_SUCCESS;
}

// 注冊Callout并設定過濾點
NTSTATUS RegisterCalloutForLayer(IN PDEVICE_OBJECT pDevObj, IN const GUID *layerKey, IN const GUID *calloutKey, IN FWPS_CALLOUT_CLASSIFY_FN classifyFn, IN FWPS_CALLOUT_NOTIFY_FN notifyFn, IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn, OUT ULONG32 *calloutId, OUT ULONG64 *filterId, OUT HANDLE *engine)
{
	NTSTATUS status = STATUS_SUCCESS;

	// 注冊Callout
	status = RegisterCallout(pDevObj, calloutKey, classifyFn, notifyFn, flowDeleteNotifyFn, calloutId);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	// 設定過濾點
	status = SetFilter(layerKey, calloutKey, filterId, engine);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	return status;
}

// 注冊Callout
NTSTATUS RegisterCallout(PDEVICE_OBJECT pDevObj, IN const GUID *calloutKey, IN FWPS_CALLOUT_CLASSIFY_FN classifyFn, IN FWPS_CALLOUT_NOTIFY_FN notifyFn, IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn, OUT ULONG32 *calloutId)
{
	NTSTATUS status = STATUS_SUCCESS;
	FWPS_CALLOUT sCallout = { 0 };

	// 設定Callout
	sCallout.calloutKey = *calloutKey;
	sCallout.classifyFn = classifyFn;
	sCallout.flowDeleteFn = flowDeleteNotifyFn;
	sCallout.notifyFn = notifyFn;

	// 注冊Callout
	status = FwpsCalloutRegister(pDevObj, &sCallout, calloutId);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("注冊Callout失敗 \n");
		return status;
	}

	return status;
}

// 設定過濾點
NTSTATUS SetFilter(IN const GUID *layerKey, IN const GUID *calloutKey, OUT ULONG64 *filterId, OUT HANDLE *engine)
{
	HANDLE hEngine = NULL;
	NTSTATUS status = STATUS_SUCCESS;
	FWPM_SESSION session = { 0 };
	FWPM_FILTER mFilter = { 0 };
	FWPM_CALLOUT mCallout = { 0 };
	FWPM_DISPLAY_DATA mDispData = https://www.cnblogs.com/LyShark/archive/2023/06/08/{ 0 };

	// 創建Session
	session.flags = FWPM_SESSION_FLAG_DYNAMIC;
	status = FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &hEngine);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	// 開始事務
	status = FwpmTransactionBegin(hEngine, 0);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	// 設定Callout引數
	mDispData.name = L"MY WFP LyShark";
	mDispData.description = L"WORLD OF DEMON";
	mCallout.applicableLayer = *layerKey;
	mCallout.calloutKey = *calloutKey;
	mCallout.displayData = https://www.cnblogs.com/LyShark/archive/2023/06/08/mDispData;

	// 添加Callout到Session中
	status = FwpmCalloutAdd(hEngine, &mCallout, NULL, NULL);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	// 設定過濾器引數
	mFilter.action.calloutKey = *calloutKey;
	mFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
	mFilter.displayData.name = L"MY WFP LyShark";
	mFilter.displayData.description = L"WORLD OF DEMON";
	mFilter.layerKey = *layerKey;
	mFilter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL;
	mFilter.weight.type = FWP_EMPTY;

	// 添加過濾器
	status = FwpmFilterAdd(hEngine, &mFilter, NULL, filterId);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	// 提交事務
	status = FwpmTransactionCommit(hEngine);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	*engine = hEngine;
	return status;
}

// Callout函式 classifyFn 事前回呼函式
VOID NTAPI classifyFn(_In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut)
{
	// 資料包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0
	WORD wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;

	// 定義本機地址與本機埠
	ULONG ulLocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
	UINT16 uLocalPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16;

	// 定義對端地址與對端埠
	ULONG ulRemoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
	UINT16 uRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;

	// 獲取當前行程IRQ
	KIRQL kCurrentIrql = KeGetCurrentIrql();

	// 獲取行程ID
	ULONG64 processId = inMetaValues->processId;
	UCHAR szProcessPath[256] = { 0 };
	CHAR szProtocalName[256] = { 0 };
	RtlZeroMemory(szProcessPath, 256);

	// 獲取行程路徑
	for (ULONG i = 0; i < inMetaValues->processPath->size; i++)
	{
		// 里面是寬字符存盤的
		szProcessPath[i] = inMetaValues->processPath->data[i];
	}

	// 獲取當前協議型別
	ProtocalIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, szProtocalName);

	// 設定默認規則 允許連接
	classifyOut->actionType = FWP_ACTION_PERMIT;

	// 禁止指定行程網路連接
	if (NULL != wcsstr((PWCHAR)szProcessPath, L"iexplore.exe"))
	{
		// 設定拒絕規則 拒絕連接
		classifyOut->actionType = FWP_ACTION_BLOCK;
		classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
		classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
		DbgPrint("[LyShark.com] 攔截IE網路鏈接請求... \n");
	}

	// 輸出對端地址字串 并阻斷鏈接
	char szRemoteAddress[256] = { 0 };
	char szRemotePort[128] = { 0 };

	char szLocalAddress[256] = { 0 };
	char szLocalPort[128] = { 0 };

	sprintf(szRemoteAddress, "%u.%u.%u.%u", (ulRemoteIp >> 24) & 0xFF, (ulRemoteIp >> 16) & 0xFF, (ulRemoteIp >> 8) & 0xFF, (ulRemoteIp)& 0xFF);
	sprintf(szRemotePort, "%d", uRemotePort);

	sprintf(szLocalAddress, "%u.%u.%u.%u", (ulLocalIp >> 24) & 0xFF, (ulLocalIp >> 16) & 0xFF, (ulLocalIp >> 8) & 0xFF, (ulLocalIp)& 0xFF);
	sprintf(szLocalPort, "%d", uLocalPort);

	// DbgPrint("本端: %s : %s --> 對端: %s : %s \n", szLocalAddress, szLocalPort, szRemoteAddress, szRemotePort);

	// 如果對端地址是 8.141.58.64 且對端埠是 443 則拒絕連接
	if (strcmp(szRemoteAddress, "8.141.58.64") == 0 && strcmp(szRemotePort, "443") == 0)
	{
		DbgPrint("[LyShark.com] 攔截網站訪問請求 --> %s : %s \n", szRemoteAddress, szRemotePort);
		// 設定拒絕規則 拒絕連接
		classifyOut->actionType = FWP_ACTION_BLOCK;
		classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
		classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
	}
	else if (strcmp(szRemotePort, "0") == 0)
	{
		DbgPrint("[LyShark.com] 攔截Ping訪問請求 --> %s \n", szRemoteAddress);

		// 設定拒絕規則 拒絕連接
		classifyOut->actionType = FWP_ACTION_BLOCK;
		classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
		classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
	}

	// 顯示
	DbgPrint("[LyShark.com] 方向: %d -> 協議型別: %s -> 本端地址: %u.%u.%u.%u:%d -> 對端地址: %u.%u.%u.%u:%d -> IRQL: %d -> 行程ID: %I64d -> 路徑: %S \n",
	wDirection,
	szProtocalName,
	(ulLocalIp >> 24) & 0xFF,
	(ulLocalIp >> 16) & 0xFF,
	(ulLocalIp >> 8) & 0xFF,
	(ulLocalIp)& 0xFF,
	uLocalPort,
	(ulRemoteIp >> 24) & 0xFF,
	(ulRemoteIp >> 16) & 0xFF,
	(ulRemoteIp >> 8) & 0xFF,
	(ulRemoteIp)& 0xFF,
	uRemotePort,
	kCurrentIrql,
	processId,
	(PWCHAR)szProcessPath);

}

// Callout函式 notifyFn 事后回呼函式
NTSTATUS NTAPI notifyFn(_In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, _In_ const GUID* filterKey, _Inout_ FWPS_FILTER2* filter)
{
	NTSTATUS status = STATUS_SUCCESS;
	return status;
}

// Callout函式 flowDeleteFn 事后回呼函式
VOID NTAPI flowDeleteFn(_In_ UINT16 layerId, _In_ UINT32 calloutId, _In_ UINT64 flowContext)
{
	return;
}

// 默認派遣函式
NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
	NTSTATUS status = STATUS_SUCCESS;
	pIrp->IoStatus.Status = status;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);

	return status;
}

// 創建設備
NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject)
{
	NTSTATUS status = STATUS_SUCCESS;
	PDEVICE_OBJECT pDevObj = NULL;
	UNICODE_STRING ustrDevName, ustrSymName;
	RtlInitUnicodeString(&ustrDevName, DEV_NAME);
	RtlInitUnicodeString(&ustrSymName, SYM_NAME);

	status = IoCreateDevice(pDriverObject, 0, &ustrDevName, FILE_DEVICE_NETWORK, 0, FALSE, &pDevObj);
	if (!NT_SUCCESS(status))
	{
		return status;
	}
	status = IoCreateSymbolicLink(&ustrSymName, &ustrDevName);
	if (!NT_SUCCESS(status))
	{
		return status;
	}
	return status;
}

// 卸載驅動
VOID UnDriver(PDRIVER_OBJECT driver)
{
	// 洗掉回呼函式和過濾器,關閉引擎
	WfpUnload();

	UNICODE_STRING ustrSymName;
	RtlInitUnicodeString(&ustrSymName, SYM_NAME);
	IoDeleteSymbolicLink(&ustrSymName);
	if (driver->DeviceObject)
	{
		IoDeleteDevice(driver->DeviceObject);
	}
}

// 驅動入口
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	NTSTATUS status = STATUS_SUCCESS;
	Driver->DriverUnload = UnDriver;
	for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
	{
		Driver->MajorFunction[i] = DriverDefaultHandle;
	}

	// 創建設備
	CreateDevice(Driver);

	// 啟動WFP
	WfpLoad(Driver->DeviceObject);

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

上方代碼是一個最基本的WFP過濾框架頭部函式,宣告部分來源于微軟的定義此處不做解釋,需要注意GUID_ALE_AUTH_CONNECT_CALLOUT_V4代表的是一個隨機UUID值,該值可以任意定義只要不一致即可,驅動程式運行后會率先執行WfpLoad()這個函式,該函式內部通過RegisterCalloutForLayer()注冊了一個過濾點,此處我們必須要注意三個回呼函式,classifyFn, notifyFn, flowDeleteFn 他們分別的功能時,事前回呼,事后回呼,事后回呼,而WFP框架中我們最需要注意的也就是對這三個函式進行重定義,也就是需要重寫函式來實作我們特定的功能,

NTSTATUS RegisterCalloutForLayer
(
    IN const GUID* layerKey,
    IN const GUID* calloutKey,
    IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
    IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
    IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
    OUT UINT32* calloutId,
    OUT UINT64* filterId
}

既然是防火墻那么必然classifyFn事前更重要一些,如果需要監控網路流量則需要在事前函式中做處理,而如果是監視則可以在事后做處理,既然要在事前進行處理,那么我們就來看看事前是如何處理的流量,

// Callout函式 classifyFn 事前回呼函式
VOID NTAPI classifyFn(_In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut)
{
	// 資料包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0
	WORD wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;

	// 定義本機地址與本機埠
	ULONG ulLocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
	UINT16 uLocalPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16;

	// 定義對端地址與對端埠
	ULONG ulRemoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
	UINT16 uRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;

	// 獲取當前行程IRQ
	KIRQL kCurrentIrql = KeGetCurrentIrql();

	// 獲取行程ID
	ULONG64 processId = inMetaValues->processId;
	UCHAR szProcessPath[256] = { 0 };
	CHAR szProtocalName[256] = { 0 };
	RtlZeroMemory(szProcessPath, 256);

	// 獲取行程路徑
	for (ULONG i = 0; i < inMetaValues->processPath->size; i++)
	{
		// 里面是寬字符存盤的
		szProcessPath[i] = inMetaValues->processPath->data[i];
	}

	// 獲取當前協議型別
	ProtocalIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, szProtocalName);

	// 設定默認規則 允許連接
	classifyOut->actionType = FWP_ACTION_PERMIT;

	// 禁止指定行程網路連接
	if (NULL != wcsstr((PWCHAR)szProcessPath, L"qq.exe"))
	{
		// 設定拒絕規則 拒絕連接
		classifyOut->actionType = FWP_ACTION_BLOCK;
		classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
		classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
	}

	// 輸出對端地址字串 并阻斷鏈接
	char szRemoteAddress[256] = { 0 };
	char szRemotePort[128] = { 0 };

	char szLocalAddress[256] = { 0 };
	char szLocalPort[128] = { 0 };

	sprintf(szRemoteAddress, "%u.%u.%u.%u", (ulRemoteIp >> 24) & 0xFF, (ulRemoteIp >> 16) & 0xFF, (ulRemoteIp >> 8) & 0xFF, (ulRemoteIp)& 0xFF);
	sprintf(szRemotePort, "%d", uRemotePort);

	sprintf(szLocalAddress, "%u.%u.%u.%u", (ulLocalIp >> 24) & 0xFF, (ulLocalIp >> 16) & 0xFF, (ulLocalIp >> 8) & 0xFF, (ulLocalIp)& 0xFF);
	sprintf(szLocalPort, "%d", uLocalPort);

	// DbgPrint("本端: %s : %s --> 對端: %s : %s \n", szLocalAddress, szLocalPort, szRemoteAddress, szRemotePort);

	// 如果對端地址是 8.141.58.64 且對端埠是 443 則拒絕連接
	if (strcmp(szRemoteAddress, "8.141.58.64") == 0 && strcmp(szRemotePort, "443") == 0)
	{
		DbgPrint("攔截網站訪問請求 --> %s : %s \n", szRemoteAddress, szRemotePort);
		// 設定拒絕規則 拒絕連接
		classifyOut->actionType = FWP_ACTION_BLOCK;
		classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
		classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
	}
	else if (strcmp(szRemotePort, "0") == 0)
	{
		DbgPrint("攔截Ping訪問請求 --> %s \n", szRemoteAddress);

		// 設定拒絕規則 拒絕連接
		classifyOut->actionType = FWP_ACTION_BLOCK;
		classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
		classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
	}

	/*
	// 顯示
	DbgPrint("方向: %d -> 協議型別: %s -> 本端地址: %u.%u.%u.%u:%d -> 對端地址: %u.%u.%u.%u:%d -> IRQL: %d -> 行程ID: %I64d -> 路徑: %S \n",
	wDirection,
	szProtocalName,
	(ulLocalIp >> 24) & 0xFF,
	(ulLocalIp >> 16) & 0xFF,
	(ulLocalIp >> 8) & 0xFF,
	(ulLocalIp)& 0xFF,
	uLocalPort,
	(ulRemoteIp >> 24) & 0xFF,
	(ulRemoteIp >> 16) & 0xFF,
	(ulRemoteIp >> 8) & 0xFF,
	(ulRemoteIp)& 0xFF,
	uRemotePort,
	kCurrentIrql,
	processId,
	(PWCHAR)szProcessPath);
	*/
}

當有新的網路資料包路由到事前函式時,程式中會通過如下案例直接得到我們所需要的資料包頭,ProtocalIdToName函式則是一個將特定型別數字轉為字串的轉換函式,

// 資料包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0
WORD wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;

// 定義本機地址與本機埠
ULONG ulLocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
UINT16 uLocalPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16;

// 定義對端地址與對端埠
ULONG ulRemoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
UINT16 uRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;

// 獲取當前行程IRQ
KIRQL kCurrentIrql = KeGetCurrentIrql();

// 獲取行程ID
ULONG64 processId = inMetaValues->processId;
UCHAR szProcessPath[256] = { 0 };
CHAR szProtocalName[256] = { 0 };
RtlZeroMemory(szProcessPath, 256);

// 獲取行程路徑
for (ULONG i = 0; i < inMetaValues->processPath->size; i++)
{
	// 里面是寬字符存盤的
	szProcessPath[i] = inMetaValues->processPath->data[i];
}

// 獲取當前協議型別
ProtocalIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, szProtocalName);

攔截瀏覽器上網: 防火墻的默認規則我們將其改為放行所有classifyOut->actionType = FWP_ACTION_PERMIT;,當我們需要攔截特定行程上網時則只需要判斷呼叫原,如果時特定行程則直接設定拒絕網路訪問,

// 設定默認規則 允許連接
classifyOut->actionType = FWP_ACTION_PERMIT;

// 禁止指定行程網路連接
if (NULL != wcsstr((PWCHAR)szProcessPath, L"iexplore.exe"))
{
	// 設定拒絕規則 拒絕連接
	classifyOut->actionType = FWP_ACTION_BLOCK;
	classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
	classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
	DbgPrint("[LyShark.com] 攔截IE網路鏈接請求... \n");
}

當這段驅動程式被加載后,則用戶使用IE訪問任何頁面都將提示無法訪問,

攔截指定IP地址: 防火墻的另一個重要功能就是攔截主機自身訪問特定網段,此功能只需要增加過濾條件即可實作,如下當用戶訪問8.141.58.64這個IP地址是則會被攔截,如果監測到用戶時Ping請求則也會被攔截,

// 如果對端地址是 8.141.58.64 且對端埠是 443 則拒絕連接
if (strcmp(szRemoteAddress, "8.141.58.64") == 0 && strcmp(szRemotePort, "443") == 0)
{
	DbgPrint("攔截網站訪問請求 --> %s : %s \n", szRemoteAddress, szRemotePort);
	// 設定拒絕規則 拒絕連接
	classifyOut->actionType = FWP_ACTION_BLOCK;
	classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
	classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
}
else if (strcmp(szRemotePort, "0") == 0)
{
	DbgPrint("攔截Ping訪問請求 --> %s \n", szRemoteAddress);

	// 設定拒絕規則 拒絕連接
	classifyOut->actionType = FWP_ACTION_BLOCK;
	classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
	classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
}

當這段驅動程式被加載后,則用戶主機無法訪問8.141.58.64且無法使用ping命令,

抓取底層資料包: 如果僅僅只是想要輸出流經自身主機的資料包,則只需要對特定資料包進行解碼即可得到原始資料,

// 輸出對端地址字串 并阻斷鏈接
char szRemoteAddress[256] = { 0 };
char szRemotePort[128] = { 0 };

char szLocalAddress[256] = { 0 };
char szLocalPort[128] = { 0 };

sprintf(szRemoteAddress, "%u.%u.%u.%u", (ulRemoteIp >> 24) & 0xFF, (ulRemoteIp >> 16) & 0xFF, (ulRemoteIp >> 8) & 0xFF, (ulRemoteIp)& 0xFF);
sprintf(szRemotePort, "%d", uRemotePort);

sprintf(szLocalAddress, "%u.%u.%u.%u", (ulLocalIp >> 24) & 0xFF, (ulLocalIp >> 16) & 0xFF, (ulLocalIp >> 8) & 0xFF, (ulLocalIp)& 0xFF);
sprintf(szLocalPort, "%d", uLocalPort);

// 顯示
DbgPrint("[LyShark.com] 方向: %d -> 協議型別: %s -> 本端地址: %u.%u.%u.%u:%d -> 對端地址: %u.%u.%u.%u:%d -> IRQL: %d -> 行程ID: %I64d -> 路徑: %S \n",
wDirection,
szProtocalName,
(ulLocalIp >> 24) & 0xFF,
(ulLocalIp >> 16) & 0xFF,
(ulLocalIp >> 8) & 0xFF,
(ulLocalIp)& 0xFF,
uLocalPort,
(ulRemoteIp >> 24) & 0xFF,
(ulRemoteIp >> 16) & 0xFF,
(ulRemoteIp >> 8) & 0xFF,
(ulRemoteIp)& 0xFF,
uRemotePort,
kCurrentIrql,
processId,
(PWCHAR)szProcessPath);

當這段驅動程式被加載后,則用戶可看到流經本機的所有資料包,

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17134954.html
本博客中的文章,轉載請注明出處,

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/554627.html

標籤:其他

上一篇:移動應用架構新思路——WePY+小程式容器

下一篇:返回列表

標籤雲
其他(160593) Python(38215) JavaScript(25484) Java(18209) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7235) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4585) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1983) 功能(1967) HtmlCss(1955) Web開發(1951) C++(1933) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1879) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 驅動開發:內核封裝WFP防火墻入門

    WFP框架是微軟推出來替代TDIHOOK傳輸層驅動介面網路通信的方案,其默認被設計為分層結構,該框架分別提供了用戶態與內核態相同的AIP函式,在兩種模式下均可以開發防火墻產品,以下代碼我實作了一個簡單的驅動過濾防火墻。WFP 框架分為兩大層次模塊,用戶態基礎過濾引擎`BFE (BaseFilteri... ......

    uj5u.com 2023-06-08 09:34:53 more
  • 移動應用架構新思路——WePY+小程式容器

    WePY(微信小程式開發框架)是一個基于組件化開發思想的微信小程式開發框架。它類似于Vue.js框架,通過封裝小程式原生的API,提供了更加簡潔、高效的開發方式。 WePY的主要特點包括: 組件化開發:WePY將頁面拆分為多個組件,每個組件有自己的樣式、模板和邏輯。這種組件化的開發方式能夠提高代碼的 ......

    uj5u.com 2023-06-08 09:34:45 more
  • 如何做架構設計?

    我們要尋求更好的技術方案,推動架構的良性演進,每一步都是經過深度思考的,而架構設計方法就是幫助我們思考的框架。通過做架構設計,我們應該提升軟體的質量和效率,降低風險和成本。 ......

    uj5u.com 2023-06-08 09:34:40 more
  • AI人工智能領域精美繪圖模板分享

    ![cover.jpeg](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e86fc6dcb452419498a7db6878591e30~tplv-k3u1fbpfcp-watermark.image?) #### 1 人工智能的發展歷程 如今 ......

    uj5u.com 2023-06-08 09:34:36 more
  • 當“代碼農”遇上“碼農”:揭秘主干開發的那些事兒

    前段時期我負責部門內部主干開發落地相關事宜,這個程序中,也真真切切的體會到了多人開發程序中,面對特性分支管理中,大家遇到的一些困擾,尤其面對敏捷迭代的開發方式,合并沖突,集成測驗,代碼重用等方面,都與高效兩個字背離。當然,我在推進主干開發程序中,也遇到了一些問題和坎坷,在這里,集中的做一次分享。 ......

    uj5u.com 2023-06-08 09:34:30 more
  • 驅動開發:內核封裝WFP防火墻入門

    WFP框架是微軟推出來替代TDIHOOK傳輸層驅動介面網路通信的方案,其默認被設計為分層結構,該框架分別提供了用戶態與內核態相同的AIP函式,在兩種模式下均可以開發防火墻產品,以下代碼我實作了一個簡單的驅動過濾防火墻。WFP 框架分為兩大層次模塊,用戶態基礎過濾引擎`BFE (BaseFilteri... ......

    uj5u.com 2023-06-08 09:34:04 more
  • 移動應用架構新思路——WePY+小程式容器

    WePY(微信小程式開發框架)是一個基于組件化開發思想的微信小程式開發框架。它類似于Vue.js框架,通過封裝小程式原生的API,提供了更加簡潔、高效的開發方式。 WePY的主要特點包括: 組件化開發:WePY將頁面拆分為多個組件,每個組件有自己的樣式、模板和邏輯。這種組件化的開發方式能夠提高代碼的 ......

    uj5u.com 2023-06-08 09:33:49 more
  • 如何做架構設計?

    我們要尋求更好的技術方案,推動架構的良性演進,每一步都是經過深度思考的,而架構設計方法就是幫助我們思考的框架。通過做架構設計,我們應該提升軟體的質量和效率,降低風險和成本。 ......

    uj5u.com 2023-06-08 09:33:45 more
  • AI人工智能領域精美繪圖模板分享

    ![cover.jpeg](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e86fc6dcb452419498a7db6878591e30~tplv-k3u1fbpfcp-watermark.image?) #### 1 人工智能的發展歷程 如今 ......

    uj5u.com 2023-06-08 09:33:41 more
  • 當“代碼農”遇上“碼農”:揭秘主干開發的那些事兒

    前段時期我負責部門內部主干開發落地相關事宜,這個程序中,也真真切切的體會到了多人開發程序中,面對特性分支管理中,大家遇到的一些困擾,尤其面對敏捷迭代的開發方式,合并沖突,集成測驗,代碼重用等方面,都與高效兩個字背離。當然,我在推進主干開發程序中,也遇到了一些問題和坎坷,在這里,集中的做一次分享。 ......

    uj5u.com 2023-06-08 09:33:35 more