iOS 微信数据库分析

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.总结

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 点击查看原文 Web SDK 开发手册 SDK 概述 网易云信 SDK 为 Web 应用提供一个完善的 IM 系统...
    layjoy阅读 13,339评论 0 15
  • 2017年4月4日,第二届DAC亚洲邀请赛总决赛,当IG战队站在颁奖台上,由burning接过冠军奖杯的时候,距离...
    牙签Tiny阅读 193评论 0 0
  • 我想我们都曾经经历过那个青涩的阶段,踌躇满志,血气方刚,却又无畏莽撞,固执或者说执着得以为,单纯就是美,努力就够了...
    楊璇阅读 364评论 0 0
  • 文/夜奔 塞北玉门关外,春风不度,一派荒凉。俗话说,穷山恶水多刁民。这贫瘠的土地上孕育出了不少凶悍的人物,但凡过往...
    一夜星辰阅读 751评论 0 0