赞
踩
上一边我们实现了利用PCAP,将报文从一个网卡复制到另一个网卡。实现了一种截断正常网络传输而不影响原有传输的一种方案。
考虑到网络数据是带有校验的,如果我们修改了tcp的payload部分,那么则需要重新进行校验,所以本篇主要研究了对TCP和UDP部分数据的校验计算。
首先简单介绍一下网上的关于校验和的计算,
TCP和UDP数据基本一样,公式就是伪头部+tcp(或udp)的全部数据,计算和放入一个unsigned int的数字中,然后如果高16位有数据,则将其移入低16位加入,循环操作直到高16位没有数据为止,然后取反作为校验和。
伪头部的结构,共12个字节。
typedef struct _check_subhdr // udp计算checksum时的 亚头部: 4 byte源ip地址 + 4 byte目的ip地址 + 0x00 + 1 byte协议 + UDP 长度(2byte)
{
unsigned int src_ip;
unsigned int dst_ip;
char mbz; // must be zero
char protocol;
unsigned short len; // 这里的长度是指 udp packet中 udp头部和数据部分的总长度
} check_subhdr;
校验和的计算函数如下
u_short core_checksum(u_short *buffer,int size) { unsigned int cksum=0; while(size>1) { unsigned short tmp=*buffer++; cksum+=tmp; size-=sizeof(u_short); //printf("0x%04x+",tmp); } if(size) { cksum+=*(u_char *)buffer; } //printf("=cksum:%08x\n",cksum); //将32位数转换成16 while (cksum>>16) { cksum=(cksum>>16)+(cksum & 0xffff); } //printf("cksum2:%08x\n",cksum); return (u_short) (~cksum); }
计算TCP报文的TCP部分校验的函数如下
u_short get_tcp_checksum(struct iphdr *ipptr,struct tcphdr *tcpptr,int pkt_len) { int i=0; char cleancnum[2]={0,0}; //u_short tcp_part_len = pkt_len-sizeof(struct ether_header)-sizeof(struct iphdr); u_short tcp_part_len = ntohs(ipptr->tot_len)-(ipptr->ihl*4); check_subhdr tcp_subhdr; tcp_subhdr.protocol = ipptr->protocol; tcp_subhdr.dst_ip = ipptr->daddr; tcp_subhdr.src_ip = ipptr->saddr; tcp_subhdr.mbz = 0x00; tcp_subhdr.len = htons(tcp_part_len); int subhdr_len = sizeof(check_subhdr); int buf_size = tcp_part_len + subhdr_len; // 亚包头 + udp包头 + 数据部分的总长度 if (pkt_len < buf_size) return 0; u_char* buffer = (u_char*)malloc(buf_size); memset(buffer, 0x00, buf_size); memcpy(buffer, (char*)&tcp_subhdr, subhdr_len); //memcpy(buffer + subhdr_len, (char*)udpptr, udp_part_len); //先拷贝16个字节有效数据, memcpy(buffer + subhdr_len, (char*)tcpptr, 16); //拷贝两个00作为校验码 memcpy(buffer + subhdr_len + 16, (char*)cleancnum, 2); //拷贝剩余部分数据 memcpy(buffer + subhdr_len + 18, ((char*)tcpptr)+18, tcp_part_len-18); u_short checksum=0; checksum = core_checksum((u_short *)buffer,buf_size); free(buffer); return checksum; }
这里遇到了一个思考两天才解决的问题
//u_short tcp_part_len = pkt_len-sizeof(struct ether_header)-sizeof(struct iphdr);
u_short tcp_part_len = ntohs(ipptr->tot_len)-(ipptr->ihl*4);
从网上找到的这个tcp部分的长度计算,正常情况下,我们就这么算,除去eth头和IP头,剩下的部分就是所有tcp的字段,猛一看没什么问题,实际用起来,就会发现,偶尔就会有一些报文计算出来的校验和和报文里的不一致,但是源报文的pcap在wireshark中,并不会出现校验错误。这个问题一直困扰了我一天,直到我发现有些报文的IP头中的数据总长度,存在异常,
如图,IP部分总长度表示40,即IP和后面的TCP加起来一共40个字节,但是显然后面还有6个字节,按照开始的计算方式,TCP长度是26个,并不是20,所以算来算去,总是出错。
最终调整为
u_short tcp_part_len = ntohs(ipptr->tot_len)-(ipptr->ihl*4);
问题得以解决,所以不能看你收到了多少字节,要看IP头部告诉你,应该是多少字节。
这样,本篇到此,实现了数据的TCP和UDP部分校验和的计算,为后面修改报文数据,打下了基础。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。