TWRP 恢复分区取证与物理镜像提取

Android 设备的提取难度是分层的。常见的可用手段按「对设备状态的改变」从轻到重排列:

最后更新 2026-10-09版本 v1.0维护 DigiForensics查看历史 0

定位:本文讨论在设备已锁定、ADB 无 root、无法常规提取的条件下,如何通过第三方 recovery 拿到全分区物理镜像,以及拿到之后如何把它变成可用的取证材料。

前置警告:本流程会改变设备状态(在 recovery 或 boot 分区写入一个 recovery 镜像)。这些操作在多数情形下不触碰 userdata,但并非绝对安全,且可能触及 FRP(出厂重置保护)锁、运营商锁、引导加载器锁等状态。本文的读者是已取得合法授权的取证人员;授权范围、设备所有权、以及「是否允许改变设备状态」必须事先确认。

与本板块其他文章的关系:Android 数据存储总览 讲了数据在哪,本文讲怎么把它完整取出来;应用私有数据与 SharedPreferences 讲的是拿到之后怎么分析应用数据。

关键词:TWRP · NANDroid 备份 · recovery · A/B 分区 · 动态分区 super · lpunpack · FBE 文件级加密 · metadata 分区 · Odin · fastboot · boot 镜像

一、概述

Android 设备的提取难度是分层的。常见的可用手段按「对设备状态的改变」从轻到重排列:

手段 需要条件 对设备状态的影响 拿到的证据
常规逻辑提取 已解锁 + ADB 授权 几乎无(开关一次 USB 调试) 应用数据、文件、数据库、日志
root 提取 已 root 无(但使用痕迹会留在系统里) 完整 /data,可读取其他应用私有目录
recovery 物理镜像 bootloader 可解锁,或可进 download 模式 会写入 recovery / boot 分区 全分区原始镜像(含已删除内容)
硬件提取(JTAG / ISP / 芯片脱焊) 需拆机、专业设备 物理破坏 全部 flash,含被锁死的分区

只有第三种和第四种能绕过锁屏密码。 具体来说:

  • 锁屏密码与屏幕锁定(PIN / 图案 / 密码)会加密 userdata(FBE 场景下还涉及 metadata 分区)。recovery 若能成功解密,锁屏就等于被绕过了——因为 recovery 走的是启动早期的路径,不经过 Android 的锁屏凭据输入流程。
  • FRP 锁(Factory Reset Protection)与 bootloader 锁是另一回事:FRP 锁的是「恢复出厂后需原 Google 账号」,bootloader 锁的是「能否刷写分区」。recovery 提取不需要解锁 bootloader(部分机型),但可能触碰 FRP 状态。
  • 加密等级(FLAG_SECURE_BOOT / Verified Boot)在部分新机型上会阻止第三方 recovery 启动。

所以适用场景是明确的:设备屏幕锁定、用户要求提取设备内数据、且已确认 recovery 路径可行。

拿到物理镜像有三条路,各有明确的适用边界:

路线 适用设备 前提 输出
NANDroid 备份(TWRP 内置功能) 绝大多数可进 recovery 的设备 recovery 可启动 tar / 压缩 / 加密的备份文件
dd 直读块设备 同上 recovery 可启动、能挂载外部介质 与物理镜像等价的整盘 dump
网络传输(netcat / adb) 同上 有 USB 数据线、无外部 SD 卡槽 通过 adb 端口转发传到工作站

三条路不冲突。常见做法是先做 NANDroid 备份(快、可靠、带元数据),再对需要的分区做 dd 直读。dd 的优势是能精确控制读哪几个分区,不至于把巨大的 system 一起拉出来。

有三件事不在这条路径的范围内:不讨论如何解锁 bootloader、不提供 FRP 绕过、不涉及任何移除 FRP 锁的技巧。理由不是技术做不到,而是绕过 FRP 的实质是在未获授权的情况下取得他人设备数据,这不属于取证范畴。

设备若因 FRP 或运营商锁无法处理,正确的做法是停止操作、记录设备状态、回到委托方沟通,而不是寻找技术手段。判据很简单:一个取证流程如果需要绕过设备所有者的保护机制,那它本质上已经是攻击而非取证。

真正要处理的技术内容是三块:recovery 的分区原理、备份产物格式、以及拿到备份后如何正确解析成可分析的材料。这一部分是纯取证工程,且对已经拿到设备但需要完整提取的合法场景有实际价值。

二、核心原理

2.1 Android 分区布局的三个演进阶段

要理解 recovery 能做什么,必须先理解现代 Android 的分区布局。

它经历了三个阶段。不同年代的设备差异极大,这是 recovery 步骤对不上号的主要原因:

阶段一:单槽固定分区(Android 6 及更早)

userdata   boot   system   cache   recovery   ...

每个分区都是 GPT 里的物理分区,尺寸固定。recovery 是独立分区。

阶段二:A/B 双槽(Android 7–9)

boot_a   boot_b
system_a system_b
...      (关键分区成对出现)
userdata(只有一个)

引入 A/B 是为了「无缝更新」:更新写进非活动槽,重启切换,失败可回滚。A/B 设备开始,recovery 分区可能被取消,recovery 内容并入 boot。

阶段三:动态分区 super(Android 10+)

super(一个物理分区,里面装着一堆逻辑分区)
    ├── system_a   system_b
    ├── vendor_a   vendor_b
    ├── product_a  product_b
    ├── system_ext_a
    └── odm_a
userdata
metadata

system、vendor 等变成了 super 里的逻辑分区,尺寸可动态调整。

这对取证有直接影响:GPT 里不再有 system 分区,只有一个 super。 拿到裸镜像后,必须用 lpunpack 把 super 拆开才能看到里面的逻辑分区(见 3.8)。

关键转折点是 Android 12/13 引入的 init_boot 与 vendor_boot 分离,以及 Android 11+ 的 Virtual A/B(快照机制)——后者让 OTA 不再需要完整的第二份副本,而是用写时复制快照,进一步改变了分区布局。

2.2 A/B 双槽:为什么恢复镜像会「消失」

这一节解释一个新手最常遇到的困惑:明明刷了 TWRP,重启一次就没了。

原因在 2.1 的阶段二里。Android 有一个机制叫启动时的完整性校验与恢复:

  1. 官方 ROM 的 boot 分区里带有一个恢复出厂 recovery 的逻辑(在某些机型上是 boot 里包含 stock recovery ramdisk,或由 vbmeta 校验);
  2. 官方系统每次正常启动时,会检查 recovery 是否被修改,若检测到非官方 recovery 并配置了自动还原,会用 stock recovery 覆盖回去;
  3. 因此正确的操作纪律是:刷完 TWRP 后不要让它正常启动进 Android,直接手动按键进入 recovery。

对取证而言这条纪律有另一层意义:TWRP 只要停留在 recovery 里不被启动还原,就不影响 userdata。

反过来说,如果设备已经被重启进 Android 且 TWRP 被覆盖,需要重新刷一次。但这个「重刷」动作本身会在设备上留下痕迹(见 2.7),所以尽量一次做对。

2.3 动态分区 super 与逻辑分区

super 分区里的逻辑分区由一套「元数据」描述。这套元数据是取证时必须单独提取的东西:

  • 几何元数据(geometry):描述 super 的几何布局(块大小、几何版本号)。
  • LP 元数据(metadata):一个头部 + 若干分区表项,列出每个逻辑分区的名字、起始块号、大小、属性、group。

提取方法:lpdump 打印元数据,lpunpack 解出逻辑分区镜像。

取证价值:LP 元数据 本身是一个高价值的小工件。

它记录了设备的完整分区布局与更新组划分,是重建设备系统结构的依据,体积只有几 KB。把它单独导出并哈希留档,成本极低,收益明确。

A/B 设备的元数据是两份(每个 slot 一份),互相对应的分区名带 _a / _b 后缀。分析时必须两个 slot 都导出,否则只能看到一半的布局。

2.4 文件级加密 FBE:能不能读取决于 recovery 能力

这一节是全文最容易被误解的地方,单独讲。

FBE(File-Based Encryption,Android 7+)的工作方式:

  • 密钥不是一个全盘密钥,而是按文件派生。每个文件有自己的加密密钥,密钥本身用「用户凭据派生密钥」再加密一次(称为「凭据加密」)。
  • 密钥材料存放在 metadata 分区(加 /data 下的 metadata 目录,以及 /data/misc/keystore)。metadata 分区本身是被整体加密的,需由 TWRP/系统解密。
  • 每个文件的密钥还与路径名绑定——同一个文件被改名后,其密钥派生就变了。这是 FBE 的一个重要特征(见陷阱 3.11)。

因此对取证的实际影响是:

