本程序由爱页工作室(www.ayeah.net)使用C#在VS2008上开发
目前发布第一版v0.9
有以下功能:
1、输入QQ号码提取所有聊天记录为TXT文件(包括普通聊天记录、群聊天记录、临时会话。。。)
2、一键打包成rar(自动调用rar.exe)
3、发送到邮箱中做备份(使用SMTP)
其它说明:
本程序需要.net framework 2.0以上版本运行库
本程序为绿色版,不用安装,不写注册表,解压到任意目录即用,可自动识别QQ所在目录
聊天记录导出不需要密码,对于本地消息加密的MsgEX.DB暂未测试。
本程序只作聊天记录备份之用,请勿用于偷窥他人隐私。
点击这里下载
ayeah
2008.12.03
爱页工作室
www.ayeah.net
参考了以下内容:
1、解密QQ消息文件格式
http://blog.csdn.net/vbvan/archive/2007/12/14/1937440.aspx
2、Decompiling CHM (help) files with C#
http://www.codeproject.com/csharp/decompilingchm.asp?print=true
3、Google,CSDN,Google....
Storage.cs(从RelatedObjects.Storage.dll反编译而来)
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Collections;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Security;
- namespace RelatedObjects.Storage
- {
- [ComImport, SuppressUnmanagedCodeSecurity, Guid("0000000B-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- public interface IStorage
- {
- [return: MarshalAs(UnmanagedType.Interface)]
- UCOMIStream CreateStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
- [return: MarshalAs(UnmanagedType.Interface)]
- UCOMIStream OpenStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr reserved1, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
- [return: MarshalAs(UnmanagedType.Interface)]
- IStorage CreateStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
- [return: MarshalAs(UnmanagedType.Interface)]
- IStorage OpenStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr pstgPriority, [In, MarshalAs(UnmanagedType.U4)] int grfMode, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.U4)] int reserved);
- void CopyTo(int ciidExclude, [In, MarshalAs(UnmanagedType.LPArray)] Guid[] rgiidExclude, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.Interface)] IStorage pstgDest);
- void MoveElementTo([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.Interface)] IStorage pstgDest, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName, [In, MarshalAs(UnmanagedType.U4)] int grfFlags);
- void Commit(int grfCommitFlags);
- void Revert();
- int EnumElements([In, MarshalAs(UnmanagedType.U4)] int reserved1, IntPtr reserved2, [In, MarshalAs(UnmanagedType.U4)] int reserved3, [MarshalAs(UnmanagedType.Interface)] out IEnumSTATSTG ppenum);
- void DestroyElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsName);
- void RenameElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsOldName, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName);
- void SetElementTimes([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In] FILETIME pctime, [In] FILETIME patime, [In] FILETIME pmtime);
- void SetClass(ref Guid clsid);
- void SetStateBits(int grfStateBits, int grfMask);
- int Stat(out STATSTG pStatStg, int grfStatFlag);
- }
- [ComImport, Guid("0000000D-0000-0000-C000-000000000046"), SuppressUnmanagedCodeSecurity, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- public interface IEnumSTATSTG
- {
- [PreserveSig]
- int Next(int celt, out STATSTG rgVar, out int pceltFetched);
- [PreserveSig]
- int Skip(int celt);
- [PreserveSig]
- int Reset();
- int Clone(out IEnumSTATSTG newEnum);
- }
- public class Ole32
- {
- // Methods
- [DllImport("Ole32.dll")]
- public static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string wcsName, IStorage pstgPriority, int grfMode, IntPtr snbExclude, int reserved, out IStorage storage);
- }
- public class IStorageWrapper : IBaseStorageWrapper
- {
- // Methods
- public IStorageWrapper(string workPath)
- {
- Ole32.StgOpenStorage(workPath, null, 0x10, IntPtr.Zero, 0, out this.storage);
- IBaseStorageWrapper.BaseUrl = workPath;
- STATSTG pStatStg = new STATSTG();
- base.storage.Stat(out pStatStg, 1);
- base.EnumIStorageObject(base.storage);
- }
- }
- public class IBaseStorageWrapper
- {
- // Fields
- private static string baseUrl;
- public FileObjects foCollection = new FileObjects();
- protected IStorage storage;
- // Methods
- protected void EnumIStorageObject(IStorage stgArgument)
- {
- this.EnumIStorageObject(stgArgument, "");
- }
- protected void EnumIStorageObject(IStorage stgArgument, string BasePath)
- {
- IEnumSTATSTG mstatstg;
- STATSTG statstg;
- int num;
- stgArgument.EnumElements(0, IntPtr.Zero, 0, out mstatstg);
- mstatstg.Reset();
- while (mstatstg.Next(1, out statstg, out num) == 0)
- {
- FileObjects.FileObject fo = new FileObjects.FileObject();
- fo.FileType = statstg.type;
- switch (statstg.type)
- {
- case 1:
- {
- IStorage storage = stgArgument.OpenStorage(statstg.pwcsName, IntPtr.Zero, 0x10, IntPtr.Zero, 0);
- if (storage != null)
- {
- string basePath = BasePath + statstg.pwcsName.ToString();
- fo.FileStorage = storage;
- fo.FilePath = BasePath;
- fo.FileName = statstg.pwcsName.ToString();
- this.foCollection.Add(fo);
- this.EnumIStorageObject(storage, basePath);
- }
- break;
- }
- case 2:
- {
- UCOMIStream stream = stgArgument.OpenStream(statstg.pwcsName, IntPtr.Zero, 0x10, 0);
- fo.FilePath = BasePath;
- fo.FileName = statstg.pwcsName.ToString();
- fo.FileStream = stream;
- this.foCollection.Add(fo);
- break;
- }
- case 3:
- Console.WriteLine("[Property:ILockBytes] Ignoring...");
- break;
- case 4:
- Console.WriteLine("[Property:IProperty] Ignoring...");
- break;
- default:
- Console.WriteLine("Unknown object, skipping and continuing...");
- break;
- }
- if (statstg.type == 1)
- {
- Console.WriteLine("Type: STORAGE");
- }
- }
- }
- // Properties
- public static string BaseUrl
- {
- get
- {
- return baseUrl;
- }
- set
- {
- baseUrl = "mk:@MSITStore:" + value + "::/";
- }
- }
- // Nested Types
- public class FileObjects : CollectionBase
- {
- // Methods
- public void Add(FileObject fo)
- {
- base.List.Add(fo);
- }
- public FileObject Item(int index)
- {
- return (FileObject)base.List[index];
- }
- public void Remove(int index)
- {
- if ((index < (base.Count - 1)) && (index > 0))
- {
- base.List.RemoveAt(index);
- }
- }
- // Nested Types
- public class FileObject : Stream
- {
- // Fields
- private string fileName;
- private string filePath;
- private IStorage fileStorage;
- private UCOMIStream fileStream;
- private int fileType;
- private string fileUrl;
- // Methods
- public override void Close()
- {
- if (this.fileStream != null)
- {
- this.fileStream.Commit(0);
- Marshal.ReleaseComObject(this.fileStream);
- this.fileStream = null;
- GC.SuppressFinalize(this);
- }
- }
- public override void Flush()
- {
- if (this.fileStream == null)
- {
- throw new ObjectDisposedException("theStream");
- }
- this.fileStream.Commit(0);
- }
- public override int Read(byte[] buffer, int offset, int count)
- {
- if (this.fileStream == null)
- {
- throw new ObjectDisposedException("theStream");
- }
- int length = 0;
- object obj2 = length;
- GCHandle handle = new GCHandle();
- try
- {
- handle = GCHandle.Alloc(obj2, GCHandleType.Pinned);
- IntPtr pcbRead = handle.AddrOfPinnedObject();
- if (offset != 0)
- {
- byte[] pv = new byte[count - 1];
- this.fileStream.Read(pv, count, pcbRead);
- length = (int)obj2;
- Array.Copy(pv, 0, buffer, offset, length);
- return length;
- }
- this.fileStream.Read(buffer, count, pcbRead);
- length = (int)obj2;
- }
- finally
- {
- if (handle.IsAllocated)
- {
- handle.Free();
- }
- }
- return length;
- }
- public string ReadFromFile()
- {
- int num;
- if (this.fileStream == null)
- {
- throw new ObjectDisposedException("theStream");
- }
- Stream stream = new MemoryStream();
- byte[] buffer = new byte[this.Length];
- this.Seek(0L, SeekOrigin.Begin);
- while ((num = this.Read(buffer, 0, 0x400)) > 0)
- {
- stream.Write(buffer, 0, num);
- }
- stream.Seek(0L, SeekOrigin.Begin);
- StreamReader reader = new StreamReader(stream);
- return reader.ReadToEnd().ToString();
- }
- public void Save(string FileName)
- {
- int num;
- if (this.fileStream == null)
- {
- throw new ObjectDisposedException("theStream");
- }
- byte[] buffer = new byte[this.Length];
- this.Seek(0L, SeekOrigin.Begin);
- Stream stream = File.OpenWrite(FileName);
- while ((num = this.Read(buffer, 0, 0x400)) > 0)
- {
- stream.Write(buffer, 0, num);
- }
- stream.Close();
- }
- public override long Seek(long offset, SeekOrigin origin)
- {
- if (this.fileStream == null)
- {
- throw new ObjectDisposedException("theStream");
- }
- long num = 0L;
- object obj2 = num;
- GCHandle handle = new GCHandle();
- try
- {
- handle = GCHandle.Alloc(obj2, GCHandleType.Pinned);
- IntPtr plibNewPosition = handle.AddrOfPinnedObject();
- this.fileStream.Seek(offset, (int)origin, plibNewPosition);
- num = (long)obj2;
- }
- finally
- {
- if (handle.IsAllocated)
- {
- handle.Free();
- }
- }
- return num;
- }
- public override void SetLength(long Value)
- {
- if (this.fileStream == null)
- {
- throw new ObjectDisposedException("theStream");
- }
- this.fileStream.SetSize(Value);
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- if (this.fileStream == null)
- {
- throw new ObjectDisposedException("theStream");
- }
- if (offset != 0)
- {
- int length = buffer.Length - offset;
- byte[] destinationArray = new byte[length];
- Array.Copy(buffer, offset, destinationArray, 0, length);
- this.fileStream.Write(destinationArray, length, IntPtr.Zero);
- }
- else
- {
- this.fileStream.Write(buffer, count, IntPtr.Zero);
- }
- }
- // Properties
- public override bool CanRead
- {
- get
- {
- return (this.fileStream != null);
- }
- }
- public override bool CanSeek
- {
- get
- {
- return true;
- }
- }
- public override bool CanWrite
- {
- get
- {
- return true;
- }
- }
- public string FileName
- {
- get
- {
- return this.fileName;
- }
- set
- {
- this.fileName = value;
- }
- }
- public string FilePath
- {
- get
- {
- return this.filePath;
- }
- set
- {
- this.filePath = value;
- }
- }
- public IStorage FileStorage
- {
- get
- {
- return this.fileStorage;
- }
- set
- {
- this.fileStorage = value;
- }
- }
- public UCOMIStream FileStream
- {
- get
- {
- return this.fileStream;
- }
- set
- {
- this.fileStream = value;
- }
- }
- public int FileType
- {
- get
- {
- return this.fileType;
- }
- set
- {
- this.fileType = value;
- }
- }
- public string FileUrl
- {
- get
- {
- return (IBaseStorageWrapper.BaseUrl + this.FilePath.Replace(@"/", "/") + "/" + this.FileName);
- }
- set
- {
- this.fileUrl = value;
- }
- }
- public override long Length
- {
- get
- {
- STATSTG statstg;
- if (this.fileStream == null)
- {
- throw new ObjectDisposedException("theStream");
- }
- this.fileStream.Stat(out statstg, 1);
- return statstg.cbSize;
- }
- }
- public override long Position
- {
- get
- {
- return this.Seek(0L, SeekOrigin.Current);
- }
- set
- {
- this.Seek(value, SeekOrigin.Begin);
- }
- }
- }
- }
- }
- }
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.IO;
- using System.Text;
- using RelatedObjects.Storage;
- namespace QQBackup
- {
- public enum QQMsgType
- {
- BIM, C2C, Group, Sys, Mobile, TempSession //Disc
- }
- class QQMsgMgr
- {
- private static readonly int s_MsgTypeNum = (int)QQMsgType.TempSession + 1;
- private static readonly string[] s_MsgName = new string[] {
- "BIMMsg", "C2CMsg", "GroupMsg", "SysMsg", "MobileMsg", "TempSessionMsg"
- };
- private IStorageWrapper m_Storage;
- private byte[] m_Password;
- private List<string>[] m_MsgList = new List<string>[s_MsgTypeNum];
- public void Open(string QQID)
- {
- Open(QQID, null);
- }
- public void Open(string QQID, string QQPath)
- {
- if (QQPath == null)
- {
- using (Microsoft.Win32.RegistryKey reg = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"Software/Tencent/QQ"))
- {
- QQPath = reg.GetValue("Install") as string;
- }
- if (QQPath == null) return;
- }
- for (int i = 0; i < m_MsgList.Length; ++i)
- {
- m_MsgList[i] = new List<string>();
- }
- m_Storage = null;
- m_Password = null;
- m_Storage = new IStorageWrapper(QQPath + QQID + @"/MsgEx.db");
- m_Password = QQMsgMgr.GetGlobalPass(m_Storage, QQID);
- if (m_Password == null) m_Storage = null;
- foreach (IBaseStorageWrapper.FileObjects.FileObject fileObject in m_Storage.foCollection)
- {
- if (fileObject.FileType == 1)
- {
- for (int i = 0; i < m_MsgList.Length; ++i)
- {
- if (fileObject.FilePath == s_MsgName[i])
- {
- m_MsgList[i].Add(fileObject.FileName);
- }
- }
- }
- }
- }
- public void OutputMsg()
- {
- for (int i = 0; i < s_MsgTypeNum; ++i)
- {
- OutputMsg((QQMsgType)i);
- }
- }
- public void OutputMsg(QQMsgType type)
- {
- if (m_Storage == null) return;
- if (m_Password == null) return;
- int typeIndex = (int)type;
- if (typeIndex < 0 || typeIndex >= s_MsgTypeNum)
- {
- throw new ArgumentException("Invalid QQMsgType", "type");
- }
- string filePath = s_MsgName[typeIndex] + "//";
- Directory.CreateDirectory(filePath);
- foreach (string QQID in m_MsgList[typeIndex])
- {
- string fileName = filePath + QQID + ".txt";
- OutputMsg(type, QQID, fileName);
- }
- }
- public void OutputMsg(QQMsgType type, string QQID)
- {
- if (m_Storage == null) return;
- if (m_Password == null) return;
- int typeIndex = (int)type;
- if (typeIndex < 0 || typeIndex >= s_MsgTypeNum)
- {
- throw new ArgumentException("Invalid QQMsgType", "type");
- }
- string filePath = s_MsgName[typeIndex] + "//";
- Directory.CreateDirectory(filePath);
- string fileName = filePath + QQID + ".txt";
- OutputMsg(type, QQID, fileName);
- }
- private void OutputMsg(QQMsgType type, string QQID, string fileName)
- {
- string msgPath = s_MsgName[(int)type] + QQID;
- IList<byte[]> msgList = QQMsgMgr.DecryptMsg(m_Storage, msgPath, m_Password);
- Encoding encoding = Encoding.GetEncoding(936);
- using (FileStream fs = new FileStream(fileName, FileMode.Create))
- {
- using (StreamWriter sw = new StreamWriter(fs))
- {
- for (int i = 0; i < msgList.Count; ++i)
- {
- using (MemoryStream ms = new MemoryStream(msgList[i]))
- {
- using (BinaryReader br = new BinaryReader(ms, Encoding.GetEncoding(936)))
- {
- #if false
- fs.Write(msgList[i], 0, msgList[i].Length);
- #else
- int ticks = br.ReadInt32();
- DateTime time = new DateTime(1970, 1, 1) + new TimeSpan(0, 0, ticks);
- switch (type)
- {
- case QQMsgType.BIM:
- case QQMsgType.C2C:
- case QQMsgType.Mobile:
- ms.Seek(1, SeekOrigin.Current);
- break;
- case QQMsgType.Group:
- ms.Seek(8, SeekOrigin.Current);
- break;
- case QQMsgType.Sys:
- ms.Seek(4, SeekOrigin.Current);
- break;
- case QQMsgType.TempSession: //?
- ms.Seek(9, SeekOrigin.Current);
- break;
- }
- if (type == QQMsgType.TempSession)
- {
- int gLen = br.ReadInt32();
- string groupName = encoding.GetString(br.ReadBytes(gLen));
- if (groupName.Length > 0) sw.WriteLine("{0}", groupName);
- }
- int nLen = br.ReadInt32();
- string id = encoding.GetString(br.ReadBytes(nLen));
- sw.WriteLine("{0}: {1}", id, time.ToString());
- int cLen = br.ReadInt32();
- string msg = encoding.GetString(br.ReadBytes(cLen));
- //msg=msg.Replace("/n", Environment.NewLine);
- msg = msg.Replace(new string(new char[] { (char)21 }), "(图片)");
- msg = msg.Replace(new string(new char[] { (char)20 }), "(表情)");
- char[] t = msg.ToCharArray();
- msg=msg.Split((char)19)[0];
- msg = msg.Replace("/n", Environment.NewLine);
- sw.WriteLine(msg);
- sw.WriteLine();
- #endif
- }
- }
- }
- sw.Close();
- }
- fs.Close();
- }
- }
- public void OutputFileList()
- {
- if (m_Storage == null) return;
- Dictionary<string, long> dic = new Dictionary<string, long>();
- foreach (IBaseStorageWrapper.FileObjects.FileObject fileObject in m_Storage.foCollection)
- {
- if (fileObject.FileType == 2 && fileObject.FileName == "Index.msj")
- {
- dic[fileObject.FilePath] = fileObject.Length / 4;
- }
- }
- for (int i = 0; i < m_MsgList.Length; ++i)
- {
- Console.WriteLine("{0}", s_MsgName[i]);
- foreach (string ID in m_MsgList[i])
- {
- Console.WriteLine("/t{0}: {1}", ID, dic[s_MsgName[i] + ID]);
- }
- }
- }
- private static IBaseStorageWrapper.FileObjects.FileObject GetStorageFileObject(IStorageWrapper iw, string path, string fileName)
- {
- foreach (IBaseStorageWrapper.FileObjects.FileObject fileObject in iw.foCollection)
- {
- if (fileObject.CanRead)
- {
- if (fileObject.FilePath == path && fileObject.FileName == fileName) return fileObject;
- }
- }
- return null;
- }
- private static byte[] Decrypt(byte[] src, byte[] pass, long offset)
- {
- RedQ.QQCrypt decryptor = new RedQ.QQCrypt();
- return decryptor.QQ_Decrypt(src, pass, offset);
- }
- private static IList<byte[]> DecryptMsg(IStorageWrapper iw, string path, byte[] pass)
- {
- List<byte[]> msgList = new List<byte[]>();
- int num = 0;
- int[] pos = null;
- int[] len = null;
- using (IBaseStorageWrapper.FileObjects.FileObject fileObject = GetStorageFileObject(iw, path, "Index.msj"))
- {
- if (fileObject == null) return msgList;
- int fileLen = (int)fileObject.Length;
- num = fileLen / 4;
- pos = new int[num + 1];
- using (BinaryReader br = new BinaryReader(fileObject))
- {
- for (int i = 0; i < num; ++i)
- {
- pos[i] = br.ReadInt32();
- }
- }
- }
- using (IBaseStorageWrapper.FileObjects.FileObject fileObject = GetStorageFileObject(iw, path, "Data.msj"))
- {
- if (fileObject != null)
- {
- int fileLen = (int)fileObject.Length;
- len = new int[num];
- pos[num] = fileLen;
- for (int i = 0; i < num; ++i)
- {
- len[i] = pos[i + 1] - pos[i];
- }
- using (BinaryReader br = new BinaryReader(fileObject))
- {
- for (int i = 0; i < num; ++i)
- {
- fileObject.Seek(pos[i], SeekOrigin.Begin);
- byte[] data = br.ReadBytes(len[i]);
- byte[] msg = Decrypt(data, pass, 0);
- msgList.Add(msg);
- }
- }
- }
- }
- return msgList;
- }
- private static byte[] GetGlobalPass(IStorageWrapper iw, string QQID)
- {
- System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
- byte[] dataID = new byte[QQID.Length];
- for (int i = 0; i < QQID.Length; ++i) dataID[i] = (byte)(QQID[i]);
- byte[] hashID = md5.ComputeHash(dataID);
- IBaseStorageWrapper.FileObjects.FileObject fileObject = GetStorageFileObject(iw, "Matrix", "Matrix.db");
- if (fileObject != null)
- {
- using (BinaryReader br = new BinaryReader(fileObject))
- {
- byte[] data = br.ReadBytes((int)fileObject.Length);
- long len = data.Length;
- if (len < 6 || data[0] != 0x51 || data[1] != 0x44) return null;
- if (len >= 32768) return null;
- bool bl = false;
- int i = 6;
- while (i < len)
- {
- bl = false;
- byte type = data[i++];
- if (i + 2 > len) break;
- int len1 = data[i] + data[i + 1] * 256;
- byte xor1 = (byte)(data[i] ^ data[i + 1]);
- i += 2;
- if (i + len1 > len) break;
- for (int j = 0; j < len1; ++j) data[i + j] = (byte)(~(data[i + j] ^ xor1));
- if (len1 == 3 && data[i] == 0x43 && data[i + 1] == 0x52 && data[i + 2] == 0x4B)
- {
- bl = true;
- }
- i += len1;
- if (type > 7) break;
- if (i + 4 > len) break;
- int len2 = data[i] + data[i + 1] * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 * 256 * 256;
- byte xor2 = (byte)(data[i] ^ data[i + 1]);
- i += 4;
- if (i + len2 > len) break;
- if (type == 6 || type == 7)
- {
- for (int j = 0; j < len2; ++j) data[i + j] = (byte)(~(data[i + j] ^ xor2));
- }
- if (bl && len2 == 0x20)
- {
- byte[] dataT = new byte[len2];
- for (int j = 0; j < len2; ++j) dataT[j] = data[i + j];
- return Decrypt(dataT, hashID, 0);
- }
- i += len2;
- }
- if (i != len) return null;
- }
- }
- return null;
- }
- public void Close(string QQID) {
- if (m_Storage == null)
- {
- return;
- }
- else {
- m_Storage = null;
- }
- }
- }
- }
- 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);
- }
- #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
- }
- }
不想直接发源码是讨厌很多人拿去改掉我的名字和网址就当自己的“原创”了。。。