QQ 数据库解密与聊天记录导出 — 技术文档

QQ 数据库解密与聊天记录导出 --- 技术文档

设备 : 魅族 M5 Note (Android 7)

备份时间 : 2026-08-02

状态 : 手机已断开,NAS 为唯一副本

用途: 备份恢复、数据迁移、二次分析


目录

  1. 数据库解密技术分析
  2. 聊天记录导出工具

1. 数据库解密技术分析

1.1 背景

魅族 M5 Note 手机 QQ 数据备份,包含多个 SQLite 数据库(数百 MB 级),覆盖全部消息记录、好友列表、群组信息等。

所有文本类字段(昵称、备注、群名)和数字类字段(QQ号、消息体、时间戳)均被加密,但全部基于 XOR,存在两套不同的加密方案。


1.2 密钥一:数字/消息类字段

特征:加密后为纯 ASCII 可打印字符。

发现过程

  1. 观察到消息正文和 QQ 号加密后都是 ASCII 可打印字符
  2. 猜测为逐字节 XOR,密钥长度通过已知明文推断
  3. 从备份中的 HTML 导出文件找到已知消息明文,对比其加密密文得到密钥
  4. 验证:解密所有数字字段,得到合法的 QQ 号码序列

加密公式

复制代码
密文[i] = 明文[i] XOR Key[i % KeyLen]
  • 密钥长度固定(17 位),循环使用
  • 适用字段:消息体、QQ 号、时间戳等所有 latin1/ASCII 编码的字段

Python 实现

python 复制代码
KC = b'864499037161840'

def dec_str(s):
    """解密数字/消息类字段(密钥一)"""
    if not s: return ''
    return bytes(x ^ KC[i % len(KC)] for i, x in enumerate(s.encode('latin1'))) \
        .decode('latin1').rstrip(chr(0))

1.3 密钥二:昵称/备注类字段

特征:加密后看起来像正常的中文字符(合法的 UTF-8),不像密钥一那样全是 ASCII。

发现过程

Step 1 --- 识别编码方式

加密后的昵称仍是合法 UTF-8,推断加密是在 UTF-8 编码层面操作的。

Step 2 --- 对比密文与明文的字节差异

取已知的密文(数据库中)和对应的明文(HTML 导出文件名),逐字节比较:

字符 明文 UTF-8 密文 UTF-8 差异
例字1 xx xx xx xx xx yy 仅第 3 字节不同
例字2 xx xx xx xx xx yy 仅第 3 字节不同

发现规律:只有 UTF-8 编码的第 3 字节被修改,第 1、2 字节完全相同。

Step 3 --- 推导 XOR 密钥

对大量字符做第 3 字节的 XOR,按字符位置排序,得到:

字符位置 0 1 2 3 4 5 ...
XOR 值 K0 K1 K2 K0 K1 K2 ...

密钥长度 = 3 ,三个字节 [0x38, 0x36, 0x34] 按字符位置循环使用。

Step 4 --- 为什么只改第 3 字节

UTF-8 中文编码为 3 字节:1110xxxx 10xxxxxx 10xxxxxx

  • 第 1 字节:固定前缀 1110 + 编码高位
  • 第 2 字节:固定前缀 10 + 编码中位
  • 第 3 字节:固定前缀 10 + 编码低位(变化最大的部分)

只修改第 3 字节的效果:

  • 字符仍保持 3 字节 UTF-8 格式,仍是合法中文
  • 但字符编码范围大幅偏移,变成另一个不相关的字
  • ASCII 字符不受影响(只有 1 字节,没有第 3 字节)

Python 实现

python 复制代码
def dec_name(s):
    """解密昵称/备注类字段(密钥二)"""
    if not s: return ''
    b = s.encode('utf-8')
    result = bytearray(b)
    cycle = [0x38, 0x36, 0x34]
    for i in range(2, len(result), 3):
        char_pos = (i - 2) // 3
        result[i] ^= cycle[char_pos % 3]
    return result.decode('utf-8', errors='replace')

1.4 为什么这套加密能成功破解

要素 说明 影响
已知明文 HTML 导出文件含明文昵称,提供多组 (密文, 明文) 对 已知明文攻击成功
字节位置固定 UTF-8 中文编码结构固定,第 3 字节总是第 3 字节 能精确定位被加密字节
XOR 密钥固定 同一份数据使用相同密钥,无随机化 一次性破解可复用
密钥极短 密钥一 17 字节,密钥二仅 3 字节 少量样本即可推导
无完整性校验 修改后仍是合法 UTF-8,不会触发异常 暴力破解可行

安全评估:这是一个非常弱的加密方案,本质上是单表替换密码的变体,无密钥分发/轮换,无加密标识。设计目标可能是防君子不防小人,或者仅仅是防数据库直接以明文存储。


1.5 未破解的残留问题

  1. 同用户多处昵称不一致:不同表更新时机不同,分别记录了不同时间点的昵称。
  2. 部分昵称含 emoji/特殊符号:约 27% 的昵称解密后含有 emoji、特殊符号,这是用户真实昵称。
  3. 群聊成员昵称:群消息只记录发送者 QQ 号,群昵称存储在独立表中,需交叉关联。

1.6 附录:技术细节

UTF-8 第 3 字节 XOR 的完整逻辑

复制代码
字符 C 的 UTF-8 编码:B1 B2 B3
加密后:B1 B2 (B3 XOR K[i % 3])
其中 K = [0x38, 0x36, 0x34],i 从 0 开始

