Windows過濾驅動 WFP代碼基本流程的剖析 bypass前期準備

今天實際看一下,WFP的Callout驅動的代碼。先從DriverEntry開始:

1,在DriverEntry需要創建驅動對象和設備對象,
   
    1.1  由於不是PNP設備,需要設置創建驅動對象的標誌爲config.DriverInitFlags |= WdfDriverInitNonPnpDriver.
    1.2  調用WdfDriverCreate創建驅動對象。
    1.3  調用WdfControlDeviceInitAllocate通過驅動對象創建 WDFDEVICE_INIT結構體。
    1.4  調用WdfDeviceInitSetDeviceType設置設備類型爲FILE_DEVICE_NETWORK.
    1.5  調用WdfDeviceInitSetCharacteristics設置設備的特性爲FILE_DEVICE_SECURE_OPEN和FILE_AUTOGENERATED_DEVICE_NAME.
    1.6  調用WdfDeviceCreate創建設備對象。
    1.7  調用WdfControlFinishInitializing設置設備的初始化狀態爲完成。
    1.8  調用FwpsInjectionHandleCreate創建一個檢測的句柄。並設置在哪裏完成檢查。通過在轉發層,網絡層,流層,傳輸層。
    1.9  調用WdfDeviceWdmGetDeviceObject將框架設備對象轉換爲設備對象的指針。
    1.10 調用FwpmEngineOpen打開一個和過濾引擎的會話,這個函數會返回一個過濾引擎的句柄。
    1.11 調用FwpmTransactionBegin在當前的會話下,開始一個明確的傳輸。
    1.12 調用FwpmSubLayerAdd函數玩系統中增加一個子層。
   
          DWORD WINAPI FwpmSubLayerAdd0(
    _In_      HANDLE engineHandle,
    _In_      const FWPM_SUBLAYER0 *subLayer,
    _In_opt_  PSECURITY_DESCRIPTOR sd
      );


      這裏我們主要來看第二個參數,FWPM_SUBLAYER0這個結構體。
      
      typedef struct FWPM_SUBLAYER0_ {
    GUID               subLayerKey;
    FWPM_DISPLAY_DATA0 displayData;
    UINT16             flags;
    GUID               *providerKey;
    FWP_BYTE_BLOB      providerData;
    UINT16             weight;
    } FWPM_SUBLAYER0;
    
    這裏,我們主要看第一個GUID,後面的需要在例子後分析。這個可以定義的GUID.
    
    DEFINE_GUID(
    DD_PROXY_SUBLAYER,
    0x0104fd7e,
    0xc825,
    0x414e,
    0x94, 0xc9, 0xf0, 0xd5, 0x25, 0xbb, 0xc1, 0x69
    );
    
       DDProxySubLayer.subLayerKey = DD_PROXY_SUBLAYER;
       DDProxySubLayer.displayData.name = L"Datagram-Data Proxy Sub-Layer";
       DDProxySubLayer.displayData.description = 
       L"Sub-Layer for use by Datagram-Data Proxy callouts";
       DDProxySubLayer.flags = 0;
       DDProxySubLayer.weight = FWP_EMPTY; // auto-weight.;
       
    1.13  調用FwpsCalloutRegister註冊一個callout:
          NTSTATUS NTAPI FwpsCalloutRegister0(
      _Inout_    void *deviceObject,
      _In_       const FWPS_CALLOUT0 *callout,
      _Out_opt_  UINT32 *calloutId
    );
    這裏主要是第二個參數的設置:
    
    typedef struct FWPS_CALLOUT0_ {
        GUID                                 calloutKey;
    UINT32                              flags;
    FWPS_CALLOUT_CLASSIFY_FN0           classifyFn;
    FWPS_CALLOUT_NOTIFY_FN0             notifyFn;
    FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN0 flowDeleteFn;
    } FWPS_CALLOUT0;
    
    這裏的calloutKey是一個GUID值,我們可以定義。classifyFn爲驅動分類的函數入口。notifyFn爲通知消息的函數入口。flowDeleteFn爲流程刪除的函數入口。
    
    1.14  調用FwpmCalloutAdd,向過濾引擎增加一個callout.
   
    NTSTATUS NTAPI FwpmCalloutAdd0(
    _In_       HANDLE engineHandle,
    _In_       const FWPM_CALLOUT0 *callout,
    _In_opt_   PSECURITY_DESCRIPTOR sd,
    _Out_opt_  UINT32 *id
    );
    
    這裏還是看第二個參數,FWPM_CALLOUT0.
    
    typedef struct FWPM_CALLOUT0_ {
    GUID               calloutKey;
    FWPM_DISPLAY_DATA0 displayData;
    UINT32             flags;
    GUID               *providerKey;
    FWP_BYTE_BLOB      providerData;
    GUID               applicableLayer;
    UINT32             calloutId;
    } FWPM_CALLOUT0;
    
    所以我們看到,這裏我們有兩個CALLOUT了,一個是FWPS_CALLOUT0,一個是FWPM_CALLOUT0,FWPS_CALLOUT0是給驅動用的,所以這裏將其CALLOUT跟設備對象進行關聯,但是後面還有個FWPM_CALLOUT0,這個就是跟過濾引擎進行交互的。再看其實這兩個CALLOUT的GUID值是一樣的,所以這樣就進行了關聯。兩個CALLOUT相互關聯,又相互獨立,FWPM_CALLOUT0,負責和過濾引擎相關的操作。FWPS_CALLOUT0負責和驅動相關本身的操作。
