继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

SYNDOS —— TCP/IP Checksum极速算法

慕姐4208626
关注TA
已关注
手记 72
粉丝 5
获赞 37

  当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;
    }
}


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP