概览
这篇文章记录一次完整的 vivo S9 折腾过程。
我的初心很简单:在 vivo S9 上同时拥有已解锁 Bootloader、Magisk Root,以及尽可能新的系统体验。
这台机器的基本信息如下:
设备:vivo S9
型号:PD2072 / V2072A
硬件:PD2072MA
芯片:MediaTek Dimensity 1100 / MT6891
原系统:OriginOS 1 / Android 11
已验证可 Root 版本:PD2072_A_1.18.15
最后的结论是:
vivo S9 可以在特定 OriginOS 1 基线下完成 BL 解锁与 Root,但直接手动把 OriginOS 3 / Android 13 OTA 解包后混刷到旧启动链上,风险极高。本次尝试最终导致设备无法进入 Fastboot,只剩 MTK Preloader 端口。后续更合理的方案,是保留 OriginOS 1 作为可 Root 宿主系统,通过 DSU 运行较新的 Android / OriginOS GSI。
本文不是“一键成功教程”,而是一份完整复盘:哪些步骤成功了,哪里判断错了,哪些坑不能再踩。
一、最初状态:OriginOS 1 成功解锁 BL 并 Root
这台 vivo S9 最开始运行的是 OriginOS 1,Android 11。
通过第三方大佬的方案,设备成功解锁 Bootloader。随后我借助 DSU 启动了一个带 adb root 的 GSI 系统,从原机分区中提取 boot 镜像。
在 DSU 中确认 adbd 已经是 root:
adb root
adb shell id
输出中可以看到:
uid=0(root) gid=0(root)
随后确认 boot 分区路径:
adb shell ls -l /dev/block/by-name/boot*
本机结果为:
/dev/block/by-name/boot -> /dev/block/sdc47
/dev/block/by-name/boot_para -> /dev/block/sdc28
因为没有 init_boot 分区,所以本机 Root 应该修补 boot.img,而不是 init_boot.img。
提取原厂 boot:
adb shell "dd if=/dev/block/by-name/boot of=/data/local/tmp/stock-boot.img bs=4M"
adb shell stat -c %s /data/local/tmp/stock-boot.img
adb pull /data/local/tmp/stock-boot.img stock-boot-clean.img
certutil -hashfile stock-boot-clean.img SHA256
得到的 boot 大小为:
100663296 bytes
二、vivo S9 Root 的关键:先修 vivo 内核限制,再交给 Magisk
vivo 机型并不是简单地把 boot.img 扔给 Magisk 修补就一定能成功。
本机内核版本:
adb shell uname -r
输出:
4.14.186+
因此使用了 vivo-4.x-kernel-autopatch 对 boot 内核做 vivo 相关修补。
第一次选择“不修补 do_mount_check”后,刷入 Magisk 修补后的 boot,系统可以启动,但 Magisk 不识别 Root:
adb shell su -c id
输出:
/system/bin/sh: su: inaccessible or not found
第二次重新修补,选择:
修补 vivo do_mount_check:是
修补分区挂载:否
随后再把该 boot 放入 Magisk 修补,刷入:
fastboot flash boot magisk_patched.img
fastboot reboot
重启后验证 Root:
adb shell su -c id
成功输出:
uid=0(root) gid=0(root) groups=0(root) context=u:r:magisk:s0
这一点非常关键:
vivo S9 / 4.14 内核上,Magisk 前面可能需要先做 vivo 内核反限制补丁,否则系统能开机但 Magisk 不一定能接管 su。
三、完整分区备份:工具备份不一定完整,要人工核对 by-name
Root 后,我尝试使用多个工具备份全机分区,包括爱玩机工具箱、多系统工具箱等。
一开始发现某些工具导出的还原脚本中缺少 lk 或 boot,于是手动查看当前所有分区:
adb shell su -c "ls -l /dev/block/by-name"
本机可见关键分区包括:
boot
boot_para
dtbo
recovery
super
vendor_boot
vbmeta
vbmeta_system
vbmeta_vendor
vbmeta_oem
vbmeta_vgc
lk
lk2
preloader_ufs_a
preloader_ufs_b
md1img
nvram
nvdata
nvcfg
persist
protect1
protect2
tee1
tee2
scp1
scp2
sspm_1
sspm_2
gz1
gz2
dpm_1
dpm_2
经验:
备份工具生成的文件列表不能盲信。必须用
/dev/block/by-name作为当前设备分区的基准,再核对每个.img是否存在。
有些工具只识别 lk2,没有备份 lk。这不一定表示设备没有 lk,也可能是工具识别或权限限制问题。
后面还发现,即使 Magisk 已 Root:
adb shell su -c id
输出正常:
uid=0(root) gid=0(root) context=u:r:magisk:s0
但直接读取某些块设备仍可能被 SELinux 拦截:
adb shell su -c "dd if=/dev/block/by-name/boot of=/data/local/tmp/boot.img bs=4M"
可能返回:
Permission denied
这说明不是 su 丢了,而是 vivo 原系统 SELinux 对块设备访问仍有限制。
最终我选择合并多工具备份,并人工补齐关键缺失分区。合并原则是:
- 同名分区只保留一个;
- 若两份备份 hash 相同,任选其一;
- 若 hash 不同,优先保留明确来源、时间、大小正常的版本;
boot.img保留原厂未 Root 版本;- 另存
boot-rooted.img作为当前 Root 在用版本; - 不随意覆盖
nvram、nvdata、persist、protect*等个体化分区。

