当前位置:首页>>开发编程>>VS.NET>>新闻内容
QQ的TEA填充算法C#实现
作者:Red_angelX 发布时间:2007-12-28 13:38:03 文章来源:csdn

注:本人只是从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#中使用Split分隔字符串的技
·VS2008开发中Windows Mobile
·PC机和移动设备上绝对路径的
·C#程序加壳的方法(使用Sixx
·当前上下文中不存在名称Conf
·请插入磁盘:Visual Studio 2
·用VS.NET读取Flash格式文件信
·在ASP.NET中使用AJAX的简单方
·VS.NET 2005中常用的一些代码
·安装VS.NET 2005 SP1补丁全攻
相关信息
·C#中使用Split分隔字符串的技巧
·PC机和移动设备上绝对路径的获取(C#)
·C#程序加壳的方法(使用Sixxpack)
·当前上下文中不存在名称ConfigurationManager的解决方法
·C#的支付宝Payto接口代码
·C#实现窗口最小化到系统托盘
·解密QQ的MsgEx.db消息文件格式
·C#用Guid获取不规则的唯一值(标识)
·基于Windows Mobile 5.0的掌上天气预报设计
 画心
 愚爱
 偏爱
 火苗
 白狐
 画沙
 犯错
 歌曲
 趁我
 稻香
 小酒窝
 狮子座
 小情歌
 全是爱
 棉花糖
 我知道
 钻石糖
 Nobody
 我爱他
 套马杆
 星空物语
 最后一次
 少女时代
 穿越人海
 断桥残雪
 美了美了
 明天过后
 我很快乐
 到了明天
 心痛2009
 爱丫爱丫
 寂寞好了
 敢不敢爱
 郎的诱惑
 爱情买卖
 super girl
 我叫小沈阳
 i miss you
 姑娘我爱你
 我们都一样
 其实很寂寞
 习惯了寂寞
 我要的飞翔
 我的好兄弟
 爱的华尔兹
 李雷和韩梅梅
 贝多芬的悲伤
 爱我就跟我走
 丢了幸福的猪
 我只是个传说
 要嫁就嫁灰太狼
 即使知道要见面
 如果我变成回忆
 看得最远的地方
 斯琴高丽的伤心
 别在我离开之前离开
 不是因为寂寞才想你
 爱上你等于爱上了错
 在心里从此永远有个你
 一个人的寂寞两个人的错