之前遇到了avb提示”device is corrupt”的问题,涉及boot state。我们先看下官方的说明,目前是Android 10:

After determining the boot state of a device, you need to communicate that state to the user. If the device doesn’t have any issues, then proceed without displaying anything. Verified Boot issues fall into these categories:

  • YELLOW: Warning screen for LOCKED devices with custom root of trust set
  • ORANGE: Warning screen for UNLOCKED devices
  • RED (eio): Warning screen for dm-verity corruption
  • RED (no os found): No valid OS found

YELLOW先略过,ORAGE就是当前设备unlocked了,以前也有文章分析过。这里主要看下之前遇到的RED eio state(屏幕打印txt一样):

dm-verity corruption

Show a RED eio screen if a valid version of Android is found and the device is currently in the eio dm-verity mode. The user needs to click the power button to continue. If the user hasn’t acknowledged the warning screen within 30 seconds, the device powers off (to protect the screen against burn-in and save power).

如果找到一个有效的Android版本并且当前设备是在eio dm-verity mode时show RED eio screen。

android-red-eio-screen.png

Android系统有效?eio dm-verity mode是啥?

先不看code,我们继续看官方相关文档说明:

Verified boot requires cryptographically verifying all executable code and data that is part of the Android version being booted before it is used. This includes the kernel (loaded from the boot partition), the device tree (loaded from the dtbo partition), system partition, vendor partition, and so on.

Small partitions, such as boot and dtbo, that are read only once are typically verified by loading the entire contents into memory and then calculating its hash. This calculated hash value is then compared to the expected hash value. If the value doesn’t match, Android won’t load. For more details, see Boot Flow.

Larger partitions that won’t fit into memory (such as, file systems) may use a hash tree where verification is a continuous process happening as data is loaded into memory. In this case, the root hash of the hash tree is calculated during run time and is checked against the expected root hash value. Android includes the dm-verity driver to verify larger partitions. If at some point the calculated root hash doesn’t match the expected root hash value, the data is not used and Android enters an error state. For more details, see dm-verity corruption.

就是说avb会校验数据,小分区boot,dtbo会全部加载到memory去计算hash。大分区如system是用内核的dm-verity来完成。如果有错,会进入一个错误状态,详见dm-verity corruption。这里官方有个link会转到上面提到的RED eio screen。

啥子?system数据有问题?avbtool等check了下么问题啊,怎么check后面再写篇哈。

这可是官网说明啊。我们回看上次的root cause log:

avb_slot_verify.c:657: ERROR: vbmeta_system_a: Image rollback index is less than the stored rollback index.

uefi下的:

AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX

我们再看下code:

/* Return codes used in avb_slot_verify(), see that function for
* documentation for each field.
*
* Use avb_slot_verify_result_to_string() to get a textual
* representation usable for error/debug output.
*/
typedef enum {
AVB_SLOT_VERIFY_RESULT_OK,
AVB_SLOT_VERIFY_RESULT_ERROR_OOM,
AVB_SLOT_VERIFY_RESULT_ERROR_IO,
AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA,
AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION,
AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT
} AvbSlotVerifyResult;

对每个field的解释,注释写的很清楚了,不用grep了,直接看avb_slot_verify_result_to_string:

const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) {
const char* ret = NULL;

switch (result) {
case AVB_SLOT_VERIFY_RESULT_OK:
ret = "OK";
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
ret = "ERROR_OOM";
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
ret = "ERROR_IO";
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
ret = "ERROR_VERIFICATION";
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
ret = "ERROR_ROLLBACK_INDEX";
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
ret = "ERROR_PUBLIC_KEY_REJECTED";
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
ret = "ERROR_INVALID_METADATA";
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
ret = "ERROR_UNSUPPORTED_VERSION";
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
ret = "ERROR_INVALID_ARGUMENT";
break;
/* Do not add a 'default:' case here because of -Wswitch. */
}

if (ret == NULL) {
avb_error("Unknown AvbSlotVerifyResult value.\n");
ret = "(unknown)";
}

return ret;
}

从代码看,dm-verity corruption应该是AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,而不是AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX

相关code再show出来:

if (vbmeta_header.rollback_index < stored_rollback_index) {
avb_errorv(
full_partition_name,
": Image rollback index is less than the stored rollback index.\n",
NULL);
ret = AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX;
if (!allow_verification_error) {
goto out;
}
}

what is rollback_index and what is stored_rollback_index and why need to check the value in avb?

ok, let’s check Android doc since it’s belong to avb, avb doc must intro:

一看果然有,叫rollback protection,姑且译成回滚保护:

Rollback protection

Even with a completely secure update process, it’s possible for a non-persistent Android kernel exploit to manually install an older, more vulnerable version of Android, reboot into the vulnerable version, and then use that Android version to install a persistent exploit. From there the attacker permanently owns the device and can do anything, including disabling updates.

The protection against this class of attacks is called Rollback Protection. Rollback protection is typically implemented by using tamper-evident storage to record the most recent version of the Android and refusing to boot Android if it’s lower than the recorded version. Versions are typically tracked on a per-partition basis.

For more details on how AVB handles rollback protections, see the AVB README.

奥。。。防止黑客攻击滴。。。好吧。注意提到用tamper-evident storage来记录最近的Android版本并且拒绝启动比这个版本低的。

ok,终于对上了,人家就这么设计的。

这个tamper-evident storage是啥意思呢?对eMMC设备而言,就是RPMB分区,也就是说这个rollback index如果放到RPMB里,就别想再降级用了,RPMB想破解,没那么容易?

另外,吐槽下google文档和qcom uefi code对不上!大大厂啊。

参考