這樣,從驅動本身的驅動對象,設備對象和過濾引擎中的過濾層和CALLOUT進行聯繫上了。
  
    1.15  調用FwpmFilterAdd增加一個過濾對象到系統中。
   
          DWORD WINAPI FwpmFilterAdd0(
    _In_       HANDLE engineHandle,
    _In_       const FWPM_FILTER0 *filter,
    _In_opt_   SECURITY_DESCRIPTOR sd,
    _Out_opt_  UINT64 *id
      );
      
      這裏還是第二個參數,const FWPM_FILTER0 *filter,非常複雜的結構,這個是精髓,必須好好看。
      
      typedef struct FWPM_FILTER0_ {
    GUID                   filterKey;
    FWPM_DISPLAY_DATA0     displayData;
    UINT32                 flags;
    GUID                   *providerKey;
    FWP_BYTE_BLOB          providerData;
    GUID                   layerKey;
    GUID                   subLayerKey;
    FWP_VALUE0             weight;
    UINT32                 numFilterConditions;
    FWPM_FILTER_CONDITION0 *filterCondition;
    FWPM_ACTION0           action;
    union {
      UINT64 rawContext;
      GUID   providerContextKey;
    };
    GUID                   *reserved;
    UINT64                 filterId;
    FWP_VALUE0             effectiveWeight;
    } FWPM_FILTER0;


    我們先把,結構體中包含的結構,進行展開。
    
    typedef struct FWPM_DISPLAY_DATA0_ {
      wchar_t *name;
      wchar_t *description;
    } FWPM_DISPLAY_DATA0;
    
    typedef struct FWP_BYTE_BLOB_ {
      UINT32 size;
      UINT8  *data;
    } FWP_BYTE_BLOB;
    
    關於providerKey代表的是WFP內部定義的一些GUID.
    
    typedef struct FWP_VALUE0_ {
    
      FWP_DATA_TYPE type;
      union {
      ;  // case(FWP_EMPTY)
      UINT8                 uint8;
      UINT16                uint16;
      UINT32                uint32;
      UINT64                *uint64;
      INT8                  int8;
      INT16                 int16;
      INT32                 int32;
      INT64                 *int64;
      float                 float32;
      double                *double64;
      FWP_BYTE_ARRAY16      *byteArray16;
      FWP_BYTE_BLOB         *byteBlob;
      SID                   *sid;
      FWP_BYTE_BLOB         *sd;
      FWP_TOKEN_INFORMATION *tokenInformation;
      FWP_BYTE_BLOB         *tokenAccessInformation;
      LPWSTR                unicodeString;
      FWP_BYTE_ARRAY6       *byteArray6;
      };
    } FWP_VALUE0;
    
    這裏,我的理解是,這個值代表代表一個過濾的一個比重,這個跟你在哪一層過濾都有關係。
    
    下面看一下,最最重要的一個結構體,過濾的條件。當這所有的條件的滿足的情況下,定義的過濾動作纔開始。
    
    typedef struct FWPM_FILTER_CONDITION0_ {
      GUID                fieldKey;
      FWP_MATCH_TYPE      matchType;
      FWP_CONDITION_VALUE conditionValue;
    } FWPM_FILTER_CONDITION0;
    
    通常這個fieldKey域,微軟有明確的定義。在每一個過濾層次上,都有不一樣的過濾條件。可以看http://msdn.microsoft.com/en-us/library/windows/hardware/ff549944(v=vs.85).aspx
    
    
    typedef enum FWP_MATCH_TYPE_ { 
      FWP_MATCH_EQUAL,
      FWP_MATCH_GREATER,
      FWP_MATCH_LESS,
      FWP_MATCH_GREATER_OR_EQUAL,
      FWP_MATCH_LESS_OR_EQUAL,
      FWP_MATCH_RANGE,
      FWP_MATCH_FLAGS_ALL_SET,
      FWP_MATCH_FLAGS_ANY_SET,
      FWP_MATCH_FLAGS_NONE_SET,
      FWP_MATCH_EQUAL_CASE_INSENSITIVE,
      FWP_MATCH_NOT_EQUAL,
      FWP_MATCH_TYPE_MAX
    } FWP_MATCH_TYPE;
    
    typedef struct FWP_CONDITION_VALUE0_ {
      FWP_DATA_TYPE type;
      union {
      UINT8                 uint8;
      UINT16                uint16;
      UINT32                uint32;
      UINT64                *uint64;
      INT8                   int8;
      INT16                 int16;
      INT32                 int32;
      INT64                 *int64;
      float                 float32;
      double                *double64;
      FWP_BYTE_ARRAY16      *byteArray16;
      FWP_BYTE_BLOB         *byteBlob;
      SID                   *sid;
      FWP_BYTE_BLOB         *sd;
      FWP_TOKEN_INFORMATION *tokenInformation;
      FWP_BYTE_BLOB         *tokenAccessInformation;
      LPWSTR                unicodeString;
      FWP_BYTE_ARRAY6       *byteArray6;
      FWP_V4_ADDR_AND_MASK  *v4AddrMask;
      FWP_V6_ADDR_AND_MASK  *v6AddrMask;
      FWP_RANGE0            *rangeValue;
      };
    } FWP_CONDITION_VALUE0;
    
    這個可能要多看下MSDN中的設置,因爲跟微軟玩,必須都符合它的要求。
    
    下面再看下,過濾動作的這個結構體。
    
    typedef struct FWPM_ACTION0_ {
      FWP_ACTION_TYPE type;
      union {
      GUID filterType;
      GUID calloutKey;
      };
    } FWPM_ACTION0;
    
    這裏要提一下的就是這個calloutKey,這個值正好跟之前calloutKey相吻合,主要我們向設備對象註冊的callout,向過濾引擎註冊的callout,以及和過濾的callout都指向同一個GUID值。
    
    下面就是我們來看具體的CALLOUT函數的執行了,當滿足這些條件後,CALLOUT被過濾引擎調用。
   
    我們具體來看一下這個具體的CALLOUT函數:
   
    void NTAPI classifyFn0(
    _In_     const FWPS_INCOMING_VALUES0 *inFixedValues,
    _In_     const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
    _Inout_  void *layerData,
    _In_     const FWPS_FILTER0 *filter,
   _In_     UINT64 flowContext,
    _Out_    FWPS_CLASSIFY_OUT0 *classifyOut
    )
   
    typedef struct FWPS_INCOMING_VALUES0_ {
    UINT16               layerId;
    UINT32               valueCount;
    FWPS_INCOMING_VALUE0 *incomingValue;
    } FWPS_INCOMING_VALUES0;
   
    這個layerId就是指的是過濾層的實時標識ID.可以參考微軟的http://msdn.microsoft.com/en-us/library/windows/hardware/ff570731(v=vs.85).aspx


            具體的數據域。
    typedef struct FWPS_INCOMING_VALUE0_ {
    FWP_VALUE0 value;
    } FWPS_INCOMING_VALUE0;
   
      這個值一看就知道,就是代表那些固定的值。比如一些IP地址,PORT等等。
   
    再看:
     
    typedef struct FWPS_INCOMING_METADATA_VALUES0_ {
    UINT32                          currentMetadataValues;
    UINT32                          flags;
    UINT64                          reserved;
    FWPS_DISCARD_METADATA0          discardMetadata;
    UINT64                          flowHandle;
    UINT32                          ipHeaderSize;
    UINT32                          transportHeaderSize;
    FWP_BYTE_BLOB                   *processPath;
    UINT64                          token;
    UINT64                          processId;
    UINT32                          sourceInterfaceIndex;
    UINT32                          destinationInterfaceIndex;
    ULONG                           compartmentId;
    FWPS_INBOUND_FRAGMENT_METADATA0 fragmentMetadata;
    ULONG                           pathMtu;
    HANDLE                          completionHandle;
    UINT64                          transportEndpointHandle;
    SCOPE_ID                        remoteScopeId;
    WSACMSGHDR                      *controlData;
    ULONG                           controlDataLength;
    FWP_DIRECTION                   packetDirection;
    #if (NTDDI_VERSION >= NTDDI_WIN6SP1)
    PVOID                           headerIncludeHeader;
    ULONG                           headerIncludeHeaderLength;
    #if (NTDDI_VERSION >= NTDDI_WIN7)
    IP_ADDRESS_PREFIX               destinationPrefix;
    UINT16                          frameLength;
    UINT64                          parentEndpointHandle;
    UINT32                          icmpIdAndSequence;
    DWORD                           localRedirectTargetPID;
    SOCKADDR                        *originalDestination;
    #if (NTDDI_VERSION >= NTDDI_WIN8)
    HANDLE                          redirectRecords;
    UINT32                          currentL2MetadataValues;
    UINT32                          l2Flags;
    UINT32                          ethernetMacHeaderSize;
    UINT32                          wiFiOperationMode;
    #if (NDIS_SUPPORT_NDIS630)
    NDIS_SWITCH_PORT_ID             vSwitchSourcePortId;
    NDIS_SWITCH_NIC_INDEX           vSwitchSourceNicIndex;
    NDIS_SWITCH_PORT_ID             vSwitchDestinationPortId;
    #else 
    UINT32                          padding0;
    USHORT                          padding1;
    UINT32                          padding2;
    #endif 
    HANDLE                          vSwitchPacketContext;
    UINT32                          l2ConnectionProfileIndex;
    #endif 
    #endif 
    #endif 
    #if (NTDDI_VERSION >= NTDDI_WIN8)
    PVOID                           subProcessTag;
    UINT64                          Reserved1;
    #endif 
    } FWPS_INCOMING_METADATA_VALUES0;
    
    這個數據就是包含需要過濾的一些元數據的值。
    
    我們再來看void *layerData,這個值,可能爲NULL,取決於過濾條件和過濾層。
    
    在Stream層,這個參數指向 FWPS_STREAM_CALLOUT_IO_PACKET0 結構,對於其他的層,這個參數指向NET_BUFFER_LIST,或者爲NULL.
    
    FWPS_FILTER0 *filter
    
    這個結構體,我們之前有看過:
    
    typedef struct FWPS_FILTER0_ {
      UINT64                 filterId;
      FWP_VALUE0             weight;
      UINT16                 subLayerWeight;
      UINT16                 flags;
      UINT32                 numFilterConditions;
      FWPS_FILTER_CONDITION0 *filterCondition;
      FWPS_ACTION0           action;
      UINT64                 context;
      FWPM_PROVIDER_CONTEXT0 *providerContext;
    } FWPS_FILTER0;
    
    UINT64 flowContext,這個參數是和過濾數據相關聯的上下文結構。
    
    再來看,FWPS_CLASSIFY_OUT0 *classifyOut,這個結構體比較重要:
    
    這個是返回給調用者的結構體。
    
    struct FWPS_CLASSIFY_OUT0 {
      FWP_ACTION_TYPE actionType;
      UINT64          outContext;
      UINT64          filterId;
      UINT32          rights;
      UINT32          flags;
      UINT32          reserved;
    };
    
    可以參考http://msdn.microsoft.com/en-us/library/windows/hardware/ff551229(v=vs.85).aspx


    我們再從前面看,在DriverEntry最後面,我們有起一個線程來進行對包的數據的檢查,看是否需要修改,以及重新注入後發送。這個也必須根據其過濾條件有關。
    
    我們看一個複雜的callout的ClassifyFn函數的具體實現。
    
    void
    DDProxyClassify(
     _In_ const FWPS_INCOMING_VALUES* inFixedValues,
     _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
     _Inout_opt_ void* layerData,
     _In_ const FWPS_FILTER* filter,
     _In_ UINT64 flowContext,
     _Inout_ FWPS_CLASSIFY_OUT* classifyOut
    )


     #endif /// (NTDDI_VERSION >= NTDDI_WIN7)
    /* ++


    This is the classifyFn function of the datagram-data callout. It 
    allocates a packet structure to store the classify and meta data and 
    it references the net buffer list for out-of-band modification and 
    re-injection. The packet structure will be queued to the global packet 
    queue. The worker thread will then be signaled, if idle, to process 
    the queue.


    -- */
    {
     DD_PROXY_PENDED_PACKET* packet = NULL;
     DD_PROXY_FLOW_CONTEXT* flowContextLocal = (DD_PROXY_FLOW_CONTEXT*)(DWORD_PTR)flowContext;


     FWPS_PACKET_INJECTION_STATE packetState;
     KLOCK_QUEUE_HANDLE packetQueueLockHandle;
     BOOLEAN signalWorkerThread;


     #if(NTDDI_VERSION >= NTDDI_WIN7)
      UNREFERENCED_PARAMETER(classifyContext);
     #endif
      UNREFERENCED_PARAMETER(filter);


     _Analysis_assume_(layerData != NULL);


     //
     // We don't have the necessary right to alter the packet.
     // 首先檢查,我們是否有權利去修改這個包。
     if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0)
     {
      goto Exit;
     }


     //
     // We don't re-inspect packets that we've inspected earlier.
     //
     packetState = FwpsQueryPacketInjectionState(
       gInjectionHandle,
       layerData,
       NULL
     );


     //如果這個包注入的狀態是,之前已經被這個注入句柄注入過,就不用再處理了。
     if ((packetState == FWPS_PACKET_INJECTED_BY_SELF) ||
      (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF))
     {
      classifyOut->actionType = FWP_ACTION_PERMIT;
      if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
      {
       classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
      }


      goto Exit;
     }
                    
     //分配一個和過濾條件相匹配的空間。
     packet = ExAllocatePoolWithTag(
      NonPagedPool,
      sizeof(DD_PROXY_PENDED_PACKET),
      DD_PROXY_PENDED_PACKET_POOL_TAG
                     );
                    //分配失敗,直接退出,等待下一次處理。
     if (packet == NULL)
     {
      classifyOut->actionType = FWP_ACTION_BLOCK;
      classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
      goto Exit;
     }


     RtlZeroMemory(packet, sizeof(DD_PROXY_PENDED_PACKET));


     NT_ASSERT(flowContextLocal != NULL);


     packet->belongingFlow = flowContextLocal;
     DDProxyReferenceFlowContext(packet->belongingFlow);
     //AF_INET代表的是TCP或UDP,通過固定數據中的數據與來傳輸方向。
     if (flowContextLocal->addressFamily == AF_INET)
     {
      NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4);
      packet->direction = 
      inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].\
       value.uint32;
     }
     else
     {
      NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6);
      packet->direction = 
      inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_DIRECTION].\
      value.uint32;
     }
     //將NET_BUFFER_LIST結構體的指針賦給packer->netBufferList.
     packet->netBufferList = layerData;


     //
     // Reference the net buffer list to make it accessible outside of 
     // classifyFn.
     //
     //引用NET_BUFFER_LIST.
     FwpsReferenceNetBufferList(packet->netBufferList, TRUE);


     NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, 
                                         FWPS_METADATA_FIELD_COMPARTMENT_ID));
     packet->compartmentId = inMetaValues->compartmentId;


     if (packet->direction == FWP_DIRECTION_OUTBOUND)
     {
      NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(
        inMetaValues, 
        FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE));
      packet->endpointHandle = inMetaValues->transportEndpointHandle;


     if (flowContextLocal->addressFamily == AF_INET)
     {
      // See PREfast comments above.  Opaque pointer tricks PREfast.
      #pragma prefast ( suppress: 28193, "We are NOT ignoring this return value" )
      packet->ipv4RemoteAddr = 
      RtlUlongByteSwap( /* host-order -> network-order conversion */
       inFixedValues->incomingValue\
      [FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_ADDRESS].value.uint32);
     }
     else
     {
      RtlCopyMemory(
       (UINT8*)&packet->remoteAddr,
       inFixedValues->incomingValue\
       [FWPS_FIELD_DATAGRAM_DATA_V6_IP_REMOTE_ADDRESS].value.byteArray16,
       sizeof(FWP_BYTE_ARRAY16)
      );


     }
     packet->remoteScopeId = inMetaValues->remoteScopeId;


     if (FWPS_IS_METADATA_FIELD_PRESENT(
      inMetaValues, 
      FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA))
     {
      NT_ASSERT(inMetaValues->controlDataLength > 0);


      packet->controlData = ExAllocatePoolWithTag(
                                 NonPagedPool,
                                 inMetaValues->controlDataLength,
                                 DD_PROXY_CONTROL_DATA_POOL_TAG
                                 );
     if (packet->controlData == NULL)
     {
      classifyOut->actionType = FWP_ACTION_BLOCK;
      classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
      goto Exit;
     }


     RtlCopyMemory(
      packet->controlData,
      inMetaValues->controlData,
      inMetaValues->controlDataLength
     );


      packet->controlDataLength =  inMetaValues->controlDataLength;
     }
     }
     else
     {
      NT_ASSERT(packet->direction == FWP_DIRECTION_INBOUND);


      if (flowContextLocal->addressFamily == AF_INET)
      {
       NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4);
       packet->interfaceIndex = 
        inFixedValues->incomingValue\
        [FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_INDEX].value.uint32;
       packet->subInterfaceIndex = 
       inFixedValues->incomingValue\
       [FWPS_FIELD_DATAGRAM_DATA_V4_SUB_INTERFACE_INDEX].value.uint32;
      }
     else
     {
      NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6);
      packet->interfaceIndex = 
       inFixedValues->incomingValue\
       [FWPS_FIELD_DATAGRAM_DATA_V6_INTERFACE_INDEX].value.uint32;
      packet->subInterfaceIndex = 
       inFixedValues->incomingValue\
       [FWPS_FIELD_DATAGRAM_DATA_V6_SUB_INTERFACE_INDEX].value.uint32;
     }
      
     NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(
      inMetaValues, 
      FWPS_METADATA_FIELD_IP_HEADER_SIZE));
     NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(
      inMetaValues, 
     FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE));
     packet->ipHeaderSize = inMetaValues->ipHeaderSize;
     packet->transportHeaderSize = inMetaValues->transportHeaderSize;


     packet->nblOffset = 
      NET_BUFFER_DATA_OFFSET(NET_BUFFER_LIST_FIRST_NB(packet->netBufferList));
     }


     KeAcquireInStackQueuedSpinLock(
      &gPacketQueueLock,
      &packetQueueLockHandle
     );


     if (!gDriverUnloading)
     {
      signalWorkerThread = IsListEmpty(&gPacketQueue);


      InsertTailList(&gPacketQueue, &packet->listEntry);
      packet = NULL; // ownership transferred


      classifyOut->actionType = FWP_ACTION_BLOCK;
      classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
      classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
     }
     else
     {
      //
      // Driver is being unloaded, permit any incoming packets.
      //
      signalWorkerThread = FALSE;


      classifyOut->actionType = FWP_ACTION_PERMIT;
      if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
      {
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
      }
     }


     if (signalWorkerThread)
     {
      KeSetEvent(
       &gPacketQueueEvent, 
       0, 
       FALSE
      );
     }


      KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle);


