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

来源:https://www.cnblogs.com/LyShark/archive/2023/06/08/17134954.html
-Advertisement-
Play Games

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


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 = { 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 = 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
本博客中的文章,轉載請註明出處。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 為了保證的可讀性,本文采用意譯而非直譯。 在 JS 面試中,經常會看到一些簡單而又沙雕的題目,這些題目包含一些陷阱,但這些在我們規範的編碼下或者業務中基本不會出現。 有些面試官就是這樣,不專註於制定代碼的標準和規範上,卻用不規範的代碼去檢 ...
  • # 解決JavaScript單線程問題——webWorkers > 參考文檔 [使用 Web Workers - Web API 介面參考 | MDN (mozilla.org)](https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Worker ...
  • 基礎知識 知識點梳理見圖: 自己動手實踐案例 案例1: 訪問本地文件 <!DOCTYPE html> <html> <body> <div id="demo"> <h1>XMLHttpRequest 對象</h1> <button type="button" onclick="loadDoc()"> ...
  • vue3 偵聽器 在Vue3中,偵聽器的使用方式與Vue2相同,可以使用watch選項或$watch方法來創建偵聽器。不同之處在於,Vue3中取消了immediate選項,同時提供了新的選項和API。 創建偵聽器 可以使用watch選項或$watch方法來創建偵聽器,語法與Vue2相同。示例如下: ...
  • 報錯信息如下: 代碼如下: <!DOCTYPE html> <html> <body> <div id="demo"> <h1>XMLHttpRequest 對象</h1> <button type="button" onclick="loadDoc()">更改內容</button> </div> ...
  • 一. 目標 個人賬號的設置記憶功能-避免用戶每次登錄之後重新對錶單欄位做展示設置 二、存儲方案 輕量方案 結合localstorage低容量存儲(5M),根據LRU只存最近訪問的20至30張表格列配置數據 全量方案 大記憶體G級別,使用indexedDb進行存儲,有多少表格操作列數據就存多少, 結合第 ...
  • ###為什麼建議使用對象來替換枚舉? ### 在設計模型時,我們經常會使用枚舉來定義類型,比如說,一個員工類 Employee,他有職級,比如P6/P7。順著這個思路,設計一個 Level 類型的枚舉: ``` class Employee { private String name; /** * ...
  • 摘要:本文將探索內核中解析PE文件的相關內容。 本文分享自華為雲社區《驅動開發:內核PE結構VA與FOA轉換》,作者: LyShark 。 本章將探索內核中解析PE文件的相關內容,PE文件中FOA與VA、RVA之間的轉換也是很重要的,所謂的FOA是文件中的地址,VA則是記憶體裝入後的虛擬地址,RVA是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...