修改IP、TCP、UDP校验码

本文介绍如何在修改IPv4数据报文的源或目标IP后,正确更新IP及TCP/UDP校验码的方法。通过提供具体的C语言实现代码,包括IP校验码、TCP/UDP校验码的计算与修正过程。

ipv4数据报文中修改的来源ip或目的ip后,一般需要修改对应的校验码。首先ip校验码是最基础的,然后因为TCP/UDP对应的校验码会根据伪报文头检查(其中包含src ip 和 dst ip),所以也需要修改tcp/udp报文的校验码。这里修复了一些其他文章中的BUG,例如缓存溢出、构造TCP/UDP伪报文采用的参数等,通过实验能够正常运行。

1.IP检验码

unsigned int Checksum(unsigned int cksum, void *pBuffer, unsigned int size)
{
	unsigned int num = 0;
	unsigned char *p = (unsigned char *)pBuffer;
	if ((NULL == pBuffer) || (0 == size))
	{
		return cksum;
	}
	while (size > 1)
	{
		cksum += ((unsigned short)p[num] << 8 & 0xff00) | (unsigned short)(p[num + 1] & 0x00FF);
		size  -= 2;
		num   += 2;
	}
	if (size > 0)
	{
		cksum += ((unsigned short)p[num] << 8) & 0xFFFF;
		num += 1;
	}
	while (cksum >> 16)
	{
		cksum = (cksum & 0xFFFF) + (cksum >> 16);
	}
	return cksum;
}

unsigned short ChangeIpv4Checksum(char *ipHeader)
{
    unsigned short ipHeadLen  = (ipHeader[0] & 0x0F) << 2;
    ipHeader[10] = 0;
    ipHeader[11] = 0;

    unsigned int sum = Checksum(0, (void *)(&ipHeader[0]), ipHeadLen);
    unsigned int tmp = ~sum;

    ipHeader[10] = (unsigned char)(tmp >> 8);
    ipHeader[11] = (unsigned char)(tmp);

    return ipHeadLen;
}

2.TCP/UDP校验码 

  执行修改当前数据包TCP或UDP校验码

//获取Tcp/Udp报文首地址指针,ethPkt表示ipv4报文首地址,0x45 00 .....
unsigned char *header = &ethPkt[ipHeadLen];
//计算ipv4报文中Tcp/Udp报文长度
unsigned short len = ntohs(*(unsigned short*)(&ethPkt[2])) - ipHeadLen;
//根据修改的源IP或目的IP来计算校验码,ethPkt[9]表示tcp或协议类型
ChangeTcpUdpChecksum(header, source_ip.to_uint(), target_ip.to_uint(), ethPkt[9], len);

计算TCP或UDP校验码函数,其中构造TCP/UDP伪报头中采用长度为TCP/UDP段之后报文长度,ptc表示采用哪种协议。

Checksum函数和IP一样,都是计算校验和求反。

struct psd_header
{
    unsigned int saddr; //tcp source addr
    unsigned int daddr; //tcp target addr
    char mbz;           //NULL
    char ptcl;          //protocol type
    unsigned short tcpl; //TCP length = headerLen + bodyLen;
};

/*
 * Header tcp/udp header,
 * len is tcp/udp packet length ,contains data segement 这里重点,计算伪报文头的传递长度中包含数据段长度
 * ptc is tcp/udp protocol code ,0x06 to tcp or 0x11 to udp
 * */
void ChangeTcpUdpChecksum(unsigned char *Header, unsigned int ui_source_ip, unsigned int ui_target_ip, char ptc, unsigned short len)
{
    unsigned char SendBuf[2048] = {0};
    unsigned short header_len = (Header[12] & 0xFF) >> 2;

    Header[16]=0;
    Header[17]=0;

    struct psd_header *p_psd = NULL;
    p_psd = (struct psd_header *)malloc(sizeof(struct psd_header));
    p_psd->saddr = (unsigned int)htonl(ui_source_ip);
    p_psd->daddr = (unsigned int)htonl(ui_target_ip);
    p_psd->mbz = (char)0x00;
    p_psd->ptcl = ptc;
    p_psd->tcpl = (unsigned short)htons(len);

    //copy and compute checksum
    memcpy(SendBuf,p_psd,sizeof(struct psd_header));
    memcpy(SendBuf+sizeof(struct psd_header),Header,len);
    unsigned int tcp_sum=Checksum(0,(void *)&SendBuf,sizeof(struct psd_header)+len);
    unsigned int tcp_tmp = ~tcp_sum;
    Header[16]=(unsigned char)(tcp_tmp >> 8);
    Header[17]=(unsigned char)(tcp_tmp);
    free(p_psd);

    return ;
}

参考引用:

https://blog.youkuaiyun.com/jiangqin115/article/details/39315085

https://blog.youkuaiyun.com/wangshiqilin_fjy/article/details/7889316

https://blog.youkuaiyun.com/zhangskd/article/details/11770647

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值