[Winpcap-users] Calculating TCP checksum after altering payload - 2

Vidar Evenrud Seeberg vseeberg at netcom.no
Wed Feb 8 11:55:43 GMT 2006


Hello again!

Thank you, Davis and Vasily, for quick replies!
I am still stuck and hope that you (and hopefully also others) could look
into this problem some more. It should be an interesting topic since it
regards checksum  calculations after changing payload information (e.g. in
anonymizing software, for which my research concerns).

It still produces incorrect checksum, no matter if I use csum, check_tcp_sum
or in_cksum as functions for TCP CRC calculations. csum and in_cksum
produces the same CRC. The  CRC from tcp_check_sum differs from the two
others. I had to alter check_tcp_sum a bit for dealing with the new payload
I created. This is because the new payload is not  necessarily following the
TCP-portion in RAM.

I am sniffing packets on the machine where I am the recipient. I don't think
this is the cause of trouble because the TCP checksum is calculated
correctly for (packets  without payload) and for the (IP CRC no matter if
payload is present or not).

I hope some of you can afford to spend some time debugging my code and give
me some advise leading me towards a solution. Please have in mind that I am
not a coding guru,  so I would appreciate as detailed explanations as
possible.

Many thanks in advance!

Here is the code including the three functions providing checksums in
different ways. I have commented the code thoroughly to make it easier to
understand what I do and how I think.


#include <stdio.h> 
#include "string.h"
#include "stdlib.h"

// WinPCAP includes 
#include <pcap.h> 
#include <remote-ext.h> 

//Defines
#define SIZE_ETHERNET 14
#define ETHER_ADDR_LEN 6

//**********************************************************
// STRUCTURES
//**********************************************************

struct ethernet_header {
	u_char ether_dhost[ETHER_ADDR_LEN];    /* destination host address
*/
	u_char ether_shost[ETHER_ADDR_LEN];    /* source host address */
	u_short ether_type;                    /* IP? ARP? RARP? etc */
};

// 6 byte MAC Address 
typedef struct mac_address { 
    u_char byte1; 
    u_char byte2; 
    u_char byte3; 
    u_char byte4; 
    u_char byte5; 
    u_char byte6; 
}mac_address; 


// 4 bytes IP address 
typedef struct ip_address{ 
    u_char byte1; 
    u_char byte2; 
    u_char byte3; 
    u_char byte4; 
}ip_address; 


// 20 bytes IP Header 
typedef struct ip_header{ 
    u_char ver_ihl;		// Version (4 bits) + Internet header length
(4 bits) 
    u_char tos; 		// Type of service 
    u_short tlen; 		// Total length 
    u_short identification; 	// Identification 
    u_short flags_fo; 		// Flags (3 bits) + Fragment offset (13
bits) 
    u_char ttl; 		// Time to live 
    u_char proto; 		// Protocol 
    u_short crc; 		// Header checksum 
    ip_address saddr; 		// Source address 
    ip_address daddr; 		// Destination address 
    // u_int op_pad; 		// Option + Padding -- NOT NEEDED! 
}ip_header; 


typedef struct tcp_header { 
	u_short sport; 	// Source port 
	u_short dport; 	// Destination port 
	u_int seqnum; 	// Sequence Number 
	u_int acknum; 	// Acknowledgement number 
	u_char th_off; 	// Header length 
	u_char flags; 	// packet flags 
	u_short win; 	// Window size 
	u_short crc; 	// Header Checksum 
	u_short urgptr; // Urgent pointer...still don't know what this is...

}tcp_header; 



//**********************************************************
// Function prototypes
//**********************************************************
u_char *anonymizePayload(u_char *pkt_data);
u_short csum (unsigned short *buf, int nwords);
u_short check_tcp_sum(struct ip_header *ip, struct tcp_header *tcp, int
totlen, int tcplen, int paylen, char *payloads);
u_short in_cksum(u_short *addr, int len);



