首先看看TCP校验和计算原理:
TCP校验字段占两个字节如0x62,0x0d,实际上是这样计算来的:上述算出来的校验和(设为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
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 ; i0xffff)
{
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, //一个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++; //获得实际信息的长度
length=length+j;
checksum(psh,mIP,yIP,length);
printf("\n\nlength:%x%x\n\n",sum[0],sum[1]); //看答案是否是620d
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好友提出其他的建议和意见!