前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Windows驱动_WFP之四WFP代码基本流程的剖析

Windows驱动_WFP之四WFP代码基本流程的剖析

作者头像
战神伽罗
发布2020-06-01 10:08:51
2.7K0
发布2020-06-01 10:08:51
举报

总说程序员是孤独的,因为,大部分的时间都在和机器打交道。大部分的时间都在自言自语。我的内心需要足够的强大。这种强大时建立的自信的基础上的。而自信又是建立在实力基础上的。实力又是建立在积累的基础上。积累又是建立在时间的基础上。所以归根结底,就是,需要花费更多的时间。第二,需要有足够的兴趣爱好。这两点对于现在的我来说,都有。既然,自己选择了这条路,就应该义无反顾的走下去,坚持的走下去。孤独,我不怕,困难,我也不怕,永远向上的动力,爱好,对知识的渴望,支持者我。相信自己,相信明天。

今天实际看一下,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函数玩系统中增加一个子层。

代码语言:javascript
复制

 
           DWORD WINAPI FwpmSubLayerAdd0(
     _In_      HANDLE engineHandle,
     _In_      const FWPM_SUBLAYER0 *subLayer,
     _In_opt_  PSECURITY_DESCRIPTOR sd
       );

这里我们主要来看第二个参数,FWPM_SUBLAYER0这个结构体。

代码语言:javascript
复制

 
     typedef struct FWPM_SUBLAYER0_ {
     GUID               subLayerKey;
     FWPM_DISPLAY_DATA0 displayData;
     UINT16             flags;
     GUID               *providerKey;
     FWP_BYTE_BLOB      providerData;
     UINT16             weight;
     } FWPM_SUBLAYER0;

这里,我们主要看第一个GUID,后面的需要在例子后分析。这个可以定义的GUID.

代码语言:javascript
复制

 
     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
     );

这里主要是第二个参数的设置:

代码语言:javascript
复制

 
     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.

代码语言:javascript
复制

 
     NTSTATUS NTAPI FwpmCalloutAdd0(
     _In_       HANDLE engineHandle,
     _In_       const FWPM_CALLOUT0 *callout,
     _In_opt_   PSECURITY_DESCRIPTOR sd,
     _Out_opt_  UINT32 *id
     );

这里还是看第二个参数,FWPM_CALLOUT0.

代码语言:javascript
复制

 
     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增加一个过滤对象到系统中。

代码语言:javascript
复制

 
      DWORD WINAPI FwpmFilterAdd0(
     _In_       HANDLE engineHandle,
     _In_       const FWPM_FILTER0 *filter,
     _In_opt_   SECURITY_DESCRIPTOR sd,
     _Out_opt_  UINT64 *id
       );

这里还是第二个参数,const FWPM_FILTER0 *filter,非常复杂的结构,这个是精髓,必须好好看。

代码语言:javascript
复制

 
     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;

我们先把,结构体中包含的结构,进行展开。

代码语言:javascript
复制

 
     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.

代码语言:javascript
复制

 
     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;

这里,我的理解是,这个值代表代表一个过滤的一个比重,这个跟你在哪一层过滤都有关系。 下面看一下,最最重要的一个结构体,过滤的条件。当这所有的条件的满足的情况下,定义的过滤动作才开始。

代码语言:javascript
复制

 
     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

代码语言:javascript
复制

 
 
     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中的设置,因为跟微软玩,必须都符合它的要求。 下面再看下,过滤动作的这个结构体。

代码语言:javascript
复制

 
     typedef struct FWPM_ACTION0_ {
       FWP_ACTION_TYPE type;
       union {
       GUID filterType;
       GUID calloutKey;
       };
     } FWPM_ACTION0;

这里要提一下的就是这个calloutKey,这个值正好跟之前calloutKey相吻合,主要我们向设备对象注册的callout,向过滤引擎注册的callout,以及和过滤的callout都指向同一个GUID值。 下面就是我们来看具体的CALLOUT函数的执行了,当满足这些条件后,CALLOUT被过滤引擎调用。 我们具体来看一下这个具体的CALLOUT函数:

代码语言:javascript
复制

 
     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

具体的数据域。

代码语言:javascript
复制

     typedef struct FWPS_INCOMING_VALUE0_ {
     FWP_VALUE0 value;
     } FWPS_INCOMING_VALUE0;

这个值一看就知道,就是代表那些固定的值。比如一些IP地址,PORT等等。 再看:

代码语言:javascript
复制

 
     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 这个结构体,我们之前有看过:

代码语言:javascript
复制

 
     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,这个结构体比较重要: 这个是返回给调用者的结构体。

代码语言:javascript
复制

 
     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函数的具体实现。

代码语言:javascript
复制

 
     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中,微软定义了非常多了不好理解的数据结构和一些过滤层,

这应该是一个大工程,需要自己经常,反复揣摩。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档