wfp网络过滤框架总结(一)

By feivirus 2013-11-24

 

一。基本术语定义

callout 为扩展wfp性能提供的一个功能,由一系列call function和一个guid key组成,wfp内置了几个callouts。用户可以通过callout drivers自己添加callout。

callout driver 实现一个或者多个callouts 的内核驱动,这个驱动通过向filter engine注册callouts,来通知filter engine当计算机处理网络连接或者网络包

时回调对应的callout function

callout function 由callout driver 实现的定义一个callout的一个函数。一个callout由三种函数定义组成,nofityFn处理连接或者包的通知,类似一个网络事件。

classifyFn处理一次分类决策,即拒绝还是放行数据包的操作。flowDeleteFn处理流的删除,这个函数是可选的。

filter 为TCP/IP网络数据定义了一些过滤条件,以及当所有的条件都成立时采取的放行还是拒绝的决策行为。如果一个filter要求对网络数据包做另外的处理时,它可以

指定一个callout完成操作。如果这个filter的所有条件都成立时,filter engine会把网络数据转发给特定的callout做另外的处理。

通过FwpmFilterAdd添加一个filter,filter中指定对应的callout和filterCondition.

filter engine wfp平台的一个组件,用于存储filter和完成filter的过滤决定。filters在指定的filtering layers中添加到filter engine中,从而filter engine完成对过滤

      的拒绝,放行或者交给新的callout处理。如果一个filter在filter engine中指定新的callout来处理,则filter engine会调用这个callout的calssifyFn来处理。

filtering layer TCP/IP网络栈中的一个层,filter engine匹配对应的网络数据找到合适的filters集合,交给这个层处理。每一层由一个filter layer identifier唯一标示。

Run-time Filtering Layer Identifiers (FWPS_XXX)用于内核层的callout驱动。Management Filtering Layer Identifiers (FWPM_XXX)和 Base Filtering Engine (BFE)通信,既可以用于应用层也可以用于内核层。FWPM过滤由128的guid标示,FWPS由64为的luid标示。FWPS的位数少,提供了在实际网络流量中内核驱动处理时

整数比较的性能。

过程基本上是注册callout,注册filter->在filtering layer监控到事件发送,提交给对于的filter,filter通过filter.action.calloutKey 找到对应的callout,提交给callout的nofityFn或者ClassifyFn处理。

二。wfp架构

applications

|

network stack --filter engine-->各种callout

|

network hardware

在tcp/ip栈中的关键点处都有filtering layer,在layer中网络数据被交给filter engine处理。filter engine中的filters可以指定一个callout来完成对应过滤条件的丢弃或者

放行,交给新的callout的过滤操作。

 

三。callout driver可以完成的过滤操作

1.初始化callout driver

在DriverEntry函数中完成callout driver的初始化操作。初始化需要三步操作,指定一个Unload函数,创建一个设备对象,向filter engine注册callouts。

前两部和以前的驱动开发一样,wdm驱动中通过IoCreateDevice,在wdf驱动中通过WdfDeviceCreate创建设备对象。即使对应的filter engine当前没有运行,

第三部向这个filter engine注册callout也是可以的。callout driver通过FwpsCalloutRegister0函数注册一个callout.函数的参数是设备对象和一个FWPS_CALLOUT0 

类型的callout结构,结构中包含这个callou的ClassifyFn,NotifyFn,FlowDeleteFn函数指针。这三个函数指针需要提前声明。一个callout驱动可以实现多个callout,

这个驱动通过FwpsCalloutRegister0函数每次向filter engine注册一个callout。

2.处理notify callouts

filter engine调用callout的notifyFn函数来通知callout驱动对应的网络事件的发生。主要是filter的添加,删除事件。

添加filter:当添加到filter engine的filter指定了一个callout执行filter的操作时,filter engine会回调callout的notifyFn函数,并且在notifyType参数中传递

