耗时4个月我把微信研究了个遍,以下内容仅代表个人经验或猜想,转载请注明出处。
前言
我深信有意义的不是微信,而是隐藏在对话框背后的一个个深刻故事。未来,每个人都能拥有AI的陪伴,而你的数据能够赋予它有关于你过去的珍贵记忆。我希望每个人都有将自己的生活痕迹👨👩👦👚🥗🏠️🚴🧋⛹️🛌🛀留存的权利,而不是遗忘💀。
AI的发展不仅仅是技术的提升,更是情感💞的延续。每一个对话、每一个互动都是生活中独一无二的片段,是真实而动人的情感交流。因此,我希望AI工作者们能够善用这些自己的数据,用于培训独特的、属于个体的人工智能。让个人AI成为生活中的朋友,能够理解、记录并分享我们的欢笑、泪水和成长。
那天,AI不再是高不可攀的存在,而是融入寻常百姓家的一部分。因为每个人能拥有自己的AI,将科技的力量融入生活的方方面面。这是一场关于真情实感的革命,一场让技术变得更加人性化的探索,让我们共同见证未来的美好。
所以《留痕》
MemoTrace 支持解析、导出、分析三大功能,支持微信3和微信4全版本,支持导出多种格式的聊天记录、支持导出几乎全部类型的消息类型,既有适合开发者二次开发的后端接口又有适于小白用户使用的界面应用程序。
后面内容供学习使用,为避免头疼,普通用户请转至官网 memotrace.cn 下载使用
微信3数据库目录结构
text├─Msg │ │ Applet.db # 小程序 │ │ BizChat.db # │ │ BizChatMsg.db │ │ ChatMsg.db │ │ ChatRoomUser.db │ │ ClientGeneral.db │ │ CustomerService.db │ │ Emotion.db # 表情包 │ │ Favorite.db # 微信收藏 │ │ FTSContact.db # 搜索联系人 │ │ FTSFavorite.db # 搜索收藏 │ │ FunctionMsg.db │ │ HardLinkFile.db # 微信文件 │ │ HardLinkImage.db # 微信图片 │ │ HardLinkVideo.db # 微信视频 │ │ ImageTranslate.db │ │ LinkHistory.db │ │ MicroMsg.db # 微信联系人 │ │ Misc.db # 微信联系人头像 │ │ MultiSearchChatMsg.db │ │ NewTips.db │ │ OpenIMContact.db # 企业微信联系人 │ │ OpenIMMedia.db # 企业微信语音 │ │ OpenIMMsg.db # 企业微信聊天记录 │ │ OpenIMResource.db # 企业微信联系人公司信息 │ │ PreDownload.db │ │ PublicMsg.db # 公众号消息 │ │ PublicMsgMedia.db # 公众号语音消息 │ │ Sns.db # 朋友圈数据 │ │ StoreEmotion.db │ │ SyncMsg.db │ │ Voip.db │ └─Multi # Multi文件夹里的数据库采用了分库操作,将一个大数据库分成多个便于提高检索效率 │ FTSMSG0.db # 搜索聊天记录 │ FTSMSG1.db │ MediaMSG0.db # 语音消息 0 │ MediaMSG1.db # 语音消息 1 │ MSG0.db # 聊天记录 0 │ MSG1.db # 聊天记录 1
微信4数据库目录结构
text├─db_storage │ │ │ ├─biz │ │ biz.db │ ├─contact # 联系人 │ │ contact.db │ │ contact_fts.db │ │ fmessage_new.db │ │ wa_contact_new.db │ ├─emoticon # 表情包 │ │ emoticon.db │ ├─favorite # 微信收藏 │ │ favorite.db │ │ favorite_fts.db │ ├─hardlink # 微信图片、视频、文件 │ │ hardlink.db │ ├─head_image # 微信头像 │ │ head_image.db │ ├─ilinkvoip │ │ ilinkvoip.db │ ├─message # 聊天记录 │ │ biz_message_0.db # 公众号消息 │ │ media_0.db # 语音消息 │ │ message_0.db # 聊天记录 │ │ message_1.db │ │ message_fts.db │ │ message_resource.db │ │ message_revoke.db │ ├─newtips │ │ newtips.db │ ├─session # 聊天会话窗口 │ │ session.db │ ├─sns # 朋友圈 │ │ sns.db │ ├─teenager │ │ teenager.db │ ├─tencentpay │ │ tencentpay.db │ ├─wcfinder │ │ wcfinder.db │ └─websearch │ websearch.db
不管是微信3还是微信4,聊天数据、图片、视频、文件、表情包、语音都是存储在不同的数据库中的,因此解析一条聊天记录要综合查询多个数据库的内容。
为了避免跨数据库连接操作,MemoTrace为每个数据库都建立一个连接,各数据库之间互相不产生依赖,由上层模块综合调用各个数据库,组装出每一条完整的数据。
微信3和微信4的数据库不尽相同,部分数据库字段有很大调整,所以MemoTrace把v3和v4分成独立的两个部分,把v3和v4的聊天记录(Message
)和联系人(Contact
)抽象得出相同的一个数据结构,通过ManagerV3
和ManagerV4
实现不同数据库模块的分布管理和扩展,通过DataBaseInterface
层对外提供统一接口,隔离内部实现。
详细设计将在后面给出。
微信3
微信4.0之前的图片都采用了最简单的异或加密,即采用一个异或密钥(一个0-255的整数)与图片文件的每个字节进行异或运算。由于异或运算具有可逆性,采用相同的密钥再做一次异或运算即可得到原始图片文件。
异或运算性质
假设:
C
P
K
L
i
个字节:i
不同格式的图片通常有固定的文件头,例如:
89 50 4E 47 0D 0A 1A 0A
FF D8 FF E0
或 FF D8 FF DB
42 4D
47 49 46 38 39 61
或 47 49 46 38 37 61
根据异或运算的性质:
可得:
所以,我们可以通过已知的加密数据 C
和已知的文件头 P
计算密钥的前 L
个字节:
密钥是一个0-255范围内的整数所以:
微信4 点击查看详细文档
微信4的图片采用AES-EBC和异或混合加密方式。
.dat文件头(15个字节)
大小(字节) | 内容/类型 | 说明 |
---|---|---|
6 | 0x07085631 | .dat文件标识符 |
4 | int (小端序) | AES-EBC128 加密长度 |
4 | int (小端序) | 异或加密长度 |
1 | 0x01 | 未知 |
文件末尾采用异或加密,加密长度最大为1MB,多余部分未加密。
例如:一个文件小于1kB,则全部是AES加密,如果大于1kB且小于1MB(实际是1MB零1KB),则前1KB部分采用AES加密,剩余部分采用异或加密。如果文件大于1MB,则前1KB采用AES加密,后面1MB采用异或加密,中间部分未加密。
0xcfcd208495d565ef
微信3
text├─Applet ├─Backup ├─FileStorage # 存储微信图片、视频、文件 │ ├─Cache # 缩略图缓存 │ ├─CustomEmotion # 表情包 │ ├─Fav # 收藏 │ ├─File # 微信文件 │ ├─MsgAttach # 微信里与每个人的聊天数据 │ │ └─aefd036248e0d4d0f07900a6239272e4 # 该联系人wxid的md5加密 │ │ ├─Image # 聊天图片 │ │ │ └─2024-10 │ │ ├─Thumb # 聊天中产生的缩略图 │ │ │ └─2024-10 │ │ ├─File # 合并转发的聊天记录里的文件 │ │ │ └─2024-10 │ │ └─Video # 合并转发的聊天记录里的视频 │ │ └─2024-10 │ ├─Sns # 朋友圈数据 │ ├─Video # 聊天视频 │ │ └─2025-02 │ └─XEditor ├─Msg # 聊天记录数据库 └─ResUpdateV2
微信4
text├─business ├─cache # 缓存 ├─config # 配置文件 ├─db_storage # 聊天记录数据库 ├─msg # 与好友的聊天数据 │ ├─attach # 微信里与每个人的聊天数据 │ │ └─aefd036248e0d4d0f07900a6239272e4 # 该联系人wxid的md5加密 │ │ │ ├─2022-01 # 日期 │ │ │ │ ├─Img # 聊天图片 │ │ │ │ └─Rec # 合并转发的聊天记录数据 │ │ │ │ └─2090_136022 │ │ │ │ └─Img │ ├─file # 微信文件 │ └─video # 微信视频 ├─Msg # 聊天记录数据库 └─temp
微信4的文件命名方式跟之前有些微区别,但其数据存放是类似的(文件、视频单独存放在独立的文件夹中,图片按联系人分组存放到不同的文件夹中)
该系统由多个模块组成,主要用于管理聊天会话、历史记录、联系人树以及数据导出与分析。系统的主要组成部分包括 GUI(用户界面)、wxManager(微信管理器)和 application(应用层),各个部分协同工作,实现聊天数据的存储、管理、分析和导出。
面向对象设计
DataBaseInterface
作为数据库访问接口,可以适配不同版本的数据库。设计模式
wxManager
采用策略模式,根据不同的数据库版本选择合适的管理器(ManagerV3
或 ManagerV4
)。GUI
和 application
无需直接操作数据库。数据流与通信
GUI
发送请求给 wxManager
。wxManager
通过数据库接口访问数据。application
获取 wxManager
提供的数据并进行处理。GUI
或以报告形式导出。wxManager 作为系统的核心数据管理层,负责数据的存储、检索和管理。该模块采用策略模式和外观模式,使得不同版本的数据管理模块可以独立实现。
数据库接口(DataBaseInterface)
该接口提供对数据库的统一访问方法,包括:
getMessages(params): List[Message]
获取聊天消息。getContacts(params)
获取联系人信息。other()
其他数据库操作。数据管理模块
wxManager 支持多个数据库管理模块:
MicroMsgDB
:存储微信消息。MsgDB
:存储消息的具体信息。contactDB
:存储联系人信息。messageDB
:存储聊天消息。应用层提供数据导出、统计分析和年度报告功能。
GUI 负责提供用户交互界面,包括以下功能模块:
设计思路
微信聊天消息的类型有很多(文本、图片、视频、语音、链接、合并转发的聊天记录等近二十种类型),除了文本消息,其他类型在数据库里基本都是以xml(或者压缩后的xml)的形式存储的,每种类型的xml格式都不相同,所以解析方式也不相同,所以需要有大量的if-else来处理不同的类型。为了避免这种麻烦,MemoTrace采用面向对象的设计原则,对消息进行抽象封装,基类定义各种消息的共同属性和方法,由子类针对特定消息类型进行单独处理。
to_text
和 to_json
)。@dataclass
提供字段验证和类型提示。sort_seq
的消息排序功能。Message
Message
是所有消息类型的基类,定义了消息的基本属性和方法。子类通过继承并扩展此类,添加各自特有的功能和数据。
属性:
Message属性描述
2024-12-01 12:00:00
)。MessageType
枚举(如文本、图片、视频等)。方法:
is_chatroom(self) -> bool
: 判断消息是否来自群聊,通过 talker_id
是否以 @chatroom
结尾来实现。to_json(self)
: 转换为 JSON 格式(需子类实现)。to_text(self)
: 转换为纯文本(需子类实现)。__lt__(self, other)
: 定义消息的排序逻辑,基于 sort_seq
属性。用途:
以下是从Message
类派生的各类子类,针对不同的消息内容或功能进行设计。
TextMessage
(文本)
FileMessage
(文件)
LinkMessage
(链接)
类层次结构体现了明确的职责分离:
Message
是根类,提供通用的属性和方法。TextMessage
、FileMessage
、LinkMessage
)按功能分组。SystemMessage
、ImageMessage
、MusicShareMessage
)针对特定消息类型提供功能。相关信息
上面说到每种消息类型的xml结构都不相同,需要单独解析,而且一个聊天记录的解析往往需要联合查询多个数据库,因此从数据库对象转成Message对象需要大量的if-else操作,为了避免这种麻烦,MemoTrace使用 工厂方法模式 创建聊天消息,针对每一种消息类型为其创建单独的工厂方法,每种消息的创建都是独立的,具有极强的扩展性。工厂方法模式通过定义创建对象的接口来实现消息对象的灵活构建,同时结合单例模式共享工厂实例,提升了系统的扩展性和性能。
Singleton
管理共享数据(如联系人信息)。5.2.2.1. 抽象工厂类:MessageFactory
MessageFactory
是所有具体工厂类的基类,定义了统一的 create
方法,用于创建消息对象。create(data, username, database_manager)
:抽象方法,由具体工厂类实现,负责根据输入数据创建消息对象。5.2.2.2. 单例基类:Singleton
set_shared_data
和 get_shared_data
,管理工厂之间共享的数据。contacts
字典缓存联系人信息,避免重复查询数据库。messages
字典缓存聊天记录,方便查询引用消息。get_contact(wxid, database_manager)
方法从缓存或数据库获取联系人。5.2.2.3. 工厂实现类
每个工厂实现类继承自 MessageFactory
和 Singleton
,用于创建具体的消息对象。
TextMessageFactory
TextMessage
)。create
方法,根据输入的数据构造一个 TextMessage
实例。ImageMessageFactory
ImageMessage
)。create
方法,根据输入的数据(包括时间戳、发送者 ID、图片 URL 和分辨率)构造一个 ImageMessage
实例。5.2.2.4. 工厂注册表:FACTORY_REGISTRY
text
, image
)到具体工厂类实例的映射。示例注册表内容:
python# 工厂注册表
FACTORY_REGISTRY = {
-1: UnknownMessageFactory(),
MessageType.Text: TextMessageFactory(),
MessageType.Image: ImageMessageFactory(),
MessageType.Audio: AudioMessageFactory(),
MessageType.Video: VideoMessageFactory(),
···
MessageType.Quote: QuoteMessageFactory(),
MessageType.Transfer: TransferMessageFactory(),
MessageType.RedEnvelope: RedEnvelopeMessageFactory(),
}
MessageFactory
create(data, username, database_manager)
:
data
:从数据库查询或外部获取的消息数据(通常是元组)。username
:聊天对象的唯一标识(如 wxid)。database_manager
:用于数据库操作的接口。Singleton
__new__
方法确保每个子类只有一个实例。set_shared_data
/get_shared_data
用于设置和获取共享数据。contacts
:存储联系人缓存信息。set_contacts
:初始化联系人缓存。get_contact(wxid, database_manager)
:从缓存或数据库获取联系人。TextMessageFactory
TextMessage
实例。create(data, username, database_manager)
:
TextMessage
对象。ImageMessageFactory
ImageMessage
实例。create(data, username, database_manager)
:
timestamp
、sender_id
、image_url
和 resolution
,并创建一个 ImageMessage
实例。FACTORY_REGISTRY
是一个字典,存储消息类型到具体工厂实例的映射。text
,从注册表获取 TextMessageFactory
的实例,并调用其 create
方法。FACTORY_REGISTRY
中。text
类型对应 TextMessageFactory
,image
类型对应 ImageMessageFactory
。FACTORY_REGISTRY
获取对应的工厂实例。create
方法,生成消息对象。MessageFactory
Singleton
TextMessageFactory
ImageMessageFactory
FACTORY_REGISTRY
TextMessage
, ImageMessage
等设计思路
微信联系人分为普通好友、群聊、企业微信联系人、公众号四种类型
前面提到一个聊天消息的构建需要联合查询多个数据库的信息,微信v3和微信v4的数据库结构相似但差异也很大。在 MemoTrace 1.0的时候获取聊天记录直接操作MSG和其他相关数据库(如下图所示),任何应用的修改可能会影响多个模块,耦合度较高,不易扩展和维护,获取联系人的时候甚至出现了循环依赖,违反了单一职责原则,这是绝对不允许的。
MemoTrace 2.0 为了降低模块与应用之间的耦合,增加扩展性,采用一个中间模块统一管理各个子系统,这就是外观模式。每个数据库采用一个模块单独管理,各数据库之间互不依赖,Manager
查询多个子数据库,集中处理业务逻辑,减少各个模块的重复代码。
由于微信数据库是固定的,2.0 加一个中间模块好像并没有太大作用,所以到2.1仍然有部分功能没有整合到 Maanager
中(主要是因为1.0的代码能用就懒得改了),直到微信4.0的出现才意识到这种设计的好处。只需要再增加一个与 Manager1
相同接口的 Manager2
即可不更改上层应用程序而增加微信4.0的支持。
经历三个阶段才形成了下面的数据库模块架构:
这部分存储了微信里的联系人信息,包括好友、群聊、企业微信联系人、公众号等信息
sqlCREATE TABLE biz_info(
id INTEGER PRIMARY KEY,
username TEXT,
type INTEGER,
accept_type INTEGER,
child_type INTEGER,
version INTEGER,
external_info TEXT,
brand_info TEXT,
brand_icon_url TEXT,
brand_list TEXT,
brand_flag INTEGER,
belong TEXT,
ext_buffer BLOB
)
名称 | 类型 | 说明 |
---|---|---|
id | INTEGER | 跟name2id表的序号相对应,可以唯一确定一个联系人 |
username | TEXT | 原始wxid,可以唯一确定一个联系人 |
type | INTEGER | 公众号类型,1是公众号,0是订阅号 |
accept_type | INTEGER | |
child_type | INTEGER | |
version | INTEGER | |
external_info | TEXT | 存储了公众号的详细信息,公众号底部菜单信息 |
brand_info | TEXT | |
brand_icon_url | TEXT | logo链接 |
brand_list | TEXT | |
brand_flag | INTEGER | |
belong | TEXT | |
ext_buffer | BLOB |
sqlCREATE TABLE biz_session_feeds(
username TEXT,
showname TEXT,
desc TEXT, type INTEGER,
unread_count INTEGER,
update_time INTEGER,
create_time INTEGER,
biz_attr_version INTEGER
)
名称 | 类型 | 说明 |
---|---|---|
username | TEXT | |
showname | TEXT | 屏幕上显示的名字 |
desc | TEXT | 消息描述信息 |
type | INTEGER | 消息类型 |
unread_count | INTEGER | 未读消息的个数 |
update_time | INTEGER | 更新时间 |
create_time | INTEGER | 创建时间 |
biz_attr_version | INTEGER |
sqlCREATE TABLE chat_room(
id INTEGER PRIMARY KEY,
username TEXT,
owner TEXT,
ext_buffer BLOB
)
名称 | 类型 | 说明 |
---|---|---|
id | INTEGER | 跟name2id表的序号相对应 |
username | TEXT | 群聊的username |
owner | TEXT | 群主的username |
ext_buffer | BLOB(protobuf) | 存储了群成员的username和群昵称 |
pythonsyntax = "proto3";
package app.protobuf;
option go_package=".;proto";
message ChatRoomData {
message ChatRoomMember {
string wxID = 1;
string displayName = 2;
int32 state = 3;
}
repeated ChatRoomMember members = 1;
int32 field_2 = 2;
int32 field_3 = 3;
int32 field_4 = 4;
int32 room_capacity = 5;
int32 field_6 = 6;
int64 field_7 = 7;
int64 field_8 = 8;
}
sqlCREATE TABLE chat_room_info_detail(
room_id_ INTEGER PRIMARY KEY,
username_ TEXT,
announcement_ TEXT,
announcement_editor_ TEXT,
announcement_publish_time_ INTEGER,
chat_room_status_ INTEGER,
room_top_msg_closed_id_list_text_ TEXT,
xml_announcement_ TEXT,
ext_buffer_ BLOB
)
名称 | 类型 | 说明 |
---|---|---|
room_id_ | INTEGER | 跟name2id表的序号相对应 |
username_ | TEXT | 群聊的username |
announcement_ | TEXT | 存文本形式的群公告 |
announcement_editor_ | TEXT | 群功能编辑者的username |
announcement_publish_time_ | INTEGER | 发布时间 |
chat_room_status_ | INTEGER | |
room_top_msg_closed_id_list_text_ | TEXT | |
xml_announcement_ | TEXT | xml格式的群功能,可以解析出来更多信息,如图片、文件之类的信息 |
ext_buffer_ |
sqlCREATE TABLE chatroom_member(
room_id INTEGER,
member_id INTEGER,
CONSTRAINT room_member UNIQUE(room_id, member_id)
)
名称 | 类型 | 说明 |
---|---|---|
room_id | INTEGER | 群聊id,跟name2id表的序号相对应 |
member_id | INTEGER | 群员id,跟name2id表的序号相对应 |
contact存储了所有的联系人,包括好友、群聊、公众号、企业微信联系人
sqlCREATE TABLE contact(
id INTEGER PRIMARY KEY,
username TEXT,
local_type INTEGER,
alias TEXT,
encrypt_username TEXT,
flag INTEGER,
delete_flag INTEGER,
verify_flag INTEGER,
remark TEXT,
remark_quan_pin TEXT,
remark_pin_yin_initial TEXT,
nick_name TEXT,
pin_yin_initial TEXT,
quan_pin TEXT,
big_head_url TEXT,
small_head_url TEXT,
head_img_md5 TEXT,
chat_room_notify INTEGER,
is_in_chat_room INTEGER,
description TEXT,
extra_buffer BLOB,
chat_room_type INTEGER
)
名称 | 类型 | 说明 |
---|---|---|
id | INTEGER | 序号,跟name2id表的序号相对应 |
user naI He | TEXT | 联系人的wixd |
local_type | INTEGER | 类型 |
alias | TEXT | 微信里显示的微信号 |
encrypt_username | TEXT | |
flag | INTEGER | 好像是联系人的真正类型,跟3.0数据库的Type对应 |
delete_flag | INTEGER | |
verify_flag | INTEGER | |
remark | TEXT | 备注名 |
remark_quan_pin | TEXT | 备注名全拼 |
remark_pin_yin_initial | TEXT | 备注名拼音缩写 |
nick_name | TEXT | 微信昵称 |
pin_yin_initial | TEXT | 微信昵称拼音缩写 |
quan_pin | TEXT | 微信昵称全拼 |
big_head_url | TEXT | 好友头像大图 |
small_head_url | TEXT | 好友头像小图 |
head_img_md5 | TEXT | 头像的md5,可以通过head_image.db查询对应的头像 |
chat_room_notify | INTEGER | "chat_room_notify" INTEGER |
is_in_chat_room | INTEGER | "is_in_chat_room" INTEGER |
description | TEXT | "description" TEXT |
extra_buffer | BLOB(protobuf) | 存储了好友的详细信息,性别、地区、签名等 |
chat_room_type | INTEGER | "chat_room_type" INTEGER |
相关信息
local_type说明:
1:通讯录好友(包括公众号、手动添加到通讯录的群聊)
2:未添加到通讯录的群聊
3:群中的陌生人
5:企业微信好友
6:群聊中的陌生企业微信好友
相关信息
flag说明:
flag要转化为二进制,每一位代表不同的含义
第7位:代表是否是星标好友
第12位: 代表是否是置顶好友
第17位:代表是否屏蔽对方的朋友圈
第24位:代表是否是仅聊天好友
extra_buffer
对应的 .proto
文件描述
textsyntax = "proto3"; package example; // 顶级消息定义 message ContactInfo { // varint 类型字段,根据数值范围选用 uint32 或 uint64 uint32 gender = 2; // 性别:1 男 2:女 0:未知 uint32 field3 = 3; string signature = 4; // 自助者天助!!! string country = 5; // CN string province = 6; // Shaanxi string city = 7; // Xi'an uint32 field8 = 8; string field9 = 9; uint32 field10 = 10; // 4294967295 uint32 field11 = 11; uint32 field12 = 12; // 修改后的嵌套消息,对应 JSON 中 field 14 的数据结构 MessageField14 phone_info = 14; string field15 = 15; uint32 field16 = 16; uint32 field17 = 17; uint32 field18 = 18; uint32 field19 = 19; string field20 = 20; string field21 = 21; uint32 field22 = 22; uint32 field23 = 23; uint32 field24 = 24; string field25 = 25; string field26 = 26; // 嵌套消息,朋友圈背景 MessageField27 moments_info = 27; string field28 = 28; string field29 = 29; string label_list = 30; string field31 = 31; string field32 = 32; // 嵌套消息,对应 JSON 中 field 33 的 length_delimited 数据 MessageField33 field33 = 33; string field34 = 34; string field35 = 35; MessageField36 field36 = 36; uint32 field37 = 37; uint32 field38 = 38; // 4294967295 } // 定义 field14 对应的嵌套消息 // 修改后的嵌套消息,用于 field 14 message MessageField14 { uint32 field1 = 1; // varint 类型字段,存储数字 repeated MessageField14_Result2 field2 = 2; // 这是一个 length_delimited 类型的字段,包含多个结果 } message MessageField14_Result2 { string phone_numer = 1; // string 类型字段,存储电话号码 } // 定义 field27 对应的嵌套消息 message MessageField27 { uint32 field1 = 1; string background_url = 2; // 图片 URL uint64 field3 = 3; // 14588734692813845087(大数,用 uint64) uint32 field4 = 4; // 6785 uint32 field5 = 5; // 4320 } // 定义 field33 对应的嵌套消息 message MessageField33 { string field1 = 1; } message MessageField36 { MessageField36_Result results = 1; } message MessageField36_Result { string field1 = 1; }
head_image 表
sqlCREATE TABLE head_image(
username TEXT PRIMARY KEY,
md5 TEXT,
image_buffer BLOB,
update_time INTEGER
)
名称 | 类型 | 说明 |
---|---|---|
username | INTEGER | wxid |
md5 | TEXT | 头像的md5 |
image_buffer | BLOB | 头像缩略图的二进制数据 |
update_time | INTEGER | 更新时间 |
Msg_md5表,每个联系人都单独建了一个表,命名规则:Msg_{md5(wxid)} , 对联系人的wxid用md5编码可以得到聊天记录所在的表
sqlCREATE TABLE Msg_xxxx(
local_id INTEGER PRIMARY KEY AUTOINCREMENT,
server_id INTEGER,
local_type INTEGER,
sort_seq INTEGER,
real_sender_id INTEGER,
create_time INTEGER,
status INTEGER,
upload_status INTEGER,
download_status INTEGER,
server_seq INTEGER,
origin_source INTEGER,
source TEXT,
message_content TEXT,
compress_content TEXT,
packed_info_data BLOB,
WCDB_CT_message_content INTEGER DEFAULT NULL,
WCDB_CT_source INTEGER DEFAULT NULL
)
名称 | 类型 | 说明 |
---|---|---|
local_id | INTEGER | 自增id |
server_id | INTEGER | 服务端的id,每条消息的唯一id |
local_type | INTEGER | 消息类型 |
sort_seq | INTEGER | 用于排序的字段,时间戳*100,然后从0计数,如果某一秒发送了多条消息,可以通过该字段区分先后顺序 |
real_sender_id | INTEGER | 发送者id,可以通过Name2Id表获取实际的发送者username |
create_time | INTEGER | 秒级时间戳 |
status | INTEGER | 消息状态 |
upload_status | INTEGER | 上传状态 |
download_status | INTEGER | 下载状态 |
server_SEQ | INTEGER | 服务端接收的顺序id |
origin_source | INTEGER | |
message_content | TEXT | 消息的实际内容,local_type是1时message_content是文本数据,其他类型都是 Zstandard 压缩后的xml二进制数据 |
compress_content | TEXT | 压缩后的内容 |
packed_info_data | BLOB | protobuf数据,存储了某些类型的聊天记录的信息(图片文件名、语音转文字记录、合并转发的聊天记录文件名) |
WCD B_CT_message_content | INTEGER | |
WCD B_CT_source | INTEGER |
local_type字段说明
local_type | 类型 | 说明 |
---|---|---|
1 | 文本 | |
3 | 图片 | |
34 | 语音 | |
42 | 好友名片 | 包含好友名片和公众号名片 |
43 | 视频 | |
47 | 表情包 | |
48 | 发送的位置信息 | |
50 | 音视频通话 | |
66 | 企业微信好友名片 | |
10000 | 系统消息 | 撤销信息,进群通知等 |
25769803825 | 文件 | |
21474836529 | 分享链接 | |
292057776177 | 分享链接 | |
4294967345 | 分享链接 | |
326417514545 | 分享链接 | |
17179869233 | 分享链接 | |
244813135921 | 引用消息 | |
81604378673 | 合并转发的聊天记录 | |
8594229559345 | 红包 | |
219043332145 | 视频号 | |
141733920817 | 小程序 | |
154618822705 | 小程序 | |
103079215153 | 转发的收藏笔记 | |
266287972401 | 拍一拍 | |
12884901937 | 音乐分享 |
packed_info_data 字段存储了某些聊天记录的附加信息,例如语音转文字,图片名(2025年3月微信测试版修改了img命名方式才有了这个东西),合并转发的聊天记录文件夹名
textsyntax = "proto3"; // 2025年3月微信测试版修改了img命名方式才有了这个东西 message PackedInfoDataImg { int32 field1 = 1; int32 field2 = 2; string filename = 3; }
textsyntax = "proto3"; package example; // 顶级消息定义 message PackedInfoData { // varint 类型字段,根据数值范围选用 uint32 或 uint64 uint32 field1 = 1; uint32 field2 = 2; MessageField5 info = 5; } // 定义 field14 对应的嵌套消息 // 修改后的嵌套消息,用于 field 14 message MessageField5 { uint32 field1 = 1; string audioTxt = 2; // 语音转文字结果 }
textsyntax = "proto3"; message PackedInfoData { int32 field1 = 1; int32 field2 = 2; NestedMessage field7 = 7; AnotherNestedMessage info = 9; } message NestedMessage { SubMessage1 field1 = 1; SubMessage2 field2 = 2; string field3 = 3; } message SubMessage1 { int32 field1 = 1; string field2 = 2; } message SubMessage2 { string field1 = 1; string field2 = 2; string field3 = 3; } message AnotherNestedMessage { string dir = 1; }
公众号消息记录,结构同message_x.db
sqlCREATE TABLE VoiceInfo(
chat_name_id INTEGER,
create_time INTEGER,
local_id INTEGER,
svr_id INTEGER,
voice_data BLOB,
data_index TEXT DEFAULT '0'
)
名称 | 类型 | 说明 |
---|---|---|
chat_name_id | INTEGER | 结合Name2Id表可以查出发送者的wxid |
create_time | INTEGER | 创建秒级时间戳 |
local_id | INTEGER | 对应message数据库的local_id |
svr_id | INTEGER | 对应message数据库的server_id |
voice_data | BLOB | 语音silk的二进制数据 |
data_index | TEXT |
video_hardlink_info_v3
sqlCREATE TABLE video_hardlink_info_v3(
md5_hash INTEGER,
md5 TEXT, type INTEGER,
file_name TEXT,
file_size INTEGER,
modify_time INTEGER,
dir1 INTEGER,
dir2 INTEGER,
_rowid_ INTEGER PRIMARY KEY ASC,
extra_buffer BLOB
)
名称 | 类型 | 说明 |
---|---|---|
md5hash | INTEGER | |
md5 | TEXT | 文件的md5 |
type | INTEGER | 文件类型:3:正常的文件,5:合并转发的聊天记录里的文件 |
file_name | INTEGER | 文件名 |
file_size | INTEGER | 文件大小(B) |
modeify_time | INTEGER | 修改时间 |
dir1 | INTEGER | 文件夹1的id,dir2id表对应第id行usernamez字段为dir1的文件夹名 |
dir2 | INTEGER | 文件夹2的id,type为3时dir2值为0,只需要从msg/video/dir1文件夹中找就行了 |
_rowid | INTEGER | |
extra_buffer | BLOB | protocbuf数据,存储另一个文件夹dir3,type为5时有效。.proto 文件描述:syntax = "proto3"; package example; message FileInfoData { string dir3 = 1; uint32 file_size = 2; } |
sqlselect file_size,type,file_name,dir2id.username,_rowid_,modify_time
from video_hardlink_info_v3
join dir2id on dir2id.rowid = dir1
where md5=10086;
合并转发的聊天记录
9e20f478899dc29eb19741386f9343c8
是wxid的md5加密,409af365664e0c0d
是上述 extra_buffer
字段里的 dir3
文件夹最后的5代表的该文件是合并转发的聊天记录第5条消息,如果存在嵌套的合并转发的聊天记录,则依次递归的添加上一层的文件名后缀,例如:合并转发的聊天记录有两层
plain0:文件(文件夹名为0) 1:图片 (文件名为1) 2:合并转发的聊天记录 0:文件(文件夹名为2_0) 1:图片(文件名为2_1) 2:视频(文件名为2_2.mp4)
pythondef parser_merged(merged_messages, level):
for index, inner_msg in enumerate(merged_messages):
wxid_md5 = hashlib.md5(username.encode("utf-8")).hexdigest()
if inner_msg.type == MessageType.Image:
inner_msg.path = os.path.join(
'msg', 'attach', wxid_md5, month,
'Rec', dir0, 'Img', f"{level}{'_' if level else ''}{index}")
inner_msg.thumb_path = os.path.join(
'msg', 'attach', wxid_md5, month,
'Rec', dir0, 'Img', f"{level}{'_' if level else ''}{index}_t")
elif inner_msg.type == MessageType.Video:
inner_msg.path = os.path.join(
'msg', 'attach',wxid_md5,month,
'Rec', dir0, 'V', f"{level}{'_' if level else ''}{index}.mp4")
elif inner_msg.type == MessageType.File:
inner_msg.path = os.path.join(
'msg', 'attach',wxid_md5,month,
'Rec', dir0, 'F', f"{level}{'_' if level else ''}{index}",
inner_msg.file_name)
elif inner_msg.type == MessageType.MergedMessages:
parser_merged(
inner_msg.messages,
f'{index}' if not level else f'{level}_{index}'
)
parser_merged(msg.messages, '')
session.db 存储了微信聊天界面显示的会话窗口信息
sqlCREATE TABLE SessionTable(
username TEXT PRIMARY KEY,
type INTEGER,
unread_count INTEGER,
unread_first_msg_srv_id INTEGER,
is_hidden INTEGER,
summary TEXT,
draft TEXT,
status INTEGER,
last_timestamp INTEGER,
sort_timestamp INTEGER,
last_clear_unread_timestamp INTEGER,
last_msg_locald_id INTEGER,
last_msg_type INTEGER,
last_msg_sub_type INTEGER,
last_msg_sender TEXT,
last_sender_display_name TEXT,
last_msg_ext_type INTEGER
)
emoticon.db 存储了用户收藏的表情包信息
sqlCREATE TABLE kNonStoreEmoticonTable(
type INTEGER,
md5 TEXT,
caption TEXT,
product_id TEXT,
aes_key TEXT,
thumb_url TEXT,
tp_url TEXT,
auth_key TEXT,
cdn_url TEXT,
extern_url TEXT,
extern_md5 TEXT,
encrypt_url TEXT
)
名称 | 类型 | 说明 |
---|---|---|
type | INTEGER | |
md5 | TEXT | 表情包的md5,可以从message数据库里获取 |
caption | TEXT | |
product_id | TEXT | |
aes_key | TEXT | |
thumb_url | TEXT | 表情包缩略图url(可以直接引用) |
cdn_url | TEXT | 表情包url(可以直接引用) |
extern_url | TEXT | |
extern_md5 | TEXT | |
encrypt_url | TEXT |
SnsTimeLine 表存储了每条朋友圈的详细信息
sqlCREATE TABLE SnsTimeLine(
tid INTEGER PRIMARY KEY DESC,
user_name TEXT,
content TEXT
)
本文作者:司小远
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!