首先看看TCP校验和计算原理:
TCP校验字段占两个字节如0x62,0x0d,实际上是这样计算来的:对方IP+我方IP+报文长度+信息长度+0x06(协议类型)+
TCP各字段长度以及数据各字段和(注意此时将对应的校验和字段--TCP的第17,18位,实际对应数组下标的16,17位--都设为0x00.同时
每两个字节组合为一个数值如0x01,0x02组合为0x0102,数据abc对应为0x61,0x62,0x63,则组合成0x6162,0x6300)
上述算出来的校验和(设为resule)假如超过0xffff的话,将超过的部分添加到result的低位去再用0xffff减去该值,如:0x54321最终应成为:0xffff-(0x4321+0x05)
其次根据抓包工具抓住的TCP报文我们看到下列图片中已经有一个包含了双方IP,TCP报文以及信息的报文,根据图片中给出的信息,依照上述原理手动计算一个TCP校验和:
本地Ip:0xc0,0xa8,0x9f,0x01;
对方IP:0xc0,0xa8,0x9f,0x82
TCP字段:0x04, 0xc6, 0x87, 0x01, 0x4b, 0xd7, 0x89, 0x9f, 0x4e, 0x3b, 0x90, 0xae, 0x50, 0x18, 0xff, 0xff, 0xeb, 0x69, 0x00, 0x00
从上述实际报文可见该TCP的校验和为eb69
将对应的校验和字段设为0x00后TCP报文实际为:
0x04, 0xc6, 0x87, 0x01, 0x4b, 0xd7, 0x89, 0x9f, 0x4e, 0x3b, 0x90, 0xae, 0x50, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
数据字段abc,对应0x61,0x62,0x63;
TCP报文长度为20,信息长度为3,23化为16进制即0x17
所以result=(0xc0a8+0x9f01)+(0xc0a8+0x9f82)+(0x04c6+0x8701+0x4bd7+0x899f+0x4e3b+0x90ae+0x5018+0xffff+0x0000+0x0000)+(0x17+0x06)+(0x6162+0x6300)
得出result=0x7148f,将超出0xffff的位加到低位上去,reuslt=0xffff-(0x148f + 0x0007)=0xeb69正好和抓包工具显示的校验和字段是一样的.
最后就是给出一个计算校验和的函数了
- #include <stdio.h>
- INT8U sum[2]={0}; //两个保存校验和的全局变量
- INT8U Info[30]={0}; //保存实际数据的全局变量
- /*
- *计算校验和的函数,传递过来的参数分别是:指向纯TCP报文的的指针data,指向本地IP的指针mIP,指向对方IP的指针yIP,实际要传送的数据长度 length
- *该函数计算TCP校验和字段的实际填充信息,通过全局变量数组sum[2]返回.
- */
- void checksum(INT8U *data,INT8U *mIP,INT8U *yIP,INT16U length)
- {
- INT16U csum;
- INT16U i;
- INT32U dat[30]={0}; //保存TCP的的数组
- INT32U shu[30]={0}; //保存信息的数组
- INT16U temp;
- INT32U result,temp1,temp2,temp3,temp4=0,temp5=0;
- //将IP字段从数组中取出组合成新的数据
- temp1=((INT16U)mIP[0]<<8)+(INT16U)mIP[1]+((INT16U)mIP[2]<<8)+(INT16U)mIP[3];
- temp2=((INT16U)yIP[0]<<8)+(INT16U)yIP[1]+((INT16U)yIP[2]<<8)+(INT16U)yIP[3];
- //数据长度+协议类型
- temp3=(INT16U)(length)+0x06;
- for(i=0 ; i<length ; i++)
- {
- if(i%2==0) //将偶数位左移放到高位
- {
- dat[i]=(INT16U)data[i]<<8;
- shu[i]=(INT16U)Info[i]<<8;
- }
- else //奇数位不变
- {
- dat[i]=(INT16U)data[i];
- shu[i]=(INT16U)Info[i];
- }
- }
- dat[16]=0x00; //校验和字段初始置0
- dat[17]=0x00;
- for(i=0 ; i<length ; i++)
- {
- temp4=dat[i]+temp4;
- temp5=shu[i]+temp5;
- }
- result=temp1+temp2+temp3+temp4+temp5;
- if(result>0xffff)
- {
- temp=result/0xffff;//得到高位
- csum=(INT16U)result;//得到低位
- csum=0xffff-(csum+temp);
- }
- //将计算出来的数据和高位和低位分别放到数组对应字段中
- sum[0]=csum>>8;
- sum[1]=(INT8U)csum;
- }
- int main()
- {
- INT8U length=20;
- INT8U i,j=0;
- INT8U info[30]={'a','b','c'};
- INT8U mIP[]={0xc0,0xa8,0x9f,0x01};
- INT8U yIP[]={0xc0,0xa8,0x9f,0x82};
- INT8U PSH_ACK[30]={0x04,0xc6,0x87,0x01,0x4b,0xd7,0x89,0x9f,0x4e,0x3b,<span style="white-space:pre"> </span>//一个TCP数组
- 0x90,0xae,0x50,0x18,0xff,0xff,0x00,0x00,0x00,0x00};
- for(i=0;i<30;i++) //数据放到全局变量
- Info[i]=info[i];
- while(Info[j]!=0)
- j++; <span style="white-space:pre"> </span>//获得实际信息的长度
- length=length+j;
- checksum(psh,mIP,yIP,length);
- printf("\n\nlength:%x%x\n\n",sum[0],sum[1]); <span style="white-space:pre"> </span><span style="background-color: rgb(255, 255, 255); ">//看答案是否是620d</span>
- return 0;
- }
注意其中的INT8U等类型是这样定义的
- typedef unsigned char INT8U; /* 无符号8位整型变量 */
- typedef unsigned short INT16U; /* 无符号16位整型变量 */
- typedef unsigned int INT32U; /* 无符号32位整型变量 */
该计算TCP校验和的函数最初是为"嵌入式IP_TCP协议栈"的实现而编写的,其环境是
操作系统: uCOS
网络芯片: enc28j60
硬件环境: 使用Proteus + LPC2124
开发环境: Keil V0.3
当放到嵌入式keil v0.3的时候,将相关头文件和库函数如printf和stdio.h更换掉,或干脆不用.
当然因为它最初是在vs里完成的也可以用在windows里需要计算校验和的地方. 当然该函数有不尽人意的地方,比如完全可以将信息放到TCP数组的里面,这样可以减少很多累赘的语句提高运行速度,希望各位IT好友提出其他的建议和意见!
来源:http://blog.csdn.net/wangshiqilin_fjy/article/details/7889316