FWPS_CALLOUT_NOTIFY_ADD_FILTER。如果指定了callout处理操作的filters已经添加到了filter engine中,对于这种已存在的filters,callout再通过这个filter注册callout时,filter engine不会调用这个callout 的notifyFn。filter engine只会在向指定的callout添加新的filter时调用notifyFn函数。这种情况下,filter engine不一定会回调这个callout

的每一个filter。

filter 删除:当添加到filter engine中的filter被删除时,对于callout的notifyFn函数会被调用,并且传递一个FWPS_CALLOUT_NOTIFY_DELETE_FILTER参数,filterKey参数为NULL。

当callout的notifyFn函数不识别接收到的NotifyType参数时,应该忽略而且返回STATUS_SUCCESS。

当filter添加到filter engine时,callout驱动可以为filter指定一个context。这个callout的classifyFn函数可以使用这个context保存一些状态信息,当下次filter engine回调它时使用。当filter被删除时,callout驱动完成context的清理。

3.处理classify callouts

当有网络数据需要被某个callout处理时,filter engine会回调它的classify函数。这种情况发生在当指定的callout注册的某个filter的过滤条件全部满足时。如果这个filter没有

过滤条件,filter engine会调用对应的callout的classifyFn函数。

canctiwall简易wfp防火墙中通过RegisterCalloutForLayer注册了accept和connect的ALE层事件处理网络数据的连接和接收,匹配对应的规则。

过滤引擎传输几种不同的数据项到callout的classifyFn函数。这些数据项包括fixed data values,metadata values,原始网络数据,过滤信息和流的context。

特定的传输数据依赖于指定的filtering lawyer和classsifyFn的条件。

下面列举一些callout可以完成的典型操作。

(1)使用callout做深度检查

当callout要做深度检查时,它的classsifyFn函数可以对数据的任意fixed data fields,metadata fields,原始包数据或者相关数据

做组合检查。

处理过程如下:

VOID NTAPI

 ClassifyFn(

    IN const FWPS_INCOMING_VALUES0  *inFixedValues,

    IN const FWPS_INCOMING_METADATA_VALUES0  *inMetaValues,

    IN OUT VOID  *layerData,

    IN const FWPS_FILTER0  *filter,

    IN UINT64  flowContext,

    OUT FWPS_CLASSIFY_OUT  *classifyOut

    )

{

  PNET_BUFFER_LIST rawData;

  ... 

   //检查是否有FWPS_RIGHT_ACTION_WRITE 标志,该标志指示了本callout是否有权限修改过滤action

   //如果这个标志没有设置,callout仍然可以返回block为了否决被前一个filter返回的permit行为。

 if (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))

  {

    // 不指定action直接返回

 return;

  }

 

  //获取 inFixedValues数据

  ...

 

  // 获取 metadata 字段从 inMetaValues中

  ...

 

  //得到原始数据的指针

 rawData = (PNET_BUFFER_LIST)layerData;

 

  // Get any filter context data from filter->context

  ...

 

  // Get any flow context data from flowContext

  ...

 

  //检查数据来源用以决定对数据的action

 

  ...

 

  // 如果允许数据通过

 if (...) {

   

 classifyOut->actionType = FWP_ACTION_PERMIT;

 

    //检查是否要清除FWPS_RIGHT_ACTION_WRITE 标志

 if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)

    {

       // 清除FWPS_RIGHT_ACTION_WRITE 标志

 classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;

    }

 

 return;

  }

 

  ...

 

  // 如果拒绝数据通过

 if (...) {

 

    // 设置拒绝数据

 classifyOut->actionType = FWP_ACTION_BLOCK;

 

    // 清除 FWPS_RIGHT_ACTION_WRITE 标志位

 classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;

 

 return;

  }

 

  ...

 

  //如果允许或者拒绝行为需要继续向在filter engine后面的filter传递

 if (...) {

 

    // 设置继续向下一个filter传递的标志

 classifyOut->actionType = FWP_ACTION_CONTINUE;

 

 return;

  }

 

  ...

}

