给那些发现bug想要自己动手改device tree甚至改源码修复的人提供一下我修bug时的思路。看着玩也可以。
基础信息:
- 型号: vivo Y73s
- 代号: PD2031
- SoC: MediaTek MT6853 (Dimensity 720)
- Android版本: 10 (SDK 29)
- 加密方式: FBE (File-Based Encryption) with aes-256-xts:aes-256-cts v1
- TEE: Trustonic MobiCore
- Keymaster: HIDL 4.0
使用 twrpdtgen 生成设备树
创建 device/vivo/k6853v1_64_6360/
最初的目录结构:
device/vivo/k6853v1_64_6360/
├── Android.bp
├── Android.mk
├── AndroidProducts.mk
├── BoardConfig.mk
├── device.mk
├── extract-files.sh
├── omni_k6853v1_64_6360.mk
├── README.md
├── recovery.fstab
├── setup-makefiles.sh
├── vendorsetup.sh
├── prebuilt/
│ ├── kernel
│ ├── dtb.img
│ └── dtbo.img
└── recovery/root/
├── init.recovery.mt6853.rc
├── init.recovery.platform.rc
├── init.recovery.svc.rc
├── init.recovery.touch.rc
├── init.recovery.wifi.rc
└── ueventd.rc
啥也用不了,除了TWRP加载界面有短暂的USB连接 (进主界面后USB连接断开) 以外就没了。
TWRP启动后触摸屏无响应,无法进行任何操作。
分析vivo官方rec后发现,vivo使用自定义触摸固件加载机制,需要从原厂的rec里提取:
- 正确的产品型号属性 (
ro.vivo.product.model) - 触控固件更新程序 (
vts_app_recovery)
文件: init.recovery.mt6853.rc
on early-init
setprop ro.vivo.product.model PD2031
setprop ro.product.model PD2031
setprop ro.product.board k6853v1_64_6360
on boot
exec u:r:recovery:s0 root root -- /system/bin/vts_app_recovery --update
添加文件:
recovery/root/system/bin/vts_app_recovery- 从原厂recovery提取的触控固件更新程序
进入 TWRP 主界面前有短暂的 ADB 连接,但进入主界面后 ADB 断开,电脑无法识别设备。
设备使用 USB configfs 模式,TWRP 进入主界面后 USB 配置被重置,需要正确配置 configfs 参数。
文件: init.recovery.mt6853.rc
on init
setprop sys.usb.configfs 1
setprop sys.usb.ffs.aio_compat 1
设备进入TWRP后,一旦在Recovery内点击重启进入Recovery后,重启只能进入TWRP,形成重启循环。
起初为了修复adb reboot recovery无效果的问题,我在init拦截了reboot recovery的请求并且插入执行了一个脚本(/system/bin/rebootrecovery.sh),该脚本会向misc分区写入重启进rec的指令。
可是我没有写清除指令的脚本,就导致了这个问题,手机重启后持续引导至recovery。
脚本: recovery/root/system/bin/clear_bcb.sh
dd if=/dev/zero of=/dev/block/by-name/misc bs=512 count=1触发: init.recovery.mt6853.rc
on post-fs
exec u:r:recovery:s0 root root -- /system/bin/clear_bcb.sh
脚本: recovery/root/system/bin/rebootrecovery.sh
MISC_PARTITION="/dev/block/by-name/misc"
echo -n "boot-recovery" > /tmp/bcb_command
dd if=/dev/zero bs=2048 count=1 2>/dev/null | dd of=$MISC_PARTITION bs=2048 count=1 2>/dev/null
dd if=/tmp/bcb_command of=$MISC_PARTITION bs=1 count=13 2>/dev/null
sync
# 然后reboot
/system/bin/reboot触发:
on property:sys.powerctl=reboot,recovery
exec u:r:recovery:s0 root root -- /system/bin/rebootrecovery.sh
FBE解密需要Trustonic TEE支持,但TEE daemon无法启动。
on post-fs
wait /dev/block/by-name/persist
mount ext4 /dev/block/by-name/persist /mnt/vendor/persist noatime
chown system system /mnt/vendor/persist
chmod 0771 /mnt/vendor/persist
mkdir /mnt/vendor/persist/mcRegistry 0771 system system
on post-fs
chmod 0666 /dev/mobicore
chmod 0666 /dev/mobicore-user
chmod 0660 /dev/t-base-tui
chmod 0660 /dev/rpmb0
chown system system /dev/mobicore
chown system system /dev/mobicore-user
chown system system /dev/t-base-tui
chown system system /dev/rpmb0
service mobicore /vendor/bin/mcDriverDaemon \
--P1 /mnt/vendor/persist/mcRegistry \
-r /vendor/app/mcRegistry/06090000000000000000000000000000.drbin \
-r /vendor/app/mcRegistry/020f0000000000000000000000000000.drbin \
-r /vendor/app/mcRegistry/05120000000000000000000000000000.drbin \
-r /vendor/app/mcRegistry/05070000000000000000000000000000.drbin \
-r /vendor/app/mcRegistry/030b0000000000000000000000000000.drbin \
-r /vendor/app/mcRegistry/030c0000000000000000000000000000.drbin
disabled
seclabel u:r:recovery:s0
注: init.recovery.platform.rc和init.recovery.mt6853.rc内都有这段配置,理论上来讲保留1个,另外一个可以注释掉,但是我秉持着能用就不动的原则还是留下了。
从原厂rec提取以下trustlet文件到 /vendor/app/mcRegistry/:
06090000000000000000000000000000.drbin- Keymaster TA020f0000000000000000000000000000.drbin- Gatekeeper TA05120000000000000000000000000000.drbin05070000000000000000000000000000.drbin030b0000000000000000000000000000.drbin030c0000000000000000000000000000.drbin
TEE 解密失败,日志显示 boot_patchlevel = 20300101(默认值),而原厂应该是 20190606。
FBE 密钥在原厂系统创建时绑定了 boot_patchlevel = 20190606,TEE 验证时发现不匹配导致解密失败。
验证流程:
Bootloader ─── 读取 AVB footer ─── 验证签名 ───┬── 原厂签名: 传递 20190606 给 TEE ─→ ✓ 验证通过
└── 错误签名: 传递 20300101 给 TEE ─→ ✗ 验证失败
TWRP 使用非原厂密钥签名,bootloader 不信任,因此向 TEE 传递默认值 20300101。
通过分析 magiskboot 重打包后的原厂 recovery,发现:
- 原厂recovery打开adb功能后,检查dmesg发现tee可以正常返回20190606。
- 我想直接修改twrp源码让他强制返回20190606,但是理论上这是不可能的。因为boot_patchlevel 在 kernel 启动前就已经写入 TEE 安全内存。
- 我突然想起来,原厂的recovery应该是没有adb功能的,我是通过magiskboot解包修改prop.default文件后强开的adb权限
- 问题是修改了文件再重打包,那么 AVB 签名本来应该失效了。
- 经过分析发现,magiskboot 保留了完整的原厂 AVB footer(包括 Vivo 公钥)!
- 所以可得 vivo 的 Bootloader 只检查公钥匹配,不真正验证 Hash
- 因此可以用原厂 AVB footer + TWRP 内容创建混合镜像
创建 make_hybrid.py 脚本:
脚本一共做了4件事
- 从原厂 recovery 提取 Vivo AVB footer (保存到 vivo_avb_footer.bin)
- 从 TWRP recovery.img 提取内容(去除 AVB footer)
- 将 Vivo AVB footer 附加到 TWRP 内容后
- 输出混合镜像
使用方法:
# 编译后运行
python3 device/vivo/k6853v1_64_6360/make_hybrid.py混合镜像刷入后,TEE 正确读取 boot_patchlevel = 20190606,解密验证通过。
Keymaster服务因SDK版本不匹配而拒绝工作。设备原厂是Android 10 (SDK 29),但TWRP基于更高版本。
可以使用resetprop强行修改ro的prop。
on post-fs
exec u:r:recovery:s0 root root -- /system/bin/resetprop ro.build.version.sdk 29
exec u:r:recovery:s0 root root -- /system/bin/resetprop ro.system.build.version.sdk 29
exec u:r:recovery:s0 root root -- /system/bin/resetprop ro.vendor.build.version.sdk 29
exec u:r:recovery:s0 root root -- /system/bin/resetprop ro.product.build.version.sdk 29
exec u:r:recovery:s0 root root -- /system/bin/resetprop ro.odm.build.version.sdk 29
exec u:r:recovery:s0 root root -- /system/bin/resetprop ro.system_ext.build.version.sdk 29
service vendor.keymaster-4-0-trustonic /vendor/bin/hw/android.hardware.keymaster@4.0-service.trustonic
disabled
user root
group root drmrpc
seclabel u:r:recovery:s0
service gatekeeper-1-0 /vendor/bin/android.hardware.gatekeeper@1.0-service
disabled
user root
group root
seclabel u:r:recovery:s0
service wait_for_keymaster /system/bin/wait_for_keymaster
disabled
user root
seclabel u:r:recovery:s0
on property:recovery.service=1
stop keystore2
start vendor.keymaster-4-0-trustonic
start gatekeeper-1-0
exec_start wait_for_keymaster
start guardianangle
启动顺序必须是: mobicore → keymaster → gatekeeper → wait_for_keymaster , 错了就炸解密。
问题:
经过上面的修改后,解密仍然失败,有 signal: 6 (SIGABRT 崩溃)且崩溃非常快,几乎在解密开始时就发生了。
在recovery.log里观察到 Is_Encrypted Is_Decrypted 同时存在, 这表示 DE 已解密但 CE 未解密。
最后发现TWRP原有的Keymaster实现与Trustonic TEE的HIDL 4.0接口不兼容。
这里是一个坑,如果我选择安卓10的版本构建的话就不会出这种事,但好巧不巧我用的安卓12....
system_vold 这是安卓10的实现。有兴趣的可以看看。
文件: system/vold/Keymaster.cpp, system/vold/Keymaster.h
主要修改:
#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
using ::android::hardware::keymaster::V4_0::IKeymasterDevice;
class Keymaster {
public:
Keymaster();
bool isValid() { return mDevice != nullptr; }
KeymasterOperation begin(km::KeyPurpose purpose,
const km::AuthorizationSet& inParams,
const std::string& blob);
private:
sp<IKeymasterDevice> mDevice;
};问题: convertToHidl() 在处理不同tag类型时会导致Recovery崩溃。
解决方案: 根据tag类型正确设置union成员
switch (typeFromTag(param.tag)) {
case TagType::ENUM:
case TagType::ENUM_REP:
hParam.f.algorithm = static_cast<Algorithm>(param.f.integer);
break;
case TagType::UINT:
case TagType::UINT_REP:
hParam.f.integer = param.f.integer;
break;
case TagType::ULONG:
case TagType::ULONG_REP:
hParam.f.longInteger = param.f.longInteger;
break;
case TagType::DATE:
hParam.f.dateTime = param.f.dateTime;
break;
case TagType::BOOL:
hParam.f.boolValue = param.f.boolValue;
break;
case TagType::BYTES:
hParam.blob = kmBlob2hidlVec(param.blob);
break;
}问题: GetEntryForMountPoint() 返回null后代码尝试访问成员,导致Recovery崩溃。
文件: system/vold/FsCrypt.cpp:489
// 修复前
GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT)->fs_mgr_flags.wrapped_key = ...
// 修复后
auto* entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
if (entry) entry->fs_mgr_flags.wrapped_key = ...问题: Recovery模式下 /vendor 未挂载,ReadDefaultFstab() 失败,导致无法解密。
解决方案: 添加硬编码回退
if (!ReadDefaultFstab(&fstab_default)) {
// 硬编码回退 - Vivo Y73S使用 aes-256-xts:aes-256-cts v1
options->version = 1;
options->contents_mode = 1; // FSCRYPT_MODE_AES_256_XTS
options->filenames_mode = 4; // FSCRYPT_MODE_AES_256_CTS
options->flags = 0;
options->use_hw_wrapped_key = false;
return true;
}问题: isFsKeyringSupported() 检测不准确,导致使用内核不支持的ioctl,导致无法解密。
文件: system/vold/KeyUtil.cpp
switch (options.version) {
case 1:
policy->key_raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size());
// TWRP修复: 对v1策略强制使用legacy keyring
return installKeyLegacy(key, policy->key_raw_ref);
break;
}问题: Recovery模式下 "fscrypt" keyring不存在。
解决方案: 写一个
static bool fscryptKeyring(key_serial_t* device_keyring) {
*device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0);
if (*device_keyring == -1) {
// 创建keyring
*device_keyring = add_key("keyring", "fscrypt", NULL, 0, KEY_SPEC_SESSION_KEYRING);
if (*device_keyring == -1) {
return false;
}
}
return true;
}TWRP中启用MTP后,电脑无法识别设备进行文件传输。
on boot
mkdir /config/usb_gadget/g1/functions/ffs.mtp 0770 shell shell
mkdir /dev/usb-ffs/mtp 0770 shell shell
mount functionfs mtp /dev/usb-ffs/mtp rmode=0770,fmode=0660,uid=2000,gid=2000
on property:sys.usb.config=mtp && property:sys.usb.configfs=1 && property:sys.usb.ffs.mtp.ready=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
write /config/usb_gadget/g1/idProduct 0x4ee1
symlink /config/usb_gadget/g1/functions/ffs.mtp /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1 && property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.ffs.ready=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
write /config/usb_gadget/g1/idProduct 0x4ee2
symlink /config/usb_gadget/g1/functions/ffs.mtp /config/usb_gadget/g1/configs/b.1/f1
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
问题: on property:sys.usb.config=none 触发器会阻止MTP切换,导致MTP功能无法使用。
解决方案: 注释掉该触发器
# 在原本的init里注释掉这个,这段会阻止MTP切换
# on property:sys.usb.config=none
# setprop sys.usb.config adb
| 文件 | 修改内容 |
|---|---|
system/vold/Keymaster.cpp |
重写HIDL 4.0实现 |
system/vold/Keymaster.h |
重写头文件 |
system/vold/KeyStorage.cpp |
添加debug和错误处理 |
system/vold/KeyUtil.cpp |
强制legacy keyring,自动创建fscrypt keyring |
system/vold/FsCrypt.cpp |
空指针修复,硬编码回退选项 |
device/.../init.recovery.mt6853.rc |
完整的init配置 |
device/.../recovery.fstab |
分区表 |
device/.../BoardConfig.mk |
编译配置 |
| 文件 | 来源 | 用途 |
|---|---|---|
vts_app_recovery |
原厂recovery | 触控固件更新 |
clear_bcb.sh |
自编写 | 清除BCB防止重启循环 |
rebootrecovery.sh |
自编写 | 拦截recovery重启命令 |
wait_for_keymaster |
原厂 | 等待keymaster服务就绪 |
guardianangle |
原厂 | 安全守护进程 |
mcDriverDaemon |
原厂 | Trustonic TEE daemon |
keymaster@4.0-service.trustonic |
原厂 | Keymaster HAL服务 |
gatekeeper@1.0-service |
原厂 | Gatekeeper HAL服务 |
*.drbin, *.tlbin |
原厂 | Trustonic trustlets |
make_hybrid.py |
自编写 | 创建混合镜像(TWRP+Vivo AVB footer) |
vivo_avb_footer.bin |
原厂recovery提取 | Vivo AVB签名footer |
2026年1月2日——1月6日
输入用户密码后,TWRP 发生软重启,无法完成解密。
Keystore blob 偏移量错误:
- 最初错误估计是 offset 44
- 实际正确值是 offset 40
Offset 0-3: Header (version=3, type=4, flags=8, info=0)
Offset 4-38: Padding (35 bytes of zeros)
Offset 39: Length byte (0xe0 = 224)
Offset 40+: Keymaster blob (224 bytes)
使用 getKeyCharacteristics() 测试不同偏移量:
offset 0: error -26 (INVALID_KEY_BLOB)
offset 4: error -26 (INVALID_KEY_BLOB)
offset 40: error 0 (SUCCESS) ← 正确偏移量
offset 44: error -33 (KEY_REQUIRES_UPGRADE)
修改 system/vold/Decrypt.cpp:
size_t best_offset = 40; //直接硬编码进去
if (key_blob.size() > best_offset) {
key_blob = key_blob.substr(best_offset);
}在 TWRP 中刷入 Magisk 时报错:
! Unable to extract boot image
openat '/dev/block/by-name/boot': : Permission denied
magiskboot 工具无法直接打开块设备,即使 SELinux 为 Permissive。
修改 bootable/recovery/twrpinstall/twinstall.cpp,在安装 zip 时:
预处理:
- 读取
/dev/block/by-name/boot原始符号链接目标 - 将 boot 分区内容拷贝到
/tmp/twrp_boot.img - 保存原始副本到
/tmp/twrp_boot_orig.img - 重定向符号链接到临时文件
后处理:
- 恢复原始符号链接
- 比较临时文件与原始副本
- 仅当文件被修改时,才写回到 boot 分区
- 清理临时文件
选择"安装 TWRP App"时,设备发生 SIGSEGV 崩溃:
I:Installing app to '/system_root/system/priv-app/twrpapp'
libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xffffffffffffffe0
TWRP 尝试将 app 安装到动态分区 system,挂载失败后访问空指针导致崩溃。
安装的时候不要选"安装为系统应用"。
TWRP 启动时显示大量红色错误消息,很难看:
Failed to mount '/vendor' (Permission denied)
Failed to mount '/system_root' (Device or resource busy)
Failed to mount '/persist' (Device or resource busy)
/vendor和/system_root:动态分区在早期启动阶段尚未映射/persist:已在 init 脚本中挂载到/mnt/vendor/persist
// Skip error display for dynamic partitions
if (!Removable && Display_Error &&
Mount_Point != "/vendor" && Mount_Point != "/system_root" && Mount_Point != "/persist")
gui_msg(...);- 移除
first_stage_mount标志,TWRP 不识别这个 - 将挂载点从
/mnt/vendor/xxx改为/xxx - 更新外部存储设备路径
SD卡:/devices/platform/11230000.msdc*
USB OTG:/devices/platform/11200000.usb_xhci*
取消 ADB sideload 操作后,TWRP 无限期挂起,无法返回主界面。
adb_install.cpp 中的 SetUsbConfig() 函数调用 WaitForProperty() 等待 USB 状态变化,但当取消 sideload 时,USB 状态可能不会改变,导致永久阻塞。
在 WaitForProperty() 调用中添加 5 秒超时:
文件: bootable/recovery/twrpinstall/adb_install.cpp
#include <chrono>
static bool SetUsbConfig(const std::string& state) {
android::base::SetProperty("sys.usb.config", state);
return android::base::WaitForProperty("sys.usb.state", state, std::chrono::seconds(5));
}补丁文件: patches/bootable_recovery/0003-Fix-adb-sideload-5s-timeout.patch
将 TWRP/OrangeFox 界面中的设备信息改为更友好的显示:
| 项目 | 修改前 | 修改后 |
|---|---|---|
| Platform | k6853v1_64_6360 | mt6853 |
| Device | k6853v1_64_6360 | PD2031 |
TWRP: device/vivo/k6853v1_64_6360/omni_k6853v1_64_6360.mk
PRODUCT_MODEL := PD2031OrangeFox: device/vivo/k6853v1_64_6360/omni_k6853v1_64_6360.mk
PRODUCT_MODEL := PD2031
FOX_DEVICE_MODEL := PD2031
PRODUCT_PLATFORM := mt6853默认只能备份 boot, cache, data, super 分区,无法备份其他重要分区(如 nvram, proinfo 等)。 也无法单独刷入 system, vendor 等逻辑分区。
在 recovery.fstab 中为所有分区添加 backup=1 和 flashimg=1 标志。
| 分区 | 说明 |
|---|---|
| oem | OEM 定制分区 |
| vgc | Vivo 游戏中心 |
| system | 系统分区 |
| system_ext | 系统扩展分区 |
| vendor | 厂商分区 |
| product | 产品分区 |
| metadata | 元数据分区 |
| data | 用户数据分区 |
| cache | 缓存分区 |
| protect_f | 保护分区 1 |
| protect_s | 保护分区 2 |
| nvdata | NV 数据 |
| nvcfg | NV 配置 |
| persist | 持久化数据 |
| 分区 | 说明 | 重要性 |
|---|---|---|
| nvram | 基带 NVRAM | 极高 - 包含 IMEI |
| proinfo | 设备信息 | 高 |
| bootloader/lk | 引导加载器 | 高 |
| boot | 内核 | 高 |
| recovery | Recovery | 高 |
| vbmeta* | AVB 验证 | 中 |
| tee1/tee2 | TEE 固件 | 高 |
| dtbo/odmdtbo | 设备树 | 中 |
| md1img/md1dsp | 基带固件 | 高 |
| scp1/scp2 | 协处理器固件 | 中 |
| sspm_1/sspm_2 | 安全子系统 | 中 |
| gz1/gz2 | GenieZone 固件 | 中 |
/system_root ext4 system flags=display=system;logical;backup=1;wipeingui;flashimg=1
/vendor ext4 vendor flags=display=vendor;logical;backup=1;wipeingui;flashimg=1
/nvram emmc /dev/block/by-name/nvram flags=display=nvram;backup=1
/bootloader emmc /dev/block/by-name/lk flags=display=bootloader;backup=1;flashimg=1
~/fox_12.1/
cd ~/fox_12.1
source build/envsetup.sh
lunch omni_k6853v1_64_6360-eng
mka recoveryimage
python3 device/vivo/k6853v1_64_6360/make_hybrid_fox.py在 omni_k6853v1_64_6360.mk 中添加:
# OrangeFox specific
FOX_DEVICE_MODEL := PD2031
PRODUCT_PLATFORM := mt6853FBE 解密需要 ro.build.version.security_patch 属性与系统创建密钥时的值匹配。之前使用硬编码的 PLATFORM_SECURITY_PATCH 值,如果用户系统升级后安全补丁日期变化,则无法解密。
Keymaster HAL 从系统属性 ro.build.version.security_patch 读取安全补丁级别:
// hardware/interfaces/keymaster/4.0/support/keymaster_utils.cpp
constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
uint32_t getOsPatchlevel() {
std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
return getOsPatchlevel(patchlevel.c_str());
}如果此属性值与密钥创建时不一致,TEE 将拒绝解密操作。
创建自动检测脚本,在 keymaster 启动前从系统分区读取真实的安全补丁级别。
脚本: recovery/root/system/bin/auto_patchlevel.sh 此脚本开机时优先启动,可能会导致开机变慢。
Init 配置: init.recovery.mt6853.rc
on post-fs
exec u:r:recovery:s0 root root -- /system/bin/auto_patchlevel.sh
-
读取顺序优先级:
- 已挂载的
/system_root/system/build.prop - 动态分区
/dev/block/mapper/system_a - 动态分区
/dev/block/mapper/system - 传统分区
/dev/block/by-name/system
- 已挂载的
-
设置的属性:
ro.build.version.security_patchro.vendor.build.security_patch(实际不应该设置,因为vivo y73s的vendor日期都没变过。)ro.system.build.version.security_patch
-
执行时机:
- 在
on post-fs阶段 - 在 SDK 版本修复之后
- 在 keymaster 服务启动之前
- 在
- 通用性: 无需为每个安全补丁日期生成不同的镜像
- 自动化: Recovery 启动时自动检测,无需用户干预
- 兼容性: 支持系统升级后继续正常解密
在实际测试中发现多个问题需要修复:
现象: ro.build.version.security_patch=[AutoPatchLevel] Searching...
原因: log_info() 输出到 stdout,被 $() 命令替换捕获
解决: 日志输出到 stderr (>&2) 和直接写入 /tmp/recovery.log
原因: System 分区可能不在 super 分区开头,50MB 搜索范围不够
解决 (v1):
# 方法1: grep -a 搜索整个分区 (快速)
grep -a -o "ro.build.version.security_patch=[0-9-]*" "$super_dev"
# 方法2: 分段搜索 200MB (fallback)
for skip in 0 50 100 150; do
dd if="$super_dev" bs=1M skip=$skip count=50 | strings | grep ...
done说明: Vivo Y73S 的 vendor 分区从出厂至今未更新过,始终为 2020-07-05。
解决: 不修改 ro.vendor.build.security_patch
| 文件 | 用途 |
|---|---|
recovery/root/system/bin/auto_patchlevel.sh |
自动检测并设置安全补丁级别 (v2.2) |
安装某些大型zip刷机包时报错 "Zip 文件已损坏",但该 zip 文件完全正常。其他zip(如 Magisk) 可以正常安装。
可能是奇奇怪怪的压缩格式导致的。
unzip使用7zzs
patches/bootable_recovery/0004-Large-zip-file-support.patch
patches/bootable_recovery/0006-Zip64-fallback-and-7zzs-ramdisk.patch
尝试刷入 system.img 到 system 分区时报错:
E:Cannot flash images to file systems
即使 recovery.fstab 中已设置 flashimg=1 标志。
partition.cpp 中 Flash_Image() 函数的检查逻辑顺序错误:
// 原代码
if (Backup_Method == BM_FILES) { // ext4 分区走这里
LOGERR("Cannot flash images to file systems\n");
return false; // 直接返回,不检查 Can_Flash_Img
} else if (!Can_Flash_Img) {
...
}对于 ext4/f2fs 分区,Backup_Method = BM_FILES(文件备份模式),代码首先检查这个条件就直接报错,根本没机会检查 Can_Flash_Img 标志。
修改检查顺序:先检查 Can_Flash_Img,如果允许刷入镜像,则使用 dd/sparse 方式。
文件: bootable/recovery/partition.cpp
bool TWPartition::Flash_Image(PartitionSettings *part_settings) {
// ...
// 先检查是否允许刷入镜像
if (!Can_Flash_Img) {
LOGERR("Cannot flash images to partitions %s\n", Display_Name.c_str());
return false;
}
// 大小检查...
// 对于文件系统分区 (BM_FILES) 或 emmc 分区 (BM_DD),都使用 dd/sparse 方式刷入
if (Backup_Method == BM_FILES || Backup_Method == BM_DD) {
if (!part_settings->adbbackup) {
if (Is_Sparse_Image(full_filename)) {
return Flash_Sparse_Image(full_filename);
}
}
return Raw_Read_Write(part_settings);
}
// ...
}patches/bootable_recovery/0005-Allow-flash-image-to-filesystem-partitions.patch
patches/
├── apply-patches.sh
├── revert-patches.sh
├── system_vold/ # Vold FBE 解密补丁
│ ├── 0001-Keymaster-HIDL-4.0-for-Trustonic-TEE.patch
│ ├── 0002-KeyStorage-add-debug-logging.patch
│ ├── 0003-KeyUtil-force-legacy-keyring.patch
│ ├── 0004-FsCrypt-hardcoded-options-and-fixes.patch
│ ├── 0005-Decrypt-add-debug-logging.patch
│ └── 0006-Direct-Keymaster-HIDL-for-synthetic-password.patch
└── bootable_recovery/ # Recovery 功能补丁
├── 0001-Magisk-boot-partition-compatibility.patch
├── 0002-Suppress-mount-errors-for-dynamic-partitions.patch
├── 0003-Fix-adb-sideload-5s-timeout.patch
├── 0004-Large-zip-file-support.patch
├── 0005-Allow-flash-image-to-filesystem-partitions.patch
└── 0006-Zip64-fallback-and-7zzs-ramdisk.patch
| 功能 | 状态 | 备注 |
|---|---|---|
| 触摸屏 | ✅ 正常 | |
| ADB 连接 | ✅ 正常 | |
| FBE 解密 (默认密码) | ✅ 正常 | |
| FBE 解密 (用户密码) | ✅ 正常 | 自动检测安全补丁日期,但是锁屏密码还是得你自己填 |
| MTP 传输 | ✅ 正常 | |
| 文件浏览 | ✅ 正常 | |
| Magisk 安装 | ✅ 正常 | |
| OTA 包安装 | ✅ 正常 | |
| 分区镜像刷入 | 理论上支持 system/vendor 等单独刷入 | |
| ADB Sideload | ✅ 正常 | 取消操作不再卡死 |
| 分区备份 | ✅ 正常 | 支持 50+ 分区 |
| 分区刷入 | ✅ 正常 | 支持单独刷入逻辑分区 |
| TWRP App 安装 | 动态分区不支持 | |
| 外部存储 (SD卡) | ❓ 待测试 | |
| 外部存储 (USB OTG) | ❓ 待测试 |
2026年1月11日
待更新。。。。。