WFP框架是微軟推出來替代TDIHOOK傳輸層驅動介面網路通訊的方案,其預設被設計為分層結構,該框架分別提供了使用者態與核心態相同的AIP函數,在兩種模式下均可以開發防火牆產品,以下程式碼我實現了一個簡單的驅動過濾防火牆。
WFP 框架分為兩大層次模組,使用者態基礎過濾引擎BFE (BaseFilteringEngine)
,以及核心態過濾引擎 KMFE (KMFilteringEngine)
,基礎過濾引擎對上提供C語言呼叫方式的API以及RPC介面,這些介面都被封裝在FWPUCLNT.dll
模組中,開發時可以呼叫該模組中的匯出函數.
預設情況下WFP一次需要註冊3個回撥函數,只有一個是事前回撥,另外兩個是事後回撥,通常情況下我們只關注事前回撥即可,此外WFP能過濾很對內容,我們需要指定過濾條件標誌來輸出我們所需要的資料.
FWPM_LAYER_ALE_AUTH_CONNECT_V4
意思是設定IPV4過濾.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);
當這段驅動程式被載入後,則使用者可看到流經本機的所有封包。