zoukankan      html  css  js  c++  java
  • Win64 驱动内核编程-16.WFP网络监控驱动(防火墙)

    WFP驱动监控网络

        WFP 是微软推出来替代 TDI HOOKNDIS HOOK 等拦截网络通信的方案,WFP 的框架非常庞大,在 RING3 和 RING0 各有一套类似的函数,令人兴奋的是,即使在 R3 使用 WFP,也可以做到全局拦截访问网络。由于 WFP 的范围太广,实在难以一言概括,感兴趣的朋友可以自行到 MSDN 上查看微软对它的官方概述。本文的目的,是给大家理顺 WFP 的框架,并利用 WFP 拦截指定进程访问网络,或拦截对指定 IP 地址/端口的访问。

        一个标准的 WFP 程序大体是这样子的: 首先使用 FwpmEngineOpen  开启 WFP  引擎 (获得个 一个 WFP  的 使用 句柄) )用 ,然后用 FwpmTransactionBegin  设置 对网络通信 内容 的 过滤 权限(是只读) 还是允许修改) , 然后用 FwpsCalloutRegister FwpmCalloutAdd FwpmFilterAdd  选择你 要过滤的内容, 并 添加 过滤器对象 和 回调函数, 最后用 用 FwpmTransactionCommit  确认刚才的内容, 让 刚才添加的 回调函数 开始生效 。 当用 你不用 WFP  的 时候, 就要用FwpmFilterDeleteById FwpmCalloutDeleteById FwpsCalloutUnregisterById  把你刚才添加的过滤器对象和回调函数删除掉,然后用 用 FwpmEngineClose  关闭 WFP 擎 引擎 ( 类似于 关闭句柄) )。

         一个概述已经是这样子了,实现起来就更加麻烦了。为了方便大家学习,作者(胡文亮)已经把微软的 WFP 实例进行了最大简化,并把核心的注册回调功能封装成了一个函数。下面一步一步进行分析。在具体分析之前,有一些重要的内容需要先说明,否则后面的内容肯定会让大家一头雾水。一、WFP  一次性 要 注册的回调函数 不是 1  个 ,而是 3  个 。 但只有 1  个 是 “ 事前 ”回调, 另外 2  个 都是 “ 事后 ” 回调 (一般 只 使用“ 事前 ” 回调 ,不使用“ 事后 ”回调) )。二、WFP  能过滤 的内容很多,你必须选择一个 感兴趣的内容。 这个“ 感 兴趣 的 内容” 用官话来说叫做 “过滤 条件标志 ”, 这货 其实 是一个 常量 , 由于我们是 要过滤进程联网, 而且 一般都是用 IPV4  协议,所以 “过滤 条件标志 ”为 为 FWPM_LAYER_ALE_AUTH_CONNECT_V4 ( 在 这个

    页面 可以的 查到所有的 “过滤 条件标志” ”) )。三、 你个 必须为这个 “过滤 条件标志 ”指定一个 GUID ,当然 GUID  的 值 随便设置 就行 ,只要 在 系统范围内不重复。 。 代码中的 GUID  就是 随意设定的,它的名称为:GUID_ALE_AUTH_CONNECT_CALLOUT_V4

     

    我用的VS2015+wdk10开发的,VS15里面需要设置这些东西:

    添加库:ntoskrnl.lib ndis.lib fwpkclnt.lib uuid.lib

    添加宏定义:NDIS_SUPPORT_NDIS6

     注册的代码:

    NTSTATUS RegisterCalloutForLayer
    (
        IN const GUID* layerKey,
        IN const GUID* calloutKey,
        IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
        IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
        IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
        OUT UINT32* calloutId,
        OUT UINT64* filterId
    )
    {
    NTSTATUS        status = STATUS_SUCCESS;
    FWPS_CALLOUT    sCallout = {0};
    FWPM_FILTER     mFilter = {0};
    FWPM_FILTER_CONDITION mFilter_condition[1] = {0};
    FWPM_CALLOUT    mCallout = {0};
    FWPM_DISPLAY_DATA mDispData = {0};
    BOOLEAN         bCalloutRegistered = FALSE; 
    sCallout.calloutKey = *calloutKey;
    sCallout.classifyFn = classifyFn;
    sCallout.flowDeleteFn = flowDeleteNotifyFn;
    sCallout.notifyFn = notifyFn;
    //要使用哪个设备对象注册
    status = FwpsCalloutRegister( gDevObj,&sCallout,calloutId );
    if( !NT_SUCCESS(status))
    goto exit;
    bCalloutRegistered = TRUE;
    mDispData.name = L"WFP TEST";
    mDispData.description = L"TESLA.ANGELA's WFP TEST";
    //你感兴趣的内容
    mCallout.applicableLayer = *layerKey;
    //你感兴趣的内容的GUID
    mCallout.calloutKey = *calloutKey;
    mCallout.displayData = mDispData;
    //添加回调函数
    status = FwpmCalloutAdd( gEngineHandle,&mCallout,NULL,NULL);
    if( !NT_SUCCESS(status))
    goto exit;
    mFilter.action.calloutKey = *calloutKey;
    //在callout里决定
    mFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    mFilter.displayData.name = L"WFP TEST";
    mFilter.displayData.description = L"TESLA.ANGELA's WFP TEST";
    mFilter.layerKey = *layerKey;
    mFilter.numFilterConditions = 0;
    mFilter.filterCondition = mFilter_condition;
    mFilter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL;
    mFilter.weight.type = FWP_EMPTY;
    //添加过滤器
    status = FwpmFilterAdd( gEngineHandle,&mFilter,NULL,filterId );
    if( !NT_SUCCESS( status))
    goto exit;
    exit:
    if( !NT_SUCCESS(status))
    {
    if( bCalloutRegistered )
    {
    FwpsCalloutUnregisterById( *calloutId );
    }
    }
    return status;
    }
    NTSTATUS WallRegisterCallouts()
    {
    NTSTATUS    status = STATUS_SUCCESS;
    BOOLEAN     bInTransaction = FALSE;
    BOOLEAN     bEngineOpened = FALSE;
    FWPM_SESSION session = {0};
    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
    //开启WFP引擎
    status = FwpmEngineOpen( NULL,
                             RPC_C_AUTHN_WINNT,
                             NULL,
                             &session,
                             &gEngineHandle );
    if( !NT_SUCCESS(status))
    goto exit;
    bEngineOpened = TRUE;
    //确认过滤权限
    status = FwpmTransactionBegin( gEngineHandle,0 );
    if( !NT_SUCCESS(status))
    goto exit;
    bInTransaction = TRUE;
    //注册回调函数
    status = RegisterCalloutForLayer(
                 &FWPM_LAYER_ALE_AUTH_CONNECT_V4,
                 &GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
                 WallALEConnectClassify,
                 WallNotifyFn,
                 WallFlowDeleteFn,
                 &gAleConnectCalloutId,
                 &gAleConnectFilterId);
    if( !NT_SUCCESS(status))
    {
    DbgPrint("RegisterCalloutForLayer-FWPM_LAYER_ALE_AUTH_CONNECT_V4 failed!
    ");
    goto exit;
    }
    //确认所有内容并提交,让回调函数正式发挥作用
    status = FwpmTransactionCommit(gEngineHandle );
    if( !NT_SUCCESS(status))
    goto exit;
    bInTransaction = FALSE;
    exit:
    if( !NT_SUCCESS(status))
    {
    if( bInTransaction)
    {
    FwpmTransactionAbort( gEngineHandle );
    }
    if( bEngineOpened )
    {
    FwpmEngineClose( gEngineHandle );
    gEngineHandle = 0;
    }
    }
    return status;
    }

    注销的代码:

    NTSTATUS WallUnRegisterCallouts()
    {
    if( gEngineHandle != 0 )
    {
    //删除FilterId
    FwpmFilterDeleteById( gEngineHandle,gAleConnectFilterId );
    //删除CalloutId
    FwpmCalloutDeleteById( gEngineHandle,gAleConnectCalloutId );
    //清空FilterId
    gAleConnectFilterId = 0;
    //反注册CalloutId
    FwpsCalloutUnregisterById( gAleConnectCalloutId );
    //清空CalloutId
    gAleConnectCalloutId = 0;
    //关闭引擎
    FwpmEngineClose( gEngineHandle );
    gEngineHandle = 0;
    }
    return STATUS_SUCCESS;
    }

    三个回调函数以及其他:

    #include <ntddk.h>
    #pragma warning(push)
    #pragma warning(disable:4201)       // unnamed struct/union
    #pragma warning(disable:4995)
    #include <ndis.h>
    #include <fwpsk.h>
    #pragma warning(pop)
    #include <fwpmk.h>
    #include <limits.h>
    #include <ws2ipdef.h>
    #include <in6addr.h>
    #include <ip2string.h>
    #include <strsafe.h>
    #include <wdm.h>
     
    #define INITGUID
    #include <guiddef.h>
    #define bool BOOLEAN
    #define true TRUE 
    #define false FALSE
    #define DEVICE_NAME L"\Device\MyDriver"
    #define DEVICE_DOSNAME L"\DosDevices\MyDriver"
    #define kmalloc(_s) ExAllocatePoolWithTag(NonPagedPool, _s, 'SYSQ')
    #define kfree(_p) ExFreePool(_p)
     
    DEFINE_GUID // {6812FC83-7D3E-499a-A012-55E0D85F348B}
    (
    GUID_ALE_AUTH_CONNECT_CALLOUT_V4, 
    0x6812fc83, 
    0x7d3e, 
    0x499a, 
    0xa0, 0x12, 0x55, 0xe0, 0xd8, 0x5f, 0x34, 0x8b
    );
     
    PDEVICE_OBJECT  gDevObj;
     
    HANDLE	gEngineHandle = 0;
    HANDLE	gInjectHandle = 0;
    //CalloutId
    UINT32	gAleConnectCalloutId = 0;
    //FilterId
    UINT64	gAleConnectFilterId = 0;
     
    /*
    以下两个回调函数没啥用
    */
    NTSTATUS NTAPI WallNotifyFn
    (
        IN FWPS_CALLOUT_NOTIFY_TYPE  notifyType,
        IN const GUID  *filterKey,
        IN const FWPS_FILTER  *filter
    )
    {
    return STATUS_SUCCESS;
    }
     
    VOID NTAPI WallFlowDeleteFn
    (
        IN UINT16  layerId,
        IN UINT32  calloutId,
        IN UINT64  flowContext
    )
    {
    return;
    }
     
    //协议代码转为名称
    char* ProtocolIdToName(UINT16 id)
    {
    char *ProtocolName=kmalloc(16);
    switch(id)	//http://www.ietf.org/rfc/rfc1700.txt
    {
    case 1:
    strcpy_s(ProtocolName,4+1,"ICMP");
    break;
    case 2:
    strcpy_s(ProtocolName,4+1,"IGMP");
    break;
    case 6:
    strcpy_s(ProtocolName,3+1,"TCP");
    break;
    case 17:
    strcpy_s(ProtocolName,3+1,"UDP");
    break;
    case 27:
    strcpy_s(ProtocolName,3+1,"RDP");
    break;
    default:
    strcpy_s(ProtocolName,7+1,"UNKNOWN");
    break;
    }
    return ProtocolName;
    }
     
    //最重要的过滤函数
    //http://msdn.microsoft.com/en-us/library/windows/hardware/ff551238(v=vs.85).aspx
    void NTAPI WallALEConnectClassify
    (
        IN const FWPS_INCOMING_VALUES0* inFixedValues,
        IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
        IN OUT void* layerData,
        IN const void* classifyContext,
        IN const FWPS_FILTER* filter,
        IN UINT64 flowContext,
        OUT FWPS_CLASSIFY_OUT* classifyOut
    )
    {
    char *ProtocolName=NULL;
    DWORD LocalIp,RemoteIP;
    LocalIp=inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
    RemoteIP=inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
    ProtocolName=ProtocolIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16);
    DbgPrint("[WFP]IRQL=%d;PID=%ld;Path=%S;Local=%u.%u.%u.%u:%d;Remote=%u.%u.%u.%u:%d;Protocol=%s
    ",
             (USHORT)KeGetCurrentIrql(),
             (DWORD)(inMetaValues->processId),
             (PWCHAR)inMetaValues->processPath->data,	//NULL,//
             (LocalIp>>24)&0xFF,(LocalIp>>16)&0xFF,(LocalIp>>8)&0xFF,LocalIp&0xFF,
             inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16,
             (RemoteIP>>24)&0xFF,(RemoteIP>>16)&0xFF,(RemoteIP>>8)&0xFF,RemoteIP&0xFF,
             inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16,
     ProtocolName);
    kfree(ProtocolName);
    classifyOut->actionType = FWP_ACTION_PERMIT;//允许连接
     
    //禁止IE联网(设置“行动类型”为FWP_ACTION_BLOCK)
    // if(wcsstr((PWCHAR)inMetaValues->processPath->data,L"iexplore.exe"))
    // {
    // classifyOut->actionType = FWP_ACTION_BLOCK;
    // classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
    // classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
    // }
    return;
    }
    执行结果,进行网络监控,并且禁止ie访问网络:


    刚刚看有一个哥们写的也不错,留在这方便以后用的时候一起查看:

    http://blog.csdn.net/rodney443220/article/details/37653793

    还有一点要注意。回调函数执行的时候IRQL不一定是0,还可能是2,看上面图片输出。

     

  • 相关阅读:
    在ConcurrentModificationException异常上的联想
    记录一下自己爬虎牙LOL主播的爬虫思路
    ajax解决跨域问题
    解决多线程下数据库操作问题
    浅谈时间复杂度
    想一下,最大公约数怎么求
    IO流与IO缓冲
    循环移位
    3Sum探讨(Java)
    Two Sum(两个数的相加)
  • 原文地址:https://www.cnblogs.com/csnd/p/12062014.html
Copyright © 2011-2022 走看看