微软引入了两种新的网络过滤系统,WFP和NDISfilter

Windows 8 是微软公司推出的最新的客户端OS,内部名称 Windows NT 80 。相对于 Windows NT 5.x ,其网络结构变化非常大,原有的 TDI NDIS 系统挂接方法不再适用。在Windows8系统中,微软引入了两种新的网络过滤系统, WFP NDISfilter
WFP (Windows Filtering Platform)



其包含从用户态到核心态的一系列应用层,根据需要可以在某一层设置回调函数拦截数据。
1、  callout
callout WFP 系统提供的扩展其功能的一种机制, callout 由一组 callout 函数组成,每组有三种函数,
ClassifyFunction ,处理收到的网络数据,例如端口号IP 地址等。NotifyFunction,处理加载、删除callout事件。
FlowDeleteFunction ,删除层与层之间关联的上下文。
callout 由callout 驱动具体实现,每个驱动可以注册多个callout。
2 、WFP 的层和层数据
WFP 有很多层, 每一层分成若干子层,具体有哪些请参阅微软文档,我以 FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 层为例进行讲解。这层位于 ALE 层,是其子层之一。面向连接的应用程序准备连接,面向无连接的程序准备通信,都发生在这一层。假如在这里拒绝了上述操作,应用程序就不能访问网络,这类似以前的 TDI 程序的 Create 事件,就是应用程序访问网络的请求刚到协议栈还没有处理。 WFP 每一层都有其特定的数据,根据这些数据又有特定的过滤条件,例如这层包括 FWPS_METADATA_FIELD_PROCESS_ID 类型数据,这个类型由 UINT64 类型数据定义,表示和本次网络访问请求相关的进程 ID ,可是 FWPS_LAYER_INBOUND_IPPACKET_V4 层就不包括这一类型的数据,其实 FWPS_LAYER_INBOUND_IPPACKET_V4 已经到了 IP 层,这里进程 ID 已经没用了。因此在 FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 蹭可以以进程 ID 作为过滤条件,而到了 FWPS_LAYER_INBOUND_IPPACKET_V4 层就不能用进程 ID 作为过滤条件了。每一层都有哪些数据类型,根据这些数据有哪些过滤条件可用,请参阅微软的 WFP 文档层标识符等章节。
综上, WFP 系统很像一个已经有了数据过滤引擎的防火墙,但是没有规则。我们编写用户层的程序给 WFP 引擎设置规则,编写核心态的 callout 驱动处理 WFP 抓到的网络数据包。根据微软的文档所示, WFP 能够到达 IP 层,假如我们想进行 MAC 层的处理,就必须利用 NDISfilter 驱动。
3、 应用WFP 实现应用程序访问网络时提示
这是个人防火墙的基本功能之一,当有应用程序访问网络时询问用户是否允许。首先我们编写一个callout 驱动,用来处理WFP抓到的网络数据。由于WFP抓到的数据只送到callout驱动不会送到用户层程序,所以这里必须用驱动根据数据判定放行还是阻止。Callout驱动向系统注册callout函数,
     FWPS_CALLOUT0   sCallout ;
    sCallout. calloutKey = * calloutKey;
     sCallout. flags =  flags;
     sCallout. classifyFn =  ClassifyFunction; //在实例程序代码中是 MonitorCoFlowEstablishedCalloutV4
     sCallout. notifyFn =  NotifyFunction;
     sCallout. flowDeleteFn =  FlowDeleteFunction;
