简介:微信聊天记录提取与分析工具是一款面向程序员、数据分析学习者和课程作业开发者的含完整源代码的实用软件,可提取微信聊天记录并导出为 HTML、Word、CSV 等格式,同时生成年度聊天报告和沟通行为统计,用于个人记录回顾、沟通习惯分析或教学实践。资源包为 ZIP 压缩格式,共 309 个文件,约 24.96MB,主要包含 Python 源码、前端页面(html/svg/png)、数据配置(json/proto)、项目说明(md)及依赖清单等,并集成 ffmpeg.exe 辅助多媒体处理,目录结构清晰,便于直接阅读或二次开发。压缩包内含完整项目模板与示例页面(如 home、charts、wordcloud 等),可帮助理解从聊天数据清洗、统计分析、可视化到 Web 展示的完整实现流程,适合课程作业或入门练手。目前已有 249 人学习/下载,需要说明的是,该资源来自网络分享,仅限学习交流使用,请勿用于商业,使用时要重视隐私保护与相关版权规定。
1. 微信聊天记录提取与分析:先拿到 EnMicroMsg.db,再谈挖掘价值
换了手机、需要做工作审计、想拿自己的聊天记录当语料去精调一个模型——这三类诉求一旦出现,你就会发现微信的聊天记录不是“设置里点导出”就能拿出来的。安卓端文字、图片、语音几乎都存在 /data/data/com.tencent.mm 下的一个 SQLCipher 加密数据库里,图片是被异或处理过的 dat 文件,iOS 端又是另一套备份逻辑。这篇笔记围绕“微信聊天记录提取与分析工具”这个方向,把存储位置、解密思路、导出脚本、图片还原和后续分析串成一条能落地的路线,适合正在做数据迁移、取证分析或 LLM 语料整理的人直接照着操作。下文所有命令都在你自有设备上跑,别拿别人的聊天记录来测。
2. 微信聊天记录存储形态:数据库、dat 图片与表结构,三块硬骨头
2.1 安卓端存储路径与微信数据库解密的基本认知
先记住一个路径:/data/data/com.tencent.mm/MicroMsg/[32位随机hash]/EnMicroMsg.db。这个随机 hash 目录是微信为每个账号生成的,不同手机、不同微信账号都不一样,同一台设备上切换账号后也会生成新的目录。所以拿到 root 权限之后,第一件事不是盲目翻文件,而是先列出 MicroMsg 下所有目录,找到体积最大的那个 EnMicroMsg.db,那才是当前账号的主数据库。如果一台设备上登过多个账号,MicroMsg 下会有一堆不同 hash 的目录,普通用户根本分不清哪个对应哪个账号,只能看目录修改时间或数据库大小来猜。
这个数据库不是用普通 SQLite 打开的,微信在 Android 端默认启用了 SQLCipher 加密。要解开它,常见做法是拿到两个信息:设备 IMEI 和微信内部 uin。老版本微信的数据库密钥算法是 MD5(IMEI + uin) 取前 7 位小写字符串;新版本为了规避风险,密钥里不再只依赖 IMEI,而是加入了 Android ID、厂商信息等参数。你会在网上看到各种“一键解密”工具,它们大多还是基于这套旧算法加暴力枚举新参数组合。我的建议是别在一棵树上吊死:先按老算法试,解不开就去看 CompatibleInfo.cfg 或 system_config_prefs.xml 里的 uin,再考虑提取 Android ID 做扩展。
这里还有个容易被忽略的点:数据库文件本身可能被拆分。微信在聊天记录膨胀后,会把部分历史消息放到同目录下的其他 db 文件,或者生成以“.db-wal”“.db-shm”结尾的 SQLite 预写日志文件。如果你直接复制正在运行的微信目录,数据库可能处于 checkpoint 未完成状态,明文库里会缺最近几条消息。血泪经验是:提取前先在微信里退出账号,或者至少杀掉进程,再 adb pull。看到 wal 文件别忽略,它里面往往躺着最后几小时的聊天记录,处理时可以用 sqlite3 的 recovery 手段合并。
2.2 微信 dat 图片:异或加密下的文件头还原原理
聊天里的图片和缩略图,在安卓端缓存目录里大多存成 .dat 文件,具体在 MicroMsg 下的 /sdcard/Android/data/com.tencent.mm/MicroMsg/[hash]/ 或内部存储的 /data/data/com.tencent.mm/MicroMsg/[hash]/ 里。微信的图片加密不是 AES 那种重加密,而是单字节异或——一个固定 key 对文件每个字节做 XOR。正因为 key 是单字节,文件头特征才能被用来反推。这种加密方式与其说是防破解,不如说是防止用户直接拿到图片文件,毕竟单字节异或的强度几乎为零,懂点十六进制的人几分钟就能拆穿。
JPEG 文件的起始字节固定是 FF D8 FF,PNG 固定是 89 50 4E 47。把 dat 文件前两个字节分别与对应的明文头字节做异或,如果两次异或结果相等,那个值就是密钥。举个例子,某 dat 文件开头是 5C 5F,jpg 头是 FF D8,那么 5C XOR FF = A3,5F XOR D8 = 87,两次结果不等说明它可能是 png 或者其他编码;换成 png 头 89 50 再试,89 XOR 5C = D5,50 XOR 5F = 0F,还是不等,那就说明这个文件根本不在预期格式里。只有当推测出的 key 在两次计算中一致时,才能确定某类格式成立。
网上流传的“微信 dat 转 jpg 软件”多数就是把这个异或逻辑封装成了批量工具。自己写脚本的优势是能控制边界:比如微信长图、gif、视频缩略图,文件头可能是不同格式;还有部分 dat 文件是视频帧,转出来的不是 jpg。所以做批量还原之前,先抓几个样本人工验证,别上来就对整个目录跑。你在搜索引擎里找到的开源脚本大多只写了一个硬编码 key,换个微信版本就可能失效,关键还是要理解探测 key 的原理。
2.3 message 表、rcontact 表和 chatroom 表:字段怎么读
解开数据库之后,正常情况下能看到几十张表,但重量级的就三张:message 存聊天内容,rcontact 存联系人,chatroom 存群信息。以我常处理的真机库为例,message 表的关键字段大致如下:
| 字段 | 含义 | 典型值 |
|---|---|---|
| msgId | 消息自增ID | 1234567890 |
| type | 消息类型 | 1文本,3图片,34语音,43视频,47表情,49文件/链接,50语音通话 |
| isSend | 是否自己发送 | 0接收,1发送 |
| createTime | 消息时间戳 | 1610000000 |
| talker | 会话对象 | 联系人的username或群id |
| content | 消息正文 | 文本内容或XML/文件路径 |
| imgPath | 本地图片路径 | 相对路径如 xxx.dat |
type 字段是读库时最容易翻车的地方。微信几乎每几个版本就会新增类型,比如小程序卡片是 49 的一种子类型,转账和红包在 content 里是通过 XML 表达的。直接按 type 值硬编码判断,大概率会遇到“文本内容被存成 XML”或者“图片消息 content 为空”的尴尬。更稳的做法是先用 SELECT DISTINCT type 统计出当前库里的所有类型,再逐类抽样看 content 样例,最后再写业务逻辑。
rcontact 表里 username 是主键,nickname 是昵称,需要和 message.talker 关联才能把聊天记录里的一串加密 id 转成可读联系人。注意很多用户会在微信设置里改“备注名”,这个备注名存在 rcontact 的 conRemark 字段里,取名字的时候要优先看 conRemark,否则你会把同一个人统计成几个不同的人。chatroom 表则记录了群成员、群公告等,chatroomname 需要去 rcontact 表里再关联一次才能拿到群名。
这一章没有给出完整代码,是因为我习惯先摸清库的底细再动手。如果连表结构与类型都没梳理清楚,后面所有脚本都在黑匣子上盖房子。
3. 用 Python 提取并解密聊天记录:SQLCipher 解密与 message 表导出
3.1 准备工作:root 备份、IMEI 与 uin 的获取
在开始解密之前,先要把数据库文件完整拿出来。已经 root 的安卓机最直接:
adb shell "su -c 'cp /data/data/com.tencent.mm/MicroMsg/*/EnMicroMsg.db /sdcard/Download/EnMicroMsg.db'" adb pull /sdcard/Download/EnMicroMsg.db ./注意上面用了通配符 *,如果存在多个 hash 目录,这条命令只会复制匹配到的第一个目录;更严谨的做法是先列出目录再逐个处理。没有 root 的设备,可用 adb backup 方式导出应用数据,但微信对新版本做了备份限制,拿到的是一个加密的 .ab 文件,还要用 Android Backup Extractor 解一次。移动端取证工具里常见的“安卓恢复误删微信聊天记录以及缓存”操作,本质也是在这些内部存储目录里找残留的 db 或 wal 文件,越早冻结设备成功率越高。
获取 uin 的老办法是读 MicroMsg 目录下的 system_config_prefs.xml,里面会有形如 1234567890 的内容。新版微信将部分配置迁移到数据库内的 user_info 表,但 CompatibleInfo.cfg 里的 imei 字符串与 uin 仍是解密的关键输入。IMEI 在 Android 10 以上普通应用拿不到了,不过对已 root 设备来说,执行下面命令即可:
adb shell "su -c 'settings get secure android_id'"android_id 可当作新版本密钥算法的一个替代参数。你需要在尝试时组合 IMEI / android_id / 厂商序列号,多组合试探,这一步没法完全自动化,因为厂商差异化逻辑没有公开文档。我一般会把候选密钥写进一个文本文件,然后用脚本逐个尝试连接数据库,哪个能打开就是哪个,比手动一次一次敲 sqlcipher 效率高得多。
3.2 用 sqlcipher 解密 EnMicroMsg.db 并导出为明文库
拿到 db 和密钥之后,SQLCipher 命令行是最快的验证方式。Linux 或 macOS 装好 sqlcipher 后执行:
sqlcipher EnMicroMsg.db "PRAGMA key='<你的密钥>'; ATTACH DATABASE 'plain.db' AS plain KEY ''; SELECT sqlcipher_export('plain'); DETACH DATABASE plain;"这句命令做了四件事:打开加密库,挂载一个空的明文库 plain,调用 sqlcipher_export 把整个加密库的内容复制到明文库,最后解除挂载。如果不使用 sqlcipher_export,而是直接在 SQLCipher 会话里把每张表 COPY,大概率会漏掉索引和触发器;导出到 plain.db 后,后续就能直接用 sqlite3 或 Python sqlite3 模块读取。
如果 PRAGMA key 执行后报 “file is not a database”,先怀疑密钥拼写。老算法密钥是 MD5(IMEI + uin) 的前 7 位,注意 MD5 结果是 32 位十六进制,取前 7 位而不是 7 个字节。很多脚本翻车都栽在这个地方,比如把 IMEI 写成带横杠格式、uin 写错位、或者把前 7 位理解成前 7 个十六进制数字,这三种情况会得到完全不同的密钥。成功导出后,建议顺手做一次完整性检查:
sqlite3 plain.db "PRAGMA integrity_check;"返回 ok 才能说明这次解密是可信的。如果返回 missing index 之类,先别急着分析,回头检查源文件是否在微信运行时被拷出过。还有一个容易忽略的细节:SQLCipher 导出时不会自动处理 wal 文件里的内容,如果你在运行时复制了主 db,导出后很可能缺最近的消息,这时需要把 wal 文件也一起拷出来,放到同一目录,再打开数据库,SQLite 会自动回放。
3.3 Python 读取 message 表并输出带中文字段的 CSV
明文库拿到后,我用下面的脚本把和某个会话的聊天记录拉出来,输出成适合后续统计的 CSV。这个脚本也是我本地“微信聊天记录提取与分析工具”的核心模块之一,参数化了会话对象和输出路径:
import sqlite3 import csv DB_PATH = "plain.db" TALKER = "wxid_xxxxxxxx" # 要导出的联系人/群 id OUT_CSV = "chat_export.csv" conn = sqlite3.connect(DB_PATH) cur = conn.cursor() cur.execute(""" SELECT createTime, isSend, type, content FROM message WHERE talker = ? ORDER BY createTime ASC """, (TALKER,)) with open(OUT_CSV, "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["time", "is_send", "type", "content_raw"]) for row in cur.fetchall(): ts, is_send, msg_type, content = row # 时间戳单位是秒,转成可读时间 time_str = __import__("datetime").datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S") writer.writerow([time_str, is_send, msg_type, content]) conn.close() print("done:", OUT_CSV)这里有几个参数值得解释:TALKER 是 talker 字段里的原始 id,我在实际操作里是从数据库先查出所有 distinct talker,再人工确认对应联系人,而不是靠猜;encoding 用 utf-8-sig 是为了让 Excel 打开不乱码;content_raw 保留原文,不要因为想做分析就顺手截断。时间戳转换里,微信存储的是秒级 Unix 时间戳,个别库会出现毫秒级或带时区偏移,你需要先查一条已知消息对比,再决定是否除以 1000。
如果你面对的库非常大(几十万条消息),直接 fetchall 会吃很多内存。更好的是用 cur.execute 后边迭代游标边写 CSV,或者加 LIMIT 分批导出。事后想恢复误删的聊天记录时,这类明文导出也能作为半成品备份材料。字段名里的 time 列我建议直接用 yyyy-MM-dd HH:mm:ss 格式,不要用原始时间戳,免得以后每次分析都要转换。
4. 微信 dat 转 jpg 的避坑排查:异或还原脚本、误判与修复
4.1 识别 dat 文件加密特征:用文件头判断 XOR 密钥
进入图片还原之前,先写一个探测脚本,确认 dat 文件到底是不是预期的图片格式、密钥是多少。下面的脚本读取一个文件的前 4 个字节,与常见文件头逐格式比对:
import sys dat_path = sys.argv[1] headers = { "jpg": bytes([0xFF, 0xD8, 0xFF]), "png": bytes([0x89, 0x50, 0x4E]), "gif": bytes([0x47, 0x49, 0x46]), } with open(dat_path, "rb") as f: head = f.read(4) for fmt, h in headers.items(): key1 = head[0] ^ h[0] key2 = head[1] ^ h[1] key3 = head[2] ^ h[2] if key1 == key2 == key3: print(f"format={fmt}, xor_key=0x{key1:02x}") break else: print("unknown format or not simple xor")逻辑很简单:对每种候选格式,用明文头三个字节分别与 dat 前三个字节异或,如果三次结果一致,说明 key 成立。为什么取三个字节?两个字节偶尔会因为 JPEG 头里 FF D8 之后跟着的字节也是 FF 而产生巧合,三个字节能显著降低误判概率。单字节异或加密的黑匣子在这一步基本就被拆穿了。如果探测脚本输出的 xor_key 是 0x00,说明这个 dat 文件本身没有加密,直接改后缀就行;反之若所有候选格式都不匹配,可能是视频抽帧文件或应用了多字节异或,这类直接放弃,别浪费时间。
4.2 批量还原脚本:从单张到整个目录逐字节异或
确认密钥后,批量还原就很简单了。先写一个单文件还原函数,再扩展到整个目录。注意微信 dat 文件可能很大(几 MB 的长图),要用块读取而不是一次性读入:
import os import sys XOR_KEY = 0xA3 # 从探测脚本得到 SRC_DIR = "imgs_dat" DST_DIR = "imgs_restored" os.makedirs(DST_DIR, exist_ok=True) def restore_file(src_path, dst_path): with open(src_path, "rb") as fin, open(dst_path, "wb") as fout: while True: chunk = fin.read(64 * 1024) if not chunk: break # 对每个字节做异或 transformed = bytes([b ^ XOR_KEY for b in chunk]) fout.write(transformed) for name in os.listdir(SRC_DIR): if not name.lower().endswith(".dat"): continue src = os.path.join(SRC_DIR, name) dst = os.path.join(DST_DIR, os.path.splitext(name)[0] + ".jpg") restore_file(src, dst) print("restored:", len(os.listdir(DST_DIR)))代码里的 XOR_KEY 是写死的,实际运用中建议把 key 也自动探测出来再传入,避免人工看图改参数。块大小 64KB 兼顾了内存和速度,几十张 5MB 的图片跑下来也就一两秒。还原后不要急着删 dat 原文件:万一手滑把 key 判断错了,原文件还在就有后悔药。我通常先还原单个文件用图片查看器打开确认,再批量处理。你从网上下载的“微信 dat 转 jpg 软件”大多就是这个逻辑,只是包了个界面,参数不可调,遇到分片或多格式时就无能为力。
4.3 常见坑:还原后花屏、0 字节、数量对不上、误删后悔药
这里列几个我在处理 dat 文件时反复遇见的状况,每条按现象、原因、解决的顺序讲清楚。
现象一:还原后图片能打开但中间有几行花屏或后半部分色块。原因:微信对超过一定大小的图片做了切片存储,连续文件里夹着其他编码的头尾;也可能 dat 文件本身就是多个图片拼接,单字节异或无法正确还原分片边界。解决:先看文件实际大小,超过微信限制的长图优先怀疑是分片,可用十六进制编辑器读取文件头确认起始格式,再按分片边界重新还原。
现象二:还原后文件为 0 字节。原因:源 dat 文件本身就是 0 字节,或者被微信删除后残留了空文件占位。解决:跳过这类文件并在日志里记录文件名,不要中断整个批次。删除文件的误删场景下,空文件说明缓存已被回收,要恢复得去文件系统层找未覆写的扇区,这已经不是普通脚本能解决的问题,建议尽早冻结设备并做镜像。
现象三:还原出的 jpg 数量比聊天记录里的图片消息少。原因:有大量缩略图、头像、表情存在别的目录,并非每条图片消息都会立刻生成完整 dat 文件;另外 iOS 端图片存储方式与安卓不同,这套脚本只看安卓缓存目录。解决:先统计聊天记录里 type=3 的消息数量,再和 dat 目录文件数比对,如果差异在缩略图范围内可忽略;差异过大要检查是否选错 hash 目录。
现象四:误把非 dat 文件也当成加密图片处理,还原出一堆不可读文件。原因:目录里混入了微信其他功能生成的 dat 文件,比如二维码缓存、视频帧。解决:处理前对文件扩展名做白名单过滤,同时在批量跑之前先做文件头探测。血泪经验告诉我,批量脚本永远要加 --dry-run 参数,先输出将处理的文件列表给人确认再真正执行,这个习惯能挡住大多数手滑。
5. 聊天记录分析落地方案:从时间线统计到 AI 聊天记录模型精调
5.1 分析前的数据清洗:类型字段、时间戳、撤回消息的处理
拿到 CSV 或直接连 plain.db 开始分析前,第一件事是把脏数据隔离。我在上一章导出了 content_raw 和 type,这里需要专门处理三类:撤回消息、系统通知、XML 消息。撤回消息在微信库里通常仍保留原内容,但 type 或 content 里带有特殊标记;系统通知如“你已添加了XX,现在可以开始聊天了”不是有效语料;XML 消息包含小程序卡片、链接、转账等,需要按需求过滤或解析。
一个简单的清洗脚本可以做基础过滤:
import sqlite3 conn = sqlite3.connect("plain.db") cur = conn.cursor() # 只看文本消息,排除系统/图片/视频 cur.execute(""" SELECT createTime, isSend, talker, content FROM message WHERE type = 1 AND content IS NOT NULL AND content NOT LIKE '%sysmsg%' AND length(content) > 0 ORDER BY createTime ASC """)注意 type=1 只是文本,但微信会把一部分文件传输消息也标记为 type=1 且 content 里存了 XML,所以后续仍要人工抽样。撤回消息在普通库里很少直接暴露“撤回”标记,更多表现为时间戳异常(撤回操作会产生一条新的系统消息)。清洗目标是让统计分析不把系统消息当成对话内容,否则执行出来的关键词统计会混入大量客服话术。
5.2 用 SQL 做对话频次、活跃时段与关键词统计
明文库里做统计,SQL 是最顺手的手段,不需要额外引入 pandas。几个常用统计查询:
-- 每日消息量 SELECT date(createTime, 'unixepoch', 'localtime') AS day, COUNT(*) FROM message WHERE talker = 'wxid_xxx' GROUP BY day ORDER BY day; -- 24小时活跃分布 SELECT strftime('%H', createTime, 'unixepoch', 'localtime') AS hour, COUNT(*) FROM message GROUP BY hour ORDER BY hour; -- 双方发言占比 SELECT isSend, COUNT(*) FROM message GROUP BY isSend;第一句里 date 函数把秒级时间戳转成日期,localtime 保证结果和手机本地时间一致。如果你发现统计结果整体偏移 8 小时,多半是忘了加 localtime 参数;微信存的是 UTC 时间,只在展示时转本地时区。聊天记录分析里,时间戳处理是重灾区,我一般先用一条已知消息做基准校验,确认无误后再批量跑。关键词统计建议先导出文本后在外面做,SQL 里 LIKE 只能做子串匹配,不适合中文分词。可以把 CSV 导入到支持全文检索的 sqlite FTS5,或者用 jieba 在 Python 里做词频,重点不是算法多高级,而是先把“谁、什么时候、聊了什么”这三个维度拉清楚。
5.3 把聊天记录做成语料:为 ai 聊天记录模型精调 LLM 的格式转换
如果你的目标是“使用聊天记录模型精调 llm”,那么前面导出和清洗都在为这一步铺路。精调对话模型需要把原始聊天记录转换成 instruction/input/output 或对话轮次结构。一个人和另一个人的聊天是双向的,每句话都可以看成“上一句输入,下一句输出”,但直接按时间顺序硬截断会造成语义断裂。常见做法是分场景切分:先按会话主题聚类,再把连续 8-16 轮对话组成一个 sample。
import json import csv samples = [] max_turns = 8 conversation = [] with open("chat_export.csv", "r", encoding="utf-8-sig") as f: for row in csv.DictReader(f): msg = { "from": "user" if row["is_send"] == "1" else "assistant", "content": row["content_raw"].strip(), } if not msg["content"]: continue conversation.append(msg) if len(conversation) >= max_turns * 2: samples.append(conversation) conversation = [] break # 真实场景要按时间窗口滚动切分这段只是示意切片逻辑,真正落地时不要按固定轮数硬切,而是根据时间间隔(比如间隔超过 10 分钟就断开)来分割对话块。每种角色映射也要想清楚:把自己映射成 user 还是 assistant,会强烈影响精调后的模型行为。如果只是做“根据聊天记录生成的 AI 聊天记录”风格迁移,可以混合两个方向,但要在样本里加角色标识。ai 聊天记录这个方向的落地难点不在模型,而在数据质量——聊天记录里充满了口语、错别字、表情描述和名片分享,不做清洗直接训练,模型学到的噪音比语义多。
注意:隐私是红线。自己设备上的数据自用可以,拿去跑公开训练集或上传到第三方 API 前必须做去标识化处理,把手机号、地址、身份证号替换成占位符。这条线守不住,后面所有落地都是给自己挖坑。
5.4 可视化展示:微信小程序或其他报表的呈现路径
分析做完,总要给人看。如果你想把“聊天记录分析报告”做成一个可交互页面,微信小程序开发是个低成本选择:小程序里用 web-view 渲染 echarts,后端或本地生成 JSON 数据文件,前端拉取后展示消息量趋势、对话热力图、关键词云。但要注意,小程序审核对用户隐私数据非常敏感,你不能把聊天记录明文放在云开发数据库里,更不要图省事上传到公网。
离线路线更稳妥:用 Python 生成一份静态 HTML 报告,内含趋势图和词云,直接浏览器打开。常见做法是把 SQL 统计结果写成 JSON,再用 ECharts 的 CDN 渲染。整个过程只需要 pandas + pyecharts,不需要额外搭服务。可视化不是这章核心,核心是把“提取-解密-清洗-统计”这条链跑通,展示只是收尾工作。真正投入精力之前,先想清楚你要回答的问题:是看沟通频率,还是看关键话题,还是做个性化 AI 助手。问题定义清楚了,可视化形式自然就定了。
6. 验证你的提取结果:抽样比对、字段检查与时间戳校准
到底有没有漏消息?提取出的内容是不是和手机微信里看到的一致?我习惯在最后做一次抽样校验。随机挑 20 条消息,先在手机聊天记录里翻到对应时间段,再比对导出的 CSV 里的内容、方向、时间,误差不能超过 1 分钟。时间校准尤其重要:
import sqlite3, random conn = sqlite3.connect("plain.db") cur = conn.cursor() cur.execute("SELECT msgId, createTime, isSend, content FROM message WHERE type = 1 LIMIT 100000") rows = cur.fetchall() sample = random.sample(rows, 20) for msgId, ts, is_send, content in sample: print(msgId, ts, is_send, content[:30])把打印出来的记录和微信里的实际消息逐条比对,如果个别消息时间戳对不上,先怀疑自己在解密时拿到的不是最新数据库,重新 pull 再跑一遍;如果方向字段 isSend 反了,检查数据库版本,有的历史版本字段含义相反。这里没有玄学,只有核对习惯。
我做这类提取分析做多了以后,养成了一个习惯:任何脚本跑完都不会立刻删中间文件,解密后的 plain.db、还原前的 dat 目录、清洗前的 CSV 全部留底,等抽样校验通过才清理。一道工序一个备份,翻车时永远能倒退到上一步。遗憾是没早一点养成这个习惯,早期做项目时因为批量还原误判 key,把原图覆盖了,后来只能重新提取,白白多花了半天。希望帮到你。
本文还有配套的精品资源,点击获取