QQ聊天记录快速备份 v0.9

云盘同步本地文件和QQ聊天记录 一、腾讯微云同步QQ聊天记录和QQ接受的文件1. 下载QQ,开始安装QQ时,点击自定义安装选项,如下图 以上是QQ安装的默认形式,为了以后安全的考虑,我们将QQ软件安装目录定义为D:\CMWang\QQsetup(若将QQ的在C盘,重装系统QQ所有信息都会丢失),QQ的聊天记录和QQ上接受别人发给你的文件建议储存在D:\CMWang\QQtempfiles\QQrecord,为何新建了两层的子目录 阅读详情


本程序由爱页工作室(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.youkuaiyun.com/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,优快云,Google....


Storage.cs(从RelatedObjects.Storage.dll反编译而来)

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Collections;
  6. using System.IO;
  7. using System.Runtime.InteropServices;
  8. using System.Security;

  9. namespace RelatedObjects.Storage
  10. {
  11.     [ComImport, SuppressUnmanagedCodeSecurity, Guid("0000000B-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  12.     public interface IStorage
  13.     {
  14.         [return: MarshalAs(UnmanagedType.Interface)]
  15.         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);
  16.         [return: MarshalAs(UnmanagedType.Interface)]
  17.         UCOMIStream OpenStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr reserved1, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
  18.         [return: MarshalAs(UnmanagedType.Interface)]
  19.         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);
  20.         [return: MarshalAs(UnmanagedType.Interface)]
  21.         IStorage OpenStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr pstgPriority, [In, MarshalAs(UnmanagedType.U4)] int grfMode, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.U4)] int reserved);
  22.         void CopyTo(int ciidExclude, [In, MarshalAs(UnmanagedType.LPArray)] Guid[] rgiidExclude, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.Interface)] IStorage pstgDest);
  23.         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);
  24.         void Commit(int grfCommitFlags);
  25.         void Revert();
  26.         int EnumElements([In, MarshalAs(UnmanagedType.U4)] int reserved1, IntPtr reserved2, [In, MarshalAs(UnmanagedType.U4)] int reserved3, [MarshalAs(UnmanagedType.Interface)] out IEnumSTATSTG ppenum);
  27.         void DestroyElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsName);
  28.         void RenameElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsOldName, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName);
  29.         void SetElementTimes([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In] FILETIME pctime, [In] FILETIME patime, [In] FILETIME pmtime);
  30.         void SetClass(ref Guid clsid);
  31.         void SetStateBits(int grfStateBits, int grfMask);
  32.         int Stat(out STATSTG pStatStg, int grfStatFlag);
  33.     }

  34.     [ComImport, Guid("0000000D-0000-0000-C000-000000000046"), SuppressUnmanagedCodeSecurity, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  35.     public interface IEnumSTATSTG
  36.     {
  37.         [PreserveSig]
  38.         int Next(int celt, out STATSTG rgVar, out int pceltFetched);
  39.         [PreserveSig]
  40.         int Skip(int celt);
  41.         [PreserveSig]
  42.         int Reset();
  43.         int Clone(out IEnumSTATSTG newEnum);
  44.     }

  45.     public class Ole32
  46.     {
  47.         // Methods
  48.         [DllImport("Ole32.dll")]
  49.         public static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string wcsName, IStorage pstgPriority, int grfMode, IntPtr snbExclude, int reserved, out IStorage storage);
  50.     }

  51.     public class IStorageWrapper : IBaseStorageWrapper
  52.     {
  53.         // Methods
  54.         public IStorageWrapper(string workPath)
  55.         {
  56.             Ole32.StgOpenStorage(workPath, null, 0x10, IntPtr.Zero, 0, out this.storage);
  57.             IBaseStorageWrapper.BaseUrl = workPath;
  58.             STATSTG pStatStg = new STATSTG();
  59.             base.storage.Stat(out pStatStg, 1);
  60.             base.EnumIStorageObject(base.storage);
  61.             
  62.         }
  63.     }

  64.     public class IBaseStorageWrapper
  65.     {
  66.         // Fields
  67.         private static string baseUrl;
  68.         public FileObjects foCollection = new FileObjects();
  69.         protected IStorage storage;

  70.         // Methods
  71.         protected void EnumIStorageObject(IStorage stgArgument)
  72.         {
  73.             this.EnumIStorageObject(stgArgument, "");
  74.         }

  75.         protected void EnumIStorageObject(IStorage stgArgument, string BasePath)
  76.         {
  77.             IEnumSTATSTG mstatstg;
  78.             STATSTG statstg;
  79.             int num;
  80.             stgArgument.EnumElements(0, IntPtr.Zero, 0, out mstatstg);
  81.             mstatstg.Reset();
  82.             while (mstatstg.Next(1, out statstg, out num) == 0)
  83.             {
  84.                 FileObjects.FileObject fo = new FileObjects.FileObject();
  85.                 fo.FileType = statstg.type;
  86.                 switch (statstg.type)
  87.                 {
  88.                     case 1:
  89.                         {
  90.                             IStorage storage = stgArgument.OpenStorage(statstg.pwcsName, IntPtr.Zero, 0x10, IntPtr.Zero, 0);
  91.                             if (storage != null)
  92.                             {
  93.                                 string basePath = BasePath + statstg.pwcsName.ToString();
  94.                                 fo.FileStorage = storage;
  95.                                 fo.FilePath = BasePath;
  96.                                 fo.FileName = statstg.pwcsName.ToString();
  97.                                 this.foCollection.Add(fo);
  98.                                 this.EnumIStorageObject(storage, basePath);
  99.                             }
  100.                             break;
  101.                         }
  102.                     case 2:
  103.                         {
  104.                             UCOMIStream stream = stgArgument.OpenStream(statstg.pwcsName, IntPtr.Zero, 0x10, 0);
  105.                             fo.FilePath = BasePath;
  106.                             fo.FileName = statstg.pwcsName.ToString();
  107.                             fo.FileStream = stream;
  108.                             this.foCollection.Add(fo);
  109.                             break;
  110.                         }
  111.                     case 3:
  112.                         Console.WriteLine("[Property:ILockBytes] Ignoring...");
  113.                         break;

  114.                     case 4:
  115.                         Console.WriteLine("[Property:IProperty] Ignoring...");
  116.                         break;

  117.                     default:
  118.                         Console.WriteLine("Unknown object, skipping and continuing...");
  119.                         break;
  120.                 }
  121.                 if (statstg.type == 1)
  122.                 {
  123.                     Console.WriteLine("Type: STORAGE");
  124.                 }
  125.             }
  126.         }

  127.         // Properties
  128.         public static string BaseUrl
  129.         {
  130.             get
  131.             {
  132.                 return baseUrl;
  133.             }
  134.             set
  135.             {
  136.                 baseUrl = "mk:@MSITStore:" + value + "::/";
  137.             }
  138.         }

  139.         // Nested Types
  140.         public class FileObjects : CollectionBase
  141.         {
  142.             // Methods
  143.             public void Add(FileObject fo)
  144.             {
  145.                 base.List.Add(fo);
  146.             }

  147.             public FileObject Item(int index)
  148.             {
  149.                 return (FileObject)base.List[index];
  150.             }

  151.             public void Remove(int index)
  152.             {
  153.                 if ((index < (base.Count - 1)) && (index > 0))
  154.                 {
  155.                     base.List.RemoveAt(index);
  156.                 }
  157.             }

  158.             // Nested Types
  159.             public class FileObject : Stream
  160.             {
  161.                 // Fields
  162.                 private string fileName;
  163.                 private string filePath;
  164.                 private IStorage fileStorage;
  165.                 private UCOMIStream fileStream;
  166.                 private int fileType;
  167.                 private string fileUrl;

  168.                 // Methods
  169.                 public override void Close()
  170.                 {
  171.                     if (this.fileStream != null)
  172.                     {
  173.                         this.fileStream.Commit(0);
  174.                         Marshal.ReleaseComObject(this.fileStream);
  175.                         this.fileStream = null;
  176.                         GC.SuppressFinalize(this);
  177.                     }
  178.                 }

  179.                 public override void Flush()
  180.                 {
  181.                     if (this.fileStream == null)
  182.                     {
  183.                         throw new ObjectDisposedException("theStream");
  184.                     }
  185.                     this.fileStream.Commit(0);
  186.                 }

  187.                 public override int Read(byte[] buffer, int offset, int count)
  188.                 {
  189.                     if (this.fileStream == null)
  190.                     {
  191.                         throw new ObjectDisposedException("theStream");
  192.                     }
  193.                     int length = 0;
  194.                     object obj2 = length;
  195.                     GCHandle handle = new GCHandle();
  196.                     try
  197.                     {
  198.                         handle = GCHandle.Alloc(obj2, GCHandleType.Pinned);
  199.                         IntPtr pcbRead = handle.AddrOfPinnedObject();
  200.                         if (offset != 0)
  201.                         {
  202.                             byte[] pv = new byte[count - 1];
  203.                             this.fileStream.Read(pv, count, pcbRead);
  204.                             length = (int)obj2;
  205.                             Array.Copy(pv, 0, buffer, offset, length);
  206.                             return length;
  207.                         }
  208.                         this.fileStream.Read(buffer, count, pcbRead);
  209.                         length = (int)obj2;
  210.                     }
  211.                     finally
  212.                     {
  213.                         if (handle.IsAllocated)
  214.                         {
  215.                             handle.Free();
  216.                         }
  217.                     }
  218.                     return length;
  219.                 }

  220.                 public string ReadFromFile()
  221.                 {
  222.                     int num;
  223.                     if (this.fileStream == null)
  224.                     {
  225.                         throw new ObjectDisposedException("theStream");
  226.                     }
  227.                     Stream stream = new MemoryStream();
  228.                     byte[] buffer = new byte[this.Length];
  229.                     this.Seek(0L, SeekOrigin.Begin);
  230.                     while ((num = this.Read(buffer, 0, 0x400)) > 0)
  231.                     {
  232.                         stream.Write(buffer, 0, num);
  233.                     }
  234.                     stream.Seek(0L, SeekOrigin.Begin);
  235.                     StreamReader reader = new StreamReader(stream);
  236.                     return reader.ReadToEnd().ToString();
  237.                 }

  238.                 public void Save(string FileName)
  239.                 {
  240.                     int num;
  241.                     if (this.fileStream == null)
  242.                     {
  243.                         throw new ObjectDisposedException("theStream");
  244.                     }
  245.                     byte[] buffer = new byte[this.Length];
  246.                     this.Seek(0L, SeekOrigin.Begin);
  247.                     Stream stream = File.OpenWrite(FileName);
  248.                     while ((num = this.Read(buffer, 0, 0x400)) > 0)
  249.                     {
  250.                         stream.Write(buffer, 0, num);
  251.                     }
  252.                     stream.Close();
  253.                 }

  254.                 public override long Seek(long offset, SeekOrigin origin)
  255.                 {
  256.                     if (this.fileStream == null)
  257.                     {
  258.                         throw new ObjectDisposedException("theStream");
  259.                     }
  260.                     long num = 0L;
  261.                     object obj2 = num;
  262.                     GCHandle handle = new GCHandle();
  263.                     try
  264.                     {
  265.                         handle = GCHandle.Alloc(obj2, GCHandleType.Pinned);
  266.                         IntPtr plibNewPosition = handle.AddrOfPinnedObject();
  267.                         this.fileStream.Seek(offset, (int)origin, plibNewPosition);
  268.                         num = (long)obj2;
  269.                     }
  270.                     finally
  271.                     {
  272.                         if (handle.IsAllocated)
  273.                         {
  274.                             handle.Free();
  275.                         }
  276.                     }
  277.                     return num;
  278.                 }

  279.                 public override void SetLength(long Value)
  280.                 {
  281.                     if (this.fileStream == null)
  282.                     {
  283.                         throw new ObjectDisposedException("theStream");
  284.                     }
  285.                     this.fileStream.SetSize(Value);
  286.                 }

  287.                 public override void Write(byte[] buffer, int offset, int count)
  288.                 {
  289.                     if (this.fileStream == null)
  290.                     {
  291.                         throw new ObjectDisposedException("theStream");
  292.                     }
  293.                     if (offset != 0)
  294.                     {
  295.                         int length = buffer.Length - offset;
  296.                         byte[] destinationArray = new byte[length];
  297.                         Array.Copy(buffer, offset, destinationArray, 0, length);
  298.                         this.fileStream.Write(destinationArray, length, IntPtr.Zero);
  299.                     }
  300.                     else
  301.                     {
  302.                         this.fileStream.Write(buffer, count, IntPtr.Zero);
  303.                     }
  304.                 }

  305.                 // Properties
  306.                 public override bool CanRead
  307.                 {
  308.                     get
  309.                     {
  310.                         return (this.fileStream != null);
  311.                     }
  312.                 }

  313.                 public override bool CanSeek
  314.                 {
  315.                     get
  316.                     {
  317.                         return true;
  318.                     }
  319.                 }

  320.                 public override bool CanWrite
  321.                 {
  322.                     get
  323.                     {
  324.                         return true;
  325.                     }
  326.                 }

  327.                 public string FileName
  328.                 {
  329.                     get
  330.                     {
  331.                         return this.fileName;
  332.                     }
  333.                     set
  334.                     {
  335.                         this.fileName = value;
  336.                     }
  337.                 }

  338.                 public string FilePath
  339.                 {
  340.                     get
  341.                     {
  342.                         return this.filePath;
  343.                     }
  344.                     set
  345.                     {
  346.                         this.filePath = value;
  347.                     }
  348.                 }

  349.                 public IStorage FileStorage
  350.                 {
  351.                     get
  352.                     {
  353.                         return this.fileStorage;
  354.                     }
  355.                     set
  356.                     {
  357.                         this.fileStorage = value;
  358.                     }
  359.                 }

  360.                 public UCOMIStream FileStream
  361.                 {
  362.                     get
  363.                     {
  364.                         return this.fileStream;
  365.                     }
  366.                     set
  367.                     {
  368.                         this.fileStream = value;
  369.                     }
  370.                 }

  371.                 public int FileType
  372.                 {
  373.                     get
  374.                     {
  375.                         return this.fileType;
  376.                     }
  377.                     set
  378.                     {
  379.                         this.fileType = value;
  380.                     }
  381.                 }

  382.                 public string FileUrl
  383.                 {
  384.                     get
  385.                     {
  386.                         return (IBaseStorageWrapper.BaseUrl + this.FilePath.Replace(@"/", "/") + "/" + this.FileName);
  387.                     }
  388.                     set
  389.                     {
  390.                         this.fileUrl = value;
  391.                     }
  392.                 }

  393.                 public override long Length
  394.                 {
  395.                     get
  396.                     {
  397.                         STATSTG statstg;
  398.                         if (this.fileStream == null)
  399.                         {
  400.                             throw new ObjectDisposedException("theStream");
  401.                         }
  402.                         this.fileStream.Stat(out statstg, 1);
  403.                         return statstg.cbSize;
  404.                     }
  405.                 }

  406.                 public override long Position
  407.                 {
  408.                     get
  409.                     {
  410.                         return this.Seek(0L, SeekOrigin.Current);
  411.                     }
  412.                     set
  413.                     {
  414.                         this.Seek(value, SeekOrigin.Begin);
  415.                     }
  416.                 }
  417.             }
  418.         }
  419.     }

  420.  

  421. }