在 filter->action.type中指定callout返回的action,如果需要了解action的具体有哪些值,可以看msdn中FWPS_ACTION0_ 结构的定义。

如果一个callout必须完成对数据包的额外处理后才可以决定action,它可以先pend数据包直到处理完成。如果需要了解如何pend数据包,可以看

msdn中Types of Callouts 和 FwpsPendOperation0的定义。

在一些filtering layer中,filter engine传给callout的classifyFn的原始数据指针layerData可能为NULL。

(2)做流数据的深度检查

类似上面,区别在

//获取流数据io packet的指针

 ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET0 *)layerData;

  // 得到数据流指针

 dataStream = ioPacket->dataStream;

如果需要更多的流数据做action决定时

 if (...) {

    // 通知filter engine需要多少个字节

 ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA;

 ioPacket->countBytesRequired = bytesRequired;

 ioPacket->countBytesEnforced = 0;

//通知filter engine 传递给下一个filter继续处理

 classifyOut->actionType = FWP_ACTION_CONTINUE;

 return;

  }

(3)检查包和流数据

 这部分包括下面几个点:

 (3.1)包的检查点

    进来的数据包被指定按下面的顺序提交给本机,依次向上遍历wfp layer。

    a. IP Packet(network layer)网络层

所有的ip包,包括ip包分片,在这层做检查。但是,如果包是IPsec-protected,对包内容的深度检查和修改这层不能做因为包还没有生效或者解密

    b. Transport Layer 传输层

       所有排列好的或者重组的包都可以在这层检查。IPsec-protected包已经生活或者解密

    c.  Application Layer Enforcement (ALE) Receive or Accept应用层加强层接收包或者连接

到达本地端点的第一个包被提交到这层。例如,一个将要到达的tcp的syn段或者一次udp流的第一个udp消息被提交。一个被要求重新授权

的包,比如防火墙策略改变了,也被提交到这层,此时,ALE的reauthorization标志将会被设置。

    d. datagram 数据或者流

;

    a. ALE connect

Tcp连接请求(在syn产生之前)和第一个udp消息在这层提交发送给远程端点

    b.datagram data or stream 

    c.transport and icmp error

    d.ip packet

    如果一个目的地址是本地的包被修改为非本地地址,它应该被注入到forwarding layer.

 (3.2)wfp layer 要求和限制

    a.forwarding layer

     ip包的转发功能可以通过netsh interface ipv4 set interface开启。NET_BUFFER_LIST链表结构可以用来提交ip包的在路由

     上的组装(group)功能,这个不同于在目的地址的报的重组(reassembly).当一个包组装提交到这层时,FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP标志位

     会被传送到classifyFn函数中。此时NET_BUFFER_LIST结构指示了一个包分片的第一个节点。

     一个转发的注入包能被再次提交给callout驱动。为了阻止死循环,驱动先调用FwpsQueryPacketInjectionState0函数在调用classifyFn之前。

    驱动应该允许包,把包状态FWPS_PACKET_INJECTION_STATE 设置为FWPS_PACKET_INJECTED_BY_SELF或者FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF 

    来使包不被改变。

    b.netwok layer

    c.transport layer and ale

 (3.3)包的提交格式

     net buffer list可以描述整个ip包,对于不同的layers, wfp提交一个从ip头开始的不同的偏移,例如对于接收包的网络层,net buffer list

从ip头部开始。对于传输层,net buffer list从传输层头部开始。

     对于发送的包,在net buffer list包的一些被发送时,callout driver必须按下面步骤做:

     复制而且block整个net buffer list。建立一个新的net buffer list来描述原来list的子集,把新的list注入到发送路径上去。

 (3.4)callout 的类型

Inline Inspection Callout,这种类型返回FWP_ACTION_CONTINUE值。它不修改任何网络包,比如只是做网络流量的统计监控。这种的 FWPS_ACTION0 结构

