2026平航杯 手机取证题解析
🏆 2026平航杯手机取证 Writeup
💡 报告元数据
- 首席取证官:
yagami - 任务目录:
/mnt/d/文档/hermes-work/mobile-forensics/ - 生成时间:2026-07-08
- 生成模式:完整型取证交付 Writeup
🛠️ 工具链与环境底座
1. 算力与架构
| 组件分类 | 部署详情 |
|---|---|
| 自动化编排 | Hermes Agent |
| 基座大模型 | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-Q6_K_P.gguf |
| 运行架构 | 本地 |
2. 任务信息
| 项目 | 详情 |
|---|---|
| 检材1 | 早起王手机 — Pixel 6, Android 14, Google/oriole (/mnt/z/1-手机/早起王的手机/) |
| 检材2 | 倩倩手机 — nova 12 Pro, HarmonyOS 6.0.0.380 (/mnt/z/1-手机/倩倩的手机/) |
| 检材3 | 倩倩手机逆向包 — RAR压缩包 (/mnt/z/1-手机/倩倩手机逆向包.rar) |
| 题目数量 | 17题 (Q1-Q17) |
| 完成状态 | 17/17 ✅ 全部完成并验证 |
📊 考题最终答案汇总看板
| 题号 | 核心考点 | 权威标准答案 | 状态 | 等级 | 核心证据链检索摘要 |
|---|---|---|---|---|---|
| Q1 | Android手机型号识别 | Pixel 6 | 🟢 已验证 | L1 | PhoneInfo JSON直接提取Model字段 |
| Q2 | 高德地图POI搜索记录取证 | 西湖 | 🟢 已验证 | L2 | PoiDetailUserBehavior.xml + pre_set_word_local_storage.xml双证据交叉 |
| Q3 | 微信FTS5加密数据库解密与加好友时间 | 2026-03-3015:13:08 | 🟢 已验证 | L2 | MMKV提取FTS密码→SQLCipher解密→FTS5MetaMessage系统消息时间戳 |
| Q4 | 微信朋友圈protobuf解码 | 麻薯小蛋糕 | 🟢 已验证 | L1 | SnsMicroMsg.db未加密,protobuf解码朋友圈内容 |
| Q5 | HarmonyOS备份元数据分析 | 6.0.0.380 | 🟢 已验证 | L1 | .info.json中BackupFileVersionInfo.versionName直接读取 |
| Q6 | SQLite WAL文件原始数据提取 | wxid_uh5tfx2zi8yh22 | 🟢 已验证 | L1 | fts_contact.db-wal原始WAL帧数据中搜索"舔狗"联系人记录 |
| Q7 | HarmonyOS备忘录AES-GCM解密 | 冰糖 | 🟢 已验证 | L2 | 备忘录cloud_notes原文 + 截图交叉验证 |
| Q8 | 加密截图解密与视觉分析 | zero sievert | 🟢 已验证 | L1 | .tb文件xorEncrypt解密后截图视觉分析 |
| Q9 | HarmonyOS搜狐新闻数据库统计 | 33 | 🟢 已验证 | L1 | gen_natural_store.db主库sync_data表精确COUNT查询 |
| Q10 | HarmonyOS逆向包module.json分析 | com.koishi.fpt | 🟢 已验证 | L1 | module.json中bundleName字段直接提取 |
| Q11 | ABC字节码字符串提取 | 6 | 🟢 已验证 | L1 | ABC字节码strings提取"密码长度至少为6位" |
| Q12 | ABC字节码加密后缀识别 | .tb | 🟢 已验证 | L1 | ABC字节码strings提取.tb后缀 |
| Q13 | ABC字节码文件类型分类统计 | 5 | 🟢 已验证 | L1 | ABC字节码中提取图片类型列表:jpg/jpeg/png/gif/webp共5种 |
| Q14 | SO模块NAPI方法数IDA反汇编验证 | 2 | 🟢 已验证 | L2 | IDA反汇编napi_define_properties count=2 + SO rodata交叉验证 |
| Q15 | SO rot13函数ROT+16发现与密码恢复 | 217cb94a01679e39 | 🟢 已验证 | L2 | IDA确认rot13=ROT+16 → vault_prefs ROT-16逆运算恢复原始密码 |
| Q16 | xorEncrypt自定义算法解密JSON | 1472580369123 | 🟢 已验证 | L1 | xorEncrypt公式 output[i]=data[i]^(key[i%k]+(i%k)) 解密notes JSON |
| Q17 | PNG文件末尾隐写数据提取 | flag | 🟢 已验证 | L1 | PNG IEND chunk后27字节隐藏flag数据 |
⚔️ 深度取证与解题全链路复盘
🧩 Q1 早起王的手机-1:手机型号
-
题目:分析早起王的手机,手机型号为?【答案格式:Xiaomi13】
-
标准答案:
Pixel 6 -
状态等级:🟢 已验证 / L1
-
解题主线:早起王手机检材根目录下有
PhoneInfo-1775206177238JSON文件,直接读取Model字段即可。 -
💡 关键证据:
- PhoneInfo-1775206177238 JSON内容:
"Model":"Pixel 6","Device":"oriole","Brand":"google"
- PhoneInfo-1775206177238 JSON内容:
-
🛠️ 核心命令:
cat /mnt/z/1-手机/早起王的手机/PhoneInfo-1775206177238
# Model: Pixel 6, Device: oriole, Brand: google
- 验证闭环:从原始检材独立复现,JSON字段直接提取,无歧义。
🧩 Q2 早起王的手机-2:高德地图最可能去的景点
-
题目:分析早起王的手机,早起王最近想旅行,结合高德地图搜索记录,他最可能去的景点是哪个?【答案格式:黄山】
-
标准答案:
西湖 -
状态等级:🟢 已验证 / L2
-
解题主线:从root.tar中提取高德地图(
com.autonavi.minimap)数据,查看shared_prefs中的POI访问行为和预设搜索词。 -
💡 关键证据:
- 证据源1:PoiDetailUserBehavior.xml 中最后访问的POI为
"name":"杭州西湖风景名胜区" - 证据源2:pre_set_word_local_storage.xml 中预设搜索词
"杭州西湖":1
- 证据源1:PoiDetailUserBehavior.xml 中最后访问的POI为
-
🛠️ 核心命令:
mkdir -p /tmp/zqw-phone && cd /tmp/zqw-phone
tar xf /mnt/z/1-手机/早起王的手机/root.tar --strip-components=3 data/data/com.autonavi.minimap/
cat shared_prefs/PoiDetailUserBehavior.xml
# "name":"杭州西湖风景名胜区"
cat shared_prefs/pre_set_word_local_storage.xml
# "杭州西湖":1
- 验证闭环:双证据交叉验证 — POI访问记录 + 预设搜索词均指向"杭州西湖",答案简化为"西湖"。
🧩 Q3 早起王的手机-3:加上倩倩微信的时间
-
题目:分析早起王的手机,早起王在什么时间加上倩倩微信的?【答案格式:2025-08-1807:09:19】
-
标准答案:
2026-03-3015:13:08 -
状态等级:🟢 已验证 / L2
-
解题主线:
- 阶段1 — FTS密码提取:在微信MMKV配置文件中搜索FTS数据库加密密码
- 阶段2 — SQLCipher解密:用提取的密码解密FTS5IndexMicroMsg_encrypt.db
- 阶段3 — 好友验证消息定位:在FTS5MetaMessage中查找最早的系统消息(好友验证通过)
-
💡 关键证据:
- FTS密码来源:
mmkv_private/ConfigStorage2中明文存储USERINFO_FTS_MASTER_DB_ENCRYPT_PWD_STRING → 20623a1 - SQLCipher参数:page_size=4096, kdf_iter=64000, hmac_reserve=48, digest=sha1
- 好友验证消息:FTS5MetaMessage.docid=38:
aux_index=wxid_zuaa9igqlro22,talker=wxid_zuaa9igqlro22,timestamp=1774854788001 - 消息内容:FTS5IndexMessage_content docid=38:
"我通过了你的朋友验证请求,现在我们可以开始聊天了" - 时间戳转换:1774854788001 → 2026-03-30 15:13:08 (UTC+8)
- FTS密码来源:
-
🛠️ 核心命令:
# 阶段1: MMKV提取FTS密码
grep -a "USERINFO_FTS_MASTER_DB_ENCRYPT_PWD_STRING" /tmp/zqw-wechat/data/data/com.tencent.mm/MicroMsg/bdf648ed55f29d2f2b30888e605a2a86/mmkv_private/ConfigStorage2
# → USERINFO_FTS_MASTER_DB_ENCRYPT_PWD_STRING ... 20623a1
# 阶段2: SQLCipher解密FTS数据库
DB="/tmp/zqw-wechat/data/data/com.tencent.mm/MicroMsg/bdf648ed55f29d2f2b30888e605a2a86/FTS5IndexMicroMsg_encrypt.db"
sqlcipher "$DB" <<'EOF'
PRAGMA key = '20623a1';
PRAGMA cipher_page_size = 4096;
PRAGMA kdf_iter = 64000;
PRAGMA cipher_hmac_algorithm = HMAC_SHA1;
PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;
.output /tmp/zqw-wechat/FTS5Index_plain.db.sql
.dump
EOF
# → 82张表,解密成功
# 阶段3: 查询好友验证消息
grep "^INSERT INTO \"FTS5MetaMessage\" VALUES(38," /tmp/zqw-wechat/FTS5Index_plain.db.sql
# → aux_index=wxid_zuaa9igqlro22, timestamp=1774854788001
grep "^INSERT INTO \"FTS5IndexMessage_content\" VALUES(38," /tmp/zqw-wechat/FTS5Index_plain.db.sql
# → content = "我通过了你的朋友验证请求,现在我们可以开始聊天了"
# 时间戳转换
python3 -c "from datetime import datetime,timezone,timedelta; ts=1774854788001; print(datetime.fromtimestamp(ts/1000,tz=timezone.utc).astimezone(timezone(timedelta(hours=8))).strftime('%Y-%m-%d %H:%M:%S'))"
# → 2026-03-30 15:13:08
-
验证闭环:FTS5MetaMessage中docid=38是该wxid最早的37条消息之一,内容为系统自动生成的好友验证通过消息。联系人索引建档时间(2026-04-01 15:23:33)晚于首次消息时间,确认为FTS异步索引延迟。
-
⚠️ 修正复盘:最初误用联系人索引建档时间 2026-04-01 15:23:33 作为答案,后经交叉验证发现FTS5MetaMessage中最早消息时间为 2026-03-30 15:13:08,且内容为好友验证通过系统消息,修正为正确答案。
🧩 Q4 早起王的手机-4:倩倩2026年3月30号吃了什么
-
题目:分析早起王的手机,倩倩在2026年3月30号吃了什么?【答案格式:西湖醋鱼】
-
标准答案:
麻薯小蛋糕 -
状态等级:🟢 已验证 / L1
-
解题主线:早起王手机中微信SnsMicroMsg.db未加密,直接查询倩倩(wxid_zuaa9igqlro22)的朋友圈数据,protobuf解码后找到2026-03-30发布的内容。
-
💡 关键证据:
- SnsMicroMsg.db (protobuf解码):
createTime=1774857137→ 2026-03-30 15:52:17 - 朋友圈内容:
"今天吃了麻薯小蛋糕 开心开心开心!"
- SnsMicroMsg.db (protobuf解码):
-
🛠️ 核心命令:
DB="/tmp/zqw-wechat/data/data/com.tencent.mm/MicroMsg/bdf648ed55f29d2f2b30888e605a2a86/SnsMicroMsg.db"
sqlite3 "$DB" "SELECT snsId, userName, createTime, content FROM SnsInfo WHERE userName='wxid_zuaa9igqlro22' ORDER BY createTime DESC;"
python3 -c "from datetime import datetime; print(datetime.fromtimestamp(1774857137))"
# 2026-03-30 15:52:17 — "今天吃了麻薯小蛋糕 开心开心开心!"
- 验证闭环:从原始检材独立复现,SnsMicroMsg.db未加密直接查询,protobuf解码后内容明确。
🧩 Q5 倩倩的手机-1:系统版本
-
题目:分析倩倩的手机,倩倩手机的系统版本是多少?【答案格式:5.2.3.123】
-
标准答案:
6.0.0.380 -
状态等级:🟢 已验证 / L1
-
解题主线:直接读取HarmonyOS备份元数据文件
.info.json,从中提取BackupFileVersionInfo.versionName。 -
💡 关键证据:
- .info.json:
BackupFileVersionInfo.versionName = "6.0.0.380" - versionCode = “60000380” 也对应 6.0.0.380
- .info.json:
-
🛠️ 核心命令:
python3 -c "import json; info=json.load(open('/mnt/z/1-手机/倩倩的手机/备份/.info.json')); print(info['BackupFileVersionInfo']['versionName'])"
# → 6.0.0.380
-
验证闭环:从原始检材直接读取,无歧义。
-
⚠️ 修正复盘:原答案 5.0.0.123 是推断值(无直接证据),实际应从 .info.json 直接读取为 6.0.0.380。
🧩 Q6 倩倩的手机-2:"舔狗"的微信内部ID
-
题目:分析倩倩的手机,"舔狗"的微信内部ID是多少?【答案格式:wxid_ab12】
-
标准答案:
wxid_uh5tfx2zi8yh22 -
状态等级:🟢 已验证 / L1
-
解题主线:HarmonyOS备份中微信模块的
fts_contact.db主文件为69KB全零占位符,实际数据在WAL文件(3.8MB)中。直接在WAL原始二进制数据中搜索"舔狗"字符串,找到完整联系人记录。 -
💡 关键证据:
- fts_contact.db-wal offset 424271 处原始数据:
舔狗\0越过山丘\0...\0wxid_uh5tfx2zi8yh22 - 联系人信息:昵称=“舔狗”, 签名=“越过山丘”, wxid=wxid_uh5tfx2zi8yh22
- fts_contact.db-wal offset 424271 处原始数据:
-
🛠️ 核心命令:
# 在WAL文件中搜索"舔狗"
grep -a -o -P ".{0,50}舔狗.{0,100}" fts_contact.db-wal
# → 舔狗\0越过山丘\0...\0wxid_uh5tfx2zi8yh22
- 验证闭环:WAL原始数据中昵称、签名、wxid完整对应,无歧义。
🧩 Q7 倩倩的手机-3:推荐游戏的好友名字
-
题目:分析倩倩的手机,倩倩曾给一位好友推荐游戏,这个好友叫什么名字?【答案格式:杨梅】
-
标准答案:
冰糖 -
状态等级:🟢 已验证 / L2
-
解题主线:
- 阶段1 — HarmonyOS备忘录解密:用 per_backup_key 派生模块key,AES-GCM解密备忘录数据
- 阶段2 — 备忘录原文提取:从 notepad.db cloud_notes 表中找到游戏推荐相关记录
- 阶段3 — 截图交叉验证:解密加密截图确认聊天对象
-
💡 关键证据:
- 证据源1 — 备忘录原文:HarmonyOS备忘录模块
com.huawei.hmos.notepad,解密后cloud_notes.dataJSON内容:
"找一下截图在哪\n很久以前我给好友**冰糖**推荐过一款游戏,但我这笨脑子却想不起来了。所幸我当时有截过图,但是被放到什么位置来着?" - HTML实体解码:
找一下截图在哪→ “找一下截图在哪” - 证据源2 — 解密截图:Snipaste_2026-04-03_10-56-53.jpg 顶部显示"冰糖君酱"(微信备注名),聊天内容中"冰糖若喜欢…“断句为"冰糖 若(如果) 喜欢…”
- 证据源1 — 备忘录原文:HarmonyOS备忘录模块
-
🛠️ 核心命令:
# HarmonyOS备忘录解密流程
# 1. per_backup_key = "59fpxqgiqe+c7sqtXHCkmA==" (ASCII字节)
# 2. PBKDF2(per_backup_key, module.encryptionSalt, count=10000) → module_key
# 3. AES-GCM(module_key, module.encryptionIv, encrypted_blob) → plaintext
# 4. 解压 part.0.tar → notepad.db
# 5. cloud_notes.data JSON: "很久以前我给好友冰糖推荐过一款游戏"
-
验证闭环:备忘录原文是倩倩自己的记录,明确写的是"好友冰糖"。截图顶部"冰糖君酱"是微信中的备注名/显示名。题目问"这个好友叫什么名字",以备忘录原文为准。
-
⚠️ 修正复盘:一度误将截图顶部"冰糖君酱"(微信备注名)作为答案,后经HarmonyOS备忘录原文交叉验证确认为"冰糖"。
🧩 Q8 倩倩的手机-4:推荐的游戏名
-
题目:分析倩倩的手机结合逆向包,推荐的游戏叫什么?【答案格式:far echo】
-
标准答案:
zero sievert -
状态等级:🟢 已验证 / L1
-
解题主线:解密加密截图 Snipaste_2026-04-03_10-56-53.jpg.tb,视觉分析聊天内容找到游戏名。
-
💡 关键证据:
- 解密截图中的聊天内容:
"话说回来我刚打了半天另一款平面射击的类塔科夫游戏呢,叫 zero sievert" - 推荐消息:
"冰糖若喜欢原版塔科夫的话,这个游戏大概也会对你的口味,可以了解下(就是画风和机制完全不一样)"
- 解密截图中的聊天内容:
-
验证闭环:截图视觉分析直接提取游戏名 “zero sievert”。
🧩 Q9 倩倩的手机-5:阅读搜狐新闻条数
-
题目:分析倩倩的手机,倩倩一共阅读过多少条搜狐新闻?【答案格式:11】
-
标准答案:
33 -
状态等级:🟢 已验证 / L1
-
解题主线:从HarmonyOS备份中解密
com.sohu.harmonynews模块,在gen_natural_store.db主库的sync_data表中统计 article 开头的记录数。 -
💡 关键证据:
- gen_natural_store.db sync_data表:
SELECT COUNT(*) FROM sync_data WHERE key LIKE 'article%'→ 33条 - 总记录35条(33条article + CHANNEL_LIST_DATA + NEWS_LIST_DATA)
- gen_natural_store.db sync_data表:
-
🛠️ 核心命令:
sqlite3 "$DB" "SELECT COUNT(*) FROM sync_data WHERE key LIKE 'article%';"
# → 33
-
验证闭环:主库精确查询,排除非article记录。
-
⚠️ 修正复盘:原答案39来自WAL文件提取(可能包含已删除或重复记录),经主库精确查询修正为33条。
🧩 Q10 倩倩的手机-6:数据加密app包名
-
题目:分析倩倩手机逆向包,数据加密app的包名是什么?【答案格式:com.komeiji.satori】
-
标准答案:
com.koishi.fpt -
状态等级:🟢 已验证 / L1
-
解题主线:解压逆向包RAR,读取
module.json中的bundleName字段。 -
💡 关键证据:
- module.json:
"bundleName": "com.koishi.fpt"
- module.json:
-
🛠️ 核心命令:
mkdir -p /tmp/qianqian-reverse && cd /tmp/qianqian-reverse
unrar x /mnt/z/1-手机/倩倩手机逆向包.rar
cd /tmp/qianqian-reverse/app_extract/hap_extract
cat module.json | python3 -m json.tool | grep bundleName
# "bundleName": "com.koishi.fpt"
- 验证闭环:module.json直接提取,无歧义。
🧩 Q11 倩倩的手机-7:初始化密码最少位数
-
题目:接上题,初始化app时需要至少几位数的密码?【答案格式:10】
-
标准答案:
6 -
状态等级:🟢 已验证 / L1
-
解题主线:从ABC字节码中提取字符串,找到密码长度校验提示。
-
💡 关键证据:
- ABC字节码strings:
"密码长度至少为6位","请输入新密码(至少6位)"
- ABC字节码strings:
-
🛠️ 核心命令:
strings ets/modules.abc > /tmp/abc_strings.txt
grep "至少为6位" /tmp/abc_strings.txt
# "密码长度至少为6位"
- 验证闭环:两处字符串均确认最少6位。
🧩 Q12 倩倩的手机-8:加密文件后缀
-
题目:接上题,加密后的文件名的后缀是什么?【答案格式:.enc】
-
标准答案:
.tb -
状态等级:🟢 已验证 / L1
-
解题主线:从ABC字节码中提取
.tb后缀字符串,vault目录中实际加密文件也使用.tb后缀。 -
💡 关键证据:
- ABC字节码strings:
.tbsuffix found - vault目录文件:
139346145_p0.png.tb,Snipaste_2026-04-03_10-56-53.jpg.tb,*.json.tb
- ABC字节码strings:
-
🛠️ 核心命令:
grep "\.tb" /tmp/abc_strings.txt
# .tb
- 验证闭环:ABC字节码 + vault目录实际文件双重确认。
🧩 Q13 倩倩的手机-9:自动识别图片后缀种类数
-
题目:接上题,app会自动识别几种后缀的文件为图片类型?【答案格式:8】
-
标准答案:
5 -
状态等级:🟢 已验证 / L1
-
解题主线:从ABC字节码中提取文件类型列表,区分图片和视频类型。
-
💡 关键证据:
- ABC字节码中文件类型列表:
- 图片类型(5种): jpg, jpeg, png, gif, webp
- 视频类型(4种): mp4, mov, avi, mkv
- 题目问的是"图片类型",答案为5
- ABC字节码中文件类型列表:
-
🛠️ 核心命令:
python3 -c "
with open('ets/modules.abc','rb') as f: data=f.read()
pos = data.find(b'jpg')
print(data[pos-10:pos+80].decode('utf-8',errors='replace'))
"
# jpg, jpeg, png, gif, webp (5种图片)
- 验证闭环:ABC字节码中明确列出5种图片后缀。
🧩 Q14 倩倩的手机-10:SO模块导入方法数
-
题目:接上题,app共从用于自定义加密的so模块导入了几个方法?【答案格式:8】
-
标准答案:
2 -
状态等级:🟢 已验证 / L2
-
解题主线:
- 阶段1 — ABC字节码初步分析:发现9个导入符号(t1,u1,v1,w1,z1,a2,b2,c2,d2),但其中q1(random)和s1(base64)是TypeScript工具函数,非SO导入
- 阶段2 — IDA反汇编验证:加载libcrypto.so,分析NAPI模块注册流程
- 阶段3 — 交叉确认:napi_define_properties调用时count=2,属性表仅rot13和xorEncrypt
-
💡 关键证据:
- 证据源1 — IDA反汇编:
sub_5D58调用napi_define_properties,参数W2=#2(AArch64调用约定x2=第三个参数=count),明确定义了2个NAPI属性 - 证据源2 — 属性表0xd8f0:方法1=
rot13(→sub_5DE0), 方法2=xorEncrypt(→sub_603C) - 证据源3 — NAPI module结构体0xed48:version=1, register_func=sub_5D58, module_name=“crypto”
- ABC字节码中的9个符号(t1,u1,v1,w1,z1,a2,b2,c2,d2)是ArkTS编译后的局部混淆名,大部分对应标准C库函数(malloc,free,memcpy等),非NAPI注册方法
- 证据源1 — IDA反汇编:
-
🛠️ 核心命令:
# IDA MCP 工具调用 (非shell命令):
# 1. 查看RegisterCryptoModule反汇编
mcp_ida_disasm(addr="RegisterCryptoModule")
# → NOP; ADR X0, unk_ED48; B .napi_module_register
# 2. 查看sub_5D58反汇编 (napi_define_properties调用者)
mcp_ida_disasm(addr="sub_5D58")
# → MOV W2, #2 (count=2个属性)
# → BL .napi_define_properties
# 3. 读取属性表数据
mcp_ida_get_string(addrs=["0x23f0", "0x3312"])
# → "rot13", "xorEncrypt"
# 结论: Q14答案 = 2 (L2 IDA反汇编 + ABC交叉验证)
-
验证闭环:IDA MCP反汇编确认NAPI只注册了2个方法,ABC字节码9个导入符号中大部分是标准C库函数。
-
⚠️ 修正复盘:原答案9误将ABC字节码中的混淆名t1,u1,v1,w1,z1,a2,b2,c2,d2当作SO导入方法,经IDA反汇编确认实际只有2个NAPI方法(rot13, xorEncrypt)。
🧩 Q15 倩倩的手机-11:app设置的密码
-
题目:接上题,app设置的密码是多少?【答案格式:514aalla4191a98】
-
标准答案:
217cb94a01679e39 -
状态等级:🟢 已验证 / L2
-
解题主线:
- 阶段1 — vault_prefs分析:提取
password_hash=217sr94q01679u39 - 阶段2 — SO rot13函数IDA反汇编:发现rot13(sub_5DE0)实际是ROT+16算法
- 阶段3 — ROT-16逆运算恢复原始密码:对password_hash做ROT-16逆变换
- 阶段1 — vault_prefs分析:提取
-
💡 关键证据:
- vault_prefs:
password_hash=217sr94q01679u39(ROT+16变换后的存储值) - IDA反汇编 sub_5DE0:
MOV W15,#0xAF+ 除法优化 =(ch+0xAF)%26+'a'= ROT+16算法 - 精确验证:对a-z全部26字母测试,shift均为16
- a→q(shift=16), b→r, c→s… z→p
- 密码变换链:用户输入
217cb94a01679e39→ ROT+16 →217sr94q01679u39→ vault_prefs存储 - 原始密码恢复:ROT-16(
217sr94q01679u39) =217cb94a01679e39
- vault_prefs:
-
🛠️ 核心命令:
# IDA MCP 工具调用:
mcp_ida_disasm(addr="sub_5DE0")
# 关键指令: MOV W15, #0xAF (175) -> 偏移量
# 算法: (ch + 0xAF) % 26 + 'a' = ROT+16
# Python精确验证rot13=ROT+16
python3 -c "
def ida_rot(ch):
c = ord(ch)
w13 = (c + 0xAF) & 0xFFFF
w15 = (w13 * 0x4F) >> 11
w13 = w13 - (w15 * 0x1A)
w13 = w13 + 0x61
return chr(w13 & 0xFF)
for ch in 'abcdefghijklmnopqrstuvwxyz':
r = ida_rot(ch)
shift = (ord(r) - ord(ch)) % 26
print(f'{ch} -> {r} (shift={shift})')
"
# 全部shift=16,确认是ROT+16
# ROT-16逆运算恢复原始密码
python3 -c "
pw_hash = '217sr94q01679u39'
result = ''
for ch in pw_hash:
if 'a' <= ch <= 'z':
result += chr((ord(ch) - ord('a') - 16) % 26 + ord('a'))
else:
result += ch
print(f'password_hash: {pw_hash}')
print(f'ROT-16恢复: {result}')
"
# → 217cb94a01679e39
# 验证ROT+16正向变换
python3 -c "
original = '217cb94a01679e39'
result = ''
for ch in original:
if 'a' <= ch <= 'z':
result += chr((ord(ch) - ord('a') + 16) % 26 + ord('a'))
else:
result += ch
print(f'original: {original}')
print(f'ROT+16: {result}')
"
# → 217sr94q01679u39 (与vault_prefs一致)
- 验证闭环:正向ROT+16(217cb94a01679e39) = 217sr94q01679u39,与vault_prefs中password_hash完全一致。用存储值作为xorEncrypt key成功解密所有3个.tb文件(JSON完美、PNG 16/16匹配、JPG FFD8正确)。
🧩 Q16 倩倩的手机-12:门锁密码
-
题目:接上题,app中存储的门锁密码是多少?【答案格式:5141141919810】
-
标准答案:
1472580369123 -
状态等级:🟢 已验证 / L1
-
解题主线:用xorEncrypt算法解密notes JSON文件,提取门锁密码。
-
💡 关键证据:
- xorEncrypt加密公式:
output[i] = data[i] ^ (key[i % key_len] + (i % key_len)) - 解密key =
217sr94q01679u39(vault_prefs存储值) - 解密后JSON内容:
{"id":"b88c3348-9389-4fad-8cd6-3490e6bbdd26","title":"门锁密码","content":"1472580369123","updatedAt":1775196161152}
- xorEncrypt加密公式:
-
🛠️ 核心命令:
# xorEncrypt解密 (Python)
key = b'217sr94q01679u39'
with open('b88c3348-9389-4fad-8cd6-3490e6bbdd26.json.tb', 'rb') as f:
ct = f.read()
pt = bytes(ct[i] ^ (key[i % len(key)] + (i % len(key))) for i in range(len(ct)))
import json
data = json.loads(pt)
print(data['content'])
# → 1472580369123
- 验证闭环:解密后JSON结构完整,title=“门锁密码”,content=“1472580369123”。
🧩 Q17 倩倩的手机-13:加密图片隐藏flag
-
题目:接上题,加密图片里面的隐藏的flag是多少?【答案格式:flag{123456!}】
-
标准答案:
flag{happy_forensics_2026!} -
状态等级:🟢 已验证 / L1
-
解题主线:用xorEncrypt解密PNG文件后,检查PNG结构发现IEND chunk后有27字节额外数据。
-
💡 关键证据:
- 解密后的PNG文件 (3200x2000, 5.7MB) 包含709个chunk
- 最后一个正常chunk是 IEND (0字节数据)
- IEND之后有27字节额外数据:
flag{happy_forensics_2026!} - 标准PNG阅读器忽略IEND后的数据,属于文件末尾隐写
-
🛠️ 核心命令:
# xorEncrypt解密PNG
key = b'217sr94q01679u39'
with open('139346145_p0.png.tb', 'rb') as f:
ct = f.read()
pt = bytes(ct[i] ^ (key[i % len(key)] + (i % len(key))) for i in range(len(ct)))
# 检查PNG IEND后数据
png_data = bytearray(pt)
iend_pos = png_data.find(b'IEND')
if iend_pos >= 0:
after_iend = png_data[iend_pos+8:] # IEND + 4字节CRC
print(after_iend[:50])
# → b'flag{happy_forensics_2026!}'
- 验证闭环:PNG文件结构完整(709个chunk),IEND后27字节为纯文本flag。
🛑 未完成或不可提交题目审计
全量收敛:17/17题全部完成并验证,无未完成题目。
| 题号 | 状态 | 备注 |
|---|---|---|
| Q1-Q17 | 🟢 已验证 | 全部从原始检材独立复现验证通过 |
📂 附录:自动化取证证据大索引
证据索引
| 编号 | 题号 | 证据描述 | 来源文件 |
|---|---|---|---|
| FINDING-Q1-001 | Q1 | PhoneInfo JSON Model字段 | PhoneInfo-1775206177238 |
| FINDING-Q2-001 | Q2 | 高德POI访问记录 | PoiDetailUserBehavior.xml |
| FINDING-Q2-002 | Q2 | 高德预设搜索词 | pre_set_word_local_storage.xml |
| FINDING-Q3-001 | Q3 | FTS5数据库解密成功 | FTS5IndexMicroMsg_encrypt.db |
| FINDING-Q3-002 | Q3 | 联系人索引建档时间(非答案) | FTS5MetaContact |
| FINDING-Q3-003 | Q3 | MMKV FTS密码查找方法验证 | mmkv_private/ConfigStorage2 |
| FINDING-Q3-004 | Q3 | 好友验证消息时间戳修正 | FTS5MetaMessage docid=38 |
| FINDING-Q4-001 | Q4 | 朋友圈protobuf解码内容 | SnsMicroMsg.db |
| FINDING-Q5-001 | Q5 | 系统版本直接证据 | .info.json |
| FINDING-Q6-001 | Q6 | "舔狗"微信ID从WAL提取 | fts_contact.db-wal |
| FINDING-Q7-001 | Q7 | 截图视觉分析(冰糖君酱) | Snipaste_2026-04-03_10-56-53.jpg |
| FINDING-Q7-002 | Q7 | 备忘录原文确认"冰糖" | notepad.db cloud_notes |
| FINDING-Q7Q8-001 | Q7/Q8 | 游戏推荐截图完整分析 | Snipaste_2026-04-03_10-56-53.jpg |
| FINDING-Q9-001 | Q9 | 搜狐新闻主库精确统计 | gen_natural_store.db sync_data |
| FINDING-Q10-001 | Q10 | app包名从module.json提取 | module.json |
| FINDING-Q11-001 | Q11 | 密码最少位数字符串 | ABC字节码strings |
| FINDING-Q12-001 | Q12 | 加密后缀.tb | ABC字节码strings |
| FINDING-Q13-001 | Q13 | 图片类型5种列表 | ABC字节码文件类型列表 |
| FINDING-Q14-001 | Q14 | SO实际导出2个NAPI方法 | libcrypto.so反汇编 |
| FINDING-Q14-IDA-001 | Q14 | IDA MCP验证NAPI方法数 | IDA反汇编 |
| FINDING-Q15-001 | Q15 | xorEncrypt算法完整破解 | libcrypto.so + PNG已知明文攻击 |
| FINDING-Q15-IDA-002 | Q15 | IDA确认rot13=ROT+16 | IDA反汇编 sub_5DE0 |
| FINDING-Q16-001 | Q16 | 门锁密码从加密notes提取 | b88c3348…json.tb解密JSON |
| FINDING-Q17-001 | Q17 | PNG IEND后隐藏flag | 解密PNG文件末尾27字节 |
| FINDING-HarmonyOS-Backup-Decrypt | Q6-Q9 | HarmonyOS备份二次解密完整流程 | .info.json + PBKDF2 + AES-GCM |
命令索引
| 编号 | 描述 |
|---|---|
| CMD-20260707-001 | Q3 FTS5IndexMicroMsg_encrypt.db 独立解密验证 |
| CMD-20260707-Q3V1 | Q3 答案验证 — 好友验证消息时间戳确认 |
| CMD-20260707-Q5V1 | Q5 独立验证 — .info.json直接读取 |
| CMD-20260707-Q14-IDA | Q14 IDA MCP 独立验证 — NAPI方法数 |
| CMD-20260707-Q15-IDA | Q15 IDA MCP rot13=ROT+16 验证 |
| CMD-20260708-V001 | 全量验证批次 — 17题从原始检材独立复现 |
- 全链路取证闭环完成 -