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: me@lyshark.com #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);

当这段驱动程序被加载后,则用户可看到流经本机的所有数据包。

驱动开发:内核封装WFP防火墙入门的更多相关文章

  1. Android深度探索HAL与驱动开发 第三章 Git入门

    Git功能十分复杂,简单来说它使你的开发更为快捷和可控,尤其是在开源项目上展现的友好的交互和回馈. 熟悉一些git指令操作对开发者的帮助可以避免开发者受到一些外在因素打断开发进度,甚至延误项目的che ...

  2. Windows驱动开发-内核常用内存函数

    搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool

  3. Windows内核驱动开发入门学习资料

    声明:本文所描述的所有资料和源码均搜集自互联网,版权归原始作者所有,所以在引用资料时我尽量注明原始作者和出处:本文所搜集资料也仅供同学们学习之用,由于用作其他用途引起的责任纠纷,本人不负任何责任.(本 ...

  4. Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

    1. openSUSE是一款优秀的linux. 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. 7.内存设置为2 ...

  5. 驱动开发入门——NTModel

    上一篇博文中主要说明了驱动开发中基本的数据类型,认识这些数据类型算是驱动开发中的入门吧,这次主要说明驱动开发中最基本的模型--NTModel.介绍这个模型首先要了解R3层是如何通过应用层API进入到内 ...

  6. Windows驱动开发入门指引

       1.  前言 因工作上项目的需要,笔者需要做驱动相关的开发,之前并没有接触过相关的知识,折腾一段时间下来,功能如需实现了,也积累了一些经验和看法,所以在此做番总结. 对于驱动开发的开发指引,微软 ...

  7. 2013-6-2 [转载自CSDN]如何入门Windows系统下驱动开发

    [序言]很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都以英文为主,这样让很多驱动初学者很头疼.本人从事驱动开发时间不长也不短,大概 ...

  8. Windows内核安全与驱动开发

    这篇是计算机中Windows Mobile/Symbian类的优质预售推荐<Windows内核安全与驱动开发>. 编辑推荐 本书适合计算机安全软件从业人员.计算机相关专业院校学生以及有一定 ...

  9. 如何正确入门Windows系统下驱动开发领域?

    [作者]猪头三个人网站 :http://www.x86asm.com/ [序言]很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都以英文 ...

  10. 驱动开发:内核R3与R0内存映射拷贝

    在上一篇博文<驱动开发:内核通过PEB得到进程参数>中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这 ...

随机推荐

  1. odoo 开发入门教程系列-一些用户界面

    一些用户界面 数据文件 (XML) 参考: 该主题关联文档可以查看Data Files. 上一章,我们通过CSV文件添加了数据.当需要添加数据格式简单时,用CSV格式还是很方便的,当数据格式更复杂时( ...

  2. void关键字

    在C++中,void表示为无类型,主要有三个用途: (1)函数的 返回值用void,表示函数没有返回值. void func(int a, int b) { //函数体代码 return; } (2) ...

  3. 基于OpenAI的代码编辑器,有点酷有点强!

    最近随着OpenAI的一系列大动作,把软件领域搅的天翻地覆.各行各业各领域,都出现了大量新产品. 开发工具领域首当其冲,各种新工具层出不穷,今天TJ就给大家推荐一个全新的开发工具:Cursor 从官网 ...

  4. webgl 系列 —— 着色器语言

    其他章节请看: webgl 系列 着色器语言 本篇开始学习着色器语言 -- GLSL全称是 Graphics Library Shader Language (图形库着色器语言) GLSL 是一门独立 ...

  5. 这年头,谁的好友列表还没有躺一个ChatGPT啊?

    你要是说这个,我可不困了 大家好,我最近开始使用一款非常有趣的AI机器人,它叫做ChatGPT.ChatGPT是一款独特的聊天机器人,它可以进行智能对话,回答你的问题,还可以学习你的语言习惯,使得对话 ...

  6. Bean的自动装配(Autowired)

    Bean的自动装配(Autowired) 自动装配是Spring满足bean依赖的一种方式 Spring会在上下文中自动寻找,并自动给bean装配属性 在Spring中有三种自动装配的方式 在xml中 ...

  7. 如何在Solidity中建立DAO(去中心化自治组织)?

    本文将帮助您理解 DAO 的概念,并帮助您构建一个基本的 DAO. 什么是 DAO? 您可以将 DAO 视为基于互联网的实体(比如企业),由其股东(拥有代币和比例投票权的成员)共同拥有和管理.在 DA ...

  8. Java设计模式 —— 组合模式

    11 组合模式 11.1 组合模式概述 Composite Pattern: 组合多个对象形成树形结构以表示具有部分-整体关系的层次结构.组合模式使得客户端可以统一处理单个对象和组合对象. 组合模式关 ...

  9. 【SpringCloud】(三)Hystrix 与 Zuul

    5 Hystrix Hystrix:一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖会不可避免得调用失败,比如超时.异常等,Hystrix能保证在一个依赖出问题的情况下,不会导致整 ...

  10. defineProperty在数据劫持后是如何通知数据的更新和视图的更新的

    vue的双向绑定是由数据劫持结合发布者-订阅者模式实现的,那么什么是数据劫持?vue是如何进行数据劫持的?说白了就是通过Object.defineProperty()来劫持对象属性的setter和ge ...