status  =  FwpsCalloutRegister0 ( deviceObject , & sCallout calloutId );
FWPS_CALLOUT0 结构用来组织一组callout 函数,之后用FwpsCalloutRegister0函数注册。这里详细介绍下ClassifyFunction函数,这个函数主要处理网络数据包,
NTSTATUS MonitorCoFlowEstablishedCalloutV4(
   IN const FWPS_INCOMING_VALUES0* inFixedValues,//WFP传进来的本层特有的数据
   IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,//本层相关的扩展数据
   IN VOID* packet,
   IN const FWPS_FILTER0* filter,
   IN UINT64 flowContext,
   OUT FWPS_CLASSIFY_OUT0* classifyOut//用这个结构里的字段告知WFP对数据包做出处理
)
{
    NTSTATUS  status =  STATUS_SUCCESS;
    UINT64    flowHandle;
    UINT64    flowContextLocal;
    UINT32    index;
    UINT32    LocalIPADDRv4, remoteIPADDRv4;
    USHORT    LocalPort, remotePort;

    UNREFERENCED_PARAMETER( packet);
    UNREFERENCED_PARAMETER( filter);
    UNREFERENCED_PARAMETER( flowContext);

    index =  FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS;
    LocalIPADDRv4 =  inFixedValues-> incomingValue[ index]. value. uint32;

    index =  FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT;
    LocalPort =  inFixedValues-> incomingValue[ index]. value. uint16;

    index =  FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS;
    remoteIPADDRv4 =  inFixedValues-> incomingValue[ index]. value. uint32;

    index =  FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT;
    remotePort =  inFixedValues-> incomingValue[ index]. value. uint16;

    DbgPrint( "BaseTDI: LocalIP %lx LocalPort %d \n remoteIP %lx remotePort %d", LocalIPADDRv4, LocalPort,
        remoteIPADDRv4, remotePort);
    DbgPrint( "BaseTDI: PID %d ,PID's PATH %s", inMetaValues-> processId, inMetaValues-> processPath-> data);
    DbgPrint( "\n");

    if ( monitoringEnabled)
   {
         // 访问规则程序代码,在这里通知用户态程序
        AskUser(LocalIP, LocalPort, remoteIP, remotePort, PID);
        If  允许
           classifyOut-> actionType =  FWP_ACTION_PERMIT;  //允许发送或接收
        else
           classifyOut-> actionType =  FWP_ACTION_BLOCK;  //不允许发送或接收
   }
   return  status;
}
在完成callout驱动后,下面介绍用户态程序如何设置WFP系统。
设置的大体流程如下文所示,


