zoukankan      html  css  js  c++  java
  • Windows8核心态网络过滤研究

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

     WFP (Windows Filtering Platform)

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

    1、  callout

    calloutWFP系统提供的扩展其功能的一种机制,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(&session, sizeof(FWPM_SESSION0));

        session.displayData.name = L"TEMP WFP Session";

        session.displayData.description = L"For Adding callouts";

       

        //创建WFP引擎句柄

        resultFwpmEngineOpen0(

            NULL,

            RPC_C_AUTHN_WINNT,

            NULL,

            &session,

            &engineHandle

            );

        if (NO_ERROR != result)

        {goto cleanup;}

        //开始与引擎交互

        result = FwpmTransactionBegin0(engineHandle, 0);

        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;

        FWPM_FILTER_CONDITION0 filterConditions[1]; //需要几条规则就定义几条

     

        //初始化过滤条件

        RtlZeroMemory(filterConditions, sizeof(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(&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";

        monitorSubLayer.flags = 0;//FWMP_SUBLAYER_FLAG_PERSISTENT;

        // We don't really mind what the order of invocation is.

        monitorSubLayer.weight = 0;

     

        //与WFP引擎开始一次会话

        result = FwpmTransactionBegin0(engineHandle, 0);

        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值越大加载越靠前

     

        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(&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.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;

        }

    }

  • 相关阅读:
    (五)串口通讯方式设置
    (四)计算机上电自启动
    (三)磁盘分区
    (一)老毛桃U盘启动盘制作
    (二)操作系统安装
    计算机常见问题1:计算机网口问题
    Java日志打印方法
    MySQL数据库常见问题1:关于 “ MySQL Installer is running in Community mode ” 的解决办法
    窥见云技术未来大势,腾讯云Techo开发者大会即将在京召开
    揭秘国庆阅兵直播背后的黑科技,腾讯云提供技术支持
  • 原文地址:https://www.cnblogs.com/new0801/p/6177739.html
Copyright © 2011-2022 走看看