//**********************************************************
// FUNCTION: anonymizePayload
// Receives raw packets one by one from AnonMain.c
// Alters headerdata and/or payload
//**********************************************************
u_char *anonymizePayload(u_char *pkt_data)
{
	const struct ethernet_header *ethernet;	/* The ethernet header */
	const struct ip_header *ip;		/* The IP header */
	const struct tcp_header *tcp;		/* The TCP header */
	ip_header new_ip; 			/* IP header of new outgoing
packet*/
	tcp_header new_tcp; 			/* TCP header of new
outgoing packet*/

	char *payload;				/* Packet payload from
incoming packet*/
	u_char payloadstring[1530];		/* Payload string for new
packet*/
	int i, j;				/* Counters for loops used
e.g. when converting to short array*/
	u_int size_ip, size_tcp;		/* Size of ip and tcp
headers (without payload)*/
	int size_payload;			/* Size of payload*/
	static char * strpointer;		/* Points to a searched-for
string, part of "payloadstring"*/
	u_short tcplen;				/* Size of tcp header,
calculated*/
	u_short tcptos;				/* This is actually protocol
field in IP header (OK, BAD name...)*/
	u_short *size_tcpandpayload;		/* size_tcp+size_payload for
new packet
	
	u_short tcp_hdrcrc[1700];		/* Array built for
calculating tcp crc*/
	u_short payload_shortarray[1700];	/* This is "payloadstring"
converted to short-array*/
	u_short ip_hdrcrc[10];			/* Array built for
calculating ip crc*/
	u_short ip_tos = htons(0x0800); 	/* IP.protocol field
hardcoded*/
	u_char pkt[1600];			/* The assembled packet
returned to AnonMain.c for dumping to file*/
	u_char a, b;				/* Used to convert
"payloadstring" to a short-array*/
	
	mac_address *srcmac;			/* Source MAC*/
	mac_address *destmac; 			/* Destination MAC*/


	//-----------------------------------------------
	// MACs get values
	//-----------------------------------------------
	srcmac = (mac_address *)pkt_data; 
	destmac = (mac_address *)(pkt_data + 6); 
	
	
	//-----------------------------------------------
	// Fetching ethernet and IP headers from
	// the u_char pkt_data received from AnonMain.c
	//-----------------------------------------------
	ethernet = (struct ethernet_header*)(pkt_data);
	ip = (struct ip_header*)(pkt_data + SIZE_ETHERNET);
	size_ip = (ip->ver_ihl & 0xf) * 4; //Gets length of IP header with
options
	if (size_ip < 20) {
		printf("   * Invalid IP header length: %u bytes\n",
size_ip);
		return NULL;
	}
		

	//-----------------------------------------------
	// Working on TCP only (for now
	//-----------------------------------------------
	if( ip->proto == 0x06 ) 
	{
		//-----------------------------------------------
		// Fetching TCP header and payload
		//-----------------------------------------------
		tcp = (struct tcp_header*)(pkt_data + SIZE_ETHERNET +
size_ip); //TCP header
		size_tcp = tcp->th_off/4;
		if (size_tcp < 20) {
			printf("   * Invalid TCP header length: %u bytes\n",
size_tcp);
			return  NULL;
		}
		payload = (u_char *)(pkt_data + SIZE_ETHERNET + size_ip +
size_tcp); //This is a pointer to the payload
		size_payload = ntohs(ip->tlen) - (size_ip + (size_tcp));


		//-----------------------------------------------
		// Converting *payload to a char string
		// According to Kernighan this should be done
		// for reliable character alterations
		// Results of alterations of *strings are "undefined"
		//-----------------------------------------------
		if (size_payload > 0)
		{
			for(i = 0; i < size_payload; i++)
			{
				if (isprint(*payload))
				{
					payloadstring[i] = *payload;
				}
				else if(iscntrl(*payload))
				{
					if(*payload == 0x0D)
					{
						payloadstring[i] = '\r';
					}
					else if(*payload == 0x0A)
					{
						payloadstring[i] = '\n';
					}
					else
						payloadstring[i] = '.';
				}
				else
				{
					payloadstring[i] = '.';
				}
				payload++;
			}
			payloadstring[i] = '\0'; //Terminating the string
		}
		else
			payloadstring[0] = '\0';

	}
	
	
	//-----------------------------------------------
	// An example of payload alteration
	// "gzip" is substituted by "tull"
	//-----------------------------------------------
	if(strpointer = strstr (payloadstring,"gzip"))
		strncpy(strpointer,"tull",4);
	

	
	//----------------------------------------------------------------
	// Construct packet
	// Repeating header fields from incoming packet
	// TCP and IP CRCs must be set to zero before calculating checksum 
	//----------------------------------------------------------------

	// Setup IP Header 
	new_ip.ver_ihl = ip->ver_ihl; 
	new_ip.tos = ip->tos; 
	new_ip.tlen = ip->tlen; 
	new_ip.identification = ip->identification; 
	new_ip.flags_fo = ip->flags_fo; 
	new_ip.ttl = ip->ttl; 
	new_ip.proto = ip->proto; 
	new_ip.crc = 0x00;
	new_ip.saddr = ip->saddr;
	new_ip.daddr = ip->daddr; 
  
	 // Setup TCP Header 
	new_tcp.sport = tcp->sport;  
	new_tcp.dport = tcp->dport; 
	new_tcp.seqnum = tcp->seqnum; 
	new_tcp.acknum = tcp->acknum;
	new_tcp.th_off = tcp->th_off;
	//new_tcp.flags = 0x07; //Example of alterations. Sets FIN, SYN, RST
	new_tcp.flags = tcp->flags; 
	new_tcp.win = tcp->win; 
	new_tcp.urgptr = tcp->urgptr; 
	new_tcp.crc = 0x00; 
 

	//----------------------------------------------------------------
	// Calculate IP CRC
	// This works as it should
	//----------------------------------------------------------------
	memset(ip_hdrcrc, 0, 20); 
	memcpy(ip_hdrcrc, &new_ip, 20); 
	new_ip.crc = csum( ip_hdrcrc, 10 ); /* OBS: THIS WORKS AS IT SHOULD
*/


	//----------------------------------------------------------------
	// Calculate TCP CRC
	// This works as it should when there's no payload (the "else"-part)
	// Incorrect checksum calculated when payload is present (This is my
	// real problem)
	//----------------------------------------------------------------
	if(size_payload>0)
	{
		//Calculating TCP CRC
		//THIS PART OF THE IF STATEMENT DOES NOT WORK
		//PRODUCES INCORRECT CHECKSUM

	
//----------------------------------------------------------------
		// Convert payloadstring to short-array. I thing this is
necessary.
		// Manages putting two characters into a 2-byte short
	
//----------------------------------------------------------------
		i=j=0;
		while(i<size_payload)
		{
			a = payloadstring[i];
			i++;
			b = payloadstring[i];
			i++;
			payload_shortarray[j] = a*256+b;
			j++;
		}
		

	
//----------------------------------------------------------------
		// Constructs the u_short array for TCP CRC calculations
	
//----------------------------------------------------------------
		tcplen = htons(new_tcp.th_off/4);
		tcptos = htons(new_ip.proto);
		i = (new_tcp.th_off/4)+size_payload;
		size_tcpandpayload = &i;

		memset(tcp_hdrcrc, 0, *size_tcpandpayload+12);
		memcpy(tcp_hdrcrc, &new_tcp, ntohs(tcplen)); 
		memcpy(&tcp_hdrcrc[ntohs(tcplen)/2], &new_ip.saddr, 4); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+2], &new_ip.daddr, 4); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+4], &tcptos, 2); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+5],