主要程序代码讲解,
// WFP 系统添加 callout
DWORD  WFPAppAddCallouts()
{
     FWPM_CALLOUT0  callout;
     DWORD  result;
     FWPM_DISPLAY_DATA0  displayData;
     HANDLE  engineHandle =  NULL;
     FWPM_SESSION0  session;
     // 初始化一次会话
     RtlZeroMemory(& sessionsizeof( FWPM_SESSION0));
     session. displayData. name =  L "TEMP WFP Session";
     session. displayData. description =  L "For Adding callouts";

     // 创建WFP引擎句柄
     result =   FwpmEngineOpen0(
         NULL,
         RPC_C_AUTHN_WINNT,
         NULL,
        & session,
        & engineHandle
        );
     if ( NO_ERROR !=  result)
    { goto  cleanup;}
     // 开始与引擎交互
     result =  FwpmTransactionBegin0( engineHandle0);
     if ( NO_ERROR !=  result)
    { goto  abort;    }
ADD CALLOUT
     RtlZeroMemory(& calloutsizeof( FWPM_CALLOUT0));
     displayData. description =  MONITOR_FLOW_ESTABLISHED_CALLOUT_DESCRIPTION;
     displayData. name =  MONITOR_FLOW_ESTABLISHED_CALLOUT_NAME;

     callout. calloutKey =  TEMP_MONITOR_FLOW_ESTABLISHED_CALLOUT_V4;
     callout. displayData =  displayData;
     callout. applicableLayer =  FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
     callout. flags =  FWPM_CALLOUT_FLAG_PERSISTENT//flags 置这个标志表示callout始终被WFP加载
     result =  FwpmCalloutAdd0( engineHandle, & calloutNULLNULL);
     if ( NO_ERROR !=  result)
    { goto  abort;    }
// 结束本次会话
     result =  FwpmTransactionCommit0( engineHandle);
     if ( NO_ERROR ==  result)
    {;}
     goto  cleanup;
abort:

     // 说明本次会话失败
     result =  FwpmTransactionAbort0( engineHandle);
     if ( NO_ERROR ==  result)
    {;}
cleanup:
     // 关闭引擎
     if ( engineHandle)
    {
         FwpmEngineClose0( engineHandle);
    }
     return  result;
}
// WFP 系统添加 filter
DWORD
WFPAppAddFilters( IN   HANDLE  engineHandle /*,IN FWP_BYTE_BLOB* applicationPath*/)                 
{
     DWORD  result =  NO_ERROR;
     FWPM_SUBLAYER0  monitorSubLayer;
     FWPM_FILTER0  filter;
     FWPM_FILTER_CONDITION0  filterConditions[ 1];  // 需要几条规则就定义几条
     // 初始化过滤条件
     RtlZeroMemory( filterConditionssizeof( filterConditions));
     filterConditions[ 0]. fieldKey =  FWPM_CONDITION_IP_PROTOCOL;//所有IP协议数据
     filterConditions[ 0]. matchType =  FWP_MATCH_GREATER_OR_EQUAL; // 匹配度,大于,小于,大于等于
     filterConditions[ 0]. conditionValue. type =  FWP_UINT8;
     filterConditions[ 0]. conditionValue. uint8 =  IPPROTO_IP;

     // 初始化子层
     RtlZeroMemory(& monitorSubLayersizeof( FWPM_SUBLAYER0));
     monitorSubLayer. subLayerKey =  TEMP_MONITOR_SUBLAYER;
     monitorSubLayer. displayData. name =  L "TEMP Monitor Sub layer";
     monitorSubLayer. displayData. description =  L "TEMP Monitor Sub layer";
     monitorSubLayer. flags =  0; //FWMP_SUBLAYER_FLAG_PERSISTENT;
     // We don't really mind what the order of invocation is.
     monitorSubLayer. weight =  0;

     // 与WFP引擎开始一次会话
     result =  FwpmTransactionBegin0( engineHandle0);
     if ( NO_ERROR !=  result)
    { goto  abort;}
     // 增加一个子层
     result =  FwpmSubLayerAdd0( engineHandle, & monitorSubLayerNULL);
     if ( NO_ERROR !=  result)
    { goto  abort;}

FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4
     RtlZeroMemory(& filtersizeof( FWPM_FILTER0));

     filter. layerKey =  FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
     filter. displayData. name =  L "Flow established filter.";
     filter. displayData. description =  L "Sets up flow for traffic that we are interested in.";
     filter. action. type =  FWP_ACTION_CALLOUT_INSPECTION// 表示把符合条件数据包交给callout处理
     filter. action. calloutKey =  TEMP_MONITOR_FLOW_ESTABLISHED_CALLOUT_V4;
     filter. filterCondition =  filterConditions;
     filter. subLayerKey =  monitorSubLayer. subLayerKey;
     filter. weight. type =  FWP_EMPTY// 系统自动设置weight。weight值越大加载越靠前
     filter. numFilterConditions =  1;//过滤条件数

     result =  FwpmFilterAdd0( engineHandle,
        & filter,
         NULL,
        &( filterID[ 0]));

     if ( NO_ERROR !=  result)
    { goto  abort;}

// 结束本次会话
     result =  FwpmTransactionCommit0( engineHandle);
     if ( NO_ERROR ==  result)
    {;}
     goto  cleanup;
abort:
// 说明本次会话失败
     result =  FwpmTransactionAbort0( engineHandle);
     if ( NO_ERROR ==  result)
    {;}
cleanup:
     return  result;
}
二、NDISfilter
NDISfilter是利用系统提供的NDIS过滤引擎,获得MAC级别的网络数据包(这里可以看出WFP,NDISfilter,还有本文未提到的FileSystemMiniFilter,他们都是利用了微软提供的过滤引擎,向其注册回调函数,得到数据后处理)。关键程序代码说明,其中的详细数据结构请参阅微软文档NDISfilter一节,