QQMsgMgr.cs -- 从vbvan处修改而来,主要修改了一直占用MsgEx.db的毛病

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.IO;
  5. using System.Text;
  6. using RelatedObjects.Storage;

  7. namespace QQBackup
  8. {
  9.     public enum QQMsgType
  10.     {
  11.         BIM, C2C, Group, Sys, Mobile, TempSession //Disc
  12.     }

  13.     class QQMsgMgr
  14.     {
  15.         private static readonly int s_MsgTypeNum = (int)QQMsgType.TempSession + 1;
  16.         private static readonly string[] s_MsgName = new string[] {
  17.             "BIMMsg", "C2CMsg", "GroupMsg", "SysMsg", "MobileMsg", "TempSessionMsg"
  18.         };
  19.         private IStorageWrapper m_Storage;
  20.         private byte[] m_Password;

  21.         private List<string>[] m_MsgList = new List<string>[s_MsgTypeNum];

  22.         public void Open(string QQID)
  23.         {
  24.             Open(QQID, null);
  25.         }

  26.         public void Open(string QQID, string QQPath)
  27.         {
  28.             if (QQPath == null)
  29.             {
  30.                 using (Microsoft.Win32.RegistryKey reg = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"Software/Tencent/QQ"))
  31.                 {
  32.                     QQPath = reg.GetValue("Install") as string;
  33.                 }
  34.                 if (QQPath == null) return;
  35.             }

  36.             for (int i = 0; i < m_MsgList.Length; ++i)
  37.             {
  38.                 m_MsgList[i] = new List<string>();
  39.             }
  40.             m_Storage = null;
  41.             m_Password = null;

  42.             m_Storage = new IStorageWrapper(QQPath + QQID + @"/MsgEx.db");
  43.             m_Password = QQMsgMgr.GetGlobalPass(m_Storage, QQID);
  44.             
  45.             if (m_Password == null) m_Storage = null;

  46.             foreach (IBaseStorageWrapper.FileObjects.FileObject fileObject in m_Storage.foCollection)
  47.             {
  48.                 if (fileObject.FileType == 1)
  49.                 {
  50.                     for (int i = 0; i < m_MsgList.Length; ++i)
  51.                     {
  52.                         if (fileObject.FilePath == s_MsgName[i])
  53.                         {
  54.                             m_MsgList[i].Add(fileObject.FileName);
  55.                         }
  56.                     }
  57.                 }
  58.             }
  59.         }

  60.         public void OutputMsg()
  61.         {
  62.             for (int i = 0; i < s_MsgTypeNum; ++i)
  63.             {
  64.                 OutputMsg((QQMsgType)i);
  65.             }
  66.         }

  67.         public void OutputMsg(QQMsgType type)
  68.         {
  69.             if (m_Storage == null) return;
  70.             if (m_Password == null) return;

  71.             int typeIndex = (int)type;
  72.             if (typeIndex < 0 || typeIndex >= s_MsgTypeNum)
  73.             {
  74.                 throw new ArgumentException("Invalid QQMsgType", "type");
  75.             }

  76.             string filePath = s_MsgName[typeIndex] + "//";
  77.             Directory.CreateDirectory(filePath);

  78.             foreach (string QQID in m_MsgList[typeIndex])
  79.             {
  80.                 string fileName = filePath + QQID + ".txt";
  81.                 OutputMsg(type, QQID, fileName);
  82.             }
  83.         }

  84.         public void OutputMsg(QQMsgType type, string QQID)
  85.         {
  86.             if (m_Storage == null) return;
  87.             if (m_Password == null) return;

  88.             int typeIndex = (int)type;
  89.             if (typeIndex < 0 || typeIndex >= s_MsgTypeNum)
  90.             {
  91.                 throw new ArgumentException("Invalid QQMsgType", "type");
  92.             }

  93.             string filePath = s_MsgName[typeIndex] + "//";
  94.             Directory.CreateDirectory(filePath);

  95.             string fileName = filePath + QQID + ".txt";
  96.             OutputMsg(type, QQID, fileName);
  97.         }

  98.         private void OutputMsg(QQMsgType type, string QQID, string fileName)
  99.         {
  100.             string msgPath = s_MsgName[(int)type] + QQID;
  101.             IList<byte[]> msgList = QQMsgMgr.DecryptMsg(m_Storage, msgPath, m_Password);
  102.             Encoding encoding = Encoding.GetEncoding(936);

  103.             using (FileStream fs = new FileStream(fileName, FileMode.Create))
  104.             {
  105.                 using (StreamWriter sw = new StreamWriter(fs))
  106.                 {
  107.                     for (int i = 0; i < msgList.Count; ++i)
  108.                     {
  109.                         using (MemoryStream ms = new MemoryStream(msgList[i]))
  110.                         {
  111.                             using (BinaryReader br = new BinaryReader(ms, Encoding.GetEncoding(936)))
  112.                             {
  113. #if false
  114.                                 fs.Write(msgList[i], 0, msgList[i].Length);
  115. #else
  116.                                 int ticks = br.ReadInt32();
  117.                                 DateTime time = new DateTime(1970, 1, 1) + new TimeSpan(0, 0, ticks);
  118.                                 switch (type)
  119.                                 {
  120.                                     case QQMsgType.BIM:
  121.                                     case QQMsgType.C2C:
  122.                                     case QQMsgType.Mobile:
  123.                                         ms.Seek(1, SeekOrigin.Current);
  124.                                         break;
  125.                                     case QQMsgType.Group:
  126.                                         ms.Seek(8, SeekOrigin.Current);
  127.                                         break;
  128.                                     case QQMsgType.Sys:
  129.                                         ms.Seek(4, SeekOrigin.Current);
  130.                                         break;
  131.                                     case QQMsgType.TempSession: //?
  132.                                         ms.Seek(9, SeekOrigin.Current);
  133.                                         break;
  134.                                 }
  135.                                 if (type == QQMsgType.TempSession)
  136.                                 {
  137.                                     int gLen = br.ReadInt32();
  138.                                     string groupName = encoding.GetString(br.ReadBytes(gLen));
  139.                                     if (groupName.Length > 0) sw.WriteLine("{0}", groupName);
  140.                                 }
  141.                                 int nLen = br.ReadInt32();
  142.                                 string id = encoding.GetString(br.ReadBytes(nLen));
  143.                                 sw.WriteLine("{0}: {1}", id, time.ToString());
  144.                                 int cLen = br.ReadInt32();
  145.                                 string msg = encoding.GetString(br.ReadBytes(cLen));
  146.                                 //msg=msg.Replace("/n", Environment.NewLine);
  147.                                 msg = msg.Replace(new string(new char[] { (char)21 }), "(图片)");
  148.                                 msg = msg.Replace(new string(new char[] { (char)20 }), "(表情)");
  149.                                 char[] t = msg.ToCharArray();
  150.                                 msg=msg.Split((char)19)[0];
  151.                                 msg = msg.Replace("/n", Environment.NewLine);
  152.                                 sw.WriteLine(msg);
  153.                                 sw.WriteLine();
  154. #endif
  155.                             }
  156.                         }
  157.                     }
  158.                     sw.Close();
  159.                 }
  160.                 fs.Close();
  161.             }
  162.             
  163.         }

  164.         public void OutputFileList()
  165.         {
  166.             if (m_Storage == null) return;

  167.             Dictionary<string, long> dic = new Dictionary<string, long>();
  168.             foreach (IBaseStorageWrapper.FileObjects.FileObject fileObject in m_Storage.foCollection)
  169.             {
  170.                 if (fileObject.FileType == 2 && fileObject.FileName == "Index.msj")
  171.                 {
  172.                     dic[fileObject.FilePath] = fileObject.Length / 4;
  173.                 }
  174.             }

  175.             for (int i = 0; i < m_MsgList.Length; ++i)
  176.             {
  177.                 Console.WriteLine("{0}", s_MsgName[i]);
  178.                 foreach (string ID in m_MsgList[i])
  179.                 {
  180.                     Console.WriteLine("/t{0}: {1}", ID, dic[s_MsgName[i] + ID]);
  181.                 }
  182.             }
  183.         }

  184.         private static IBaseStorageWrapper.FileObjects.FileObject GetStorageFileObject(IStorageWrapper iw, string path, string fileName)
  185.         {
  186.             foreach (IBaseStorageWrapper.FileObjects.FileObject fileObject in iw.foCollection)
  187.             {
  188.                 if (fileObject.CanRead)
  189.                 {
  190.                     if (fileObject.FilePath == path && fileObject.FileName == fileName) return fileObject;
  191.                 }
  192.             }
  193.             return null;
  194.         }
  195.         
  196.         private static byte[] Decrypt(byte[] src, byte[] pass, long offset)
  197.         {
  198.             RedQ.QQCrypt decryptor = new RedQ.QQCrypt();
  199.             return decryptor.QQ_Decrypt(src, pass, offset);
  200.         }

  201.         private static IList<byte[]> DecryptMsg(IStorageWrapper iw, string path, byte[] pass)
  202.         {
  203.             List<byte[]> msgList = new List<byte[]>();

  204.             int num = 0;
  205.             int[] pos = null;
  206.             int[] len = null;
  207.             using (IBaseStorageWrapper.FileObjects.FileObject fileObject = GetStorageFileObject(iw, path, "Index.msj"))
  208.             {
  209.                 if (fileObject == null) return msgList;
  210.                 int fileLen = (int)fileObject.Length;
  211.                 num = fileLen / 4;
  212.                 pos = new int[num + 1];
  213.                 using (BinaryReader br = new BinaryReader(fileObject))
  214.                 {
  215.                     for (int i = 0; i < num; ++i)
  216.                     {
  217.                         pos[i] = br.ReadInt32();
  218.                     }
  219.                 }
  220.             }
  221.             using (IBaseStorageWrapper.FileObjects.FileObject fileObject = GetStorageFileObject(iw, path, "Data.msj"))
  222.             {
  223.                 if (fileObject != null)
  224.                 {
  225.                     int fileLen = (int)fileObject.Length;
  226.                     len = new int[num];
  227.                     pos[num] = fileLen;
  228.                     for (int i = 0; i < num; ++i)
  229.                     {
  230.                         len[i] = pos[i + 1] - pos[i];
  231.                     }
  232.                     using (BinaryReader br = new BinaryReader(fileObject))
  233.                     {
  234.                         for (int i = 0; i < num; ++i)
  235.                         {
  236.                             fileObject.Seek(pos[i], SeekOrigin.Begin);
  237.                             byte[] data = br.ReadBytes(len[i]);
  238.                             byte[] msg = Decrypt(data, pass, 0);
  239.                             msgList.Add(msg);
  240.                         }
  241.                     }
  242.                 }
  243.             }
  244.             return msgList;
  245.         }

  246.         private static byte[] GetGlobalPass(IStorageWrapper iw, string QQID)
  247.         {
  248.             System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
  249.             byte[] dataID = new byte[QQID.Length];
  250.             for (int i = 0; i < QQID.Length; ++i) dataID[i] = (byte)(QQID[i]);
  251.             byte[] hashID = md5.ComputeHash(dataID);
  252.             IBaseStorageWrapper.FileObjects.FileObject fileObject = GetStorageFileObject(iw, "Matrix", "Matrix.db");
  253.             if (fileObject != null)
  254.             {
  255.                 using (BinaryReader br = new BinaryReader(fileObject))
  256.                 {
  257.                     byte[] data = br.ReadBytes((int)fileObject.Length);
  258.                     long len = data.Length;
  259.                     if (len < 6 || data[0] != 0x51 || data[1] != 0x44) return null;
  260.                     if (len >= 32768) return null;

  261.                     bool bl = false;
  262.                     int i = 6;
  263.                     while (i < len)
  264.                     {
  265.                         bl = false;
  266.                         byte type = data[i++];
  267.                         if (i + 2 > len) break;
  268.                         int len1 = data[i] + data[i + 1] * 256;
  269.                         byte xor1 = (byte)(data[i] ^ data[i + 1]);
  270.                         i += 2;
  271.                         if (i + len1 > len) break;
  272.                         for (int j = 0; j < len1; ++j) data[i + j] = (byte)(~(data[i + j] ^ xor1));
  273.                         if (len1 == 3 && data[i] == 0x43 && data[i + 1] == 0x52 && data[i + 2] == 0x4B)
  274.                         {
  275.                             bl = true;
  276.                         }
  277.                         i += len1;

  278.                         if (type > 7) break;
  279.                         if (i + 4 > len) break;
  280.                         int len2 = data[i] + data[i + 1] * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 * 256 * 256;
  281.                         byte xor2 = (byte)(data[i] ^ data[i + 1]);
  282.                         i += 4;
  283.                         if (i + len2 > len) break;
  284.                         if (type == 6 || type == 7)
  285.                         {
  286.                             for (int j = 0; j < len2; ++j) data[i + j] = (byte)(~(data[i + j] ^ xor2));
  287.                         }
  288.                         if (bl && len2 == 0x20)
  289.                         {
  290.                             byte[] dataT = new byte[len2];
  291.                             for (int j = 0; j < len2; ++j) dataT[j] = data[i + j];
  292.                             return Decrypt(dataT, hashID, 0);
  293.                         }
  294.                         i += len2;
  295.                     }
  296.                     if (i != len) return null;
  297.                 }
  298.             }
  299.             return null;
  300.         }

  301.         public void Close(string QQID) {
  302.             if (m_Storage == null)
  303.             {
  304.                 return;
  305.             }
  306.             else {
  307.                 m_Storage = null;

  308.             }
  309.         }
  310.     }
  311. }