ASCII 字符不受影响的原因

ASCII 字符的 UTF-8 编码为单字节(值 < 0x80),没有第 3 字节:

  • 'A' = 0x41(1 字节)→ 不满足处理条件 → 原样输出 ✅
  • '1' = 0x31(1 字节)→ 同上 ✅

这意味着所有英文昵称、数字昵称在解密时不需要额外处理。


2. 聊天记录导出工具

2.1 概览

导出聊天记录_v2.py 是一个 Python 3 脚本,用于从 QQ 数据库提取聊天记录,输出 JSONL 格式文件。

输入 : /tmp/db_scan/databases/*.db

输出 : /mnt/shared/QQ_备份_20260802/exports_v2/account_{QQ号}/

输出结构

复制代码
exports_v2/
└── account_{QQ号}/
    ├── private/
    │   ├── 备注(昵称)-{对方QQ}.jsonl
    │   └── ...
    └── group/
        ├── {群QQ}.jsonl
        └── ...

2.2 JSONL 消息格式

每行一个消息,字段:

字段 说明
t 时间,格式 YYYY-MM-DD HH:MM:SS(北京时间 UTC+8)
from 发送者(自己显示为"你")
qq 发送者 QQ 号
self 是否为自己发送
type 消息类型(见下表)
text 文本内容(文字消息)
uuid 图片 UUID(32 位 hex,可在媒体目录查找对应文件)
urls 链接地址列表(链接消息)
group 群号(群聊消息才有)

2.3 消息类型解析

脚本通过 msgtype 字段识别消息类型:

msgtype type 值 说明
-1000 text 文字消息,XOR 解密后 UTF-8 解码
-2000 image 图片消息,提取 UUID
-1035 image+text 图片+文字描述
-2022 / -2055 / -5021 file QQ 文件传输
-1051 voice 语音消息
-5040 recall 撤回消息(含原文)
-2011 link 链接消息,提取 URL
-5008 / -5017 app 小程序、应用卡片
-2017 system 入群、退群等系统消息
-2024 / -2026 group_notice 群公告更新

2.4 处理流程

复制代码
1. 扫描所有 .db 文件
2. 构建昵称表
   - 从 Friends 表提取 QQ号 → 昵称 / 备注
   - 从 CardProfilev4 表提取历史昵称
3. 对每个账号数据库:
   a. 读取所有 mr_friend_*_New / mr_troop_*_New 表
   b. 对每条消息:
      - 解密发送者 QQ(密钥一)
      - 解析消息体(根据 msgtype)
      - 查找发送者昵称(优先用备注,其次用昵称)
      - 转换时间戳(UTC → 北京时间 UTC+8)
   c. 同时处理 slowtable 数据库(延迟消息)
   d. 按对方/群分组,写 JSONL 文件
4. 文件名规则:备注(昵称)-{QQ号}.jsonl(特殊字符已过滤)

2.5 源码结构

复制代码
#!/usr/bin/env python3
├── 密钥常量 KC = b'864499037161840'
├── dec_str()        # 密钥一解密(数字/消息)
├── dec_name()       # 密钥二解密(昵称/备注)
├── safe_fn()        # 文件名安全化(过滤特殊字符)
├── parse_msg()      # 消息体解析(24 种 msgtype)
├── build_nicks()    # 构建昵称表
├── resolve()        # QQ号 → 昵称
├── process()        # 处理单个数据库
├── 主流程           # 扫描 → 构建昵称表 → 逐个处理

2.6 运行方法

bash 复制代码
python3 导出聊天记录_v2.py

前置条件:Python 3,数据库已提取到 /tmp/db_scan/databases/,标准库即可,无需第三方包。


附录

A. 两个密钥速查表

密钥 字节 适用 加密方式
密钥一 864499037161840(17 字节) QQ号、消息体、时间戳 逐字节 XOR
密钥二 [0x38, 0x36, 0x34](3 字节) 昵称、备注、群名 UTF-8 第 3 字节 XOR,按字符位置循环

B. UUID 与媒体文件对应

图片消息中 uuid 字段(32 位 hex)对应媒体目录中的文件:

  • Cache_{uuid}.jpgCache_{uuid}(无后缀)
  • 可在 聊天缓存/ 目录中查找

文档生成时间: 2026-08-17

基于 QQ数据库解密技术分析.md + 导出聊天记录_v2.py 整合

相关推荐
程序员-Benothing13 分钟前
什么是批量数据入库?相比单条插入有什么优势?
数据库
我滴老baby18 分钟前
部署 Portainer CE,把日志、镜像和数据卷搬进网页
数据库·人工智能·架构
吠品34 分钟前
TortoiseSVN 状态图标不显示的排查与解决办法
java·服务器·数据库
ltl38 分钟前
PostgreSQL WAL 内部机制:XLogInsert 到 Checkpoint
数据库
cetcht888839 分钟前
深耕配电智能化升级 多场景项目落地筑牢电力运维安全防线
大数据·数据库·人工智能
ltl42 分钟前
RocksDB Universal 与 Write Stall:L0 积压怎么触发 DelayWrite
数据库
ltl43 分钟前
CockroachDB 对照:Range + Raft 的另一种 HTAP 落地
数据库
ltl43 分钟前
PostgreSQL Buffer Manager:Clock sweep 与 shared_buffers 边界
数据库
迷枫7122 小时前
SQL性能排查记录
java·数据库·sql