【驱动开发】Windows过滤平台(WFP,Windows Filtering Platform)
文章目录
- Windows的发展历程
- TDI简介
- WFP简介
- 用户态基础过滤引擎(BFE)
- 内核态过滤引擎(KMFE)
- 垫片(Shim)
- 分层(Layer)
- 子层(Sub Layer)
- 过滤器(Filter)
- 呼出接口(Callout)
- 呼出端口的回调函数(classifyFn、notifyFn、flowDeleteFn)
- 通过WFP API实现网络数据包过滤
Windows的发展历程
正题开始之前,先总结一下Windows的发展历程。
- Windows1.0、2.0、3.0、3.1、3.2:16位。
- Windows9x:包括
Windows 95
、Windows 98
、Windows Me
。 - WindowsNT系列:包括
Windows NT 3.1
、Windows NT 3.1
、Windows NT 4.0
、Windows 2000
、Windows XP
、Windows Server 2003
、Windows Vista
、Windows Server 2008
、Windows 7
、Windows Server 2012
、Windows 8
、Windows 10
,这些版本都源于Windows NT 内核。
比较常见的几个版本的顺序:
Windows 2000
Windows XP
Windows Vista
Windows 7
Windows 8
Windows10
TDI简介
TDI:Transport Driver Interface,传输层接口。TDI在Windows Vista之后就不再支持了,之后的版本中被WFP取代。
socket可以指定某种方式开始传输用户的数据(比如TCP或UDP),这就是传输层。
传输层的特点是:用户只需要关心实际需要传输的用户数据,而不用担心数据实际的发送次数、如何封装、如何确定发送正确性、出错何重发等。
WFP简介
Windows过滤平台(Windows Filter Platform),是从Vista系统后新增的一套系统API和服务,为网络数据包过滤提供架构支撑。
WFP框架是分层结构,可以在不同分层中进行过滤、重定向、修改网络数据包。
通过WFP框架,开发者可以在其上轻松实现防火墙、入侵检测系统、网络监视程序以及流量控制程序等。
WFP框架包含用户态API和内核态API。它只是一个提供各层网络数据包过滤的框架,自身并不是防火墙,也不存在任何过滤逻辑。
WFP框架主要分为两大层次模块:
用户态基础过滤引擎(BFE)
基础过滤引擎:BFE,Base Filtering Enging。
BFE
对上提供C管理API
以及RPC接口
,封装在fwpuclnt.dll
中,供用户态程序调用;对下和KMFE
交互,受其控制。
内核态过滤引擎(KMFE)
内核态过滤引擎:KMFE,Kernel Mode Filtering Enging,是整个WFP框架的核心。
其内部被划分为多个分层,不同分层代表着网络协议栈特定的层,在每个分层中可以存在子层和过滤器。KMFE
负责检查网络数据包是否命中过滤器的规则(Rule),对于命中的过滤器,会执行这些过滤器指定的动作(Action)。
问题一:网络数据包从哪来?
答:KMFE
需要和系统网络协议栈(如TCP/IP协议栈)交互,通过一种称为垫片(Shim)的内核模块从网络协议栈中获取网络数据。
垫片被插入到网络协议栈各个层中,包括:
- 数据流分层垫片:对应
流/报文 数据分层(IPv4/IPv6)
- ALE网络连接管理:对应
发送/接收 ALE分层(IPv4/IPv6)
- 传输分层垫片(TCP/UDP):对应
发送/接收 传输分层(IPv4/IPv6)
- 网络分层垫片(IPv4/IPv6):对应
发送/接收 IP分层(IPv4/IPv6)
不同层次的垫片获取到的网络数据不同,垫片获取到数据后,通过KMFE
提供的Classify API
,把数据传送到WFP的相应分层(Layer
)中。
垫片(Shim)
垫片是一种特殊的内核模块,被安插在系统网络协议栈(如TCP/IP协议栈)的不同层(栈)中,主要作用有:
- 获取网络协议栈的数据。
被安插在传输层,可以获取TCP/UDP等协议数据;
被安插在网络层,可以获取IP协议等数据。
Shim获取到数据后,会通过KMFE提供的分类API,把数据传递到相应的WFP分层中。 - 把KMFE的过滤结果反馈给网络协议栈。
比如KMFE需要拦截某一类网络数据,通过分类API把过滤结果通知垫片,由于垫片被安插在网络协议栈中,所以垫片可以在网络协议栈中直接拦截网络数据包。
总结:垫片负责WFP的数据来源以及执行数据拦截/放行的最终动作,属于网络协议栈和WFP框架之间的通信桥梁。但垫片对开发者来说是透明的,无须过多关注。
分层(Layer)
KMFE内部被划分成不同的分层,每一个分层代表了系统网络协议栈的一个特定的层,接收待定的数据。
分层是一个容器,里面包含了零个或多个过滤器
,此外还可能包含一个或多个子层
。
分层标识:
每个分层都有一个唯一的值来标识,在内核态,使用64位的LUID来标识一个子层;在用户态,使用128位GUID来标识一个分层。
这两种分层标识存在部分对应关系,一般来说,
把内核态用到的分层标识称为运行时过滤分层标识
;
把用户态使用到的分层标识称为管理过滤分层标识
。
常见的运行时过滤分层标识:.
FWPM_LAYER_ALE_AUTH_CONNECT_V4:连接IPv4
FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4:接收IPv4
FWPM_LAYER_ALE_AUTH_LISTEN_V4:监听IPv4
常见的管理过滤分层标识:
FWPM_LAYER_INBOUND_IPPACKET_V4:接收IPv4网络数据包层
FWPM_LAYER_INBOUND_IPPACKET_V6:接收IPv6网络数据包层
FWPM_LAYER_OUTBOUND_IPPACKET_V4:发送IPv4网络数据包层
FWPM_LAYER_OUTBOUND_IPPACKET_V6:发送IPv6网络数据包层
子层(Sub Layer)
子层是分层内更小的一个划分,一个分层可能被划分成多个子层,并且这种划分是由开发者控制的。
划分子层时需要给新子层分配一个权重(Weight),权重的值越大
,表明优先级越高
。
当有相应的网络数据到达分层时,WFP会按照分层内的子层优先级顺序传递网络数据,子层的权重值越大,越早获取到数据。
typedef struct FWPM_SUBLAYER0_ {GUID subLayerKey; // 子层标识FWPM_DISPLAY_DATA0 displayData; // 显示数据UINT32 flags; // 特性,如FWPM_SUBLAYER_FLAG_PERSISTENTGUID *providerKey;FWP_BYTE_BLOB providerData;UINT16 weight; // 权重,值越大,优先级越高
} FWPM_SUBLAYER0;
typedef struct FWPM_DISPLAY_DATA0_ {WCHAR* name; // 对象名字WCHAR* description; // 对象描述
}FWPM_DISPLAY_DATA0;
过滤器(Filter)
过滤器存在于WFP的分层中,WFP内置了一部分过滤器供开发者使用,开发者也可以添加自己的过滤器。
过滤器里保存了网络数据包的拦截规则(Rule)和处理动作(Action),Rule指明了需要过滤哪些网络数据包,当Rule被命中时,就会执行指定的Action。
一般来说,过滤器中的动作会表明是放行(Permit)还是拦截(Block)网络数据包。在实际情况中,KMFE的分层中可能会存在多个子层以及多个过滤器,对于一次网络事件而言,可能同时命中多个过滤器的规则,而这些命中规则的过滤器可能指定了不同的过滤动作,为了计算出最终的过滤动作,WFP引入了过滤仲裁器(Fileter Arbitration)模块,过滤仲裁器计算出最终的过滤动作后交给KMFE,KMFE最终将过滤结果反馈给垫片。
过滤器可以关联分层和子层,以及呼出接口。
在需要对网络数据包进行复杂的分析和处理的情况下,过滤器一般需要关联一个呼出接口,当过滤器的规则被命中时,WFP会直接执行与改过滤器关联的呼出接口内的回调函数。
typedef struct FWPM_FILTER0_ {GUID filterKey; // 过滤器标识,若FwpmFilterAdd0中初始化为0,则BFE将生成一个FWPM_DISPLAY_DATA0 displayData; // 显示数据UINT32 flags; // 特性GUID *providerKey;FWP_BYTE_BLOB providerData;GUID layerKey; // 过滤器所在的分层标识GUID subLayerKey; // 过滤器所在的子层标识FWP_VALUE0 weight; // 权重UINT32 numFilterConditions; // 过滤条件数FWPM_FILTER_CONDITION0 *filterCondition; // 过滤条件FWPM_ACTION0 action; // 操作union {UINT64 rawContext;GUID providerContextKey;};GUID *reserved;UINT64 filterId;FWP_VALUE0 effectiveWeight;
} FWPM_FILTER0;typedef struct FWPM_ACTION0_ {FWP_ACTION_TYPE type; // 操作类型,如FWP_ACTION_BLOCK(阻止流量)、FWP_ACTION_PERMIT(允许流量)union {GUID filterType; // 过滤类型GUID calloutKey; // 呼出接口标识};
} FWPM_ACTION0;
呼出接口(Callout)
呼出接口由一系列的回调函数组成,当网络数据包命中某过滤器的规则且该过滤器关联了呼出接口时,就会调用该呼出接口的回调函数。
Callout除了包含回调函数外,还包含一个GUID值,用来唯一地标识一个呼出接口。
一般来说,不同的呼出接口的回调函数实现不同的功能,系统内置了一部分呼出接口可以供开发者使用,开发者也可以向系统注册自己的呼出接口来完成特定的逻辑。
typedef struct FWPS_CALLOUT1_ {GUID calloutKey; // 呼出接口标识UINT32 flags; // 特性,可设为0FWPS_CALLOUT_CLASSIFY_FN1 classifyFn;FWPS_CALLOUT_NOTIFY_FN1 nptifyFn;FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN0 flowDeleteFn;
}FWPS_CALLOUT1
呼出端口的回调函数(classifyFn、notifyFn、flowDeleteFn)
- classifyFn:过滤器的规则被命中时被调用,可以在回调函数中获取网络数据包的相关信息,具体信息取决于过滤器所在的分层,还可以对网络数据包设置允许/拦截操作,。
- notifyFn:过滤器被添加到过滤引擎中或者从过滤引擎中移除时被调用。
- flowDeleteFn:当一个网络数据流要被终止时被调用。
void FwpsCalloutClassifyFn1([in] const FWPS_INCOMING_VALUES0 *inFixedValues, // 被过滤层中每个数据字段的值[in] const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, // 被过滤层中每个元数据字段的值[in, out] void *layerData, // 指向被过滤层中原始数据的结构指针,可能为NULL[in, optional] const void *classifyContext, // 可选,表示和呼出接口驱动关联的上下文[in] const FWPS_FILTER1 *filter, // 过滤器指针[in] UINT64 flowContext, // 和流句柄关联的上下文[in, out] FWPS_CLASSIFY_OUT0 *classifyOut // 该函数对该网络数据包的过滤结果
);
typedef struct FWPS_CLASSIFY_OUT0_ {FWP_ACTION_TYPE actionType; // 操作类型UINT64 outContext;UINT64 filterId;UINT32 rights;UINT32 flags;UINT32 reserved;
} FWPS_CLASSIFY_OUT0;NTSTATUS FwpsCalloutNotifyFn1([in] FWPS_CALLOUT_NOTIFY_TYPE notifyType, // 通知类型, FWPS_CALLOUT_NOTIFY_ADD_FILTER / FWPS_CALLOUT_NOTIFY_DELETE_FILTER / FWPS_CALLOUT_NOTIFY_TYPE_MAX[in] const GUID *filterKey, // 过滤器标识[in] FWPS_FILTER1 *filter // 过滤器指针
);
void FwpsCalloutFlowDeleteNotifyFn0([in] UINT16 layerId, // 分层标识[in] UINT32 calloutId, // 呼出接口ID[in] UINT64 flowContext // 关联的上下文指针
);
通过WFP API实现网络数据包过滤
- 调用
FwpmEngineOpen0
打开过滤引擎的会话,获得引擎句柄;使用结束后调用FwpmEngineClose0
关闭。 - 调用
FwpmTransactionBegin0
在当前会话中开始事务;使用结束后使用pfnFwpmTransactionAbort0
终止事务。 - 定义一个或多个呼出接口(自实现回调函数),然后调用用
FwpsCalloutRegister0
向过滤引擎注册呼出接口;使用结束后调用FwpsCalloutUnregisterById0
或FwpsCalloutUnregisterByKey0
卸载。 - 调用
FwpmCalloutAdd0
向过滤引擎中添加呼出接口;使用结束后调用FwpmCalloutDeleteById0
移除。 - 初始化过滤器并关联呼出接口,调用
FwpmFilterAdd0
向过滤引擎中添加过滤器;移除过滤器使用FwpmFilterDeleteById0
或FwpmFilterDeleteByKey0
。 - 调用
FwpmTransactionCommit0
在当前会话中提交当前事务。 - 关闭句柄以及终止会话事务。
【驱动开发】Windows过滤平台(WFP,Windows Filtering Platform)相关推荐
- Windows 筛选平台 (WFP)
原文路径::http://msdn.microsoft.com/zh-cn/library/windows/hardware/gg463267.aspx 引用于微软官方 Windows 筛选平台 (W ...
- 【驱动开发】WinDbg 双机调试 Windows XP
起序:最近学习驱动开发,环境搭建,记录一下. 一.软件环境 宿主机:Windows 10 虚拟机:VMware Workstation 16.1.1 Pro 系统:windows_xp_profess ...
- Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platfrom))
catalog 0.引言 1.Windows 2000网络结构和OSI模型 2.NDIS驱动 3.NDIS微端口驱动编程实例 4.NDIS中间层驱动编程实例 5.TDI驱动 6.TDI驱动 7.TDI ...
- Windows内核安全与驱动开发
这篇是计算机中Windows Mobile/Symbian类的优质预售推荐<Windows内核安全与驱动开发>. 编辑推荐 本书适合计算机安全软件从业人员.计算机相关专业院校学生以及有一定 ...
- C++开发方向之windows驱动开发
1.为什么要写这篇文章? 最近浏览招聘网站看到关于windows驱动开发的岗位,前几天一个C++客户端工作岗位,猎头也问我是否有了解windows内核. 所以,调研了一下C++的开发方向:window ...
- 如何正确入门Windows系统下驱动开发领域?
[作者] 猪头三 作者网站: http://www.x86asm.com 原文链接: http://blog.csdn.net/Code_GodFather/...0/5975901.aspx [贡献 ...
- windows驱动开发学习
序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资 料少有关系.大多学的驱动开发资料都以英文为主,这样让很多驱动初学者很头疼.本人从 事驱动开发时间不长也不短, ...
- Windows Server 2012 +WDK7600.16385.1+VS2010驱动开发环境搭建
本帖通过Augusdi的一篇博文进行重新总结 第一步:安装Visual stdio 2010 1.安装VS2010 第二步:安装WDK安装包 2.安装WindowsDriverKit7-GRMWDK_ ...
- windows驱动开发推荐书籍
[作者] 猪头三 个人网站 :http://www.x86asm.com/ [序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资 料少有关系.大多学的驱动开 ...
最新文章
- OpenCV畸变校正原理以及损失有效像素原理分析
- Windows7在Notepad++中配置Python+OpenCV
- 95E Lucky Country
- 是否finally块总是用Java执行?
- boost::math::daubechies_wavelet用法的测试程序
- CSS基础(part15)--元素的隐藏与显示
- php异步处理下载文件,php异步处理-上传文件
- 三星电视与计算机连接网络设置,三星电视怎么连接网络看电视?
- java 类加载的过程
- VC++ 6.0如何创建与调用动态链接库
- 破解寝室安装老毛子路由器校园网电信闪讯锐捷认证
- 计算机系统关机后自动重启,小白教你电脑关机后自动重启是什么原因
- SignalR 循序渐进
- 想知道如何图片转文字?这几个方法你别错过
- 关于修复mp4文件损坏的过程小记
- diffusion medical image segmentation
- 为什么要学习人工智能,人工智能热招的岗位有哪些,工资和学历有哪些关系?
- mysql fk_mysql 常用操作(错误)
- 【目标检测】一些数据集处理常用代码
- Linux如何识别U盘