iOS 微信数据库分析

原文地址: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(?) 字节长度 --> 字符串) 若干个

  1. 先取第i位(i=0),这个应该说明作用的,但是我还没弄懂它的意思。
  2. 取后一位(i+1),这个说明的是后面正文的长度,假设你取得的值是15
  3. 然后从(i+2)开始,截取15个字节,转换成字符串就是正文了
  4. 重复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.总结

以上描述了找到微信聊天记录涉及的文件的方法,不过讲道理它们都只能算是“有根据的推测”。因为聊天记录这件事不太方便收集测试数据,只能保证它们符合我能找到的记录。

展开阅读全文

没有更多推荐了,返回首页