情况 recovery 能否解密 userdata
用户从未设置过锁屏密码 能(凭据加密层不生效或用默认密钥)
设置了锁屏密码,但设备已解锁过一次 能(TWRP 拿到的是已解锁态的密钥)
设置了锁屏密码,设备重启后未解锁 取决于 recovery 构建版本

最后一行是 2026 年的现实。 新 Android 版本对 FBE 做了加固,很多 TWRP 构建已经解不开了。

表现是:进 recovery 后 userdata 显示挂载成功,但 Android/data 下的文件名是乱码、文件打不开,或者整个 /data 读出来全是零。

遇到这种情况,正确的做法不是继续折腾 recovery,而是:

  1. 记录现象(挂载成功但文件名乱码 / 读取返回 EIO / 大小为 0)——这本身就是一个有价值的观察结果;
  2. 转向其他分区——system、vendor、cache、persist、efs 等分区往往不受 FBE 保护,仍有取证价值;
  3. 评估是否需要硬件提取(JTAG/ISP)——这是一个成本与授权的决策,不是技术尝试。

绝对不要在报告里写「已提取全部用户数据」——除非 TWRP 明确显示解密成功且文件可读、可校验。

2.5 NANDroid 备份的三种产物与它们的区别

TWRP 的备份不是单一格式,而是按分区的文件系统类型选择不同的备份方式。这个区分对解析至关重要:

备份方式 内部枚举 适用分区 产物形态
BM_FILES 文件级备份 挂载为文件系统的分区(data / system / cache 等) tar 归档(可选 gzip 压缩)
BM_DD 块级备份 boot、recovery、无文件系统的分区 原始镜像(raw image)
BM_FLASH_UTILS MTD 备份 NAND/MTD 闪存设备(老机型) 厂商工具格式

NANDroid 产物文件名的编码规则(这决定了怎么判断产物类型):

{分区名}.{文件系统类型}.win{分片号}
{分区名}.{文件系统类型}.win{分片号}.{压缩或加密标记}

实例:
  data.ext4.win000              ← 未压缩未加密
  data.ext4.win000.gz           ← gzip 压缩
  data.ext4.win000.enc          ← AES 加密
  boot.emmc.win000              ← 块级原始镜像
  cache.ext4.win000             ← 同 data

⚠️ 注意 .win 里的数字是分片序号,不是大小。 超过单个文件大小限制的 tar 会被切成 win000、win001、win002……多个分片必须按序号拼回一个完整归档才能解包,顺序错了会解出垃圾。这是解析备份时最高频的错误来源。 见陷阱 4.6。

TWRP 备份还会自动排除一部分路径(如缓存目录、系统预装的部分应用)。这些排除规则在 metadata/exclude.list 里。

做完整性评估时要把它读出来——被排除的内容在备份里根本没有,报告里不能宣称「已备份全部分区内容」。

2.6 备份加密的机制与现实约束

TWRP 支持对备份做加密(编译期可用 TW_EXCLUDE_ENCRYPTED_BACKUPS 关闭)。机制是:

用户密码 P
    → PBKDF2 派生密钥 K(salt 与迭代次数存在备份元数据里)
    → AES-256 加密备份数据流

关键现实约束:

  1. 密码丢了,备份就永久不可恢复。 没有后门、没有暴力破解的「技术后门」(PBKDF2 迭代次数虽有限,但面对高质量密码仍是天文数字级)。
  2. TWRP 官方不建议、也不鼓励对取证备份加密。 因为一旦出事,分析人员自己也打不开。
  3. 如果检材方提供了加密备份的密码,正确做法是让设备方或委托方在 TWRP 里执行恢复操作解密,而不是让分析方离线破解。

工业实践是:备份不加密,取证过程的保密靠访问控制与保管链,不靠加密备份。

对本文读者的实操建议:取证备份一律不加密。 如果 TWRP 提示是否加密,选「否」。需要保护数据的话,在工作站侧对整个备份目录做加密(比如加密卷或归档加密),这样既能保护数据,又不影响后续分析。

2.7 检测层:设备被做过 TWRP 操作后留下什么

取证人员要清楚一个双向问题:既要知道怎么用 TWRP,也要知道别人用过 TWRP 会留下什么。

设备上会留下的痕迹:

位置 痕迹 说明
recovery / boot 分区 recovery 镜像被替换 与官方镜像哈希不符
backup 目录 /data/media/0/TWRP/BACKUPS/… 如果用户之前做过 NANDroid 备份,会留下完整备份
TWRP 目录 /data/media/0/TWRP/ 下的配置与日志 可能含操作历史
挂载点 recovery 挂载分区时写入的 lost+found 证明该分区曾被以读写方式挂载
系统分区 若用户刷过包,system 下有第三方应用 需与官方镜像对比

