QQ的消息实际上是存放在本地的,位于"QQ安装目录/QQ号码/MsgEx.db"内。关于QQ消息文件格式的文章,网上有不少,但是没有一篇是完整并且可重现。结合QQ聊天记录察看器 5.1,我做了一些研究,重现了读取并显示历史消息的完整过程。
一个很好的学习QQ相关算法的实例,是它的Linux版本 LumaQQ
首先,MsgEx.db文件的大致结构可以参考 QQ聊天记录查看器 5.3 华军版
IStorage的详细介绍可以在MSDN中查到,CHM就是使用了这个格式。为了方便的操作这个COM接口,我们可以直接使用 Decompiling CHM (help) files with C#中提供的RelatedObjects.Storage.dll
消息的加密密码存放在Matrix.db中,提取出来之后就可以解密实际存放消息文本的Data.msj文件了
(值得注意的是,QQ使用的数据加密算法并不是上面帖子里提到的Blowfish,而是TEA算法,可以参考 QQ的TEA填充算法C#实现)
QQ分若干种消息类型,诸如双人消息、群消息和系统公告等,格式有一些差异。
具体的细节,看看代码就清楚了。一个简单的QQ消息类的实现如下:
namespace Van.Utility.QQMsg
{
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])
{
一个很好的学习QQ相关算法的实例,是它的Linux版本 LumaQQ
首先,MsgEx.db文件的大致结构可以参考 QQ聊天记录查看器 5.3 华军版
IStorage的详细介绍可以在MSDN中查到,CHM就是使用了这个格式。为了方便的操作这个COM接口,我们可以直接使用 Decompiling CHM (help) files with C#中提供的RelatedObjects.Storage.dll
消息的加密密码存放在Matrix.db中,提取出来之后就可以解密实际存放消息文本的Data.msj文件了
(值得注意的是,QQ使用的数据加密算法并不是上面帖子里提到的Blowfish,而是TEA算法,可以参考 QQ的TEA填充算法C#实现)
QQ分若干种消息类型,诸如双人消息、群消息和系统公告等,格式有一些差异。
具体的细节,看看代码就清楚了。一个简单的QQ消息类的实现如下:
namespace Van.Utility.QQMsg
{
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])
{