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

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

Windows过滤驱动 WFP代码基本流程的剖析 bypass前期准备相关推荐

  1. 文件系统过滤驱动总结

    文件系统过滤驱动 . 1 文件系统过滤驱动工作原理 Windows NT内核操作系统的驱动模型采用分层结构,如图1所示.图中左边是一个设备对象栈,设备对象 是操作系统为帮助软件管理硬件而创建的数据结构 ...

  2. Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platfrom))

    catalog 0.引言 1.Windows 2000网络结构和OSI模型 2.NDIS驱动 3.NDIS微端口驱动编程实例 4.NDIS中间层驱动编程实例 5.TDI驱动 6.TDI驱动 7.TDI ...

  3. 【驱动开发】Windows过滤平台(WFP,Windows Filtering Platform)

    文章目录 Windows的发展历程 TDI简介 WFP简介 用户态基础过滤引擎(BFE) 内核态过滤引擎(KMFE) 垫片(Shim) 分层(Layer) 子层(Sub Layer) 过滤器(Filt ...

  4. wfp 禁用ip_WFP网络过滤驱动——限制网站访问

    [md] 0x1前言 文中的注释有的来自微软官方的解释翻译,有的来自Windows内核安全与驱动开发书中的解释,也有的来自我个人的理解.代码功能是在Windows内核安全与驱动开发第15章中Wfpsa ...

  5. wfp 禁用ip_[原创]WFP网络过滤驱动——限制网站访问

    0x1前言 文中的注释有的来自微软官方的解释翻译,有的来自Windows内核安全与驱动开发书中的解释,也有的来自我个人的理解.代码功能是在Windows内核安全与驱动开发第15章中Wfpsample代 ...

  6. WFP网络过滤驱动-限制网站访问

    文章目录 前言 WFP入门介绍 WFP基本架构 名词解释 代码基本结构 代码示例 前言 WFP Architecture - Win32 apps | Microsoft Learn是一个网络流量处理 ...

  7. Windows驱动_文件系统微小过滤驱动之一初识MiniFilter

    人的一生,就那么几十年,所有的人都无法知道下一刻会发生什么,我们活在当下,只能努力将自己目前的工作或生活过好.船到桥头自然直,况且,自己这么多年,经历了多多少少的风浪太多了,说到底还是不够自信,我应该 ...

  8. Windows 文件过滤驱动经验总结

    by ai3000 看了 ChuKuangRen 的第二版<文件过滤驱动开发教程>后,颇有感触.我想,交流都是 建立在平等的基础上,在抱怨氛围和环境不好的同时应该先想一想自己究竟付出了多少 ...

  9. Windows文件系统过滤驱动开发教程(0,1,2)

    0. 作者,楚狂人自述 我长期网上为各位项目经理充当"技术实现者"的角色.我感觉Windows文件系统驱动的开发能找到的资料比较少.为了让技术经验不至于遗忘和引起大家交流的兴趣我以 ...

最新文章

  1. Tensorflow[基础篇]——LSTM的理解与实现
  2. html制作百度首页的图片不显示,html实现像百度的首页效果一样的背景图(代码)...
  3. 当深度学习遇上量化交易——公开信息篇
  4. 原生html5时间组件,JFinal遇到了原生Html5时间组件格式转换问题怎么处理?
  5. Python与机器视觉(x)图像差分-图像相减
  6. Struts2中动态的指定返回的结果集
  7. bzoj2839 集合计数
  8. linux下中文字库,Linux下中文乱码及中文字体缺失问题的解决
  9. 企业工资管理系统论文
  10. 绑定host:windows与模拟器之绑定host集结
  11. 捋一捋二分类和多分类中的交叉熵损失函数
  12. jQuery幻灯片插件Skippr
  13. php微信摇一摇,申请开通微信摇一摇
  14. Matlab plot 光滑曲线
  15. 记录一次mongoDB错误 errmsg: cannot use the part () to traverse the element
  16. VS2017 Git failed with a fatal error. Git failed with a fatal error. Need to specify how to reconcil
  17. Android 飞机大战
  18. input密码输入框自动填充和提示
  19. Tableau数据可视化
  20. 下载vue-router

热门文章

  1. 计算机分子模拟的意义包括,计算机分子模拟
  2. 首个非逆向,不需要手机!的微信视频号采集方案
  3. 怎么恢复删除的微信聊天记录?手残党终于有救了
  4. ORACLE内核参数
  5. 数组排序之后相邻数的最大差值
  6. 2020年中国球墨铸管行业发展背景、竞争格局及政策环境分析,城市管道建设带来行业增量,新兴铸管是行业龙头「图」
  7. matlab中利用快速傅里叶变换对股票价格进行频域分析
  8. 番茄学习--番茄工具推荐
  9. 构建orangePi r1 plus (RK3328)系统的整个过程
  10. 【文件格式探究】EP.1 对ePub文件格式的初探