NDIS_FILTER_DRIVER_CHARACTERISTICS       FChars;

NdisZeroMemory(& FCharssizeof( NDIS_FILTER_DRIVER_CHARACTERISTICS));
         FChars. Header. Type =  NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS;
         FChars. Header. Size =  sizeof( NDIS_FILTER_DRIVER_CHARACTERISTICS);
         FChars. Header. Revision =  NDIS_FILTER_CHARACTERISTICS_REVISION_1;
         FChars. MajorNdisVersion =  FILTER_MAJOR_NDIS_VERSION;
         FChars. MinorNdisVersion =  FILTER_MINOR_NDIS_VERSION;
         FChars. MajorDriverVersion =  1;
         FChars. MinorDriverVersion =  0;
         FChars. Flags =  0;

         FChars. FriendlyName =  FriendlyName;
         FChars. UniqueName =  UniqueName;
         FChars. ServiceName =  ServiceName;

         FChars. SetOptionsHandler =  FilterRegisterOptions;        
         FChars. AttachHandler =  FilterAttach;// 假如是我们想挂接的网络介质,就在这里通知系统挂接
         FChars. DetachHandler =  FilterDetach;
         FChars. RestartHandler =  FilterRestart;
         FChars. PauseHandler =  FilterPause;
         FChars. SetFilterModuleOptionsHandler =  FilterSetModuleOptions;           
         FChars. OidRequestHandler =  FilterOidRequest;
         FChars. OidRequestCompleteHandler =  FilterOidRequestComplete;
         FChars. CancelOidRequestHandler =  FilterCancelOidRequest;

         FChars. SendNetBufferListsHandler =  FilterSendNetBufferLists; // 发送回调函数     
         FChars. ReturnNetBufferListsHandler =  FilterReturnNetBufferLists;
         FChars. SendNetBufferListsCompleteHandler =  FilterSendNetBufferListsComplete;
         FChars. ReceiveNetBufferListsHandler =  FilterReceiveNetBufferLists; // 接收回调函数
         FChars. DevicePnPEventNotifyHandler =  FilterDevicePnPEventNotify;
         FChars. NetPnPEventHandler =  FilterNetPnPEvent;
         FChars. StatusHandler =  FilterStatus;
         FChars. CancelSendNetBufferListsHandler =  FilterCancelSendNetBufferLists;