&size_tcpandpayload, 2); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+6],
&payload_shortarray, size_payload); 
		
	
//----------------------------------------------------------------
		// Three versions of functions calculating the TCP CRC.
		// See functions below
		// csum and in_cksum produce the same CRC
		// check_tcp_sum a different CRC
	
//----------------------------------------------------------------
		//new_tcp.crc = csum( tcp_hdrcrc, (*size_tcpandpayload+12)/2
); 
		//new_tcp.crc = in_cksum(&tcp_hdrcrc,
*size_tcpandpayload+12);
		new_tcp.crc = check_tcp_sum(&new_ip, &new_tcp,
size_tcpandpayload, size_tcp, size_payload, &payload_shortarray);
	}
	else
	{
		//This part is triggered when there is no payload
		//THIS PART OF THE IF STATEMENT WORKS AS IT SHOULD
		//PRODUCES CORRECT CHECKSUM

		tcplen = htons(new_tcp.th_off/4);
		tcptos = htons(new_ip.proto);
		
		memset(tcp_hdrcrc, 0, (ntohs(tcplen)+12)); 
		memcpy(tcp_hdrcrc, &new_tcp, ntohs(tcplen)); 
		memcpy(&tcp_hdrcrc[ntohs(tcplen)/2], &new_ip.saddr, 4);
//Fra her
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+2], &new_ip.daddr, 4); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+4], &tcptos, 2); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+5], &tcplen, 2); 
		new_tcp.crc = csum( tcp_hdrcrc, ((ntohs(tcplen)+12)/2) ); 
	}




	//----------------------------------------------------------------
	// Assembling the new outgoing packet 
	// Returns the new packet to AnonMain.c for dumping into a file
	//----------------------------------------------------------------
	memcpy( pkt, srcmac, 6 ); 
	memcpy( (pkt + 6), destmac, 6 ); 
	memcpy( (pkt + 12), &ip_tos, 2);
	memcpy( (pkt + 14), &new_ip, size_ip ); 
	memcpy( (pkt + 14 + size_ip), &new_tcp, ((new_tcp.th_off)/4) );
	memcpy( (pkt + 14 + size_ip + ((new_tcp.th_off)/4)), payloadstring,
size_payload );
 
	return &pkt;
}



