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

WFP (Windows Filtering Platform)



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



其包含从用户态到核心态的一系列应用层,根据需要可以在某一层设置回调函数拦截数据。

1、  callout

callout是WFP系统提供的扩展其功能的一种机制,callout由一组callout函数组成,每组有三种函数,

ClassifyFunction,处理收到的网络数据,例如端口号IP地址等。NotifyFunction,处理加载、删除callout事件。

FlowDeleteFunction,删除层与层之间关联的上下文。

callout由callout驱动具体实现,每个驱动可以注册多个callout。

、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和NDISfilter-LMLPHP



主要程序代码讲解,

//向WFP系统添加callout

DWORD WFPAppAddCallouts()
{
    FWPM_CALLOUT0 callout;
    DWORD result;
    FWPM_DISPLAY_DATA0 displayData;
    HANDLE engineHandle = NULL;
    FWPM_SESSION0 session;
    //初始化一次会话
    RtlZeroMemory(&session, sizeof(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;}
    //开始与引擎交互
    );
    if (NO_ERROR != result)
    {goto abort;    }
////////////////////////////////////////////////////////////////ADD CALLOUT
    RtlZeroMemory(&callout, sizeof(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, &callout, NULL, NULL);
    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;
    ]; //需要几条规则就定义几条
    //初始化过滤条件
    RtlZeroMemory(filterConditions, sizeof(filterConditions));
    ].fieldKey = FWPM_CONDITION_IP_PROTOCOL;//所有IP协议数据
    ].matchType = FWP_MATCH_GREATER_OR_EQUAL;//匹配度,大于,小于,大于等于…
    ].conditionValue.type = FWP_UINT8;
    ].conditionValue.uint8 = IPPROTO_IP;
    //初始化子层
    RtlZeroMemory(&monitorSubLayer, sizeof(FWPM_SUBLAYER0));
    monitorSubLayer.subLayerKey = TEMP_MONITOR_SUBLAYER;
    monitorSubLayer.displayData.name = L"TEMP
Monitor Sub layer";
    monitorSubLayer.displayData.description = L"TEMP
Monitor Sub layer";
    ;//FWMP_SUBLAYER_FLAG_PERSISTENT;
    // We don't really mind what the order of invocation is.
    ;
    //与WFP引擎开始一次会话
    );
    if (NO_ERROR != result)
    {goto abort;}
    //增加一个子层
    result = FwpmSubLayerAdd0(engineHandle, &monitorSubLayer, NULL);
    if (NO_ERROR != result)
    {goto abort;}
////////////////////////////////////////////FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4
    RtlZeroMemory(&filter, sizeof(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值越大加载越靠前
    ;//过滤条件数
    result = FwpmFilterAdd0(engineHandle,
        &filter,
        NULL,
        &(]));
    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(&FChars, sizeof(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.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));
        ;
        do
        {
            ASSERT(pMdl != NULL);
            if (pMdl)
            {
                NdisQueryMdl(
                    pMdl,
                    &pEthHeader,
                    &BufferLength,
                    NormalPagePriority);
            }
            if (pEthHeader == NULL)
            {
                ;
                break;
            }
            )
            {
                break;
            }
            ASSERT(BufferLength > Offset);
            BufferLength -= Offset;
            pEthHeader = (PNDISPROT_ETH_HEADER)((PUCHAR)pEthHeader + Offset);
            ],
                ],],
                ],],
                ]);
            ],
                ],],
                ],],
                ]);
            DbgPrint("\n");
            if (BufferLength < sizeof(NDISPROT_ETH_HEADER))
            {
                break;
            }
        }while (FALSE);
        pNetBufList = pNextNetBufList;
    }
}
04-24 22:59