NDIS_FILTER_DRIVER_CHARACTERISTICS 这个结构用来组织NDISfilter功能函数供NDIS系统回调,例如 FilterSendNetBufferLists ,发送数据回调函数,NDIS发送MAC帧时回调这个函数,相应数据可以在这个函数里得到处理,之后还给NDIS系统继续处理。
VOID
FilterSendNetBufferLists(
         IN   NDIS_HANDLE          FilterModuleContext,
         IN   PNET_BUFFER_LIST     NetBufferLists,
         IN   NDIS_PORT_NUMBER     PortNumber,
         IN   ULONG                SendFlags
        )
{
     PMS_FILTER           pFilter = ( PMS_FILTER) FilterModuleContext;
     NDIS_STATUS          Status =  NDIS_STATUS_SUCCESS;
     PNET_BUFFER_LIST     CurrNbl;
BOOLEAN              DispatchLevel;
// 这里开始解释PNET_BUFFER_LIST指向的网络数据,并显示如何获得MAC地址
    PNET_BUFFER_LIST       pNetBufList, pNextNetBufList;
     PMDL                   pMdl;
     PNDISPROT_ETH_HEADER   pEthHeader =  NULL;
     ULONG                  TotalLength, Offset, BufferLength;

     pNetBufList =  NetBufferLists;

     while ( pNetBufList !=  NULL)
    {
         pNextNetBufList =  NET_BUFFER_LIST_NEXT_NBL ( pNetBufList);
         // 得到当前和包相关的MDL,MDL里即MAC帧,详细的NET_BUFFER_LIST结构请参阅微软相关文档
         pMdl =  NET_BUFFER_CURRENT_MDL( NET_BUFFER_LIST_FIRST_NB( pNetBufList));   
         TotalLength =  NET_BUFFER_DATA_LENGTH( NET_BUFFER_LIST_FIRST_NB( pNetBufList));
         Offset =  NET_BUFFER_CURRENT_MDL_OFFSET( NET_BUFFER_LIST_FIRST_NB( pNetBufList));
         BufferLength =  0;
         do
        {
             ASSERT( pMdl !=  NULL);
             if ( pMdl)
            {
                 NdisQueryMdl(
                     pMdl,
                    & pEthHeader,
                    & BufferLength,
                     NormalPagePriority);
            }

             if ( pEthHeader ==  NULL)
            {
                 BufferLength =  0;
                 break;
            }

             if ( BufferLength ==  0)
            {
                 break;
            }
             ASSERT( BufferLength >  Offset);

             BufferLength -=  Offset;
             pEthHeader = ( PNDISPROT_ETH_HEADER)(( PUCHAR) pEthHeader +  Offset);

             DbgPrint( "DstMAC %x-%x-%x-%x-%x-%x", pEthHeader-> DstAddr[ 0],
                 pEthHeader-> DstAddr[ 1], pEthHeader-> DstAddr[ 2],
                 pEthHeader-> DstAddr[ 3], pEthHeader-> DstAddr[ 4],
                 pEthHeader-> DstAddr[ 5]);

             DbgPrint( "srcMAC %x-%x-%x-%x-%x-%x", pEthHeader-> SrcAddr[ 0],
                 pEthHeader-> SrcAddr[ 1], pEthHeader-> SrcAddr[ 2],
                 pEthHeader-> SrcAddr[ 3], pEthHeader-> SrcAddr[ 4],
                 pEthHeader-> SrcAddr[ 5]);
             DbgPrint( "\n");

             if ( BufferLength <  sizeof( NDISPROT_ETH_HEADER))
            {
                 break;
            }
        } while ( FALSE);
         pNetBufList =  pNextNetBufList;
    }
}
Filter Drivers主要包括以下特性:   1) 一个Filter Drivers实例叫Filter Module。Filter Module附加在一个适配器的微端口驱动上, 来自相同或不同Filter Drivers的多个Filter Module都可以被堆叠在一个网络适配器上   2) 在Filter Module被安装到驱动栈时,之上的协议驱动和之下的微端口驱动都不需要提供额外的支持功能   3) 因为Filter Module不像中间层驱动(intermediate driver)那样提供一个虚拟的微口,也不与某个设备对象联结,所以在微端口适配器(miniport adapter)之上的Filter Module 功能相当于一个修过过版本的微端口适配器(miniport adapter)。(原文:Because filter drivers do not implement virtual miniports like an intermediate driverfilter drivers are not associated with a device object. A miniport adapter with overlying filter modules functions as a modified version of the miniport adapter.)   4) NDIS使用配置信息来到决定一个Filter Module附加到一个网络适配器栈上的顺序   5) 在不用拆除整驱动栈的情况下,NDIS可以动态的插入、删除Filter Module或进行重配置   6) 当NDIS重起驱动栈的时候协议驱动可以获得在栈上的Filter Module列表   7) Filter Drivers可以过滤下层网络适配器上绝大部分的通信。Filter Module不联结某特定的绑定(Filter modules are not associated with any particular binding between overlying protocol drivers and the miniport adapter.)   8) Filter Drivers 可以选择为过滤服务也可以选择为分流的不过滤服务,选择为哪一种是可以动态配置的(Filter drivers can select the services that are filtered and can be bypassed for the services that are not filtered. The selection of the services that are bypassed and the services that are filtered can be reconfigured dynamically.)   9) NDIS 保证有效的上下文空间,也不就是说Filter Drivers不要需要通代码COPY缓冲区来获得上下文空间  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值