QQ的TEA填充算法C#实现

2010-08-28 10:52:54来源:西部e网作者:

注:本人只是从LUMA QQ的 Source Code里面把相应Java语言翻译成C#,纯技术研究,并没有对TC产品做任何逆向分析,不承担任何法律责任!转载请保持本文完整性

网上有c/c++,vb,delphi,java,perl各种版本的tea填充算法,唯独没有C#的,这让我这种狂热喜爱C#的人如何承受,于是,花3天时间看了各种代码经历无数失败的挫折终于用C#完成了该填充算法,废话不多说,直接给代码

 /*********************************************************

FILE :      QQCrypt.cs

9.22更正:一处笔误造成解密失败的BUG

                    优化部分代码

**********************************************************/


using System;

namespace RedQ
...{
    /**//// <summary>
    /// QQ Msg En/DeCrypt Class
    /// Writen By Red_angelX On 2006.9.13
    /// </summary>
    public class QQCrypt
    ...{   
        //QQ TEA-16 Encrypt/Decrypt Class
        //
        //
        //And also LumaQQ//s source code
        //  CopyRight:No CopyRight^_^
        //  Author : Red_angelX    
        //  NetWork is Free,Tencent is ****!
        //
        //Class Begin
        //AD:Find Job!!,If you Want Give me a Job,Content Me!!

        //Copied & translated from LumaQQ//s source code          `From LumaQQ///s source code:
        private byte[] Plain;                                   //指向当前的明文块
        private byte[] prePlain ;                               //指向前面一个明文块
        private byte[] Out;                                     //输出的密文或者明文
        private long Crypt, preCrypt;                           //当前加密的密文位置和上一次加密的密文块位置,他们相差8
        private long Pos;                                       //当前处理的加密解密块的位置
        private long padding;                                   //填充数
        private byte[] Key = new byte[16];                      //密钥
        private bool Header;                                    //用于加密时,表示当前是否是第一个8字节块,因为加密算法
                                                                //是反馈的,但是最开始的8个字节没有反馈可用,所有需要标
                                                                //明这种情况
        private long contextStart;                              //这个表示当前解密开始的位置,之所以要这么一个变量是为了
                                                                //避免当解密到最后时后面已经没有数据,这时候就会出错,这
                                                                //个变量就是用来判断这种情况免得出错
        public QQCrypt()
        ...{
            //
            // TODO: 在此处添加构造函数逻辑
            //
        }
       

        //Push 数据
        byte[] CopyMemory(byte[] arr,int arr_index,long input)  //lenth = 4
        ...{
            if(arr_index+4 > arr.Length)
            ...{
                // 不能执行
                return arr;
            }

            arr[arr_index+3]=(byte)((input & 0xff000000) >> 24);
            arr[arr_index+2]=(byte)((input & 0x00ff0000) >> 16);
            arr[arr_index+1]=(byte)((input & 0x0000ff00) >> 8);
            arr[arr_index]=(byte)(input & 0x000000ff);

            arr[arr_index] &= 0xff;
            arr[arr_index+1] &= 0xff;
            arr[arr_index+2] &= 0xff;
            arr[arr_index+3] &= 0xff;

            return arr;
        }

        long CopyMemory(long Out,byte[] arr,int arr_index)
        ...{
            if(arr_index+4 > arr.Length)
            ...{
                return Out;
                //不能执行
            }

            long x1 = arr[arr_index+3] << 24;
            long x2 = arr[arr_index+2] << 16;
            long x3 = arr[arr_index+1] << 8;
            long x4 = arr[arr_index];

            long o = x1 | x2 | x3 | x4;
            o &= 0xffffffff;
            return o;
        }

        long getUnsignedInt(byte[] arrayIn, int offset,int len /**//*Default is 4*/)
        ...{

            long ret = 0;
            int end = 0;
            if (len > 8)
                end = offset + 8;
            else
                end = offset + len;
            for (int i = offset; i < end; i++)
            ...{
                ret <<= 8;
                ret |= arrayIn[i] & 0xff;
            }
            return (ret & 0xffffffff) | (ret >> 32);
        }

        long Rand()
        ...{
            Random rd = new Random();
            long ret;
            ret = rd.Next() + (rd.Next() % 1024);
            return ret;
        }

        private byte[] Decipher(byte[] arrayIn,byte[] arrayKey,long offset)
        ...{
            //long Y,z,a,b,c,d;
            long sum,delta;
            //Y=z=a=b=c=d=0;
            byte[] tmpArray = new byte[24];
            byte[] tmpOut = new byte[8];
            if(arrayIn.Length < 8)
            ...{
                // Error:return
                return tmpOut;
            }
            if(arrayKey.Length < 16)
            ...{
                // Error:return
                return tmpOut;
            }
            sum = 0xE3779B90;
            sum = sum & 0xFFFFFFFF;
            delta = 0x9E3779B9;
            delta = delta & 0xFFFFFFFF;
            /**//*tmpArray[3] = arrayIn[offset];
            tmpArray[2] = arrayIn[offset + 1];
            tmpArray[1] = arrayIn[offset + 2];
            tmpArray[0] = arrayIn[offset + 3];
            tmpArray[7] = arrayIn[offset + 4];
            tmpArray[6] = arrayIn[offset + 5];
            tmpArray[5] = arrayIn[offset + 6];
            tmpArray[4] = arrayIn[offset + 7];
            tmpArray[11] = arrayKey[0];
            tmpArray[10] = arrayKey[1];
            tmpArray[9] = arrayKey[2];
            tmpArray[8] = arrayKey[3];
            tmpArray[15] = arrayKey[4];
            tmpArray[14] = arrayKey[5];
            tmpArray[13] = arrayKey[6];
            tmpArray[12] = arrayKey[7];
            tmpArray[19] = arrayKey[8];
            tmpArray[18] = arrayKey[9];
            tmpArray[17] = arrayKey[10];
            tmpArray[16] = arrayKey[11];
            tmpArray[23] = arrayKey[12];
            tmpArray[22] = arrayKey[13];
            tmpArray[21] = arrayKey[14];
            tmpArray[20] = arrayKey[15];
            Y=CopyMemory(Y,tmpArray,0);   
            z=CopyMemory(z,tmpArray,4);
            a=CopyMemory(a,tmpArray,8);
            b=CopyMemory(b,tmpArray,12);
            c=CopyMemory(c,tmpArray,16);
            d=CopyMemory(d,tmpArray,20);*/
            long Y = getUnsignedInt(arrayIn, (int)offset, 4);
            long z = getUnsignedInt(arrayIn, (int)offset + 4, 4);
            long a = getUnsignedInt(arrayKey, 0, 4);
            long b = getUnsignedInt(arrayKey, 4, 4);
            long c = getUnsignedInt(arrayKey, 8, 4);
            long d = getUnsignedInt(arrayKey, 12, 4);
            for(int i=1;i<=16;i++)
            ...{
                z -= ((Y<<4)+c) ^ (Y+sum) ^ ((Y>>5)+d);
                z &= 0xFFFFFFFF;
                Y -= ((z<<4)+a) ^ (z+sum) ^ ((z>>5)+b);
                Y &= 0xFFFFFFFF;
                sum -= delta;
                sum &= 0xFFFFFFFF;
            }

            tmpArray = CopyMemory(tmpArray,0,Y);
            tmpArray = CopyMemory(tmpArray,4,z);
            tmpOut[0] = tmpArray[3];
            tmpOut[1] = tmpArray[2];
            tmpOut[2] = tmpArray[1];
            tmpOut[3] = tmpArray[0];
            tmpOut[4] = tmpArray[7];
            tmpOut[5] = tmpArray[6];
            tmpOut[6] = tmpArray[5];
            tmpOut[7] = tmpArray[4];

            return tmpOut;   
        }

        private byte[] Decipher(byte[] arrayIn,byte[] arrayKey)
        ...{
            return Decipher(arrayIn,arrayKey,0);
        }

        private byte[] Encipher(byte[] arrayIn,byte[] arrayKey,long offset)
        ...{
            byte[] tmpOut = new byte[8];
            byte[] tmpArray = new byte[24];
            //long Y,z,a,b,c,d;
            //Y=z=a=b=c=d=0;
            long sum,delta;
            if(arrayIn.Length < 8)
            ...{
                // Error:
                return tmpOut;
            }
            if(arrayKey.Length < 16)
            ...{
                // Error:
                return tmpOut;
            }
            sum = 0;
            delta = 0x9E3779B9;
            delta &= 0xFFFFFFFF;

            /**//*tmpArray[3] = arrayIn[offset];
            tmpArray[2] = arrayIn[offset + 1];
            tmpArray[1] = arrayIn[offset + 2];
            tmpArray[0] = arrayIn[offset + 3];
            tmpArray[7] = arrayIn[offset + 4];
            tmpArray[6] = arrayIn[offset + 5];
            tmpArray[5] = arrayIn[offset + 6];
            tmpArray[4] = arrayIn[offset + 7];
            tmpArray[11] = arrayKey[0];
            tmpArray[10] = arrayKey[1];
            tmpArray[9] = arrayKey[2];
            tmpArray[8] = arrayKey[3];
            tmpArray[15] = arrayKey[4];
            tmpArray[14] = arrayKey[5];
            tmpArray[13] = arrayKey[6];
            tmpArray[12] = arrayKey[7];
            tmpArray[19] = arrayKey[8];
            tmpArray[18] = arrayKey[9];
            tmpArray[17] = arrayKey[10];
            tmpArray[16] = arrayKey[11];
            tmpArray[23] = arrayKey[12];
            tmpArray[22] = arrayKey[13];
            tmpArray[21] = arrayKey[14];
            tmpArray[20] = arrayKey[15];

            Y=CopyMemory(Y,tmpArray,0);
            z=CopyMemory(z,tmpArray,4);
            a=CopyMemory(a,tmpArray,8);
            b=CopyMemory(b,tmpArray,12);
            c=CopyMemory(c,tmpArray,16);
            d=CopyMemory(d,tmpArray,20);*/

            long Y = getUnsignedInt(arrayIn, (int)offset, 4);
            long z = getUnsignedInt(arrayIn, (int)offset + 4, 4);
            long a = getUnsignedInt(arrayKey, 0, 4);
            long b = getUnsignedInt(arrayKey, 4, 4);
            long c = getUnsignedInt(arrayKey, 8, 4);
            long d = getUnsignedInt(arrayKey, 12, 4);

            for(int i=1;i<=16;i++)
            ...{
                sum += delta;
                sum &= 0xFFFFFFFF;
                Y += ((z<<4)+a) ^ (z+sum) ^ ((z>>5)+b);
                Y &= 0xFFFFFFFF;
                z += ((Y<<4)+c) ^ (Y+sum) ^ ((Y>>5)+d);
                z &= 0xFFFFFFFF;
            }

            tmpArray = CopyMemory(tmpArray,0,Y);
            tmpArray = CopyMemory(tmpArray,4,z);

            tmpOut[0] = tmpArray[3];
            tmpOut[1] = tmpArray[2];
            tmpOut[2] = tmpArray[1];
            tmpOut[3] = tmpArray[0];
            tmpOut[4] = tmpArray[7];
            tmpOut[5] = tmpArray[6];
            tmpOut[6] = tmpArray[5];
            tmpOut[7] = tmpArray[4];
       
            return tmpOut;
        }
       

        private byte[] Encipher(byte[] arrayIn,byte[] arrayKey)
        ...{
            return Encipher(arrayIn,arrayKey,0);
        }
       
        private void Encrypt8Bytes()
        ...{
            byte[] Crypted;
            for(Pos=0;Pos<=7;Pos++)
            ...{
                if(this.Header == true)
                ...{
                    Plain[Pos] = (byte)(Plain[Pos] ^ prePlain[Pos]);
                }
                else
                ...{
                    Plain[Pos] = (byte)(Plain[Pos] ^ Out[preCrypt + Pos]);
                }
            }
            Crypted = Encipher(Plain,Key);
           
            for(int i=0;i<=7;i++)
            ...{
                Out[Crypt + i] = (byte)Crypted[i];
            }
           
            for(Pos=0;Pos<=7;Pos++)
            ...{
                Out[Crypt + Pos] = (byte)(Out[Crypt + Pos] ^ prePlain[Pos]);
            }           
            Plain.CopyTo(prePlain,0);
            preCrypt = Crypt;
            Crypt = Crypt + 8;
            Pos = 0;
            Header = false;
        }

        private bool Decrypt8Bytes(byte[] arrayIn,long offset)
        ...{
            long lngTemp;
            for(Pos=0;Pos<=7;Pos++)
            ...{
                if(this.contextStart+Pos > arrayIn.Length-1)
                ...{
                    return true;
                }
                prePlain[Pos] = (byte)(prePlain[Pos] ^ arrayIn[offset+Crypt+Pos]);
            }
            try
            ...{
                prePlain = this.Decipher(prePlain,Key);
            }
            catch
            ...{
                return false;
            }
            lngTemp = prePlain.Length - 1;
            contextStart += 8;
            Crypt+=8;
            Pos = 0;
            return true;
        }

        private bool Decrypt8Bytes(byte[] arrayIn)
        ...{
            return Decrypt8Bytes(arrayIn,0);
        }


        Public Methods!#region Public Methods!

        /**//// <summary>
        /// QQ TEA 加密函数
        /// </summary>
        /// <param name="arrayIn">要加密的字串</param>
        /// <param name="arrayKey">密钥</param>
        /// <param name="offset">偏移</param>
        /// <returns></returns>
        public byte[] QQ_Encrypt(byte[] arrayIn,byte[] arrayKey,long offset)
        ...{
            Plain = new byte[8];
            prePlain = new byte[8];
            long l;
            Pos = 1;
            padding = 0;
            Crypt = preCrypt = 0;
            arrayKey.CopyTo(Key,0);    // Key Must Be 16 Length!
            Header = true;
            Pos = 2;
            //计算头部填充字节数
            Pos = (arrayIn.Length+10) % 8;
            if(Pos != 0)
                Pos = 8-Pos;
            //输出长度
            Out = new byte[arrayIn.Length+Pos+10];
            //把POS存到PLAIN的第一个字节
            //0xf8后面3位是空的,正好给Pos
            Plain[0] = (byte)((Rand() & 0xf8) | Pos);
            //用随机数填充1到Pos的内容
            for(int i=1;i<=Pos;i++)
            ...{
                Plain[i] = (byte)(Rand() & 0xff);
            }
            Pos++;
            padding = 1;

            //继续填充两个字节随机数,满8字节就加密
            while(padding < 3)
            ...{
                if( Pos < 8)
                ...{
                    Plain[Pos] = (byte)(Rand() & 0xff);
                    padding++;
                    Pos++;
                }
                else if(Pos == 8)
                ...{
                    this.Encrypt8Bytes();
                }
            }

            int I = (int)offset;
            l = 0;
            //明文内容,满8字节加密到读完
            l = arrayIn.Length;
            while ( l > 0)
            ...{
                if(Pos<8)
                ...{
                    Plain[Pos] = arrayIn[I];
                    I++;
                    Pos++;
                    l--;
                }
                else if(Pos == 8)
                ...{
                    this.Encrypt8Bytes();
                }
            }

            //末尾填充0,保证是8的倍数
            padding = 1;
            while(padding < 9)     
            ...{
                if(Pos<8)
                ...{
                    Plain[Pos] = 0;
                    Pos++;
                    padding++;
                }
                else if(Pos == 8)
                ...{
                    this.Encrypt8Bytes();
                }
            }

            return Out;
        }


        public byte[] QQ_Encrypt(byte[] arrayIn,byte[] arrayKey)
        ...{
            return QQ_Encrypt(arrayIn,arrayKey,0);
        }

        /**//// <summary>
        ///  QQ TEA 解密函数
        /// </summary>
        /// <param name="arrayIn">要解密字串</param>
        /// <param name="arrayKey">密钥</param>
        /// <param name="offset">偏移</param>
        /// <returns></returns>
        public byte[] QQ_Decrypt(byte[] arrayIn,byte[] arrayKey,long offset)
        ...{
            byte[] error = new byte[0];
            //检查是否是8的倍数至少16字节
            if(arrayIn.Length < 16 || (arrayIn.Length % 8 != 0))
            ...{
                //Return What?
                return error;
            }
            if(arrayKey.Length != 16)
            ...{
                //Return What?
                return error;
            }
            byte[] m;
            long I,Count;
            m= new byte[offset+8];
            arrayKey.CopyTo(Key,0);
            Crypt = preCrypt = 0;
            //计算消息头部,明文开始的偏移,解密第一字节和7相与得到
            prePlain = this.Decipher(arrayIn,arrayKey,offset);
            Pos = prePlain[0] & 7;
            //计算明文长度
            Count = arrayIn.Length - Pos - 10;
            if(Count <= 0)
            ...{
                //Return What?
                return error;
            }
            Out = new byte[Count];
            preCrypt = 0;
            Crypt = 8;
            this.contextStart = 8;
            Pos++;
            padding = 1;
            //跳过头部
            while(padding < 3)
            ...{
                if(Pos<8)
                ...{
                    Pos++;
                    padding++;
                }
                else if(Pos==8)
                ...{
                    for(int i=0;i<m.Length;i++)
                        m[i]=arrayIn[i];
                    if(this.Decrypt8Bytes(arrayIn,offset) == false)
                    ...{
                        //Return What?
                        return error;
                    }
                }
            }

            //解密明文
            I=0;
            while(Count != 0)
            ...{
                if(Pos<8)
                ...{
                    Out[I] = (byte)(m[offset+preCrypt+Pos] ^ prePlain[Pos]);
                    I++;
                    Count--;
                    Pos++;
                }
                else if(Pos == 8)
                ...{
                    m = arrayIn;
                    preCrypt = Crypt - 8;
                    if(this.Decrypt8Bytes(arrayIn,offset) == false)
                    ...{
                        //Return What?
                        return error;
                    }
                }
            }

            //最后的解密部分,检查尾部是不是0
            for(padding=1;padding<=7;padding++)
            ...{
                if(Pos<8)
                ...{
                    if( (m[offset+preCrypt+Pos] ^ prePlain[Pos]) != 0 )
                    ...{
                        //Return What?
                        return error;
                    }
                    Pos++;
                }
                else if(Pos == 8)
                ...{
                    for(int i=0;i<m.Length;i++)
                        m[i] = arrayIn[i];
                    preCrypt = Crypt;
                    if(this.Decrypt8Bytes(arrayIn,offset) == false)
                    ...{
                        //Return What?
                        return error;
                    }
                }
            }
            return Out;
        }

        public byte[] QQ_Decrypt(byte[] arrayIn,byte[] arrayKey)
        ...{
            return QQ_Decrypt(arrayIn,arrayKey,0);
        }
        #endregion
    }
}

 