四、尝试升级 OriginOS 3:解包 OTA,而不是直接 OTA
我的目标是升级到更新系统,同时保留 BL 解锁和 Root。
后来找到一份 OTA 包:
PD2072E_A_6.10.1.zip
该包目标版本为:
Android 13
OriginOS 3
post-version=PD2072_A_6.10.1
post-build=vivo/PD2072/PD2072:13/TP1A.220624.014/compiler11211206:user/release-keys
先读取 OTA 文件列表:
$ota = "D:\基带备份\PD2072E_A_6.10.1.zip"
Add-Type -AssemblyName System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::OpenRead($ota)
$zip.Entries.FullName | Set-Content -LiteralPath "D:\基带备份\ota-file-list.txt" -Encoding utf8
$zip.Dispose()
确认 OTA 中没有完整 super.img,而是包含:
system.transfer.list
system.new.dat.*
system.patch.dat
vendor.transfer.list
vendor.new.dat.*
vendor.patch.dat
product.transfer.list
product.new.dat.br
product.patch.dat
oem/OEM_PD2072B_CN-ZH_FULL_SC_NULL.transfer.list
oem/OEM_PD2072B_CN-ZH_FULL_SC_NULL.new.dat.br
vgc/VGC_NULL_PD2072MA.transfer.list
vgc/VGC_NULL_PD2072MA.new.dat.br
并且包含独立固件镜像:
boot.img
dtbo.img
recovery.img
vbmeta.img
vbmeta_system.img
vbmeta_vendor.img
firmware-update/vbmeta_oem_PD2072B_CN-ZH_FULL_SC_NULL.img
firmware-update/vbmeta_vgc_NULL_PD2072MA.img
lk.img
preloader_raw.img
md1img.img
audio_dsp.img
scp.img
tee.img
sspm.img
gz.img
dpm.img
mcupm.img
读取本机属性:
adb shell getprop ro.vivo.hardware.version
adb shell getprop ro.hardware.bbk
adb shell getprop ro.vivo.oem.name
adb shell getprop ro.vgc.device.name
本机匹配:
ro.vivo.hardware.version=PD2072MA
ro.hardware.bbk=PD2072MA
ro.vivo.oem.name=PD2072B_CN-ZH_FULL_SC_NULL
ro.vgc.device.name=NULL_PD2072MA
说明 OTA 包中的对应 OEM/VGC 分支与本机属性匹配。
五、解包 new.dat / transfer.list,生成 system/vendor/product/cust/vgc
由于 OTA 没有完整 super.img,需要从 block OTA 中还原逻辑分区镜像。
核心动态分区指令:
remove vendor
remove system
remove product
add system main
add vendor main
add product main
resize system 7510220800
resize vendor 1895133184
resize product 375250944
OEM 分区:
remove cust
add cust main
resize cust 348160
VGC 分区:
remove vgc
add vgc main
resize vgc 348160
最终还原得到:
system.img 7510220800 bytes
vendor.img 1895133184 bytes
product.img 375250944 bytes
cust.img 348160 bytes
vgc.img 348160 bytes
使用 WSL 的 e2fsck 校验 ext4 镜像:
wsl --cd "D:\基带备份\PD2072_Android13_Work\images" -e sh -lc '
for f in system.img vendor.img product.img cust.img vgc.img; do
echo "===== $f ====="
e2fsck -fn "$f"
echo "exit=$?"
done
'
全部返回 exit=0,说明镜像文件本身可读、文件系统结构正常。
六、使用 otatools / lpmake 重组 super
接下来使用 Android 官方构建产物中的 otatools.zip,获取 Linux x86_64 的:
lpmake
lpunpack
avbtool
然后将 system/vendor/product/cust/vgc 合成为新的 super sparse 镜像。
在刷写前,还将 raw ext4 镜像转换为 Android sparse image,并做 roundtrip 校验:
wsl -e img2simg system.img sparse/system.img
wsl -e simg2img sparse/system.img system-roundtrip.img
逐个校验 SHA-256,确认 sparse 到 raw 还原后与原始镜像完全一致:
system Match=True
vendor Match=True
product Match=True
cust Match=True
vgc Match=True
最终得到可刷写的:
super-originos3-sparse.img
七、刷写 OriginOS 3:表面成功,但实际启动链失败
刷写前确认 Fastboot 状态:
fastboot getvar unlocked
fastboot getvar is-userspace
fastboot getvar product
输出:
unlocked: yes
is-userspace: no
product: k6891v1_64
刷写命令:
fastboot -S 120M flash super "D:\基带备份\PD2072_Android13_Work\super-originos3-sparse.img"
fastboot flash dtbo "D:\基带备份\PD2072_Android13_Work\firmware\dtbo-originos3.img"
fastboot flash recovery "D:\基带备份\PD2072_Android13_Work\firmware\recovery-originos3.img"
fastboot flash vbmeta_system "D:\基带备份\PD2072_Android13_Work\firmware\vbmeta_system-originos3.img"
fastboot flash vbmeta_vendor "D:\基带备份\PD2072_Android13_Work\firmware\vbmeta_vendor-originos3.img"
fastboot flash vbmeta_oem "D:\基带备份\PD2072_Android13_Work\firmware\vbmeta_oem-originos3.img"
fastboot flash vbmeta_vgc "D:\基带备份\PD2072_Android13_Work\firmware\vbmeta_vgc-originos3.img"
fastboot --disable-verity --disable-verification flash vbmeta "D:\基带备份\PD2072_Android13_Work\firmware\vbmeta-originos3.img"
fastboot flash boot "D:\基带备份\PD2072_Android13_Work\boot-stock-originos3.img"
所有命令均返回:
OKAY
Finished
但重启后手机黑屏,最多震动一下,无法进入系统,也无法进入 Fastboot。
电脑设备管理器周期性出现:
MediaTek PreLoader USB VCOM (Android)
这说明:
Preloader 仍存活,但 Android、Recovery、Fastboot/LK 启动链没有成功起来。
八、为什么会失败:混刷 Android 13 上层系统,但保留 Android 11 底层启动组件
这次失败的核心问题,很可能不是 super.img 构建坏了,而是刷写策略错误。
为了保留 BL 解锁状态,我刻意没有刷:
lk / lk2
preloader / sda / sdb
同时也没有同步刷 OTA 中与启动链相关的一组底层固件:
sspm
tee
scp
mcupm
gz
dpm
md1img
audio_dsp
pi_img
cam_vpu*
spmfw
OTA 原始脚本中,其实会根据当前活动分支写入:
lk 或 lk2
preloader
sspm_1 或 sspm_2
tee1 或 tee2
scp1 或 scp2
mcupm_1 或 mcupm_2
gz1 或 gz2
dpm_1 或 dpm_2
并执行类似:
switch_active("lk", "lk2")
switch_active("preloader", "preloader2")
也就是说,vivo 的 OTA 把这些底层分区视为一组升级,而我为了保留解锁状态没有刷它们。
结果变成:
Android 13 boot / dtbo / super / vbmeta
+
Android 11 lk / preloader / tee / scp / sspm / gz / dpm
这个组合很可能无法完成早期启动。
经验教训:
动态分区设备并不是只刷 super、boot、dtbo、vbmeta 就一定能跨大版本升级。尤其 vivo/MTK 机型,启动链、TEE、SCP、SSPM、GZ、DPM 等底层固件可能与新内核、新 vendor 强绑定。为了保留 BL 解锁而拆开 OTA 的固件组,风险非常高。
九、MTK 救砖排查:Preloader 在,但公开 mtkclient 没能建立会话
设备黑屏后,电脑仍能识别:
MediaTek PreLoader USB VCOM (Android)
VID_0E8D PID_2000
这意味着不是彻底硬砖。
随后尝试使用公开版 mtkclient 进行只读诊断。
一开始 Windows 端报错:
NotImplementedError: Operation not supported or unimplemented on this platform
原因是 UsbDk 未正常安装。
安装 WinFsp 与 UsbDk 后,UsbDkController -n 又报:
Enumeration failed
进一步排查发现系统 USB 类 UpperFilters 中存在:
hrdevmon
这是火绒的 USB 设备监控过滤驱动。卸载火绒并重启后,UsbDk 正常运行:
SERVICE_NAME: usbdk
STATE: RUNNING
UsbDk 能枚举到手机:
ID: 0e8d:2000
USB\VID_0E8D&PID_2000
再次运行:
py mtk.py printgpt
但结果是:
Preloader - Status: Waiting for PreLoader VCOM
Port - Handshake failed after retries
Preloader - [LIB]: Status: Handshake failed, retrying...
这说明公开 mtkclient 虽然能看到 USB 设备,但没有成功完成 MTK 协议握手,更没有走到读取 GPT 或 DA 认证阶段。
后来询问此前帮忙解锁 BL 的大佬,对方表示当初使用的是“免 DA 联机方案”,而本机升级后 DA 相关能力已被封,只有 vivo 官方能进入。
最终判断:
当前个人侧无法通过公开 mtkclient 安全恢复分区。最佳方案是送 vivo 官方售后做深度刷机,先恢复可开机状态。
十、售后恢复时的建议
送售后时,不需要说明 Root、Magisk、DSU 等细节,只需要描述故障现象:
系统升级后无法开机;
黑屏,偶尔震动;
电脑可识别 MediaTek PreLoader USB VCOM;
需要恢复完整官方系统。
如果能沟通版本,优先请求恢复:
PD2072_A_1.18.15
这是我已验证可以解锁 BL 和 Root 的 OriginOS 1 基线。
但是否能刷回旧版本,要看售后工具和官方策略。若只能刷最新版本,也应先让机器恢复开机,再考虑后续方案。
需要接受的现实:
- 售后大概率会清除用户数据;
- 可能覆盖
lk/preloader,导致 BL 解锁状态失效; - 若售后刷入新版本,不能再复用旧 Android 11 的
boot.img或 Root boot; - 重新 Root 必须从当前系统同版本重新提取 stock boot。
十一、最终方案:OriginOS 1 做宿主,DSU 跑新系统 GSI
经过这次失败后,我对 vivo S9 的路线重新排序。
如果最终能恢复到 PD2072_A_1.18.15,并再次由大佬解锁 BL,那么最稳妥的使用方式是:
OriginOS 1 / Android 11:作为稳定宿主系统,保留 BL 解锁与 Root
DSU:用于运行 Android 14 / OriginOS 5 / OriginOS 6 等 GSI
这样做的好处是:
- 不再改写真实
super/boot/lk/preloader; - DSU 启动失败时,通常重启即可回到原系统;
- 保留 Root 宿主系统,方便备份、调试、切换;
- 可以体验较新的 Android / OriginOS 用户空间。
但要理解 DSU 的边界:
DSU 只替换动态系统镜像;
仍沿用原机 Android 11 的 kernel、vendor、基带、相机 HAL;
所以不是完整意义上的官方 OriginOS 5/6 升级。
可能存在的问题包括:
相机异常
指纹异常
NFC 异常
VoLTE/5G 异常
耗电异常
亮度/刷新率异常
部分 vivo 服务不可用
十二、DSU 空间:256GB 机型不建议分 180GB 或 200GB
我的 vivo S9 是 256GB 存储,原本想给 DSU 分 180GB 甚至 200GB。
后来分析发现,不建议这么做。
DSU 的 system_gsi.img 和 userdata_gsi.img 都实际存放在宿主系统 /data 中。AOSP gsid 在安装前会检查:
/data 当前空闲空间 >= GSI system 大小 + DSU userdata 大小
/data 当前空闲比例 >= 40%
这不是“安装后必须剩 40%”,而是安装开始前的检查。
但即使 210GB 可用空间在数学上可能容纳:
180GB DSU userdata
+
8 至 12GB GSI system
也不适合日用,因为宿主 OriginOS 只剩约 20GB,后续很容易出现缓存、日志、下载、Magisk 更新、DSU 临时文件挤爆空间的问题。
更现实的 DSU 容量建议:
首次测试:16GB
普通日用:32GB
重度日用:64GB
较激进:128GB
边缘尝试:160GB
不建议:180GB / 200GB
如果使用 DSU Sideloader 的 System/Root 模式或多系统工具箱,可能通过 custom gsid 降低默认限制。但这只能突破一部分“界面或策略限制”,不能突破真实空间、fiemap、extent、文件系统碎片等底层限制。
十三、DSU 状态栏通知和数据保留
DSU 系统里状态栏会常驻一个类似:
请重启进入原版系统
的通知。
这是正常设计,不是错误。
它表示当前运行的是 Dynamic System,并提供返回原系统的入口。
DSU 是否重启后保留,取决于 sticky mode:
gsi_tool enable # 启用 sticky mode,重启后继续进入 DSU
gsi_tool disable # 关闭 sticky mode,下次重启回原系统
Discard / 舍弃 # 删除 DSU system 和 DSU userdata,数据丢失
Root 后可以执行:
adb shell su -c "gsi_tool status"
adb shell su -c "gsi_tool enable"
启用 sticky mode 后,正常重启不会清空 DSU 数据。
只有以下情况会导致 DSU 数据丢失:
手动点击舍弃 DSU;
重新安装 DSU;
DSU 文件损坏;
手动清理 /data/gsi;
恢复出厂或刷机清空 userdata。
不建议强行隐藏该系统通知。它虽然碍眼,但也是回到原系统的安全出口。
十四、这次最大的教训
这次折腾最大的教训,不是某个命令写错,而是对 vivo / MTK 大版本升级风险估计不足。
我原本以为:
保留 lk/preloader 以保留 BL 解锁
刷入 Android 13 的 super/boot/dtbo/vbmeta
即可完成半手动升级
但 vivo OTA 实际上把底层启动组件作为一组升级。只升级上层系统和 boot,不升级底层固件,可能导致早期启动链无法继续。
对这类设备,更安全的策略应该是:
不要混刷跨大版本核心分区;
不要拆开 OTA 的启动链固件组;
不要在没有官方 DA/售后恢复能力时冒险刷 lk/preloader;
不要把“Fastboot OKAY”理解成“系统一定能启动”。
十五、后续计划
如果售后能恢复到 PD2072_A_1.18.15:
1. 重新确认系统版本;
2. 找原作者再次解锁 BL;
3. 重新提取 stock boot;
4. 用 vivo 4.x kernel autopatch 修补;
5. 用 Magisk 修补 boot;
6. 刷入 Root boot;
7. 保留完整分区备份;
8. 不再尝试手动混刷 OriginOS 3;
9. 使用 DSU 测试 Android 14 / OriginOS 5 / OriginOS 6 GSI。
如果售后只能刷最新系统:
1. 先保证手机恢复正常;
2. 不复用旧 boot;
3. 不强行 Root;
4. 询问原作者是否支持当前版本解锁;
5. 若不支持,则接受官方系统或等待新方案。
最终我更倾向的日用组合是:
稳定宿主:OriginOS 1 / Android 11 / Root
新系统体验:DSU + 新版 GSI
DSU 容量:64GB 至 128GB
结语
这次折腾从成功 Root,到完整备份,再到解包 OTA、重组 super、刷写 Android 13,最后设备只剩 Preloader 端口,算是把 vivo S9 的上限和边界都摸了一遍。
结论有点遗憾,但也更清晰了:
对 vivo S9 这类 MTK + vivo 深度定制机型,BL 解锁和新系统体验很难两全。真正稳妥的方式,是把已验证可解锁的 OriginOS 1 当作宿主系统,用 DSU 去承载新系统体验,而不是手动混刷官方大版本 OTA。
Root 和 BL 解锁的自由很诱人,但在没有官方线刷包、没有可用 DA、没有完整救砖路径的前提下,每一次跨大版本刷写都应该先假设“可能救不回来”。
这次炸机的代价,就是下一次方案的边界。