今天實際看一下,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中,微軟定義了非常多了不好理解的數據結構和一些過濾層,
這應該是一個大工程,需要自己經常,反覆揣摩。