有了这个,加上一些现成的协议分析,你就可以做自己的QQ客户端了。

原文:http://blog.csdn.net/Red_angelX/archive/2006/09/19/1246701.aspx

关键词:C#

赞助商链接:

忐忑
浮夸
说谎
倾世
王妃
煎熬
武装
小三
曾经
素颜
惊叹号
爱很美
春天里
老男孩
皮影戏
走天涯
羽绒服
甩葱歌
套马杆
伤不起
有没有
苦咖啡
追梦人
那些年
大声唱
爱的供养
三寸天堂
荷塘月色
喜欢寂寞
爱情买卖
爱是你我
见或不见
火力全开
一念执着
想你的夜
依然爱你
幸福额度
红尘情歌
明天你好
狼的诱惑
你是我的眼
没那么简单
客官不可以
等你的季节
等不到的爱
美丽与勇敢
我的好兄弟
我们的歌谣
如果爱忘了
梦中的额吉
男人帮片尾曲
最重要的决定
不分手的恋爱
魔鬼中的天使
裸婚时代插曲
当我唱起这首歌
全世界宣布爱你
请安静的忘记我
亲爱的你在哪里
天龙八部之宿敌
爱情睡醒了主题曲
漂亮的姑娘就要嫁人啦
最接近天堂的地方
我爱你胜过你爱我
下辈子做你的女人