关键洞察:如果一台可疑设备上已经存在 TWRP 目录和 NANDroid 备份,这些备份本身就是极其重要的证据。

它们可能由另一个人、同一套流程在更早的时间点提取过,时间点比本次委托更早,价值可能更高。

发现这类遗留备份时,先停下自己的备份操作,把原始备份完整保留下来。

反过来,如果本方做了 TWRP 提取,必须在报告里如实记录这次设备状态改变。 见陷阱 4.1。


三、操作步骤

3.1 采集前的状态固定与合法性确认

全文最容易跳过、风险最高的一步。

★ 不可逆操作,进入前必须确认。

1. 确认设备当前状态并留档(此步不改变设备)

记录:屏幕锁定状态、USB 调试状态、bootloader 状态、连接电脑时的设备标识

确认清单(逐项打勾,任一不满足则停止):

  • 已取得书面授权,且授权范围明确包含「改变设备状态」(不只是「提取数据」)
  • 已告知委托方本操作会写入 recovery / boot 分区及其风险
  • 已在同型号的备用机上完整跑通流程(见 3.3 陷阱)
  • 设备电量充足(见 3.4)
  • 设备外观已拍照留档(操作前状态)
  • 已记录设备型号、序号、IMEI 等标识

设备状态记录的具体内容(在连接电脑时采集):

Android 侧(若能连上)

adb devices -l adb shell getprop ro.product.model adb shell getprop ro.build.version.release adb shell getprop ro.boot.verifiedbootstate # green / orange / red adb shell getprop ro.boot.flash.locked # 1 = 已锁 adb shell getprop ro.boot.vbmeta.device_state # locked / unlocked adb shell getprop ro.boot.slot_suffix # _a / _b

bootloader 侧

fastboot devices fastboot getvar current-slot # a / b fastboot getvar slot-count # 通常 2 fastboot getvar is-userspace # 区分 bootloader fastboot 与 fastbootd fastboot getvar product # 确认机型,必须与 recovery 镜像型号一致

⚠️ ro.boot.verifiedbootstate 是关键前置判断:值为 red 表示启动链校验失败,第三方 recovery 很可能无法启动;orange 表示 bootloader 已解锁但校验被破坏,recovery 通常可启动。这个值要在动设备之前就记录下来,作为后续分析「为什么 recovery 启动失败」的依据。

3.2 用 fastboot 只读导出分区布局

在任何写操作之前,先用只读命令把分区布局取出来。这是一份纯读取的、高价值的工件:

进入 bootloader(不是 fastbootd)

adb reboot bootloader

列出所有分区(只读)

fastboot getvar all 2>&1 | tee device-vars.txt

读 GPT 表(物理分区布局)

fastboot getvar partition-type:boot fastboot getvar partition-size:userdata fastboot getvar has-slot:boot

动态分区设备需要进 fastbootd

fastboot reboot fastboot fastboot getvar is-userspace # 应为 yes fastboot getvar snapshot-update-status # none / snapshotted / merging

把 fastboot getvar all 的完整输出保存下来。 它包含分区大小、slot 状态、校验状态、机型标识,是后续所有分析的事实基础。

这个动作完全不改变设备(只读),所以即使后续因授权问题终止,这一步的产物依然有效。

3.3 安装 recovery 的三条路径与选择依据

★ 这一步会写入设备,bootloader / recovery 分区内容会改变。

先判断设备布局类型:

Q: fastboot getvar has-slot:boot 返回什么?
   返回 1 / true           → A/B 设备,按 A/B 流程
   返回空或无此变量        → A-only 老设备,按老流程

Q: fastboot getvar is-userspace 返回 yes?
   是                      → 动态分区设备(super),刷写需在 fastbootd 里做

Q: fastboot getvar current-slot 返回什么?
   a / b                   → 记下当前 slot,**不要盲目 --slot=all**

路径一:A-only 设备(独立 recovery 分区)—— 最简单

fastboot flash recovery twrp.img

不要 fastboot reboot!直接拔线,手动按键进 recovery

路径二:A/B 设备(无独立 recovery 分区,recovery 在 boot 里)

方案 A:一次性启动(推荐,不写设备)

fastboot boot twrp.img

方案 B:永久写入