QQ的TEA填充算法C#實現(xiàn)

2010-08-28 10:52:54來源:西部e網(wǎng)作者:

注:本人只是從LUMA QQ的 Source Code里面把相應Java語言翻譯成C#,純技術(shù)研究,并沒有對TC產(chǎn)品做任何逆向分析,不承擔任何法律責任!轉(zhuǎn)載請保持本文完整性

網(wǎng)上有c/c++,vb,delphi,java,perl各種版本的tea填充算法,唯獨沒有C#的,這讓我這種狂熱喜愛C#的人如何承受,于是,花3天時間看了各種代碼經(jīng)歷無數(shù)失敗的挫折終于用C#完成了該填充算法,廢話不多說,直接給代碼

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

FILE :      QQCrypt.cs

9.22更正:一處筆誤造成解密失敗的BUG

                    優(yōu)化部分代碼

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


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;                                   //填充數(shù)
        private byte[] Key = new byte[16];                      //密鑰
        private bool Header;                                    //用于加密時,表示當前是否是第一個8字節(jié)塊,因為加密算法
                                                                //是反饋的,但是最開始的8個字節(jié)沒有反饋可用,所有需要標
                                                                //明這種情況
        private long contextStart;                              //這個表示當前解密開始的位置,之所以要這么一個變量是為了
                                                                //避免當解密到最后時后面已經(jīng)沒有數(shù)據(jù),這時候就會出錯,這
                                                                //個變量就是用來判斷這種情況免得出錯
        public QQCrypt()
        ...{
            //
            // TODO: 在此處添加構(gòu)造函數(shù)邏輯
            //
        }
       

        //Push 數(shù)據(jù)
        byte[] CopyMemory(byte[] arr,int arr_index,long input)  //lenth = 4
        ...{
            if(arr_index+4 > arr.Length)
            ...{
                // 不能執(zhí)行
                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;
                //不能執(zhí)行
            }

            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 加密函數(shù)
        /// </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;
            //計算頭部填充字節(jié)數(shù)
            Pos = (arrayIn.Length+10) % 8;
            if(Pos != 0)
                Pos = 8-Pos;
            //輸出長度
            Out = new byte[arrayIn.Length+Pos+10];
            //把POS存到PLAIN的第一個字節(jié)
            //0xf8后面3位是空的,正好給Pos
            Plain[0] = (byte)((Rand() & 0xf8) | Pos);
            //用隨機數(shù)填充1到Pos的內(nèi)容
            for(int i=1;i<=Pos;i++)
            ...{
                Plain[i] = (byte)(Rand() & 0xff);
            }
            Pos++;
            padding = 1;

            //繼續(xù)填充兩個字節(jié)隨機數(shù),滿8字節(jié)就加密
            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;
            //明文內(nèi)容,滿8字節(jié)加密到讀完
            l = arrayIn.Length;
            while ( l > 0)
            ...{
                if(Pos<8)
                ...{
                    Plain[Pos] = arrayIn[I];
                    I++;
                    Pos++;
                    l--;
                }
                else if(Pos == 8)
                ...{
                    this.Encrypt8Bytes();
                }
            }

            //末尾填充0,保證是8的倍數(shù)
            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 解密函數(shù)
        /// </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的倍數(shù)至少16字節(jié)
            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;
            //計算消息頭部,明文開始的偏移,解密第一字節(jié)和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
    }
}

 

有了這個,加上一些現(xiàn)成的協(xié)議分析,你就可以做自己的QQ客戶端了。

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

關(guān)鍵詞:C#