How to use WFP to change target IP

Excuse me!

I want to uses WFP to change target IP address from UDP package,I find a WFP demo in DDK(stmedit),but I can't find how to filter UDP package,I can't find any IP address structure in this demo.Can you gave me some advice or some function to change UDP package ip address.Of course,the best way is some demos.

Thank you

你好!

我想利用WFP拦截和修改部分UDP数据包目标IP,我从DDK里找到一个利用WFP的例子,但是我不知道如何去过滤UDP数据包,也没有找到任何修改IP数据包的函数。你能否可以给我一些这样的DEMO,或者这样的函数。

谢谢!

[1055 byte] By [huzhongxing] at [2008-1-21]
# 1

Hi,

Please review the "Packet Modification Examples" topic in the WDK (http://msdn2.microsoft.com/en-us/library/aa938500.aspx) to see how to use WFP to modify IP packets.

From your post, I assume that you would like to modify target IP address for outbound UDP packets. For that purpose, you can register your callout at FWPM_LAYER_DATAGRAM_DATA_V{4|6} with the FWPM_CONDITION_DIRECTION set to FWP_DIRECTION_OUTBOUND and with FWPM_CONDITION_IP_PROTOCOL set to UDP.

When your ClassifyFn function is invoked for this layer/direction, the layerData parameter pointers to an Ndis NET_BUFFER_LIST structure which contains one or more NET_BUFFER structures; and each NET_BUFFER structure corresponds to one packet. (you can refer to Ndis documentations in the WDK for details on NET_BUFFER_LIST/NETBUFFER).

One important attribute of NET_BUFFER (i.e. packet) is its "Data Offset Position". Data packets indicated by WFP to callout drivers have different offsets depending on packet directions and layers (see http://msdn2.microsoft.com/en-us/library/aa504926.aspx for more details). Data offset can be adjusted using the NdisRetreatNetBufferDataStart or NdisAdvanceNetBufferDataStart function. To get to the data buffer pointed to by the current offset, use NdisGetDataBuffer function.

At outbound datagram data layer, UDP packets are indicated with offset set to the beginning of the UDP header. Therefore you can call NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_BUFFER((NET_BUFFER_LIST*)layerData, ...) to get to the UDP header of the first UDP packet.

As mentioned in the "Inspecting Packet and Stream Data" section (http://msdn2.microsoft.com/en-us/library/aa938503.aspx), packet modification (e.g. changing target IP) must be performed on a clone of the original packet; and then the original is blocked/absorbed while the clone is modified and re-injected back to the network. Please refer to the WDK for details about clone-drop-reinject.

Also note that you may need to adjust the UDP checksum after modification.

Hope this helps,

Biao.W. [MSFT]

BiaoWang[MSFT] at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 2

Thank you very much!But I still have some unclear points.

First ,Where should I use NdisRetreatNetBufferDataStart or NdisAdvanceNetBufferDataStart to adjust Data offset?

I used NdisGetDataBuffer function follow your advice,But I find it's difficult to fill the parameters.I fill it like is:

pFrame=NdisGetDataBuffer(

NET_BUFFER_LIST_FIRST_NB(clonedNetBufferList),FWPS_LAYER_OUTBOUND_IPPACKET_V4,

NULL, 1, 0);

but I'm afraid that this looks a little laughable,I still can't get UDP header form clonedNetBufferList!

In DDK, I can't find a struct include IP address information,such as UDP target IP address. should I must build data packet structure by my self?

My english is not very good and maybe there are some mistakes,I can't read msdn as well, If I wasted your time,I feel so sorry!

huzhongxing at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 3

At which layer do you register the callout driver?

From your post it appears that it registers at OUTBOUND_IPPACKET_V4. At this layer packets begin with IP headers and here is how you could get to the memory that contain the IP header. (To modify target IP, you don't need to get to the UDP header)

BYTE* pFrame;

NET_BUFFER* packet = NET_BUFFER_LIST_FIRST_NB(clonedNetBufferList);

pFrame = NdisGetDataBuffer(packet,

inMetaValues->ipHeaderSize, NULL, 1, 0);

ASSERT(pFrame);

pFrame += 16; // Now pFrame points to destination IP, which you can modify.

packet = packet->Next;

// modify second NB and so forth if present

// call FwpsInjectNetworkSendAsync0 to inject the clone back to the network.

On the other hand, if you register at outbound DATAGRAM_DATA as I suggested earlier, then you don't need to modify the clone at all (IP header is not yet built when packets are indicated to this layer anyway). Instead you call FwpsInjectTransportSendAsync0 to re-inject the clone -- you just need to fill out the FWPS_TRANSPORT_SEND_PARAMS0 structure with the new destination address.

Biao.W. [MSFT]

BiaoWang[MSFT] at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 4

Mr. Biao.W.

i'm troubling in a problem of getting TCP data.i can get TCP header like this code.

pTcpheader = NdisGetDataBuffer(packet,

inMetaValues->ipHeaderSize+inMetaValues->transportHeaderSize, NULL, 1, 0);

pTcpheader +=inMetaValues->ipHeaderSize;

i want to get TCP data like this code.

pTcpData = NdisGetDataBuffer(packet,

inMetaValues->ipHeaderSize+inMetaValues->transportHeaderSize+NET_BUFFER_DATA_LENGTH(packet), NULL, 1, 0);

pTcpData+=inMetaValues->ipHeaderSize+inMetaValues->transportHeaderSize;

but BlueScreen happened.

if in OUTBOUND_IPPACKET_V4 we cant get the TCP data.

how should i do.

DengTianyu at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 5

Tianyu,

It is important to note that given a packet (i.e. NET_BUFFER) there is no guarantee that the IP header, the Transport header (e.g. TCP header), and the protocol data resides in one contiguous memory. It is quite possible that the IP header lives in one MDL, the TCP header lives in another MDL, and the data in yet another MDL (or more). (you can assume an IP header will not span across more than 1 MDLs; same is true for a Transport Header).

You are calling NdisGetDataBuffer with "Storage" parameter set to NULL, which means if BytesNeeded is not in one contiguous buffer (i.e. one MDL), the function will return NULL.

So the recommended method of accessing NET_BUFFER is to first move the offset to the desired spot using NdisRetreat/Advance functions; and then call NdisGetDataBuffer with BytesNeeded set to either the IP header *or* transport header size (but never the sum).

INBOUND_IPPACKET_V4 is not the ideal layer to get to TCP data because transportHeaderSize is not indicated to callouts. You should consider operating at INBOUND_TRANSPORT_V4 instead. At this layer, the offset of NET_BUFFER is already the beginning of TCP data.

If it is absolutely necessary to filter at INBOUND_IPPACKET_V4, you will need to parse the transport header and compute the TL header size yourself. Here is an example --

//

// For INBOUND_IPPACKET_V4 the packet offset is after IP header

//

pTcpheader = NdisGetDataBuffer(packet,<fixed-TCP-header-size>, NULL, 1, 0);

//

// Parse pTcpheader to account for variable sized options

//

NdisAdvanceNetBufferDataStart(packet, <real-TCP-header-size>, FALSE, NULL);

//

// Now the packet offset is at the beginning of TCP data.

//

pTcpData = NdisGetDataBuffer(packet,

NET_BUFFER_DATA_LENGTH(packet), NULL, 1, 0);

Hope this helps,

Biao.W.

BiaoWang[MSFT] at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 6

Mr. Biao.W.

Thanks a lot for your quick advices. it is very helpful to me.

i tested it.

i dont know what is the difference of the <fixed-TCP-header-size> and <real-TCP-header-size>

i hooked the OUTBOUND_IPPACKET_V4 becasue i want do something when we send data.

i tried like this code;

//

// For INBOUND_IPPACKET_V4 the packet offset is after IP header

//

pTcpheader = NdisGetDataBuffer(packet,inMetaValues->transportHeaderSize,NULL, 1, 0);

//

// Parse pTcpheader to account for variable sized options

//

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

//

// Now the packet offset is at the beginning of TCP data.

//

pTcpData = NdisGetDataBuffer(packet,

NET_BUFFER_DATA_LENGTH(packet), NULL, 1, 0);

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

// Now the packet offset is at the beginning of TCP data.
Packet = NdisGetDataBuffer(packet,NET_BUFFER_DATA_LENGTH(packet), NULL, 1, 0);

BlueScreen Happened.

you told me Parse pTcpheader to account for variable sized options

i guess it is offset,but it is always 0x50(80)

how should i account for variable sized options.

typedef struct _TCPHeader
{
USHORT sourcePort; // Source Port
USHORT destinationPort; // Destination Port
ULONG nSequence; // Sequence number
ULONG nAck; // Acknowledgement number

UCHAR unused:4; // Unused
UCHAR offset:4; // Data offset
UCHAR flags; // Flags

USHORT window; // Window size
USHORT checksum; // Checksum
USHORT urp; // Urgent Pointer
}TCPHeader, *PTCPHeader;

if it is same when i hooked OUTBOUND_IPPACKET_V4 Layer.

DengTianyu at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 7

Outbound processing is different because the packet offset begins with the IP header and you will need to advance past that. (see http://msdn2.microsoft.com/en-us/library/aa504926.aspx for more details on layer- & direction- specific packet data offsets).

Here is an example --

//

// For OUTBOUND_IPPACKET_V4 the packet offset is at the

// beginning of IP header

//

NdisAdvanceNetBufferDataStart(packet,

inMetaValues->ipHeaderSize,

FALSE,

NULL);

//

// Now the packet offset is at the beginning of TCP header.

//

pTcpheader = NdisGetDataBuffer(packet,

20 /*fixed-TCP-header-size*/,

NULL,

1,

0);

//

// Parse pTcpheader to account for variable sized options

//

NdisAdvanceNetBufferDataStart(packet,

<real-TCP-header-size>,

FALSE,

NULL);

//

// Now the packet offset is at the beginning of TCP data.

//

pTcpData = NdisGetDataBuffer(packet,

NET_BUFFER_DATA_LENGTH(packet),

NULL,

1,

0);

//

// After inspection, packet offsets must be restored prior

// to returning to WFP

//

NdisRetreatNetBufferDataStart(packet,

20 + <real-TCP-header-size>,

0,

NULL);

Please refer to the TCP RFC to see the details on TCP header format and parsing.

Biao.W.

BiaoWang[MSFT] at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 8

Mr. Biao Wang :

Thanks a lot for your reply.

i read TCP RFC and tested like your sample.

real-TCP-header-size= inMetaValues->transportHeaderSize-20;

but when i use this line to get data,blue screen has happened.

pTcpData = NdisGetDataBuffer(packet,

NET_BUFFER_DATA_LENGTH(packet),

NULL,

1,

0);

if i comment this line only,everything seems ok,but i have to get the pTcpData.

Would u like to give me some advices more.

DengTianyu at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 9

Tianyu,

As I mentioned earlier, "inMetaValues->transportHeaderSize" is not set for IPPACKET layer. IPPACKET layer is designed for Network layer filtering and not designed to filter Transport layer data such as TCP header and data.

You should consider registering at TRANSPORT layer (i.e. OUTBOUND_TRANSPORT_V4) for easy access to transport level information. At TRANSPORT layer transportHeaderSize will be set.

If you must filter at OUTBOUND_IPPACKET_V4, you will have to compute the transportHeaderSize yourself by manually parsing TCP header according to the RFC.

Biao.W.

BiaoWang[MSFT] at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 10

Additionally, you should be using the macro FWPS_IS_METADATA_FIELD_PRESENT() to determine if the metadata is available at that particular layer.

if you had made the check if(FWPS_IS_METADATA_FIELD_PRESENT(pInMetaValues,
FWPS_METADATA_TRANSPORT_HEADER_SIZE))

then you'd know that the information in that field is not valid.

As Biao states, you'd be best to get the transport info at the transport layers, as the network header has been peeled away (inbound) or not added yet (outbound)

DustyHarper at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 11

Mr. Dusty Harper:

Thanks a lot.

i want to get the TCP header+TCP data By filtering FWPM_LAYER_OUTBOUND_TRANSPORT_V4.

but i cant see TCP data. i can get the TCP header.

DengTianyu at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 12

How are you trying to get the TCP header and data?

Here is one thing that i can think of that might have hapenned:

It is possible that your header and data reside in non-contiguous memory(i.e in different MDLs). In that case, here is how you would call NdisGetDataBuffer

pTempBuffer = NdisGetDataBuffer(firstNB,
NBDataLength,
pFlatBuffer,
1,
0);

If all the data you require is in contiguous memory, your data will be returned through the pTempBuffer pointer. If your data is in non-contiugous memory, it will be copied into the location pointed to by pFlatBuffer. (You must allocate pFlatBuffer to be equal to or greater than the number of bytes you need which is NBDataLength as shown above).

You can read up on NdisGetDataBuffer here

http://msdn2.microsoft.com/en-us/library/bb259912.aspx

AnupamaVasanth at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 13

Tianyu,

I have two additions --

1) As I mentioned earlier, before filtering at a particular layer and packet direction, you should first review the "Data Offset Positions" documentation (http://msdn2.microsoft.com/en-us/library/aa504926.aspx). And you will find that for FWPS_LAYER_OUTBOUND_TRANSPORT_V4, the offset is "The beginning of the transport header". Therefore, before you can get to TCP data, you will need to advance past the TCP header by using NdisAdvanceNetBufferDataStart(packet, inMetaValues->transportHeaderSize, ...). After that you can use Anu's example above to get to the TCP data. Note that you must retreat by the same amount before you return from classifyFn.

2) One NET_BUFFER_LIST can contain multiple NET_BUFFERs (each NET_BUFFER corresponds to one TCP packet). So be sure to follow the Next pointer in the NET_BUFFER until you reach NULL.

Hope this helps,

Biao.W.

BiaoWang[MSFT] at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...
# 14

Mr. Biao.W.,Anupama Vasanth ,Dusty Harper:

Thank you very much.i saw the TCP data.(00-00-00-54-ff-53-4d-42-) it is a SMB data.

(i hooked in FWPM_LAYER_OUTBOUND_TRANSPORT_V4 )

packet = NET_BUFFER_LIST_FIRST_NB(clonedNetBufferList);

while(packet !=NULL){

//first,get tcp header

tcpHeader=NdisGetDataBuffer(packet,inMetaValues->transportHeaderSize,NULL,1,0);

//...see the tcp header,it looks no problem.

//2.Move Offset to TCP data
NdisAdvanceNetBufferDataStart(
packet,
inMetaValues->transportHeaderSize,
FALSE,
NULL);

//3. get tcp data.

//i did as Mr. Anupama Vasanth said,the blue Screen doesnt happen.it looks OK.

pFlatBuffer=(unsigned char *) ExAllocatePool(NonPagedPool, NET_BUFFER_DATA_LENGTH(packet)+1);

pPacket=NdisGetDataBuffer(packet,NET_BUFFER_DATA_LENGTH(packet),pFlatBuffer,1,0);

if(pPacket==NULL)
pTcpData=pFlatBuffer;
else
pTcpData=pPacket;

ASSERT(pTcpData);

//Reset Offset

NdisRetreatNetBufferDataStart(packet,
inMetaValues->transportHeaderSize,

0,

NULL);

DoTraceMessage(TRACE_MSG,"TCP Data Packets=%x%x%x%x%x%x%x%x%x%x \r\n",pTcpData[0],pTcpData[1],pTcpData[2],pTcpData[3],pTcpData[4],pTcpData[5],pTcpDataDevil,pTcpData[7],pTcpDataMusic,pTcpData[9]);

packet=packet->Next;

}

thanks a lot.

DengTianyu at 2007-10-3 > top of Msdn Tech,Windows Networking Development,Windows Filtering Platform (WFP)...