VC写的手机qq聊天记录导出工具

来源:互联网 发布:站酷干货 知乎 编辑:程序博客网 时间:2024/04/30 16:58
源文件下载地址:http://download.csdn.net/source/2248555最近把手机QQ从200版换到了2009版,于是以前的聊天记录也就看不到了。有时候想起和好友们开开心心聊天的点点滴滴,于是想把手机QQ里面的聊天记录导出到文本文件中方便以后没事儿时看看。参考了文章:http://blog.csdn.net/wenwu500/archive/2009/10/14/4668534.aspx里的介绍,自己用VC++ 写了一个手机QQ2008和2009聊天记录提取器,该篇文章中对已聊天记录文件的存储格式介绍的不全面,这里再补充一下。首先是聊天记录的存放位置:2008版:X:/QQ/你的QQ号码/msg/好友的QQ号码.db 文件中2009版:X:/System/data/Tencent/QQ/自己的QQ号码/好友的QQ号码/msg.info 文件中接下来分析聊天记录的格式,首先是2008版,用UltraEdit打开聊天记录db文件(打开方式是ASCII Escaped Unicode),右键选择十六进制编辑回到字符显示状态,你就可以看到聊天记录了,不过每一条记录之间存在乱码,接下来分析去除这些乱码(接下来都是以16进制来说明):手机QQ2008版聊天记录中,每一条记录都是以25 08开头,之后两个字节表示这条消息的长度:len=第三个字节×162+第二个字节-0x4e,接着四个字节是这条消息发送者的QQ号,例如你的QQ号是453244328,如果这条消息是你发送的,则这四个字节的值就是a8 f5 03 1b(用Windows自带的计算器可以计算出来,不过这里说明一下,文件中字节的存放顺序和变量中时反着的,例如,一个long型变量的值是453244328,则用十六进制表示它应该是1b03f5a8(四个字节),而在文件中存放的顺序是a8 f5 03 1b。因此如果你一个字节一个字节的读读到的顺序是反的);之后两个字节的值是05 00,没什么特殊含义。接下来的34个字节是这条消息发送或者接收到的时间,是一个UNICODE编码格式的字符串;然后是两个无意义字节 00 00;接着32个字节是这条消息发送者的昵称,也是UNICODE编码格式的字符串最后是len个字节的消息内容,同样还是UNICODE格式的字符串,不过有时在消息中也包含有表情,如果消息字符串中出现14 xx,则这两个字节就表示一个表情,其中14是前缀,xx是表情序号,对应着手机QQ中的一个表情根据以上的格式就能读取聊天记录了,读取出来后将UNICODE格式的字符串转换成ANSI格式然后保存到文本文档里就成了:void Export2008(FILE* fr, FILE* fw){    fseek(fr,-1,SEEK_CUR);    BYTE mark[15],null[4];    char timestr[36],str[40],msg[2048];    wchar_t wtimestr[18],wstr[20],buff[1024];    memset(wtimestr,0x0000,36);    memset(wstr,0x0000,40);    memset(buff,0x0000,2048);    long int len;    fread(mark,sizeof(BYTE),10,fr);    fread(wtimestr,sizeof(wchar_t),17,fr);    fread(null,sizeof(BYTE),2,fr);    fread(wstr,sizeof(wchar_t),16,fr);    len=(((long int)mark[3])*16*16+(long int)mark[2])-0x4e;    fread(buff,sizeof(char),len,fr);    CString cstr(buff);    wchar_t  enterc[2]={0};    enterc[0]=0x0c;    cstr.Replace(enterc,L"/r/n");    wchar_t  face[8]={0};    int clen=cstr.GetLength();    for(int i=0;i<clen;i++)    {        if(cstr.GetAt(i)==0x14&&i+1)        {            short int nIDFace=(short int)cstr.GetAt(i+1);            memset(face,0x00,14);            if(GetUserFace(nIDFace,face))            {                wchar_t wface[3]={0};                wface[0]=cstr.GetAt(i);                wface[1]=cstr.GetAt(i+1);                cstr.Replace(wface,face);                clen=cstr.GetLength();            }        }    }    memset(buff,0x00,2048);    wcsncpy(buff,cstr.GetBuffer(),cstr.GetLength());    cstr.ReleaseBuffer();    char c;    fread(&c,sizeof(char),1,fr);    memset(msg,0,2048);    WCharToAChar(buff,msg,2048);        memset(timestr,0,36);    WCharToAChar(wtimestr,timestr,36);    memset(str,0,40);    WCharToAChar(wstr,str,40);    fwrite(timestr,sizeof(BYTE),strlen(timestr),fw);    fwrite("    ",sizeof(BYTE),strlen("    "),fw);    fwrite(str,sizeof(BYTE),strlen(str),fw);    fwrite("/r/n",sizeof(BYTE),strlen("/r/n"),fw);    fwrite(msg,sizeof(BYTE),strlen(msg),fw);    fwrite("/r/n/r/n",sizeof(BYTE),strlen("/r/n/r/n"),fw);}手机QQ2009版的聊天记录中,每一条记录都是以a8 xx(或者 A9 XX) 开头,然后以a8 xx(或者 A9 XX)结尾,例如某条消息如下:A8 3A 4B 13 ED 06 00 00 00 00 00 00 80 D8 8F A1 59 2F 66 60 4F 84 76 C4 9E D1 91 F6 65 3B 52 27 54 26 20 14 00 4F 00 A8 3A 它就是以A8 3A 开头,又以A8 3A结尾;但是A8 3A这两个字节并不是随机的,实际上它们代表的是这一条消息的长度:len=第二个字节 +(第一个字节-0xa8)×162,注意:这个len不包括末尾的A8 XX 。之后的四个字节是c++里面的time_t型变量值,有关这个time_t数据类型资料在百度上可以查到,这里就不多说了。这个值表示的是这条消息发送或者接收的时间;接下来六个字节是无用字节,值永远是00 00 00 00 00 00;然后得一个字节是发送/接收标志,如果该字节是00,则代表是接收到的消息;如果是80,则是发送出去的消息;之后的len字节便是消息的内容,同样也是UNICODE格式;最后是和开头对应的两个字节A8 XX (或者 A9 XX);同样根据分析出来的格式便可以读取出消息的内容,然后保存到文本文件中:void Export2009(FILE* fr, FILE* fw){    fseek(fr,-1,SEEK_CUR);    BYTE markstr[2],flag;    char timestr[4],nullstr[6],msg[2048];    wchar_t buff[1024];    memset(buff,0x0000,2048);    long int  len,time;    fread(markstr,sizeof(BYTE),2,fr);    fread(timestr,sizeof(char),4,fr);    char c;    c=timestr[0];    timestr[0]=timestr[3];    timestr[3]=c;    c=timestr[1];    timestr[1]=timestr[2];    timestr[2]=c;    memcpy(&time,timestr,4);    fread(nullstr,sizeof(char),6,fr);    fread(&flag,sizeof(BYTE),1,fr);    len=(long int)markstr[1]+((long int)markstr[0]-0xa8)*16*16;    fread(buff,sizeof(BYTE),len,fr);    CString cstr(buff);    wchar_t  enterc[2]={0};    enterc[0]=0x0c;    cstr.Replace(enterc,L"/r/n");    wchar_t  face[8]={0};    int clen=cstr.GetLength();    for(int i=0;i<clen;i++)    {        if(cstr.GetAt(i)==0x14&&i+1)        {            short int nIDFace=(short int)cstr.GetAt(i+1);            memset(face,0x00,14);            if(GetUserFace(nIDFace,face))            {                wchar_t wface[3]={0};                wface[0]=cstr.GetAt(i);                wface[1]=cstr.GetAt(i+1);                cstr.Replace(wface,face);                clen=cstr.GetLength();            }        }    }    memset(buff,0x00,2048);    wcsncpy(buff,cstr.GetBuffer(),cstr.GetLength());    cstr.ReleaseBuffer();        fread(markstr,sizeof(BYTE),2,fr);       fread(&c,sizeof(char),1,fr);    memset(msg,0,2048);    WCharToAChar(buff,msg,2048);    time_t  t=time;    struct tm *ptrtime=gmtime(&t);    char  strtime[26];    sprintf(strtime,"%d-%d-%d  %d:%d:%d",ptrtime->tm_year+1900,ptrtime->tm_mon+1,ptrtime->tm_mday,ptrtime->tm_hour+8,ptrtime->tm_min,ptrtime->tm_sec);    fwrite(strtime,sizeof(char),strlen(strtime),fw);    fwrite("    ",sizeof(char),strlen("    "),fw);    if(flag==0x80)        fwrite(m_MyName,sizeof(char),strlen(m_MyName),fw);    if(flag==0x00)        fwrite(m_OpName,sizeof(char),strlen(m_OpName),fw);    fwrite("/r/n",sizeof(char),strlen("/r/n"),fw);    fwrite(msg,sizeof(BYTE),strlen(msg),fw);    fwrite("/r/n/r/n",sizeof(char),strlen("/r/n/r/n"),fw);}下面是代表QQ表情值的对照表:BOOL GetUserFace(short int nIDFace,wchar_t* face){    if(0x4e==nIDFace)         wcsncpy_s(face,8,L"[#呲牙#]",8);    else if(0x4d==nIDFace)        wcsncpy_s(face,8,L"[#调皮#]",8);    else if(0x79==nIDFace)        wcsncpy_s(face,8,L"[#流汗#]",8);    else if(0x8a==nIDFace)        wcsncpy_s(face,8,L"[#偷笑#]",8);    else if(0x99==nIDFace)        wcsncpy_s(face,8,L"[#再见#]",8);    else if(0x98==nIDFace)        wcsncpy_s(face,8,L"[#敲打#]",8);    else if(0xa2==nIDFace)        wcsncpy_s(face,8,L"[#擦汗#]",8);    else if(0x7c==nIDFace)        wcsncpy_s(face,8,L"[#猪头#]",8);    else if(0x62==nIDFace)        wcsncpy_s(face,8,L"[#玫瑰#]",8);    else if(0xb1==nIDFace)        wcsncpy_s(face,8,L"[#菜刀#]",8);    else if(0x9d==nIDFace)        wcsncpy_s(face,8,L"[#炸弹#]",8);    else if(0x89==nIDFace)        wcsncpy_s(face,8,L"[#便便#]",8);    else if(0xab==nIDFace)        wcsncpy_s(face,8,L"[#委屈#]",8);    else if(0x76==nIDFace)        wcsncpy_s(face,8,L"[#抓狂#]",8);    else if(0x74==nIDFace)        wcsncpy_s(face,8,L"[#酷#]",8);    else if(0x93==nIDFace)        wcsncpy_s(face,8,L"[#嘘#]",8);    else if(0x4a==nIDFace)        wcsncpy_s(face,8,L"[#大哭#]",8);    else if(0x46==nIDFace)        wcsncpy_s(face,8,L"[#流泪#]",8);    else if(0x8b==nIDFace)        wcsncpy_s(face,8,L"[#可爱#]",8);    else if(0x43==nIDFace)        wcsncpy_s(face,8,L"[#色#]",8);    else if(0x47==nIDFace)        wcsncpy_s(face,8,L"[#害羞#]",8);    else if(0x45==nIDFace)        wcsncpy_s(face,8,L"[#得意#]",8);    else if(0x77==nIDFace)        wcsncpy_s(face,8,L"[#吐#]",8);    else if(0x4f==nIDFace)        wcsncpy_s(face,8,L"[#微笑#]",8);    else if(0x4c==nIDFace)        wcsncpy_s(face,8,L"[#发怒#]",8);    else if(0x4b==nIDFace)        wcsncpy_s(face,8,L"[#尴尬#]",8);    else if(0x78==nIDFace)        wcsncpy_s(face,8,L"[#惊怒#]",8);    else if(0x49==nIDFace)        wcsncpy_s(face,8,L"[#睡#]",8);    else if(0x92==nIDFace)        wcsncpy_s(face,8,L"[#疑问#]",8);    else if(0x41==nIDFace)        wcsncpy_s(face,8,L"[#惊讶#]",8);    else if(0x73==nIDFace)        wcsncpy_s(face,8,L"[#难过#]",8);    else if(0x8d==nIDFace)        wcsncpy_s(face,8,L"[#傲慢#]",8);    else if(0x8c==nIDFace)        wcsncpy_s(face,8,L"[#白眼#]",8);    else if(0xb5==nIDFace)        wcsncpy_s(face,8,L"[#示爱#]",8);    else if(0x65==nIDFace)        wcsncpy_s(face,8,L"[#爱心#]",8);    else if(0xa1==nIDFace)        wcsncpy_s(face,8,L"[#冷汗#]",8);    else if(0xae==nIDFace)        wcsncpy_s(face,8,L"[#亲亲#]",8);    else if(0x7a==nIDFace)        wcsncpy_s(face,8,L"[#憨笑#]",8);    else if(0x5c==nIDFace)        wcsncpy_s(face,8,L"[#爱情#]",8);    else if(0x96==nIDFace)        wcsncpy_s(face,8,L"[#衰#]",8);    else if(0x42==nIDFace)        wcsncpy_s(face,8,L"[#撇嘴#]",8);    else if(0xad==nIDFace)        wcsncpy_s(face,8,L"[#阴险#]",8);    else if(0x90==nIDFace)        wcsncpy_s(face,8,L"[#奋斗#]",8);    else if(0x44==nIDFace)        wcsncpy_s(face,8,L"[#发呆#]",8);    else if(0xa8==nIDFace)        wcsncpy_s(face,8,L"[#右哼哼#]",8);    else if(0x70==nIDFace)        wcsncpy_s(face,8,L"[#弱#]",8);    else if(0x6f==nIDFace)        wcsncpy_s(face,8,L"[#强#]",8);    else if(0xb0==nIDFace)        wcsncpy_s(face,8,L"[#可怜#]",8);    else if(0x7b==nIDFace)        wcsncpy_s(face,8,L"[#大兵#]",8);    else if(0x94==nIDFace)        wcsncpy_s(face,8,L"[#晕#]",8);    else if(0xaa==nIDFace)        wcsncpy_s(face,8,L"[#鄙视#]",8);    else if(0x56==nIDFace)        wcsncpy_s(face,8,L"[#飞吻#]",8);    else if(0xa6==nIDFace)        wcsncpy_s(face,8,L"[#坏笑#]",8);    else if(0x7f==nIDFace)        wcsncpy_s(face,8,L"[#拥抱#]",8);    else if(0x88==nIDFace)        wcsncpy_s(face,8,L"[#握手#]",8);    else if(0xa0==nIDFace)        wcsncpy_s(face,8,L"[#胜利#]",8);    else if(0xb7==nIDFace)        wcsncpy_s(face,8,L"[#抱拳#]",8);    else if(0x63==nIDFace)        wcsncpy_s(face,8,L"[#凋谢#]",8);    else if(0x81==nIDFace)        wcsncpy_s(face,8,L"[#饭#]",8);    else if(0x67==nIDFace)        wcsncpy_s(face,8,L"[#蛋糕#]",8);    else if(0x61==nIDFace)        wcsncpy_s(face,8,L"[#西瓜#]",8);    else if(0xb2==nIDFace)        wcsncpy_s(face,8,L"[#啤酒#]",8);    else if(0xb6==nIDFace)        wcsncpy_s(face,8,L"[#瓢虫#]",8);    else if(0xb9==nIDFace)        wcsncpy_s(face,8,L"[#拳头#]",8);    else if(0xba==nIDFace)        wcsncpy_s(face,8,L"[#差劲#]",8);    else if(0x5a==nIDFace)        wcsncpy_s(face,8,L"[#发抖#]",8);    else if(0x9e==nIDFace)        wcsncpy_s(face,8,L"[#刀#]",8);    else if(0x6e==nIDFace)        wcsncpy_s(face,8,L"[#月亮#]",8);    else if(0x80==nIDFace)        wcsncpy_s(face,8,L"[#咖啡#]",8);    else if(0xbb==nIDFace)        wcsncpy_s(face,8,L"[#爱你#]",8);    else if(0xbd==nIDFace)        wcsncpy_s(face,8,L"[#OK#]",8);    else if(0xb8==nIDFace)        wcsncpy_s(face,8,L"[#勾引#]",8);    else if(0x66==nIDFace)        wcsncpy_s(face,8,L"[#心碎#]",8);    else if(0x6b==nIDFace)        wcsncpy_s(face,8,L"[#太阳#]",8);    else if(0x68==nIDFace)        wcsncpy_s(face,8,L"[#礼物#]",8);    else if(0x5e==nIDFace)        wcsncpy_s(face,8,L"[#足球#]",8);    else if(0x97==nIDFace)        wcsncpy_s(face,8,L"[#骷髅#]",8);    else if(0xc2==nIDFace)        wcsncpy_s(face,8,L"[#挥手#]",8);    else if(0x9c==nIDFace)        wcsncpy_s(face,8,L"[#闪电#]",8);    else if(0x8e==nIDFace)        wcsncpy_s(face,8,L"[#饥饿#]",8);    else if(0x8f==nIDFace)        wcsncpy_s(face,8,L"[#困#]",8);    else if(0xaf==nIDFace)        wcsncpy_s(face,8,L"[#吓#]",8);    else if(0xac==nIDFace)        wcsncpy_s(face,8,L"[#快哭了#]",8);    else if(0xa9==nIDFace)        wcsncpy_s(face,8,L"[#哈欠#]",8);    else if(0xa7==nIDFace)        wcsncpy_s(face,8,L"[#左哼哼#]",8);    else if(0xa5==nIDFace)        wcsncpy_s(face,8,L"[#糗大了#]",8);    else if(0xa4==nIDFace)        wcsncpy_s(face,8,L"[#鼓掌#]",8);    else if(0xa3==nIDFace)        wcsncpy_s(face,8,L"[#抠鼻#]",8);    else if(0x95==nIDFace)        wcsncpy_s(face,8,L"[#折磨#]",8);    else if(0x91==nIDFace)        wcsncpy_s(face,8,L"[#咒骂#]",8);    else if(0xb3==nIDFace)        wcsncpy_s(face,8,L"[#篮球#]",8);    else if(0xb4==nIDFace)        wcsncpy_s(face,8,L"[#乒乓#]",8);    else if(0xbc==nIDFace)        wcsncpy_s(face,8,L"[#NO#]",8);    else if(0x58==nIDFace)        wcsncpy_s(face,8,L"[#跳跳#]",8);    else if(0x5b==nIDFace)        wcsncpy_s(face,8,L"[#怄火#]",8);    else if(0xBE==nIDFace)        wcsncpy_s(face,8,L"[#转圈#]",8);    else if(0xBF==nIDFace)        wcsncpy_s(face,8,L"[#磕头#]",8);    else if(0xc0==nIDFace)        wcsncpy_s(face,8,L"[#回头#]",8);    else if(0xc1==nIDFace)        wcsncpy_s(face,8,L"[#跳绳#]",8);    else if(0x48==nIDFace)        wcsncpy_s(face,8,L"[#闭嘴#]",8);    else if(0xc7==nIDFace)        wcsncpy_s(face,8,L"[#右太极#]",8);    else if(0xc6==nIDFace)        wcsncpy_s(face,8,L"[#左太极#]",8);    else if(0xc5==nIDFace)        wcsncpy_s(face,8,L"[#献吻#]",8);    else if(0xc4==nIDFace)        wcsncpy_s(face,8,L"[#街舞#]",8);    else if(0xc3==nIDFace)        wcsncpy_s(face,8,L"[#激动#]",8);    else         return false;    return true;}将上面说的综合起来就是本程序了,本程序的功能只负责将聊天记录导出到文本文档中,其实稍加修改就可以做到将20082009的聊天记录相互转换。现在手机QQ出2010版了,不过只要腾讯它不像电脑上使用的QQ一样将聊天记录加密,应该也能分析出其格式从而将其导出或者转换。如果哪位朋友发现程序中有错误或者不妥的地方希望能留言指出,谢谢……
原创粉丝点击