//*****************************************************************
// FUNCTION: csum
// Calculates the TCP Checksum 
//*****************************************************************
u_short csum (unsigned short *buf, int nwords) { 
	unsigned long sum=0; 
  for( sum=0; nwords > 0; nwords-- ) 
                sum += *buf++; 
        sum = (sum >> 16) + (sum & 0xffff); 
        sum += (sum >> 16); 
  return (u_short)~sum; 
} 



//*****************************************************************
// FUNCTION: check_tcp_sum
// Calculates the TCP Checksum
// Changed David Chang's original version
// Returns checksum instead of checking correct/incorrect
// Had to explisitly tell the function where to find the new payload
// since it not necessarily follows the TCP header in memory. This is
// because *payloads pont to a altered payload and not the original one. 
//*****************************************************************

u_short check_tcp_sum(struct ip_header *ip, struct tcp_header *tcp, int
totlen, int tcplen, int paylen, char *payloads)
 {
         unsigned long sum;
         u_char *addr, *p;
 
         /* TCP headers are at least 20 bytes long */
 
         if (tcplen < 20)
                 return(-1);
 
         /* Initialize */
		
         sum = 0;
			printf("Initializing sum: %d\n", sum);
         /* Sum pseudo tcp header */
 
         p = (u_char *) &(ip->saddr);
         sum += ((*p << 8) + *(p+1));
         sum += ((*(p+2) << 8) + *(p+3));
 
			printf("Sum is now: %d\n", sum);
         p = (u_char *) &(ip->daddr);
         sum += ((*p << 8) + *(p+1));
         sum += ((*(p+2) << 8) + *(p+3));
 
         sum += (0 + ip->proto);
 
         sum += totlen;
 
         /*  Sum real tcp header and payload */
 
         addr = (u_char *) tcp;
 
         while (tcplen > 0)
         {
                 sum += ((*addr << 8) + *(addr+1));
                 addr += 2;
                 tcplen -= 2;
         }

		 addr = (u_char *) payloads;
		 while (paylen > 1)
         {
                 sum += ((*addr << 8) + *(addr+1));
                 addr += 2;
                 paylen -= 2;
         }
 
         /*  Add left-over byte, if any */
 
         if (paylen > 0)
                 sum += (*addr << 8);
 
         /*  Fold 32-bit sum to 16 bits */
 
         while (sum>>16)
                 sum = (sum & 0xFFFF) + (sum >> 16);
 
	return sum;
 }



//*****************************************************************
// FUNCTION: in_cksum
// Calculates the TCP Checksum 
// This produces the same checksum as csum, but the checksum is
// still reported as incorrect by Ethereal, and it also differs from
// the CRC of the corresponding packet of the input ethereal/tcpdump file
//*****************************************************************
u_short in_cksum(u_short *addr, int len)
{
	register int nleft = len; 
	register u_short *w = addr; 
	register int sum = 0; 
	u_short answer = 0; 
	while (nleft > 1) 
	{ 
		sum += *w++; 
		nleft -= 2; 
	} 
	if (nleft == 1) 
	{ 
		*(u_char *)(&answer) = *(u_char *) w; 
		sum += answer; 
	} 
	sum = (sum >> 16) + (sum & 0xffff); 
	sum += (sum >> 16); 
	answer = ~sum; return(answer); 
}




Best regards
Vidar evenrud Seeberg



More information about the Winpcap-users mailing list