WFP防火墙的作用
WFP防火墙是在Vista/Win2008之后的操作系统中才引入的一套流量管控平台。提供一套API接口供开发人员调用。应用层和驱动层都有不同的模块可以调用。用于取代之前的LSP过滤器、TDI过滤器、NDIS过滤器等。通过WFP API,可以实现防火墙、网络管控工具等流量管控服务。
使用WFP防火墙时注意的事项
- WFP防火墙仅在Vista/Win 2008以后的系统中方可生效。
- 在Vista/Win 2008中,过滤器的添加不支持"或"策略。如果需求中包含对个IP段、多个端口号、多个协议或多个进程同时存在时,在Vista/Win2008中,需要依次进行较为复杂的交叉匹配,依次添加。这一点需要注意。只有在Win7/Win2008 R2系统中,WFP防火墙才能直接添加策略运行。
基本概念以及决策逻辑
- Layer(层):层是过滤器的容器,其功能是将过滤器组织成为集合,由层ID决定过滤器生效的位置。每一个层里面都包含一套内置的子层,用户可以添加自定义的子层。
- SubLayer(子层):过滤器的容器,在对应的层中生效。
- Filter(过滤器):过滤器是对出/入数据包进行匹配的规则。过滤器负责告诉过滤引擎如何处理数据包。添加过滤器时,使用FWPM_FILTER0结构体中的flags参数,可以指定过滤器的生效时间。
Layer(层)的过滤决策策略:
- 按照权重由高到低的顺序对每个子层进行决策匹配
- 即使有更高优先级的子层决定阻塞流量,下方的子层也会依次得到评估的机会。所以,每个子层都可以匹配到通过当前层的所有数据包
- 如果有一个子层明确返回阻断,当前层的决策即为阻断。(这里只谈应用层的决策策略,在Callout驱动层中,返回阻断,依然会被其它策略所修改,放在以后讨论。)
SubLayer(子层)的过滤决策策略:
- 按照权重从高到底的顺序对每个过滤器进行匹配
- 在匹配的过程中,如果有过滤器明确返回了"Permit"或"Block",则马上中断匹配,权重更低的过滤器不会收到数据包的信息。如果所有过滤器都返回"Continue",则会一直匹配到列表耗尽。
- 最后一个执行匹配的过滤器返回值,即为当前子层最终的过滤决策。
常用的随手便签
netsh wfp show state
该命令可以打印系统中当前存活的WFP防火墙层、子层、过滤器等。是在调试的过程中最实用的命令,也许没有之一。
调用流程
打开与关闭WFP防火墙
打开防火墙句柄
DWORD FwpmEngineOpen0(
const wchar_t *serverName,
UINT32 authnService,
SEC_WINNT_AUTH_IDENTITY_W *authIdentity,
const FWPM_SESSION0 *session,
HANDLE *engineHandle
);
- 输入参数
- serverName:必须为空。
- authnService:身份验证方式,可选为RPC_C_AUTHN_WINNT和RPC_C_AUTHN_DEFAULT。
- authIdentity:身份验证和授权凭证,可以为NULL。
- session:会话指针,不使用的话可以为NULL。
- engineHandle:用于输出防火墙引擎句柄,其它对防火墙操作的函数大都依赖此句柄。
- 输出参数
- ERROR_SUCCESS为成功,其它参考错误码
关闭防火墙句柄
DWORD FwpmEngineClose0(
HANDLE engineHandle
);
- 输入参数
- engineHandle:防火墙引擎句柄,由FwpmEngineOpen0参数返回
- 输出参数
- ERROR_SUCCESS为成功,其它参考错误码
枚举当前所有层
创建层枚举句柄
DWORD FwpmLayerCreateEnumHandle0(
HANDLE engineHandle,
const FWPM_LAYER_ENUM_TEMPLATE0 *enumTemplate,
HANDLE *enumHandle
);
- 输入参数
- engineHandle:引擎句柄,由FwpmEngineOpen0创建。
- enumTemplate:枚举参数,实际上内容保留使用,可以为NULL。
- enumHandle:输出参数,用于传出枚举句柄
- 输出参数
- ERROR_SUCCESS为成功,其它参考错误码
执行枚举操作
DWORD FwpmLayerEnum0(
HANDLE engineHandle,
HANDLE enumHandle,
UINT32 numEntriesRequested,
FWPM_LAYER0 ***entries,
UINT32 *numEntriesReturned
);
- 输入参数
- engineHandle:引擎句柄
- enumHandle:枚举句柄,FwpmLayerCreateEnumHandle0创建
- numEntriesRequested:请求的数目
- entries:用于返回的层参数数组,需要FwpmFreeMemory0释放(这里需要注意,该参数定义为:FWPM_LAYER0** p=NULL,释放使用FwpmFreeMemory0((void**)&p))
- numEntriesReturned:本次返回的结构体数目
- 输出参数
- ERROR_SUCCESS为成功
销毁层枚举句柄
DWORD FwpmLayerDestroyEnumHandle0(
HANDLE engineHandle,
HANDLE enumHandle
);
- 输入参数
- engineHandle:引擎句柄,由FwpmEngineOpen0创建
- enumHandle:枚举句柄,由FwpmLayerCreateEnumHandle0创建
- 输出参数
- ERROR_SUCCESS为成功,其它参考错误码
参考代码
#define SESSION_LAYER L"session_layer";
GUID SESSION_GUID = {
0x2ec66da9, 0x2ae4, 0x4f83, {
0x81, 0xc7, 0x34, 0x5d, 0x19,
0x6a, 0xe3, 0xd7 } };
int main()
{
HANDLE engineHandle = NULL;
DWORD result = ERROR_SUCCESS;
FWPM_SESSION0 fwpmSession;
memset(&fwpmSession, 0, sizeof(FWPM_SESSION0));
fwpmSession.flags = FWPM_SESSION_FLAG_DYNAMIC;
result = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, &fwpmSession,
&engineHandle);
if (result != ERROR_SUCCESS)
{
printf("[%s]FwpmEngineOpen0 failed;Return value:%d.\n", __FUNCTION__,
result);
return 0;
}
HANDLE enumHandle = NULL;
result = FwpmLayerCreateEnumHandle0(engineHandle, NULL, &enumHandle);
if (result != ERROR_SUCCESS)
{
printf("[%s]FwpmFilterCreateEnumHandle0, Return value:%d\n", __FUNCTION__,
result);
FwpmEngineClose0(engineHandle);
return 0;
}
UINT32 numEntriesReturned = 0;
do
{
FWPM_LAYER0** fwpmLayerList = NULL;
numEntriesReturned = 0;
result = FwpmLayerEnum0(engineHandle, enumHandle, 1, &fwpmLayerList,
&numEntriesReturned);
if (result != ERROR_SUCCESS || numEntriesReturned == 0)
{
break;
}
std::string strLayerName = boost::locale::conv::from_utf(fwpmLayerList[0]->displayData.name, "GBK");
printf("layer name:%s\n"