三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

从零解锁QQ聊天记录数据库:密钥提取实操与避坑清单

从零解锁QQ聊天记录数据库:密钥提取实操与避坑清单

从零解锁QQ聊天记录数据库:密钥提取实操与避坑清单

【免费下载链接】qq-win-db-key全平台 QQ 聊天数据库解密项目地址: https://gitcode.com/gh_mirrors/qq/qq-win-db-key

QQ 的聊天记录以加密数据库的形式存在本地,想迁移到新设备时往往无从下手。qq-win-db-key 是一个覆盖 Android、iOS、Windows、macOS、Linux 的密钥提取脚本集,帮你找到那把"数据库钥匙",进而完成个人聊天记录的备份与二次解析。本文从原理讲到实操,带你完整走通这条链路。

换个设备,QQ聊天记录怎么就带不走了

先讲一个几乎人人都遇到的场景:手机用了两三年要换新机,微信有云端同步,QQ 呢?登录新设备后,历史消息大多只能看到一部分,本地积攒的那几 GB 聊天记录却带不过去。

原因不难理解。QQ 的本地消息存放在 SQLCipher 加密数据库中,文件本身是密文,没有密钥就读不出内容。官方提供的"导出消息记录"功能,导出的多是 mht 格式的网页快照,适合查看,不适合做结构化备份或数据迁移。想完整、可检索地把记录带走,核心问题只有一个:把数据库的密钥拿到手

网上相关的教程不算少,但大多针对单一平台、绑定某个 QQ 版本,版本一更新就失效。qq-win-db-key 把各平台的脚本集中在一个仓库里,并对旧版 PCQQ 与新版 NTQQ 分开维护,省去了四处翻找、反复试错的麻烦。

项目全景:五个平台、六套脚本,各管一段

仓库的 scripts/ 目录按平台划分,结构很直白:

平台脚本位置主要手段
Androidscripts/android/Frida 注入,hook libkernel.so
iOSscripts/ios/Frida JavaScript 注入
Linux NTQQscripts/linux/静态分析 + GDB 动态调试
macOS(ARM)scripts/macos/arm-nosip/免关闭 SIP 的提取方案
Windows NTQQscripts/windows/ntqq/PE 静态分析 + 调试器附加
Windows 旧版 PCQQscripts/windows/pcqq/Frida hook KernelUtil.dll

值得留意最后两行:Windows 下新版 NTQQ 与旧版 PCQQ 的加密链路完全不同,前者密钥来自 wrapper.node 中的nt_sqlite3_key_v2,后者则要 hook 老的 KernelUtil.dll 里的sqlite3_key。仓库把两者拆成独立目录各自维护,避免互相干扰。

想快速判断某个脚本是否适用于你的环境,先看它支持的版本号即可。以 scripts/android/android_get_key.py 为例,文件里明确列出了 8.9.58、8.9.63、8.9.68、8.9.76 等版本对应的特征码。

跟着做一遍:Android 上 15 分钟取到密钥

纸上谈兵不如动手。以最常见的 Android 平台为例,完整跑一遍密钥提取,大约十几分钟。

准备工作:一台已 root 的 Android 设备,安装 frida-server,并保证与电脑上的 frida 版本匹配。另有两条硬性要求:关闭 SELinux,关闭 Magisk Hide 与 Shamiko,否则注入会被拦下。若在 Termux 里操作,脚本会自动切换为远程设备模式。

第一步,把 QQ 打开并登录,进入主界面后保持前台运行。

第二步,运行脚本,并带上你自己的 QQ 版本号:

python scripts/android/android_get_key.py 8.9.58

脚本会先尝试附加到已运行的进程;若没检测到,则自己拉起 QQ 再注入。看到Frida script injected.就说明 hook 已生效。

第三步,触发一次密钥调用:退出登录,再重新登录。就在这个瞬间,QQ 会去解密数据库,脚本在函数入口截获参数并打印。终端里会出现类似下面的内容:

¦- targetDB: /data/data/com.tencent.mobileqq/... ¦- *zDb: nt_qq_xxx.db ¦- *pkey: abcd1234.,.,ABCD1234567812345678 ¦- nKey: 32

这里的*pkey就是要找的密钥,32 个可见字符,先复制保存好。整个过程不修改 QQ 安装包、不注入额外逻辑,只是"旁听"了一次函数调用。

脚本核心逻辑其实很朴素:先在 libkernel.so 的内存里按特征码扫描,定位nt_sqlite3_key_v2函数,再用 Interceptor.attach 在函数进入时读取参数——第 3 个参数是密钥指针,第 4 个是密钥长度。一句话概括:你不需要知道密钥怎么算出来的,只要在它被"用"的那一刻记下来就行。

解密原理:锁是 SQLCipher,钥匙藏在函数参数里

拿到密钥只是第一步,还得知道这把锁的规格。SQLCipher 是 SQLite 的加密版本,加密参数不匹配,密钥再正确也打不开库。

QQ 数据库使用的参数组合如下:

参数
页面大小 cipher_page_size4096 字节
KDF 迭代次数 kdf_iter4000
HMAC 算法SHA512
KDF 算法SHA512