的Type成员应该设置FWP_ACTION_CALLOUT_INSPECTION。

        Out-of-band Inspection Callout,Inline Modification Callout,Out-of-band Modification Callout

        Redirection Callout

为了和其他callout合作完成包的检查,修改和连接重定向,在包被pended之前,callout必须丢弃原始的包通过清除FWPS_CLASSIFY_OUT0 结构中的FWPS_RIGHT_ACTION_WRITE标志位在

classifyFn函数返回前。如果FWPS_RIGHT_ACTION_WRITE位被设置,对于classifyFn来说,意味着包可以被pended然后重注入或修改。此时,callout不能pend这次提交,不能改变action

类型。它必须等待更高weight的callout注入这个包或者修改。FwpsPendOperation0 函数用于pend一个包。

 (3.5)包注入函数

      callout驱动可以利用FwpsInjectForwardAsync0等函数注入pended或者修改一个包。

 (3.6)包修改的例子

 (3.7)流的检查

(4)修改流数据

 在callout驱动注入流数据之前,需要先创建一个注入句柄.关于如何修改流数据,可以看wfp中的Windows Filtering Platform Stream Edit Sample示例源码。

(5)数据日志记录

 callout的classifyFn可以记录的数据有data fields,metadata fields,原始网络数据和存储在context中的相关数据。例如,如果callout在网络层通过filter日志记录接收的ipv4数据包中

有多少被丢弃了,callout可以在FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD层向filter engine添加一个filter。classifyFn函数如下:

VOID NTAPI

 ClassifyFn()

{

  //增加所有丢弃包的统计数量

 InterlockedIncrement(&TotalDiscardCount);

  // 检查丢弃原因字段metadata 是否存在

 if (FWPS_IS_METADATA_FIELD_PRESENT(

 inMetaValues,

        FWPS_METADATA_FIELD_DISCARD_REASON))

  {

    // 检查是不是普通的丢包

 if (inMetaValues->discardMetadata.discardModule ==

        FWPS_DISCARD_MODULE_GENERAL)

    {

      // 检查是不是因为filter丢包

 if (inMetaValues->discardMetadata.discardReason ==

          FWPS_DISCARD_FIREWALL_POLICY)

      {

        // 增加因为filter丢包的统计数量

 InterlockedIncrement(&FilterDiscardCount);

      }

    }

  }

  // 对包的action不做改变

 classifyOut->actionType = FWP_ACTION_CONTINUE;

}

(6)为一个数据流关联一个cotext

对于支持数据流的callou在filtering layer处理数据时,callout driver可以和流关联一个context。这个context对filter engine是不透明的。callout的classify函数可以使用这个context为指定的

数据流存储状态信息,以便于filter engine下次调用callout时使用。filter engine通过flowContext传递给callout,如果没有关联context,则参数为0.

callout使用函数 FwpsFlowAssociateContext0 关联一个context。例如

// Context structure to be associated with data flows

typedef struct FLOW_CONTEXT_ {

  .

  .  // Driver-specific content

  .

} FLOW_CONTEXT, *PFLOW_CONTEXT;

#define FLOW_CONTEXT_POOL_TAG 'fcpt'

VOID NTAPI

 ClassifyFn()

{

  PFLOW_CONTEXT context;

  UINT64 flowHandle;

  NTSTATUS status;

  ...

  // 在metadata中检查流的句柄

 if (FWPS_IS_METADATA_FIELD_PRESENT(

 inMetaValues,

        FWPS_METADATA_FIELD_FLOW_HANDLE))

  {

    //获取流句柄

 flowHandle = inMetaValues->flowHandle;

    // 分配一个context结构

 context =

      (PFLOW_CONTEXT)ExAllocatePoolWithTag(

 NonPagedPool,

 sizeof(FLOW_CONTEXT),

        FLOW_CONTEXT_POOL_TAG

        );

 

    //检查是否分配失败

 if (context == NULL) {}

 else

    {

      //初始化context结构

      ...

      // 和数据流关联context

 status = FwpsFlowAssociateContext0(

 flowHandle,

        FWPS_LAYER_INBOUND_IPPACKET_V4,

 calloutId,

        (UINT64)context

        );

 

      //检查关联结果

 if (status != STATUS_SUCCESS)

      {

        // Handle error

        ...

      }

    }

  }

  ...

}

