微信聊天记录导出工具开发全记录(开发成功!v1.0)
前言
两个月前,我遇到了一个需求:把微信里的聊天记录导出来。
市面上的方案要么过时了,要么只支持旧版本。微信 4.1.x 的架构发生了很大变化,之前的工具基本都不能用了。于是我决定自己动手,从零开始逆向分析微信 Windows 版的聊天消息存储机制。
这篇文章记录了从一片空白到做出完整导出工具的整个过程,既是心得总结,也算是一份技术教程。
故事的起点
最初我面临的问题很简单:微信的聊天记录存在哪里?
传统认知里,聊天记录应该存在数据库里。微信早期版本确实如此——用 SQLite 存消息。但 4.1.x 版本之后,情况完全不同了。
第一阶段:地毯式搜索
第一周我干了件非常笨的事——全盘搜索。把电脑上所有文件按修改时间排序,逐个检查微信相关目录下的每一个文件。结果一无所获。
接着上 ProcMon(文件操作监控工具),过滤微信进程,设置几十个文件路径规则,监控微信的所有文件读写操作。翻页、搜索、切换会话……观察到的文件访问全都是配置类的,没有消息数据。
结论 1:微信 4.1.x 的聊天消息不在磁盘上。
这个发现让我很意外。消息不落地,意味着它们只存在于内存中。
第二阶段:攻入进程内存
既然在内存里,那就直接读内存。
使用 Python 的 pymem 库附加到 Weixin.exe 进程,扫描堆内存。通过消息特征的字节模式(02 05 09 01 01 04)找到了大量匹配。然后验证:翻页后刷新匹配结果,数量和内容都变了——确认消息确实在进程堆内存中,而且是实时变化的。
首次”导出”就拿到了 57 条消息,虽然有很多乱码和噪声,但方向对了。
当时的导出方式非常简单粗暴:全堆扫描特征字节 → 提取附近文本 → 拼成 JSON。
打开 Ghidra:从黑盒到白盒
内存扫描虽然可行,但很不优雅。我决定用 Ghidra(NSA 开源的逆向工具)反编译 Weixin.dll,理解消息系统的内部结构。
Weixin.dll 有 175MB,Ghidra 分析就跑了接近两小时。但结果是值得的。
找到 GetPagedMessages
通过搜索字符串特征,找到了关键函数 GetPagedMessages。从名字看,这就是微信分页加载消息的核心函数。
在 4.1.9.56 版本中它位于偏移 0x016ade70,但微信升级到 4.1.10.29 后函数地址变了。我需要在新版本中重新定位它。
最终在 Ghidra 中找到了新地址,并构建了它的 Call Tree——这个函数内部调用了 28 个子函数,把它们的功能一个个梳理清楚:
- 3 个函数负责构建查询参数(拼接 JSON:orientation、sort_order、types、sender……)
- 1 个函数负责以步长 0x2d8 遍历消息数组
- 1 个函数是 CheckMessageLiveStatus(消息状态检查)
- 多个函数负责哈希表缓存管理
关键突破:0x2d8 消息结构和 Creator
在分析过程中发现,所有消息操作都以步长 0x2d8(728 字节) 访问数组。这说明微信内部用固定大小的结构体管理消息,每个消息节点恰好 728 字节。
通过分析字段拷贝函数(FUN_181482400),还原出了消息结构体的字段布局:
| 偏移 | 字段 | 说明 |
|---|---|---|
| +0x000 | vtable | 虚表指针 |
| +0x120 | receiver | 聊天对象 ID |
| +0x268 | content_ptr | 消息内容指针 |
| +0x288 | content_ptr2 | 备用内容指针 |
然后问了那个终极问题:这 728 字节的消息节点是谁创建的?
花了将近一周,沿着调用链向上追溯:GetPagedMessages → GetMessageListBySvrIds → 消息管理器 → 网络调度器……最终在 Weixin.dll 的一个角落里找到了 FUN_181bc3b00。
反编译确认,这个函数内部调用了 6 次 alloc(0x2d8),分配后用默认模板初始化,然后插入消息容器。这就是 MessageNode 的工厂函数。
从逆向到产品
理解了消息机制后,开发导出工具就是工程问题了。
工具的技术路线经历了三个阶段:
阶段一:内存扫描(M1-M38)
直接扫描 Weixin.exe 进程堆,通过特征字节定位消息,原地提取文本。优点是简单直接,缺点是数据不完整、准确率只有 95%。
阶段二:Ghidra 驱动 Hook(M39-M50)
利用逆向得到的函数地址,用 Frida 做动态 Hook,在消息到达时实时捕获。准确率更高,但操作复杂。
阶段三:数据库解密(M73-M112)
最终方案——微信的聊天消息虽然在内存中处理,但本地有一个加密的数据库副本,包含全部聊天记录(包括离线消息)。
通过 Frida Hook 捕获微信进程中的解密密钥,然后用 WCDB(微信自研数据库引擎)直接读取解密后的数据库,拿到完整的消息数据、会话列表、联系人信息。
这就是最终工具的核心原理。
成果
项目历时近两个月,累计 100+ 轮实验,最终产出两个 GitHub 仓库:
研究仓库
包含完整的研究过程、Ghidra 分析报告、实验脚本、反编译数据、数据库解密方案等。适合对技术细节感兴趣的读者。
🔗 github.com/Ray0612/WeChat-v4-export-research
导出工具
支持一键获取解密密钥、连接数据库、浏览会话、导出聊天记录。开箱即用,无需配置。
🔗 github.com/Ray0612/WeChat-Export-Tool
心得
复盘这轮开发,几点体会:
逆向工程是耐心活 —— 一周没有任何进展是常态。一个函数需要反复对比、验证、推翻重来。Ghidra 的分析经常是几十个函数互相调用,需要耐心梳理。
静态分析 + 动态验证是黄金组合 —— Ghidra 反编译给出”可能”的答案,Frida Hook 验证是否正确。光靠静态分析容易走偏,光靠动态调试又缺乏全局视野。
微信的消息架构设计得很巧妙 —— 全部在内存中处理,通过网络与服务器同步,本地只留加密缓存。这种设计兼顾了性能和安全,但也意味着客户端几乎不可能直接读消息。
社区的力量 —— 项目参考了大量前人成果(wx_key、weflow、WeChatMsg 等)。逆向工程站在巨人的肩膀上,每个工具都建立在前人的研究之上。
如果你对这个项目感兴趣,欢迎 star、fork、提 issue。如果有兴趣一起开发,也欢迎联系。