可以这样理解:数据库是一个带密码的保险箱,密钥是密码,上面几个参数则是锁芯型号。密码对了、锁芯不对,依然打不开。

拿到密钥与参数后,用 SQLCipher 命令行工具即可完成解密:

sqlcipher encrypted.db PRAGMA key = 'abcd1234.,.,ABCD1234567812345678'; PRAGMA cipher_page_size = 4096; PRAGMA kdf_iter = 4000; .save decrypted.db

生成的 decrypted.db 就是明文数据库,之后可以用 DB Browser for SQLite 之类的工具浏览,或交给解析脚本导出成可读的聊天记录。补充一句:个别旧版本库的迭代次数可能是 64000,若PRAGMA integrity_check报错,可以改回 64000 再试。

桌面端玩法:不注入进程,从二进制里"挖"钥匙

手机端靠 Frida 注入,桌面端则是另一套打法——先静态定位,再动态取参。

以 Linux 的 scripts/linux/linux_qq_get_key.py 为例,它不碰 QQ 进程,而是对/opt/QQ/resources/app/wrapper.node做三层静态分析:

  1. readelf -lW读取程序头与段映射,找到 .rodata 段的位置;
  2. strings -t d定位nt_sqlite3_key_v2: db=%p zDb=%s这串日志字符串的偏移;
  3. objdump -D -j .text反汇编,找到引用该字符串的指令,从而锁定函数地址。

定位完成后转入 GDB,在函数入口设断点,等 QQ 运行时密钥参数传入的瞬间取走。脚本还会把计算结果缓存到本地的 ref_off_cache 文件,并校验 wrapper.node 的 SHA256——版本没变就不重复计算,加快二次使用。

Windows 新版 QQ 思路类似,scripts/windows/ntqq/windows_ntqq_get_key.ps1 会解析 PE 文件:在 .rdata 里找字符串 RVA,在 .text 里找引用它的 LEA 指令(RIP 相对寻址),再通过异常目录反推所在函数,随后带着调试器启动 QQ 提取密钥。如果只想做静态分析、不想真跑调试,加一个参数即可:

.\windows_ntqq_get_key.ps1 -NoDebugForKey

macOS 那边比较特殊,scripts/macos/arm-nosip/ 下的工具在 ARM 芯片上无需关闭系统完整性保护(SIP),对日常使用影响更小。iOS 则走 scripts/ios/ios_get_key.js 的 Frida 注入路线。

几个平台的共同点很明显:都是"静态找位置 + 动态抓参数"的组合拳,区别只在于分别用什么工具完成这两步。

踩坑实录:版本不匹配、SELinux、模拟器

实操中失败,多半是下面几类原因,按出现频率排个序:

  • 版本对不上:脚本里的特征码与具体版本绑定。Android 脚本只覆盖 8.9.58、8.9.63、8.9.68、8.9.76,其他版本需要自行更新特征码。跑之前先确认 QQ 版本,别拿旧脚本硬套新版本。
  • SELinux 没关:Android 注入失败的第一大元凶。用getenforce看一眼状态,Permissive 或 Disabled 才安全;Magisk Hide、Shamiko 这类隐藏 root 的模块同样会拦截 frida,记得先关。
  • 用了模拟器:脚本明确要求在真机上跑,不要在 x86/x64 架构的安卓模拟器里运行——libkernel.so 的特征码基于 ARM64 指令,换架构必然匹配失败。
  • 进程附加失败:提示 QQ 未运行或附加超时,先把 QQ 彻底杀掉再运行脚本,让脚本自己拉起进程;或者反过来,先登录进主界面再运行。
  • 数据库解不开:密钥正确但PRAGMA integrity_check仍报错,优先怀疑参数。把 kdf_iter 在 4000 与 64000 之间切换,再确认数据库对应 SQLCipher 3 还是 4。

这套排查顺序基本能覆盖九成问题。剩下的个例,多半与特定 QQ 版本的改动有关,需要对照源码里的特征码自行微调。

动手之前,先想清楚三件事

最后聊几句实在话。这类工具天然带着风险,README 里的警告写得比正文还长,值得逐字读一遍:

  • 先做官方备份:Windows 上可先用 PCQQ 自带的"导出消息记录(mht 格式)"保底,再用系统备份或整机备份把原始数据留一份。
  • 再评估风险:脚本可能损坏聊天记录,极端情况下可能触发账号风控。优先在不常用的设备或虚拟机里验证流程,确认无误后再处理真实数据。
  • 最后谈合规:代码仅限学习交流用途,请勿用于违反所在地法律法规或 QQ 软件许可及服务协议的行为,也不要用结果做法律取证。运行任何脚本前,先通读一遍代码,这是对自己数据负责的基本态度。

如果你已经准备好了,下一步很直接:git clone https://gitcode.com/gh_mirrors/qq/qq-win-db-key拉下仓库,按自己的平台找到对应目录,先审查、再备份、后运行。等数据库成功解密、聊天记录完整导出的那一刻,换设备时的焦虑也就到此为止——这正是这个项目最实在的价值。

【免费下载链接】qq-win-db-key全平台 QQ 聊天数据库解密项目地址: https://gitcode.com/gh_mirrors/qq/qq-win-db-key

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

← 返回列表