0%

设备bootloader上锁状态下1217无法工作问题分析

设备bootloader上锁状态下1217无法工作问题分析

背景

前面讲到r上6月份升级基线后, 因KM TA date support的升级导致recovery下无法正常使用解密服务.

当时的方案是配置recovery vbmeta区域的 prop --prop com.android.build.boot.security_patch:2020-06-05
从而在bootimg_hdr不支持识别date 信息的情况下, 可以在reocovery模式下bootloader能够读到天的信息, 并传给KM TA.

新问题

最近测试在复测该问题时, 发现手机bootloader上锁状态下, 无法执行1217功能. 因此需要重新分析下这个问题的原因.

locked 状态下的调试

debug abl的方法

locked状态下的限制

首先, 小米机器locked 状态有如下限制:

  1. 不能使用fastboot烧录新的img, 即fastboot flash  <partition.img> 是不被允许的
  2. 不能使用fastboot erase 分区, 像fastboot erase misc 无法执行
  3. locked状态切到unlocked状态, unlocked状态切到locked状态, 都会reboot 进recovery, 清除数据.

上述限制或功能必须被禁用掉, 因为调试这个问题时, 本地打的调试(带logcat 和 adb shell)功能的在locked状态肯定会报system has destroyed, 其次经常需要debug abl, 烧分区的限制肯定要禁用掉, 不然每次烧录新的abl 都需要使用9008模式烧机, 非常麻烦. locked 切 unlocked状态, 或反之的情况清除数据的功能肯定要拿掉, 因为1217测的就是解主系统加密过的userdata分区, 如果在切换过程中把userdata擦了, 那就破坏了测试的前提. 第二条清misc也是必须的, 因为recovery如果无法正常启动, 出现system has destroyed, 可以通过清misc, 启动到主系统把recovery恢复回来.

change

去除locked状态下对fastboot flash / fastboot erase 和 切换locked unlocked 对userdata数据的操作

http://gerrit.bsp.xiaomi.srv/#/c/22341/

locked状态下让avb校验失败的recovery成功启动起来

注意不能修改avb校验完的状态, 保持最小变量原则

change

即将导向system has destroyed的入口破坏掉

http://gerrit.bsp.xiaomi.srv/22334

本地recovery调试1217的方法

本地编译userdebug版本的recovery即可.

  1. 将上述change合入后, 编译abl, make aboot或者使用ninja, 注意最后abl一定要通过secureboot签名
  2. locked上锁状态可以先使用解锁工具解锁, 再通过fastboot flash abl abl.elf 烧录locked 状态修改过的abl, 然后再使用fastboot oem lock上锁.
  3. 上锁完后, fastboot flash recovery 本地的recovery.img.
  4. fastboot reboot recovery
  5. 使用下面的命令, 查看df -h的结果中是否有userdata, vold的输出的log是什么, 如果begin操作报错, 说明keymaster 返回了错误
1
adb root; adb shell mv /system/bin/recovery  /tmp/; adb shell touch /system/bin/recovery; adb shell /tmp/recovery --factory_test_reset&; sleep 6; adb shell df -h; adb logcat -s vold;

问题分析

通过之前修改recovery 打包写入avb prop的方法已经可以使recovery在unlocked模式下1217正常工作. 而locked状态下却不行. 变量只有一个就是locked状态和unlocked状态.

先根据log定位下初步原因, 测试开始提供的log中只有recovery打出的部分信息:

recovery 重启, reason= fs_mgr_mount_all

根据串口log的信息, 首先可以判定是如下情形:

  1. 主系统bcb (reason –factory_test_reset) ->2.  recovery启动(模式为1217)-> 3. recovery重启(模式变为fs_mgr_mount_all)

在第2步1217的执行步骤中触发了 RebootRecovery(fs_mgr_mount_all)的命令, 查看代码, 最终定位到

mount_all miui_factoryreset.fstab --late时 init 在执行vdc mountFstab /dev/block/bootdevice/by-name/userdata /data失败后执行了上述命令, 所以需要进一步的log判断为什么这个vdc 的操作失败了.