QQCrypt.cs -- 原封未动,直接copy过来的

  1. using System;

  2. namespace RedQ
  3. {
  4.     /**//// <summary>
  5.     /// QQ Msg En/DeCrypt Class
  6.     /// Writen By Red_angelX On 2006.9.13
  7.     /// </summary>
  8.     public class QQCrypt
  9.     {    
  10.         //QQ TEA-16 Encrypt/Decrypt Class 
  11.         // 
  12.         // 
  13.         //And also LumaQQ//s source code 
  14.         //  CopyRight:No CopyRight^_^ 
  15.         //  Author : Red_angelX     
  16.         //  NetWork is Free,Tencent is ****!
  17.         // 
  18.         //Class Begin 
  19.         //AD:Find Job!!,If you Want Give me a Job,Content Me!!

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

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

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

  53.             arr[arr_index] &= 0xff;
  54.             arr[arr_index+1] &= 0xff;
  55.             arr[arr_index+2] &= 0xff;
  56.             arr[arr_index+3] &= 0xff;

  57.             return arr;
  58.         }

  59.         long CopyMemory(long Out,byte[] arr,int arr_index)
  60.         {
  61.             if(arr_index+4 > arr.Length)
  62.             {
  63.                 return Out;
  64.                 //不能执行
  65.             }

  66.             long x1 = arr[arr_index+3] << 24;
  67.             long x2 = arr[arr_index+2] << 16;
  68.             long x3 = arr[arr_index+1] << 8;
  69.             long x4 = arr[arr_index];

  70.             long o = x1 | x2 | x3 | x4;
  71.             o &= 0xffffffff;
  72.             return o;
  73.         }

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

  76.             long ret = 0;
  77.             int end = 0;
  78.             if (len > 8)
  79.                 end = offset + 8;
  80.             else
  81.                 end = offset + len;
  82.             for (int i = offset; i < end; i++) 
  83.             {
  84.                 ret <<= 8;
  85.                 ret |= arrayIn[i] & 0xff;
  86.             }
  87.             return (ret & 0xffffffff) | (ret >> 32);
  88.         }

  89.         long Rand()
  90.         {
  91.             Random rd = new Random();
  92.             long ret;
  93.             ret = rd.Next() + (rd.Next() % 1024);
  94.             return ret;
  95.         }

  96.         private byte[] Decipher(byte[] arrayIn,byte[] arrayKey,long offset)
  97.         {
  98.             //long Y,z,a,b,c,d;
  99.             long sum,delta;
  100.             //Y=z=a=b=c=d=0;
  101.             byte[] tmpArray = new byte[24];
  102.             byte[] tmpOut = new byte[8];
  103.             if(arrayIn.Length < 8)
  104.             {
  105.                 // Error:return
  106.                 return tmpOut;
  107.             }
  108.             if(arrayKey.Length < 16)
  109.             {
  110.                 // Error:return
  111.                 return tmpOut;
  112.             }
  113.             sum = 0xE3779B90; 
  114.             sum = sum & 0xFFFFFFFF; 
  115.             delta = 0x9E3779B9; 
  116.             delta = delta & 0xFFFFFFFF; 
  117.             /**//*tmpArray[3] = arrayIn[offset]; 
  118.             tmpArray[2] = arrayIn[offset + 1]; 
  119.             tmpArray[1] = arrayIn[offset + 2]; 
  120.             tmpArray[0] = arrayIn[offset + 3]; 
  121.             tmpArray[7] = arrayIn[offset + 4]; 
  122.             tmpArray[6] = arrayIn[offset + 5]; 
  123.             tmpArray[5] = arrayIn[offset + 6]; 
  124.             tmpArray[4] = arrayIn[offset + 7]; 
  125.             tmpArray[11] = arrayKey[0]; 
  126.             tmpArray[10] = arrayKey[1]; 
  127.             tmpArray[9] = arrayKey[2]; 
  128.             tmpArray[8] = arrayKey[3]; 
  129.             tmpArray[15] = arrayKey[4]; 
  130.             tmpArray[14] = arrayKey[5]; 
  131.             tmpArray[13] = arrayKey[6]; 
  132.             tmpArray[12] = arrayKey[7]; 
  133.             tmpArray[19] = arrayKey[8]; 
  134.             tmpArray[18] = arrayKey[9]; 
  135.             tmpArray[17] = arrayKey[10]; 
  136.             tmpArray[16] = arrayKey[11]; 
  137.             tmpArray[23] = arrayKey[12]; 
  138.             tmpArray[22] = arrayKey[13]; 
  139.             tmpArray[21] = arrayKey[14]; 
  140.             tmpArray[20] = arrayKey[15]; 
  141.             Y=CopyMemory(Y,tmpArray,0);    
  142.             z=CopyMemory(z,tmpArray,4);
  143.             a=CopyMemory(a,tmpArray,8);
  144.             b=CopyMemory(b,tmpArray,12);
  145.             c=CopyMemory(c,tmpArray,16);
  146.             d=CopyMemory(d,tmpArray,20);*/
  147.             long Y = getUnsignedInt(arrayIn, (int)offset, 4);
  148.             long z = getUnsignedInt(arrayIn, (int)offset + 4, 4);
  149.             long a = getUnsignedInt(arrayKey, 0, 4);
  150.             long b = getUnsignedInt(arrayKey, 4, 4);
  151.             long c = getUnsignedInt(arrayKey, 8, 4);
  152.             long d = getUnsignedInt(arrayKey, 12, 4);
  153.             for(int i=1;i<=16;i++)
  154.             {
  155.                 z -= ((Y<<4)+c) ^ (Y+sum) ^ ((Y>>5)+d);
  156.                 z &= 0xFFFFFFFF;
  157.                 Y -= ((z<<4)+a) ^ (z+sum) ^ ((z>>5)+b);
  158.                 Y &= 0xFFFFFFFF;
  159.                 sum -= delta;
  160.                 sum &= 0xFFFFFFFF; 
  161.             }

  162.             tmpArray = CopyMemory(tmpArray,0,Y);
  163.             tmpArray = CopyMemory(tmpArray,4,z);
  164.             tmpOut[0] = tmpArray[3]; 
  165.             tmpOut[1] = tmpArray[2]; 
  166.             tmpOut[2] = tmpArray[1]; 
  167.             tmpOut[3] = tmpArray[0]; 
  168.             tmpOut[4] = tmpArray[7]; 
  169.             tmpOut[5] = tmpArray[6]; 
  170.             tmpOut[6] = tmpArray[5]; 
  171.             tmpOut[7] = tmpArray[4]; 

  172.             return tmpOut;    
  173.         }

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

  178.         private byte[] Encipher(byte[] arrayIn,byte[] arrayKey,long offset)
  179.         {
  180.             byte[] tmpOut = new byte[8];
  181.             byte[] tmpArray = new byte[24];
  182.             //long Y,z,a,b,c,d;
  183.             //Y=z=a=b=c=d=0;
  184.             long sum,delta;
  185.             if(arrayIn.Length < 8)
  186.             {
  187.                 // Error:
  188.                 return tmpOut;
  189.             }
  190.             if(arrayKey.Length < 16)
  191.             {
  192.                 // Error:
  193.                 return tmpOut;
  194.             }
  195.             sum = 0;
  196.             delta = 0x9E3779B9;
  197.             delta &= 0xFFFFFFFF;

  198.             /**//*tmpArray[3] = arrayIn[offset]; 
  199.             tmpArray[2] = arrayIn[offset + 1]; 
  200.             tmpArray[1] = arrayIn[offset + 2]; 
  201.             tmpArray[0] = arrayIn[offset + 3]; 
  202.             tmpArray[7] = arrayIn[offset + 4]; 
  203.             tmpArray[6] = arrayIn[offset + 5]; 
  204.             tmpArray[5] = arrayIn[offset + 6]; 
  205.             tmpArray[4] = arrayIn[offset + 7]; 
  206.             tmpArray[11] = arrayKey[0]; 
  207.             tmpArray[10] = arrayKey[1]; 
  208.             tmpArray[9] = arrayKey[2]; 
  209.             tmpArray[8] = arrayKey[3]; 
  210.             tmpArray[15] = arrayKey[4]; 
  211.             tmpArray[14] = arrayKey[5]; 
  212.             tmpArray[13] = arrayKey[6]; 
  213.             tmpArray[12] = arrayKey[7]; 
  214.             tmpArray[19] = arrayKey[8];
  215.             tmpArray[18] = arrayKey[9]; 
  216.             tmpArray[17] = arrayKey[10]; 
  217.             tmpArray[16] = arrayKey[11]; 
  218.             tmpArray[23] = arrayKey[12]; 
  219.             tmpArray[22] = arrayKey[13]; 
  220.             tmpArray[21] = arrayKey[14]; 
  221.             tmpArray[20] = arrayKey[15]; 

  222.             Y=CopyMemory(Y,tmpArray,0);
  223.             z=CopyMemory(z,tmpArray,4);
  224.             a=CopyMemory(a,tmpArray,8);
  225.             b=CopyMemory(b,tmpArray,12);
  226.             c=CopyMemory(c,tmpArray,16);
  227.             d=CopyMemory(d,tmpArray,20);*/

  228.             long Y = getUnsignedInt(arrayIn, (int)offset, 4);
  229.             long z = getUnsignedInt(arrayIn, (int)offset + 4, 4);
  230.             long a = getUnsignedInt(arrayKey, 0, 4);
  231.             long b = getUnsignedInt(arrayKey, 4, 4);
  232.             long c = getUnsignedInt(arrayKey, 8, 4);
  233.             long d = getUnsignedInt(arrayKey, 12, 4);

  234.             for(int i=1;i<=16;i++)
  235.             {
  236.                 sum += delta;
  237.                 sum &= 0xFFFFFFFF;
  238.                 Y += ((z<<4)+a) ^ (z+sum) ^ ((z>>5)+b);
  239.                 Y &= 0xFFFFFFFF;
  240.                 z += ((Y<<4)+c) ^ (Y+sum) ^ ((Y>>5)+d);
  241.                 z &= 0xFFFFFFFF;
  242.             }

  243.             tmpArray = CopyMemory(tmpArray,0,Y);
  244.             tmpArray = CopyMemory(tmpArray,4,z);

  245.             tmpOut[0] = tmpArray[3]; 
  246.             tmpOut[1] = tmpArray[2]; 
  247.             tmpOut[2] = tmpArray[1];
  248.             tmpOut[3] = tmpArray[0]; 
  249.             tmpOut[4] = tmpArray[7]; 
  250.             tmpOut[5] = tmpArray[6]; 
  251.             tmpOut[6] = tmpArray[5]; 
  252.             tmpOut[7] = tmpArray[4]; 
  253.         
  254.             return tmpOut;
  255.         }
  256.         

  257.         private byte[] Encipher(byte[] arrayIn,byte[] arrayKey)
  258.         {
  259.             return Encipher(arrayIn,arrayKey,0);
  260.         }
  261.         
  262.         private void Encrypt8Bytes()
  263.         {
  264.             byte[] Crypted;
  265.             for(Pos=0;Pos<=7;Pos++)
  266.             {
  267.                 if(this.Header == true)
  268.                 {
  269.                     Plain[Pos] = (byte)(Plain[Pos] ^ prePlain[Pos]);
  270.                 }
  271.                 else
  272.                 {
  273.                     Plain[Pos] = (byte)(Plain[Pos] ^ Out[preCrypt + Pos]);
  274.                 }
  275.             }
  276.             Crypted = Encipher(Plain,Key);
  277.             
  278.             for(int i=0;i<=7;i++)
  279.             {
  280.                 Out[Crypt + i] = (byte)Crypted[i];
  281.             }
  282.             
  283.             for(Pos=0;Pos<=7;Pos++)
  284.             {
  285.                 Out[Crypt + Pos] = (byte)(Out[Crypt + Pos] ^ prePlain[Pos]);
  286.             }            
  287.             Plain.CopyTo(prePlain,0);
  288.             preCrypt = Crypt;
  289.             Crypt = Crypt + 8;
  290.             Pos = 0;
  291.             Header = false;
  292.         }

  293.         private bool Decrypt8Bytes(byte[] arrayIn,long offset)
  294.         {
  295.             long lngTemp;
  296.             for(Pos=0;Pos<=7;Pos++)
  297.             {
  298.                 if(this.contextStart+Pos > arrayIn.Length-1) 
  299.                 {
  300.                     return true;
  301.                 }
  302.                 prePlain[Pos] = (byte)(prePlain[Pos] ^ arrayIn[offset+Crypt+Pos]);
  303.             }
  304.             try
  305.             {
  306.                 prePlain = this.Decipher(prePlain,Key);
  307.             }
  308.             catch
  309.             {
  310.                 return false;
  311.             }
  312.             lngTemp = prePlain.Length - 1;
  313.             contextStart += 8;
  314.             Crypt+=8;
  315.             Pos = 0;
  316.             return true;
  317.         }

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


  322.         #region Public Methods!

  323.         /**//// <summary>
  324.         /// QQ TEA 加密函数
  325.         /// </summary>
  326.         /// <param name="arrayIn">要加密的字串</param>
  327.         /// <param name="arrayKey">密钥</param>
  328.         /// <param name="offset">偏移</param>
  329.         /// <returns></returns>
  330.         public byte[] QQ_Encrypt(byte[] arrayIn,byte[] arrayKey,long offset)
  331.         {
  332.             Plain = new byte[8];
  333.             prePlain = new byte[8];
  334.             long l;
  335.             Pos = 1;
  336.             padding = 0;
  337.             Crypt = preCrypt = 0;
  338.             arrayKey.CopyTo(Key,0);    // Key Must Be 16 Length!
  339.             Header = true;
  340.             Pos = 2;
  341.             //计算头部填充字节数
  342.             Pos = (arrayIn.Length+10) % 8;
  343.             if(Pos != 0)
  344.                 Pos = 8-Pos;
  345.             //输出长度
  346.             Out = new byte[arrayIn.Length+Pos+10];
  347.             //把POS存到PLAIN的第一个字节
  348.             //0xf8后面3位是空的,正好给Pos
  349.             Plain[0] = (byte)((Rand() & 0xf8) | Pos);
  350.             //用随机数填充1到Pos的内容
  351.             for(int i=1;i<=Pos;i++)
  352.             {
  353.                 Plain[i] = (byte)(Rand() & 0xff);
  354.             }
  355.             Pos++;
  356.             padding = 1;

  357.             //继续填充两个字节随机数,满8字节就加密
  358.             while(padding < 3)
  359.             {
  360.                 if( Pos < 8)
  361.                 {
  362.                     Plain[Pos] = (byte)(Rand() & 0xff);
  363.                     padding++;
  364.                     Pos++;
  365.                 }
  366.                 else if(Pos == 8)
  367.                 {
  368.                     this.Encrypt8Bytes();
  369.                 }
  370.             }

  371.             int I = (int)offset;
  372.             l = 0;
  373.             //明文内容,满8字节加密到读完
  374.             l = arrayIn.Length;
  375.             while ( l > 0)
  376.             {
  377.                 if(Pos<8)
  378.                 {
  379.                     Plain[Pos] = arrayIn[I];
  380.                     I++;
  381.                     Pos++;
  382.                     l--;
  383.                 }
  384.                 else if(Pos == 8)
  385.                 {
  386.                     this.Encrypt8Bytes();
  387.                 }
  388.             }

  389.             //末尾填充0,保证是8的倍数
  390.             padding = 1;
  391.             while(padding < 9)      
  392.             {
  393.                 if(Pos<8)
  394.                 {
  395.                     Plain[Pos] = 0;
  396.                     Pos++;
  397.                     padding++;
  398.                 }
  399.                 else if(Pos == 8)
  400.                 {
  401.                     this.Encrypt8Bytes();
  402.                 }
  403.             }

  404.             return Out;
  405.         }


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

  410.         /**//// <summary>
  411.         ///  QQ TEA 解密函数
  412.         /// </summary>
  413.         /// <param name="arrayIn">要解密字串</param>
  414.         /// <param name="arrayKey">密钥</param>
  415.         /// <param name="offset">偏移</param>
  416.         /// <returns></returns>
  417.         public byte[] QQ_Decrypt(byte[] arrayIn,byte[] arrayKey,long offset)
  418.         {
  419.             byte[] error = new byte[0];
  420.             //检查是否是8的倍数至少16字节
  421.             if(arrayIn.Length < 16 || (arrayIn.Length % 8 != 0))
  422.             {
  423.                 //Return What?
  424.                 return error;
  425.             }
  426.             if(arrayKey.Length != 16)
  427.             {
  428.                 //Return What?
  429.                 return error;
  430.             }
  431.             byte[] m;
  432.             long I,Count;
  433.             m= new byte[offset+8];
  434.             arrayKey.CopyTo(Key,0);
  435.             Crypt = preCrypt = 0;
  436.             //计算消息头部,明文开始的偏移,解密第一字节和7相与得到
  437.             prePlain = this.Decipher(arrayIn,arrayKey,offset);
  438.             Pos = prePlain[0] & 7;
  439.             //计算明文长度
  440.             Count = arrayIn.Length - Pos - 10;
  441.             if(Count <= 0)
  442.             {
  443.                 //Return What?
  444.                 return error;
  445.             }
  446.             Out = new byte[Count];
  447.             preCrypt = 0;
  448.             Crypt = 8;
  449.             this.contextStart = 8;
  450.             Pos++;
  451.             padding = 1;
  452.             //跳过头部
  453.             while(padding < 3)
  454.             {
  455.                 if(Pos<8)
  456.                 {
  457.                     Pos++;
  458.                     padding++;
  459.                 }
  460.                 else if(Pos==8)
  461.                 {
  462.                     for(int i=0;i<m.Length;i++)
  463.                         m[i]=arrayIn[i];
  464.                     if(this.Decrypt8Bytes(arrayIn,offset) == false)
  465.                     {
  466.                         //Return What?
  467.                         return error;
  468.                     }
  469.                 }
  470.             }

  471.             //解密明文
  472.             I=0;
  473.             while(Count != 0)
  474.             {
  475.                 if(Pos<8)
  476.                 {
  477.                     Out[I] = (byte)(m[offset+preCrypt+Pos] ^ prePlain[Pos]);
  478.                     I++;
  479.                     Count--;
  480.                     Pos++;
  481.                 }
  482.                 else if(Pos == 8)
  483.                 {
  484.                     m = arrayIn;
  485.                     preCrypt = Crypt - 8;
  486.                     if(this.Decrypt8Bytes(arrayIn,offset) == false)
  487.                     {
  488.                         //Return What?
  489.                         return error;
  490.                     }
  491.                 }
  492.             }

  493.             //最后的解密部分,检查尾部是不是0
  494.             for(padding=1;padding<=7;padding++)
  495.             {
  496.                 if(Pos<8)
  497.                 {
  498.                     if( (m[offset+preCrypt+Pos] ^ prePlain[Pos]) != 0 )
  499.                     {
  500.                         //Return What?
  501.                         return error;
  502.                     }
  503.                     Pos++;
  504.                 }
  505.                 else if(Pos == 8)
  506.                 {
  507.                     for(int i=0;i<m.Length;i++)
  508.                         m[i] = arrayIn[i];
  509.                     preCrypt = Crypt;
  510.                     if(this.Decrypt8Bytes(arrayIn,offset) == false)
  511.                     {
  512.                         //Return What?
  513.                         return error;
  514.                     }
  515.                 }
  516.             }
  517.             return Out;
  518.         }

  519.         public byte[] QQ_Decrypt(byte[] arrayIn,byte[] arrayKey)
  520.         {
  521.             return QQ_Decrypt(arrayIn,arrayKey,0);
  522.         }
  523.         #endregion
  524.     }
  525. }