可以通过函数 FwpsFlowRemoveContext0 解除关联。

(7)处理鉴别异步数据

wfp callout驱动可以通过在classifyFn函数中返回 FWP_ACTION_PERMIT, FWP_ACTION_CONTINUE, or FWP_ACTION_BLOCK授权或者拒绝一个网络行为,或者放行,丢弃一个网络包。

很多情况下,callout驱动不能在classifyFn函数立即返回检查结果,必须异步的等待一些相关数据的处理。比如classifiable fields,metadata,packets可能被转发给

别的组件处理,或者应用层返回判断结果。

wfp对不同的层的异步处理机制不同,一般的处理方式如下:

a.异步的ALE层鉴别

callout驱动在classifyFn中调用FwpsPendOperation0异步处理。异步操作完成后调用FwpsCompleteOperation0函数结束。

b.异步的packet鉴别

callout驱动应该在classify函数中返回FWP_ACTION_BLOCK并设置FWPS_CLASSIFY_OUT_FLAG_ABSORB标志。网络包应该被引用或者复制。异步的操作可以是重新注入复制或者修改后的

包,或者丢弃包。

c.异步的包含packet的ALE鉴别

包含上述两种操作。

需要考虑的特殊情况如下:

a. ALE connect与Receive/Accept 层

   当在ALE connect层(FWPS_LAYER_ALE_AUTH_CONNECT_V4 or FWPS_LAYER_ALE_AUTH_CONNECT_V6)完成了一个pended的classify操作时,FwpsCompleteOperation0函数被调用。

此时,ALE的reauthorization的classify行为被触发在各自的ALE连接层。callout驱动应该从这种reauthorization行为中返回一个检查的决定。可以通过FWP_CONDITION_FLAG_IS_REAUTHORIZE

标志看是不是reauthoriaztion行为。

   callout驱动必须为每一个pended的ALE_AUTH_CONNECTclassify行为维持一个唯一的状态,以便于在 FwpsCompleteOperation0触发的reauthoriaztion期间对每一个classify行为

的classify决定都可以被查询。如果在pended的ALE_AUTH_CONNECTclassify行为期间,reauthoriaztion发生后包被引用或者复制时他们可以被重注入。

   当在ALE的Receive/Accept((FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 or FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6)完成一个classify操作时,FwpsCompleteOperation0 被调用不会

触发ALE reauthoriaztion操作。后面几句没看懂。 Instead a new call to classifyFn is made again when the cloned packet is reinjected incoming if the modification was not 

significant enough to bypass the filter. Permitting the self-injected clone from the ALE_RECV_ACCEPT layer effectively authorizes the incoming connection.

If the incoming connection is not to be allowed, discard the incoming packet after it calls FwpsCompleteOperation0.

b. ALE reauthoriaztion

   当一些事件发生时,比如策略改变(例如增加删除一个filter),检测到新的arrival接口,通过使用IPsec更新连接密钥, callout驱动可以在ALE connect或者receive/accept层reclassified.

这种reauthoriaztion不能通过调用FwpsCompleteOperation0被pended。callout驱动在reauthoriaztion时可以利用规则处理这些包。

   所有进来和出去的数据包都可以被reauthorized在ALE_AUTH_CONNECT或者ALE_RECV_ACCEPT层。例如,一个进来的数据包可以被reauthorized在ALE_AUTH_CONNECT层。callout驱动

不可以假设包的方向和connection的方向相同。

c.ALE_FLOW_ESTABLISHED 层

  FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 or FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6)层不支持异步处理数据。