根据前面debug locked 状态下recovery的方法, 最终可以在locked状态下启动本地带调试版本的recovery

得到的log如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
01-01 01:10:19.148   630   630 D vold    : metadata_key_dir/key: /metadata/vold/metadata_encryption/key
01-01 01:10:19.149 630 630 I vold : List of Keymaster HALs found:
01-01 01:10:19.149 630 630 I vold : Keymaster HAL #1: Keymaster HAL: 4 from QTI SecurityLevel: TRUSTED_ENVIRONMENT HAL: android.hardware.keymaster@4.1::IKeymasterDevice/default
01-01 01:10:19.150 630 630 D vold : Computing HMAC with params { (seed: 54483455460602eb1254138a502eea6249c665fd8feebff883464d797d9f, nonce: 3f7f4c10a7eaf94e91f869e10f3f1ca1d576596d8291933b9c5305dfd90) }
01-01 01:10:19.150 630 630 D vold : Computing HMAC for Keymaster HAL: 4 from QTI SecurityLevel: TRUSTED_ENVIRONMENT HAL: android.hardware.keymaster@4.1::IKeymasterDevice/default
01-01 01:10:19.152 630 630 I vold : Using Keymaster HAL: 4 from QTI for encryption. Security level: TRUSTED_ENVIRONMENT, HAL: android.hardware.keymaster@4.1::IKeymasterDevice/default
01-01 01:10:19.154 630 630 D vold : Key exists, using: /metadata/vold/metadata_encryption/key
01-01 01:10:19.156 630 630 I vold : List of Keymaster HALs found:
01-01 01:10:19.156 630 630 I vold : Keymaster HAL #1: Keymaster HAL: 4 from QTI SecurityLevel: TRUSTED_ENVIRONMENT HAL: android.hardware.keymaster@4.1::IKeymasterDevice/default
01-01 01:10:19.156 630 630 I vold : Using Keymaster HAL: 4 from QTI for encryption. Security level: TRUSTED_ENVIRONMENT, HAL: android.hardware.keymaster@4.1::IKeymasterDevice/default
01-01 01:10:19.161 637 637 E KeyMasterHalDevice: Begin send cmd failed
01-01 01:10:19.161 637 637 E KeyMasterHalDevice: ret: 0
01-01 01:10:19.161 637 637 E KeyMasterHalDevice: resp->status: -33
01-01 01:10:19.161 630 630 E vold : begin failed, code -33
01-01 01:10:19.148 0 0 D vdc : Waited 0ms for vold
01-01 01:10:19.161 0 0 E vdc : Command: cryptfs mountFstab /dev/block/bootdevice/by-name/userdata /data Failed: Status(-8, EX_SERVICE_SPECIFIC): '0: '

keymaster返回-33的错误导致了这样的表现.

根据之前对abl的分析, 这个大概率与abl传给KM TA的参数有关.

搜索代码, 最终定位到

1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Set Boot State */
BootStateReq.CmdId = KEYMASTER_SET_BOOT_STATE;
BootStateReq.Version = 0;
BootStateReq.Size = sizeof (BootStateReq.BootState);
BootStateReq.Offset =
(UINT8 *)&BootStateReq.BootState - (UINT8 *)&BootStateReq;
BootStateReq.BootState.Color = BootState->Color;
BootStateReq.BootState.IsUnlocked = BootState->IsUnlocked;
BootStateReq.BootState.SystemSecurityLevel = BootState->SystemSecurityLevel;
BootStateReq.BootState.SystemVersion = BootState->SystemVersion;
CopyMem (BootStateReq.BootState.PublicKey, BootStateDigest,
AVB_SHA256_DIGEST_SIZE);

DEBUG ((EFI_D_ERROR, "KeyMasterStartApp: bootstate digest: "
"%s\n", BootStateDigest));

