原文地址:http://www.jianshu.com/p/07a8d87e698b
1. 怎么得到这些文件?
过去,我可以提示用户在越狱之后用 iTools 自行把微信 App 所在文件夹复制出来。然而自从某个版本的 iOS 开始,在不越狱的情况下,我们只能看到 /User/Media
这里的文件,而需要的本地数据在 /User/Containers/Data/Application/微信的UID
。强迫用户为了这么一件事前去越狱显然不太友好,而对“聊天记录迁移”抓包也不方便,所以我想到另一种途径。
这就是 iTunes 备份。从经验判断,恢复备份之后微信里的聊天记录都还在,说明肯定这些文件在备份的时候保存到了电脑上。它们在哪里?苹果官方给了答案。简单地说,Windows 下在 `\用户\(用户名)\AppData\Roaming\Apple Computer\MobileSync\Backup`。
不过,iTunes 备份的文件夹结构不是很友好,似乎每个手机上的文件名都变成了一串序号,当然打开相关的 plist 然后看出规律也不难。好消息是,已经有很多人做了类似的事情,例如 iphonebackupbrowser,它也是用 C# 写的,用起来比较方便。
因此,我做的第一步是让用户选取做好的 iTunes 备份,从上面那个源码,稍微修改一下就可以找到 com.tencent.xin 的相关文件,从而在程序里直接通过 iOS 上的路径找到对应的文件。
下面以 iTools 为例
03a232a651...64467f3030 这个文件夹是 微信ID 的 MD5值。
如果曾经登陆过其他账号,就会有N个对应账号的文件夹。
如果想知道当前登陆的是哪个微信,就要关注LocalInfo.lst了,把这个文件导出来,然后修改其后缀名为.plist,然后打开可以看到下图
Item2纪录的是当前登陆的微信ID,然后转换成MD5值就是上图的文件夹名称了
Item3是对应的电话号码账号
2. 文件夹对应的账号其他信息
上面我们说到,文件夹名字是 微信ID 的MD5值,但是怎么才能获取该 微信号 的名字,头像等各种其他信息呢?
找到文件夹里的mmsetting.archive,按照国际惯例,改成plist后打开
账号信息一览无为,想要什么可自行提取
3. 主要的数据库
iOS版微信,基本上所有的 好友 和 聊天 数据都是存放在DB这个文件夹。
好友:WCDB_Contact.sqlite
聊天:MM.sqlite
好友:WCDB_Contact.sqlite
这个库,我们主要关心的是 Friend 这个表,然后打开一看,疯了,里面很多字段直接使用Blob的格式纪录,查看内容全部都是二进制显示,肉眼比较难直接获取有用的信息。
经过鄙人几天的努力,大概弄懂了个别字段的意思,下面详细说明
userName
这个顾名思义就是 微信号ID 了
如果以 @chatroom 结尾的,就是群
type
这个第一眼看是普通的数字,但是它真正的意义是把这个数字转换成二进制去看。
数字 | 二进制 | 意义 |
---|---|---|
0 | 0 | 微信运动 |
1 | 1 | 微信应用 |
2 | 10 | app + 群 |
3 | 11 | 好友 |
4 | 100 | 群里面的人 |
6 | 110 | 群好友,对方加你,你未通过 |
7 | 111 | 群里面的人,而且互为好友 |
11 | 1011 | 拉黑别人 |
67 | 1000011 | 标星 |
256 | 100000000 | 删除好友 |
259 | 100000011 | 不让他看我的朋友圈 |
65539 | 10000000000000011 | 不看他的朋友圈 |
从上面的表格大致可以看出来
第一位:你是否加对方为好友
第二位:对方是否加你为好友
第三位:是否群里面的
第四位:你主动拉黑对方
第七位:标星
剩下的只是大概,还未落实是否准确,还有更多的希望大家发现后留言给我,一同探讨。
dbcontactremark
这个字段是使用Blod去纪录的,而通过观察,发现是一种很有趣的纪录方式。
开头若干字节未知信息 --> (1 字节类型说明 --> 1(?) 字节长度 --> 字符串) 若干个
- 先取第i位(i=0),这个应该说明作用的,但是我还没弄懂它的意思。
- 取后一位(i+1),这个说明的是后面正文的长度,假设你取得的值是15
- 然后从(i+2)开始,截取15个字节,转换成字符串就是正文了
- 重复1-3,直至读取完所有字节为止
还是不明白的话,直接上代码好了
//解析dbcontactremark
-(NSArray*)getRemarkDataBy:(NSData *)data{
Byte *testByte = (Byte *)[data bytes];
NSMutableArray* arr = [[NSMutableArray alloc]init];
int len = 0;
int index = 0;
while (true) {
index++;
len = testByte[index];
index++;
if (index + len > [data length]) {
break;
}
NSString* str = [[NSString alloc]initWithData:[data subdataWithRange:NSMakeRange(index, len)] encoding:NSUTF8StringEncoding];
if (str != nil) {
[arr addObject:str];
}
index += len;
}
return [arr copy];
}
附上两个运行中截取数据的图(最后一列有特殊意义,文章下面会再作解析)
dbcontactheadimage
顾名思义,这个列代表的头像的信息
大致可以看出,里面有直接一个http的网址,只要把这个网址截取出来就好了,这里我用了最笨的方法
//解析dbcontactheadimage
-(NSString *)getPhotoBy:(NSData *)data{
if (data.length <= 8) {
return @"";
}
int begin = 0;
int end = 0;
Byte *byteData = (Byte *)[data bytes];
for(int i=0;i<[data length];i++){
if (byteData[i] == 104 && begin == 0) {//104 = "h"
begin = i;
}
if (byteData[i] == 26 && end == 0) {//26 = "结束"
end = i;
}
}
if (begin > 0 && end > 0) {
int len = end - begin;
NSData* tempData = [data subdataWithRange:NSMakeRange(begin, len)];
NSString* str = [[NSString alloc]initWithData:tempData encoding:NSASCIIStringEncoding];
return str;
}
return @"";
}
dbcontactchatroom
这个字段只有 群 才有内容,其内容说明了该 群 的所有成员
内容由两部分组成,第一部分是所有的成员的 微信ID,用 ;(分号)隔开。另一部分则是比较详细的XML格式
<RoomData>
<Member UserName="xxxx">
<Flag>N</Flag>
<DisplayName>xxxx</DisplayName>
</Member>
...
</RoomData>
标签
微信里还有一个叫标签的分组方式,而标签是存放在数据库里面,而是Documents/微信号的MD5/contactlabel.list
,继续改成plist就可以查看
首先红框告诉你一共有多少分组
黄框,分组ID 3 => 标签名 bbb
绿框,分组ID 2 => 标签名 aa a
篮框,分组ID 1 => 标签名 qqq
到这里,你肯定会问,那标签的成员在哪啊?
其实,上面我们曾经见过了,在分析 dbcontactremark
的时候
这里最后的一个字符串 3,4
,其实就是表示这个好友,是存在 标签ID=3
跟 标签ID=4
的这两个标签里面。
好友的资料到这里就结束了,接下来我们来介绍聊天信息
聊天:MM.sqlite
有了前面的准备,我们已经可以解析 Chat_[0-9a-f]{32} 表,并且以文本形式导出每个对话的聊天记录。怎么知道聊天的对象是谁?Chat_ 后面是 UsrName 或 alias 的 MD5 值。
首先看一下聊天记录的结构。MesLocalID 是一个比较重要的数字,虽然暂时还用不到。CreateTime 顾名思义,并且应该是 UTC+0 的。Message 就是消息本身。Type 表示消息的类型,可以自己试验一下,最后 Des 应该表示我是否为消息的接收方。
下面简单描述一下我见到过的 Type 和对应的 Message 处理:
10000: 系统消息,就是那种居中的。
34: 语音,消息里会有 <voicemsg>
标签,可以读出来长度等信息。具体文件的处理下一节再讲,下同。
47: 表情,<emoji>
标签里面可以找到一些信息。
62: 小视频,<videomsg>
。
50: 视频/语音通话,<voipinvitemsg>
。本来在微信里二者就可以切换,对用户解释得太细也没啥用。
3: 图片。
48: 位置。
42: 名片。
49: 链接。这里面包含的类别比较多,在 Message 里面会有 <title>
、<des>
、<url>
、<thumburl>
等信息。微信应该是通过 <type> 标签来确定一些特殊的应用,比如 2001 是红包,2000 转账,17 实时位置共享,6 文件。(我试过把它或者后面的模板地址改成别的,好像不管用。)
对于导出文字来说,这些特殊的东西就给用户显示个“[图片]”、“[表情]”吧。
还有一个问题是群聊,特点是用户名为 \d+@chatroom。在群聊当中,每个人(除了自己)的发言前面都会有“微信号:\n”,好让我们知道对方身份。
4.其他多媒体资源
为了给用户初恋般的体验,我还希望能尽量还原聊天的全部内容,这就需要加入对应的图片(头像)、语音、视频、动画表情等元素。
我们自然会想在 Documents/微信号的MD5
文件夹下面找这些内容。这时很容易发现:
(1) Img
Img 文件夹中有一些以 MD5 命名的文件夹,它们对应数据库中的各 Chat_ 表,而具体文件是以数字编号的,这个编号等于对应消息的 MesLocalID(上面提到过)。文件有三种后缀:.pic、.pic_hd、.pic_thum,顾名思义是正常大小的图片、原图、缩略图。基本上是 JPEG 格式吧,这个影响不大。
(2) Video
Video 文件夹类似,有 .video_thum 扩展名的缩略图,以及 .mp4 的视频本体。视频是 AVC+AAC 编码的,不过仍然不重要吧。
(3) Audio
Audio 是语音,以前是 3GP 格式,现在打开之后可以看到 SILK_V3 的字样,搜索可以直接发现编译好的转换程序。不过没有源码,也可以自行搜索其他解决方案。
然而在这个版本中,我始终没有从备份当中找到动画表情和头像这两项资源。怎么回事?
正好那段时间盘古越狱出现了,我把完整的 Documents 和 Library 文件夹复制出来,看了一遍。原来它们在 Library/WechatPrivate
里,而这个文件夹设置成了不备份。这也有道理,因为前面的几个是个人的资源,而头像和表情随时都可以再去下载,所以并不需要放在 iTunes 备份当中。
那么不越狱的情况下,我们怎么获得它们呢?记得上面提到过,在每个好友的 dbContactHeadImage 当中有正常和放大头像的地址;如果看一下含有动画表情的消息,其中也有这个表情的 GIF 地址。好的,下载就可以了。
5.总结
以上描述了找到微信聊天记录涉及的文件的方法,不过讲道理它们都只能算是“有根据的推测”。因为聊天记录这件事不太方便收集测试数据,只能保证它们符合我能找到的记录。