Exit:
   
     if (packet != NULL)
     {
      DDProxyFreePendedPacket(packet, packet->controlData);
     }


     return;
}
    
     這裏是放在函數外注入修改,這裏是通過線程來處理的,我們先看重新注入函數。
     
     NTSTATUS
     DDProxyCloneModifyReinjectInbound(
      _In_ DD_PROXY_PENDED_PACKET* packet
     )
     /* ++


     This function clones the inbound net buffer list and, if needed, 
     modifies the source port and/or source address and receive-injects 
     the clone back to the tcpip stack.


     -- */
     {
      NTSTATUS status = STATUS_SUCCESS;


      NET_BUFFER_LIST* clonedNetBufferList = NULL;
      NET_BUFFER* netBuffer;
      UDP_HEADER* udpHeader;
      ULONG nblOffset;
      NDIS_STATUS ndisStatus;


     //
     // For inbound net buffer list, we can assume it contains only one 
     // net buffer.
     //
      netBuffer = NET_BUFFER_LIST_FIRST_NB(packet->netBufferList);
   
      nblOffset = NET_BUFFER_DATA_OFFSET(netBuffer);


     //
     // The TCP/IP stack could have retreated the net buffer list by the 
     // transportHeaderSize amount; detect the condition here to avoid
     // retreating twice.
     //
      if (nblOffset != packet->nblOffset)
      {
       NT_ASSERT(packet->nblOffset - nblOffset == packet->transportHeaderSize);
       packet->transportHeaderSize = 0;
      }


      //
      // Adjust the net buffer list offset to the start of the IP header.
      //
      ndisStatus = NdisRetreatNetBufferDataStart(
          netBuffer,
          packet->ipHeaderSize + packet->transportHeaderSize,
          0,
          NULL
      );
      _Analysis_assume_(ndisStatus == NDIS_STATUS_SUCCESS);


      //
      // Note that the clone will inherit the original net buffer list's offset.
      //


      status = FwpsAllocateCloneNetBufferList(
         packet->netBufferList,
         NULL,
         NULL,
         0,
         &clonedNetBufferList
      );


      //
      // Undo the adjustment on the original net buffer list.
      //


      NdisAdvanceNetBufferDataStart(
       netBuffer,
       packet->ipHeaderSize + packet->transportHeaderSize,
       FALSE,
       NULL
      );


      if (!NT_SUCCESS(status))
      {
       goto Exit;
      }


      //
      // Check to see if port modification is required.
      //
      if ((packet->belongingFlow->protocol == IPPROTO_UDP) && 
       (packet->belongingFlow->toRemotePort != 0))
      {
       netBuffer = NET_BUFFER_LIST_FIRST_NB(clonedNetBufferList);


       //
       // Advance to the beginning of the transport header (i.e. UDP header).
       //
       NdisAdvanceNetBufferDataStart(
         netBuffer,
         packet->ipHeaderSize,
         FALSE,
         NULL
       );


       udpHeader = NdisGetDataBuffer(
         netBuffer,
         sizeof(UDP_HEADER),
         NULL,
         sizeof(UINT16),
         0
       );
       NT_ASSERT(udpHeader != NULL); // We can assume UDP header in a net buffer
                // is contiguous and 2-byte aligned.
       _Analysis_assume_(udpHeader != NULL);
      
       udpHeader->destPort = 
       packet->belongingFlow->toRemotePort; 
                                    // This is our new source port -- or
                                    // the destination port of the original
                                    // outbound traffic.
       udpHeader->checksum = 0;


       //
       // Undo the advance. Net buffer list needs to be positioned at the 
       // beginning of IP header for address modification and/or receive-
       // injection.
       //
       ndisStatus = NdisRetreatNetBufferDataStart(
         netBuffer,
         packet->ipHeaderSize,
         0,
         NULL
         );
       _Analysis_assume_(ndisStatus == NDIS_STATUS_SUCCESS);


      }


      if (packet->belongingFlow->toRemoteAddr != NULL)
      {
      status = FwpsConstructIpHeaderForTransportPacket(
         clonedNetBufferList,
         packet->ipHeaderSize,
         packet->belongingFlow->addressFamily,
         packet->belongingFlow->toRemoteAddr,  
                                       // This is our new source address --
                                       // or the destination address of the
                                       // original outbound traffic.
         (UINT8*)&packet->belongingFlow->localAddr, 
                                       // This is the destination address of
                                       // the clone -- or the source of the
                                       // original outbound traffic.
         packet->belongingFlow->protocol,
         0,
         NULL,
         0,
         0,
         NULL,
         0,
         0
         );


      if (!NT_SUCCESS(status))
      {
       goto Exit;
      }
      }


      status = FwpsInjectTransportReceiveAsync(
          gInjectionHandle,
          NULL,
          NULL,
          0,
          packet->belongingFlow->addressFamily,
          packet->compartmentId,
          packet->interfaceIndex,
          packet->subInterfaceIndex,
          clonedNetBufferList,
          DDProxyInjectComplete,
          packet
        );


        if (!NT_SUCCESS(status))
        {
         goto Exit;
        }


     clonedNetBufferList = NULL; // ownership transferred to the 
                               // completion function.


Exit:


     if (clonedNetBufferList != NULL)
     {
      FwpsFreeCloneNetBufferList(clonedNetBufferList, 0);
     }


      return status;
}

寫在最後,關於WFP,自己只是懂了點皮毛,這個需要非常好的網絡相關的知識,而且需要對

微軟的NDIS非常熟悉,雖然自己差不多將NDIS看了許多,但是還不夠熟練。

而且,關於WFP中,微軟定義了非常多了不好理解的數據結構和一些過濾層,

這應該是一個大工程,需要自己經常,反覆揣摩。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章