欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SYNDOS —— TCP/IP Checksum极速算法

程序员文章站 2022-07-10 14:34:03
...

  当SYN数据包的发送速度大幅提高后,校验和计算成了自然成了不可忽视的一部分。

  
  传统的Checksum算法网上随便一搜一大堆,甚至不少攻击器也是用此算法。该算法并没有什么缺点,具有普遍性,可计算任意数据任意长度的Checksum。对于SYN包那样很短并且每次都是固定长度的包,这种算法显然有些累赘了。我们不妨仔细分析下Checksum算法的原理:

USHORT ChechkSum(USHORT *buffer, int size)
{
   DWORD chkSum = 0;

   while(size > 1)
   {
      chkSum += *buffer++;
      size -= sizeof(USHORT);
   }

   if(size)
      chkSum += *(UCHAR*)buffer;

   chkSum = (chkSum >> 16) + (chkSum & 0xffff); 
   chkSum += (chkSum >> 16);

   return (USHORT)(~chkSum);
}

  

  下面不考虑长度为奇数的情况。先看循环里面的代码,事实上只是把这些数据当作一个USHORT[],计算这个数组的和而已。即使数据的内容不一样,但只要这个累加的和不变,那后面不管对其做什么运算,函数结果肯定是一样的。简单的说:不同的数据可能有相同的和,必然也有相同的校验和。比如1+2+3=3+2+1=2+2+2,和都是6,所以他们也有相同的校验和。

  

  既然是这样的算法,我们就可以大作优化了。比如一个固定数组:USHORT A[10]。他的和一共有65535*10+1种情况。这个应该很好理解:如果10个数全是0,那和也是0;反之全是最大数65535,那么和是655350;其余情况就在这之间波动。由此可见,N字长的数据校验和其实也只有65535*N+1种情况。在本程序中计算TCP校验和的数据有20字长(40字节),其结果也只有1,310,699种。如果你在思考的话一定也想到了经典的空间换时间算法。没错,正是如此!即使事先把这几个数的校验和全部计算好保存起来,也仅需2.5M的内存空间。不过问题是,以后如何去利用表里的这些缓存数据?

  

  因为这是一张“”与“校验和”的映射表,所以当“和”确定后即可获得“校验和”。因此在预先填充数据包的时候,可变的字段一定要置0,然后计算出这个半成品包的“和”当作“基值”。以后当可变部分填上数据时,比如IP头填上了id字段,那么这个IP头的“和”就是:基值+id,校验和当然就是:表[基值+id]。o(1)的复杂度,够快吧!

  

  当然,如果可变字段超过2个字节就不能这么简简单单的加上就可以了,还要对其做高位合并计算。比如IP头的SrcIP(4个字节)填上了数据,那么:

表[基值 + SUM(SrcIP)]

 

  才是其正确的校验和,具体的SUM运算可以用个宏来定义:

  #define HI(VALUE)  (VALUE >> 16)
  #define LOW(VALUE) (VALUE & 0xFFFF)
  #define SUM(VALUE) (HI(VALUE) + LOW(VALUE))

  

  缓存表的初始化:

USHORT TableChkSum[MAX_TBL_SIZE];

/**************************************************
 * 函数:InitChkSumTable
 * 注释:初始化Checksum映射表
 *       缓存: 字段和(sum) -> 校验和(checksum)
 **************************************************/
VOID InitChkSumTable()
{
	UINT i;
	UINT chkSum;

	for(i=0; i<MAX_TBL_SIZE; i++)
	{
		chkSum = SUM(i);
		chkSum += HI(chkSum);
		TableChkSum[i] = (USHORT)~chkSum;
	}
}

(2009/1/2)

转载于:https://www.cnblogs.com/index-html/archive/2011/03/03/checksum_algorithm.html