注:本人只是從LUMA QQ的 Source Code里面把相應(yīng)Java語(yǔ)言翻譯成C#,純技術(shù)研究,并沒(méi)有對(duì)TC產(chǎn)品做任何逆向分析,不承擔(dān)任何法律責(zé)任!轉(zhuǎn)載請(qǐng)保持本文完整性
網(wǎng)上有c/c++,vb,delphi,java,perl各種版本的tea填充算法,唯獨(dú)沒(méi)有C#的,這讓我這種狂熱喜愛(ài)C#的人如何承受,于是,花3天時(shí)間看了各種代碼經(jīng)歷無(wú)數(shù)失敗的挫折終于用C#完成了該填充算法,廢話不多說(shuō),直接給代碼
/*********************************************************
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; //指向當(dāng)前的明文塊
private byte[] prePlain ; //指向前面一個(gè)明文塊
private byte[] Out; //輸出的密文或者明文
private long Crypt, preCrypt; //當(dāng)前加密的密文位置和上一次加密的密文塊位置,他們相差8
private long Pos; //當(dāng)前處理的加密解密塊的位置
private long padding; //填充數(shù)
private byte[] Key = new byte[16]; //密鑰
private bool Header; //用于加密時(shí),表示當(dāng)前是否是第一個(gè)8字節(jié)塊,因?yàn)榧用芩惴?
//是反饋的,但是最開(kāi)始的8個(gè)字節(jié)沒(méi)有反饋可用,所有需要標(biāo)
//明這種情況
private long contextStart; //這個(gè)表示當(dāng)前解密開(kāi)始的位置,之所以要這么一個(gè)變量是為了
//避免當(dāng)解密到最后時(shí)后面已經(jīng)沒(méi)有數(shù)據(jù),這時(shí)候就會(huì)出錯(cuò),這
//個(gè)變量就是用來(lái)判斷這種情況免得出錯(cuò)
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;
//計(jì)算頭部填充字節(jié)數(shù)
Pos = (arrayIn.Length+10) % 8;
if(Pos != 0)
Pos = 8-Pos;
//輸出長(zhǎng)度
Out = new byte[arrayIn.Length+Pos+10];
//把POS存到PLAIN的第一個(gè)字節(jié)
//0xf8后面3位是空的,正好給Pos
Plain[0] = (byte)((Rand() & 0xf8) | Pos);
//用隨機(jī)數(shù)填充1到Pos的內(nèi)容
for(int i=1;i<=Pos;i++)
...{
Plain[i] = (byte)(Rand() & 0xff);
}
Pos++;
padding = 1;
//繼續(xù)填充兩個(gè)字節(jié)隨機(jī)數(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;
//計(jì)算消息頭部,明文開(kāi)始的偏移,解密第一字節(jié)和7相與得到
prePlain = this.Decipher(arrayIn,arrayKey,offset);
Pos = prePlain[0] & 7;
//計(jì)算明文長(zhǎng)度
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;
//跳過(guò)頭部
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
}
}
有了這個(gè),加上一些現(xiàn)成的協(xié)議分析,你就可以做自己的QQ客戶端了。
原文:http://blog.csdn.net/Red_angelX/archive/2006/09/19/1246701.aspx