Status = Handle.QseeComProtocol->QseecomSendCmd (
Handle.QseeComProtocol, Handle.AppId, (UINT8 *)&BootStateReq,
sizeof (BootStateReq), (UINT8 *)&BootStateRsp, sizeof (BootStateRsp));

debug分析这一过程发现locked状态下, 只有color是orange时可以让recovery在调用keymaster begin解密经主系统加密的userdata时可以正常返回.

而如果userdata没有通过主系统的加密, 即在recovery下先恢复出厂设置, 再对userdata加密时, color是green情况下, 也可以正常调用keymaster,没有返回错误, 而在recovery下加密userdata完成后, 再启动到主系统, 主系统会报fs_mgr_mount_all的错误.

通过上述现象, 可以推测 加密userdata相当于上层keymaster初始化, 使用当时的环境去加密的数据, 而换了环境之后, 再去解密, 由于环境的变化, 导致keymaster无法校验成功, 所以begin返回了invalid blob(-33), 而这个环境是什么, 通过分析BootState的字段在recovery和主系统下的区别, 只有BootStateDigest是值得怀疑的.

BootStateDigest即UserData->PublicKey的分析

上一步怀疑这个字段有问题后, 需要分析recovery模式和主系统下这个字段的数据来源.

最终发现新基线升级后, recovery的avb校验模式变成了只校验recovery分区自己, 而主系统是校验 vbmeta boot dtbo vbmeta_system. 而主系统这个字段最终来自vbmeta分区的public key. 而recovery的这个字段并没有赋值.

recovery模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if ( ( (!Info->MultiSlotBoot) ||
IsDynamicPartitionSupport ()) &&
(Info->BootIntoRecovery &&
!IsBuildUseRecoveryAsBoot ())) {
// 非ab机型
if (!Info->MultiSlotBoot) {
DEBUG ((EFI_D_ERROR, "flag AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION\n"));
VerifyFlags = VerifyFlags | AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION;
}
// 只加载recovery分区
AddRequestedPartition (RequestedPartitionAll, IMG_RECOVERY);
NumRequestedPartition += 1;
Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition,
SlotSuffix, VerifyFlags, VerityFlags, &SlotData);
...
}

问题解决

上面怀疑recovery模式和主系统下abl传给KM TA的摘要是不同的, 那可以想到将vbmeta的public key 填充到 UserData->PublicKey, 下发给KM TA 是否可以解决locked状态下recovery无法使用keymaster解密经主系统加密的数据.

方案如下:

http://gerrit.bsp.xiaomi.srv/#/c/22338/

只有非ab机型进入recovery情况下, 即AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION flag的作用范围内, 在avb verify完recovery分区后, 再avb verify下vbmeta分区, 目的是为了让其覆盖ops->user_data段, 而传进load_and_verify_vbmeta的参数需要重新设定, 因为原先的参数很多都是全局或影响avb verify recovery结果的指针, 我们这里只能覆盖ops->user_data段, 不能对其他段产生影响. 同时还需要对flag 重新置位, 绕开vbmeta相关的检查, 使得可以成功load vbmeta的public key, 并将其赋给ops->user_data段.

1
2
3
4
5
6
typedef struct {
BOOLEAN IsUserKey;
BOOLEAN IsMultiSlot;
UINTN PublicKeyLen;
CHAR8 PublicKey[MAX_USER_KEY_SIZE];
} AvbOpsUserData;

重新测试后, 上述patch有效, locked下recovery中可以正常进行1217功能.

验证

  1. locked状态下, 主系统开机后, 执行1217, 正常reboot到recovery中进行清数据的操作, 重启回主系统下, /data/miui/app下内容保留
  2. locked状态下, fastboot flash 本地编的recovery, fastboot reboot recovery后, 应该报system has destroyed
  3. unlocked 状态下, 无论是本地编的recovery还是线刷包版本的recovery, 都可以正常进行1217功能

验证时, unlocked状态切换到locked状态, 或反之的情况, 切换过程中都需要进recovery wipe data, 擦完重新开机进主系统, 保证数据要重新经主系统初始化加密一次.