d.INBOUND_TRANSPORT Layers

  当在进来的传输层((FWPS_LAYER_INBOUND_TRANSPORT_V4 or FWPS_LAYER_INBOUND_TRANSPORT_V6))要求ALE classify处理包时, callout驱动必须不完成异步处理过程。

这个可以和流的创建进行交互。当wfp对进来的包调用classify函数时,它对那些要求ALE classify处理的包设置了FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED标志。

callout驱动应该从INBOUND_TRANSPORT层对这种包放行,并且延迟处理直到ALE_RECV_ACCEPT层。

e. stream 层

在stream层(FWPS_LAYER_STREAM_V4 or FWPS_LAYER_STREAM_V6),tcp数据段代替了ip或者tcp头提交。stream层也是net buffer list可以被提交的在classify 函数中。WFP

可以对stream层将调用的函数进行复制或者注入,通过FwpsCloneStreamData0 和 FwpsStreamInjectAsync0函数。

因为提交的stream层的数据的有序性,callout驱动必须复制和处理数据只要流数据是pending的。对一个给定的数据流混合同步和异步操作对导致异常行为。

(8)连接绑定或者重定向连接

 WFP连接重定向callouts重定向一个程序的连接请求以便程序连接代理服务取代原始的连接。代理服务有两种sockets,一个用于被重定向的原始连接,一个用于新的代理发送连接。

一条wfp重定向记录是不透明的数据缓冲区,它被wfp在FWPM_LAYER_ALE_AUTH_CONNECT_REDIRECT_V4和FWPM_LAYER_ALE_AUTH_CONNECT_REDIRECT_V6层建立了对外的连接请求,从而把重定向后的

连接和原始的连接关联起来。

  win7之前,callout driver可以代理tcp连接的方式只有复制,丢弃,修改或者重注入传输层的包。有了连接绑定重定向的支持,callout driver可以修改任何tcp的4元组(本地,远程ip地址

和端口号).因为可以绑定重定向,所以没必要在连接重定向上支持本地地址和端口修改。

 重定向可以在以下重定向层完成:

FWPM_LAYER_ALE_BIND_REDIRECT_V4 (FWPS_LAYER_ALE_BIND_REDIRECT_V4)

FWPM_LAYER_ALE_BIND_REDIRECT_V6 (FWPS_LAYER_ALE_BIND_REDIRECT_V6)

FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 (FWPS_LAYER_ALE_CONNECT_REDIRECT_V4)

FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 (FWPS_LAYER_ALE_CONNECT_REDIRECT_V6)

重定向的效果取决于所在层,connect层只影响连接流,bind层影响所有连接。

可以使用 FwpsCalloutRegister1或者FwpsCalloutRegister2而不是the older FwpsCalloutRegister0注册重定向。

重定向不是适用于所有类型的网络流量,支持的类型有tcp,udp,Raw UDPv4 without the header include option,raw icmp。

示例代码如下。

(9)ale终端的生命周期管理

支持ale的callout driver可能会分配资源处理提交数据。现在讨论当相关的端点关闭时,如何配置callout driver释放这些资源。ale端点的生命周期管理在win7后的系统都支持。

为了管理ale端点相关的资源,callout必须在以下层注册,

FWPS_LAYER_ALE_RESOURCE_RELEASE_V4 (FWPM_LAYER_ALE_RESOURCE_RELEASE_V4)

FWPS_LAYER_ALE_RESOURCE_RELEASE_V6 (FWPM_LAYER_ALE_RESOURCE_RELEASE_V6)

FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 (FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4)

FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6 (FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6)

。。。

(10)使用数据包的标记

callout driv可以标记有兴趣的包或者接受标记包的事件通知。包的标记在win7之后系统支持。

为了使用包标记,callout driver必须实现 FWPS_NET_BUFFER_LIST_NOTIFY_FN0和FWPS_NET_BUFFER_LIST_NOTIFY_FN1回调函数。

你可能感兴趣的:(客户端及安全)