另外还有发邮件的代码就不发了,简单的调用cdosys.dll发的,还有调用rar.exe的也很简单,就几行代码,不贴了,自己反编译吧,我没混淆~~


不想直接发源码是讨厌很多人拿去改掉我的名字和网址就当自己的“原创”了。。。

GitHub热榜项目qzonearchive:QQ空间数据本地备份与归档工具详解 在数字化时代,个人数据的长期保存与备份已成为数据管理领域的重要课题。随着社交平台内容日益庞杂,用户对本地归档和离线访问的需求持续增长,这催生了大量开源工具的出现。这类工具通常基于成熟的Web技术栈,通过构建本地服务实现数据导出与静态化呈现。其中,GitHub Trending上热门的qzonearchive项目,正是利用PHP与MySQL搭建Web应用,帮助用户将QQ空间中的日志、说说、相册及留言板等核心内容,打包为可离线浏览的HTML档案。其低门槛部署特性,使得普通用户无需高配硬件即可完成个人数据备份,既 阅读详情

相关推荐

QQ聊天记录导出与解析:从加密数据库到Word文档的完整技术路径

QQ聊天记录并非普通文档,而是基于SQLite与AES-256加密的本地二进制数据,其存储机制涉及设备指纹、登录态票据和账号Salt联合派生密钥。理解IM客户端本地存储原理,是实现合规数据导出与结构化分析的基础技术前提。通过官方支持的HTTP本地API或可信解密工具,可安全获取JSON格式的原始消息(含时间戳、发送者、多媒体元数据),再借助python-docx等库自动化渲染为专业排版的.docx文档。该方案适用于数字取证、工作归档与开发调试场景,规避了钓鱼宏文档风险,也绕开了逆向破解等非法手段。

weixin_42533120的博客 192

几种qq聊天记录的备份方式

首先我们找到qq之前的聊天记录文件夹:(一般是在“我的文档”->Tencent File下)打开手机qq->设置->通用->聊天记录设置->备份聊天记录到电脑。上述方法将聊天记录备份后可以将聊天记录随时恢复到手机。确保这些文件夹存在,且要包含你要找回聊天记录的qq。接下来登录上新装的qq,点击消息管理(如下图):需要对手机qq聊天记录的备份以及恢复。电脑qq误删,或qq文件损坏。

zcs18338992657的博客 8083

微信备份——CloudBak实用指南

CloudBak(云朵备份) 是一个开源的微信云备份工具,能够将微信数据备份到服务器、Docker 或 NAS,并通过浏览器以类似微信网页版的界面访问数据,兼具实用性与便利性。

lzy_leogeo的博客 2336

QQ聊天记录的备份和恢复(含迁移与合并)

作为 IT 一名从业者(当年我们称为 Information Technology,现在的有道词典等也还把 IT 翻译为信息技术,百度百科已经跟新为 Internet Technology 啦),差不多十年前养成的习惯,每半年(6月30日 和 12月31日)对有价值的数据进行备份,因此 零几年的 QQ 聊天记录,现在都还在。 闲话少叙,直接放方法步骤。 1)备份 登录 QQ -> 主菜单 -> 消息管理 -> 导出全部消息记录(消息管理器窗口...

漂石的手艺(技术)活儿 3万+

python操作git推送自己分支并合并主分支(持续优化该工具)

python git

qq_44707265的博客 863

Win10本地账户密码遗忘?巧用指纹/PIN快速重置(家庭版/专业版通用指南)

本文提供了一份详细的Win10本地账户密码遗忘自救指南,适用于家庭版和专业版用户。核心方法是利用已登录的指纹或PIN码权限,通过创建新的管理员账户或使用系统工具,安全重置遗忘的本地账户密码,无需第三方工具或重装系统,有效保护个人数据。

e6f7g8h9i的博客 582

QQ聊天记录分析(换新机QQ数据备份还原/Tim迁移)

因为很多原因需要备份/导出手机QQ的聊天记录。最近因为腾讯推出了TIM,大部分用户转用TIM,在手机上几年的聊天记录很难转移,因为腾讯不开放聊天记录转移的方案。 最近在酷安网看到一个 “QQ聊天记录导出” 和“微信聊天记录导出”的软件,看了一下他备份的包就是普通的zip备份了QQ应用数据的databases文件夹。(/data/data/com.tencent.mobileqq/datab...

jonhy的专栏 10万+

数电发票“乐企直连”服务平台建设方案

数字化电子发票“乐企直连”服务平台建设方案

m0_73174802的博客 4853

【免费QQ用户也能保存聊天记录备份与恢复】

常用的QQ聊天记录备份小工具是一款叫做QQ消息备份还原助手的绿色软件,使用非常简单,快速安装后只需自己设置一个QQ聊天记录备份的文件目录,然后根据自己的实际情况决定让该工具是每天执行一次自动备份,或是每周完成一次备份工作即可。在日常电脑使用中,虽然你可能很少感觉到该软件的存在,然而它却会按照你之前的设定定时定点的自动完成一次QQ聊天记录全面备份。而且就算当QQ备份助手在工作时也不会占用系统过多资源

瞳孔裏旳阳光的专栏 1002

TSMaster二次开发实战:如何用Python搞定CAN/CANFD事件回调(附完整代码)

本文详细介绍了如何利用Python进行TSMaster二次开发,实现CAN/CANFD事件回调功能。通过实战代码演示,讲解了预发送与后处理回调的核心区别、注册流程、资源管理及性能优化技巧,帮助工程师高效构建自动化测试与数据监控应用。

star的博客 669

白炽灯寿命工程解析:从钨丝蒸发到电压控制的可靠性设计

白炽灯作为一种经典的电光源,其工作原理基于电流通过钨丝产生热辐射发光。在高温下,钨丝会持续蒸发导致灯丝逐渐变细、电阻增大,这是影响其寿命的核心物理机制。从工程角度看,白炽灯的可靠性设计涉及材料科学、热力学和电路设计的交叉,技术价值在于通过理解其失效原理,可以在成本与性能间取得最佳平衡。在实际应用中,电压波动、开关浪涌电流和机械振动是主要寿命杀手,尤其在工业控制、仪器仪表和特定照明场景中,合理的设计能大幅提升系统稳定性。本文深入探讨了钨蒸发、热应力等关键因素,并结合浪涌抑制和降压设计等实战技巧,为工程师提供了

weixin_28684655的博客 571

QQ聊天记录备份工具 v0.9

QQ聊天记录备份工具 v0.9以前每当有新版的QQ出来时,总是在QQ目录下,把我用的那个以QQ号命名的文件夹拷贝到新QQ目录里, 这样就可以保存以前的聊天记录和自定义表情,但同时也拷进了许多垃圾,不信你打开的QQ目录就知道了, 试了一下,以最新版的QQ2004II正式版为例,用一新号登录,系统自动产生一个QQ号文件夹大小为142K,文件也只有11个, 可是我查看我用了一年的号,大小却有100M

神经网络爱好者 1901

MONAILabel in 3D Slicer 案例1: 在腹部CT中自动分割脾脏

MONAILabel in 3D Slicer 案例1: 在腹部CT中自动分割脾脏。

Tina姐的博客 1939

如何实现微信QQ消息防撤回?3步快速保护你的聊天记录

你是否曾经在重要的商务沟通或朋友聊天中,眼睁睁看着对方撤回了一条关键消息,却再也无法查看内容?在即时通讯软件普及的今天,消息撤回功能虽然保护了发送者的隐私,却也让我们错失了许多有价值的信息。RevokeMsgPatcher 正是为了解决这一痛点而生的开源工具,它能够有效防止微信、QQ、TIM等主流通讯软件的消息撤回,确保你的聊天记录完整无缺。 ## 💡 为什么我们需要防撤回功能? 在日常沟通

gitblog_00631的博客 773

[ vulhub漏洞复现篇 ] ActiveMQ反序列化漏洞复现(CVE-2015-5254)

Apache ActiveMQ是美国阿帕奇(Apache)软件基金会所研发的一套开源的消息中间件,它支持Java消息服务、集群、Spring Framework等。由于ActiveMQ是一个纯Java程序,因此只需要操作系统支持Java虚拟机,ActiveMQ便可执行。Apache ActiveMQ 5.13.0之前5.x版本中存在安全漏洞,该漏洞源于程序没有限制可在代理中序列化的类。......

qq_51577576的博客 4444

太阳敏感器:卫星姿态控制的“指南针

在卫星姿态控制系统中,太阳敏感器(Sun Sensor) 是至关重要的光学姿态敏感器之一。它通过探测太阳光的方向,为卫星提供关键的姿态参考信息,确保卫星能够正确调整自身方位,维持稳定的对日定向,保障能源供应和任务执行。

ScilogyHunter的博客 1732

如何快速掌握QQ数据库解密:全平台聊天记录恢复实战指南

QQ作为国内使用最广泛的即时通讯工具之一,其聊天记录的加密存储机制一直是技术爱好者关注的热点。面对加密的QQ数据库文件,许多用户希望能够安全地访问和备份自己的聊天历史。本指南将为您提供一套完整的QQ数据库解密方案,涵盖Windows、Linux、macOS、iOS和Android五大平台,让您能够轻松恢复和迁移珍贵的聊天记录。 ## 解密QQ数据库的实际应用场景 QQ数据库解密技术并非只适用于

gitblog_01045的博客 200

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

ex-skill 前任蒸馏指南:用微信/QQ 聊天记录把前任生成一个会说话的 Claude Code AI Skill

> 本指南围绕 [README.md](https://link.gitcode.com/i/3d272bebad5050c1a3987477be4e062e) 完整展开,系统讲解 ex-skill 如何在 Claude Code 中把微信聊天记录、QQ 消息、朋友圈截图、照片和你的口述记忆,蒸馏成一个真正"像 ta"的 AI Skill —— 用 ta 的口头禅说话、用 ta 的方式回复、记得你

gitblog_01055的博客 1021

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

3分钟快速上手:微信QQ防撤回补丁终极使用指南

你是否遇到过这样的情况?同事在群里撤回了重要的项目需求,客户撤回了关键修改意见,朋友撤回了有趣的表情包,而你只能看着"对方已撤回一条消息"的提示干着急?RevokeMsgPatcher就是解决这个问题的终极方案!这个Windows平台的防撤回工具支持微信、QQ、TIM三大主流通讯软件,让你再也不用担心错过重要信息。 ## 🔍 痛点场景:那些让你抓狂的撤回时刻 想象一下这些真实场景: **职

gitblog_00861的博客 1151

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

微信消息保护工具3步安装指南:聊天记录备份与防撤回设置完全攻略

在日常工作和生活中,微信已成为我们重要的沟通工具,但"对方已撤回一条消息"的提示常常让人错过关键信息。RevokeMsgPatcher作为一款免费开源的微信消息保护工具,不仅能实现聊天记录备份,还能提供全面的防撤回设置,让你不再错失任何重要对话。本文将通过简单三步,带你快速掌握这款工具的使用方法,即使是技术小白也能轻松上手。 ## 如何定位微信消息保护的核心价值 想象一下这些场景:重要工作安排

gitblog_00785的博客 368

终极指南:Windows微信QQ防撤回补丁完整教程,保护你的聊天记录不被删除

你是否曾因为错过重要消息而后悔?在微信或QQ聊天中,对方撤回消息时,你只能看到"对方已撤回一条消息"的提示,而无法查看具体内容。现在,有了RevokeMsgPatcher这款强大的**Windows防撤回工具**,你不再需要担心错过任何重要信息。这款开源免费的**消息防撤回软件**能够帮助你永久保存聊天记录,让撤回功能形同虚设。 RevokeMsgPatcher是一款专为Windows平台设计的

gitblog_00167的博客 651

微信PC端聊天记录提取:四层解析法实战指南

聊天记录是数字时代的重要个人数据资产,其本质是结构化与非结构化混合存储的本地数据库文件。微信PC端升级常导致旧版SQLite数据库(如Msg_xxxx.db)被新客户端屏蔽,但数据物理存在、未被删除。核心原理在于识别不同存储层级——活跃库(All Messages.db)、历史存档(时间戳.db)、媒体文件(MD5命名二进制)、联系人元数据(contact.db),并针对性处理加密(RC4/AES)、格式识别(WebP/AMR/SILK)、关联还原(MsgSvrID映射)等技术环节。该方法规避官方备份失效、

weixin_34183910的博客 282

Silk v3音频转换终极指南:一键解锁微信QQ语音文件

你是否曾经保存了重要的微信语音消息或QQ聊天记录,却发现这些文件无法在普通播放器中打开?那些珍贵的语音回忆、重要的会议录音,因为特殊的Silk v3编码格式而变成了"哑巴文件"。别担心,今天我要为你介绍一个开源神器——silk-v3-decoder,它能轻松解决这个困扰无数用户的音频格式兼容性问题。 silk-v3-decoder是一个专门解码Silk v3音频格式并转换为通用格式的工具,完美支

gitblog_00547的博客 387

SQLCipher数据库逆向工程深度解析:全平台QQ聊天记录解密技术实战手册

在数字隐私保护与数据主权意识日益增强的今天,跨平台应用的数据加密机制成为技术研究的热点领域。QQ作为国内主流的即时通讯软件,其聊天记录数据库采用SQLCipher加密方案,为数据安全提供了企业级保护。然而,对于技术研究者和数据备份需求者而言,理解并掌握QQ数据库的解密技术具有重要的学术价值和实践意义。本项目通过逆向工程分析,实现了全平台QQ聊天记录数据库的解密方案,为安全研究、数据迁移和备份恢复提

gitblog_01077的博客 727
上一篇: 用Javascript实现Blogger/Blogspot的传统模板翻页功能
下一篇: 调用飞信网关服务器HTTP接口发送短信的ASP源码
平淡真无奇
博客等级 码龄22年 10粉丝 26原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值