Android P中的AVB2.0校驗

    avb校驗功能主要是由external/avb/libavb庫實現的,該庫主要完成的工作包括各個分區鏡像的校驗,簽名驗證,以及vbmeta數據的解析,包括了各種flags的處理以及dm-verity所需要的參數解析。avb校驗庫的主入口爲

 avb_slot_verify(AvbOps* ops,

                 const char* const* requested_partitions,

                 const char* ab_suffix,

                 AvbSlotVerifyFlags flags,

                 AvbHashtreeErrorMode hashtree_error_mode,

                 AvbSlotVerifyData** out_data) 

以高通平臺爲例,avb校驗在一次啓動過程中總共進行了兩次,第一次是在bootloader中進行校驗,通過上面的接口校驗各個分區的根hash和簽名。第二次是在上層init中進行的,這次校驗也是調用相同的接口,可能個別傳入的參數會有不同。
之所以在init中再做一次的原因是什麼呢?因爲我們掛載分區和dm-verity相關的參數都在鏡像的vbmeta結構中保存,因此可以使用該接口進行解析和掛載,但是該接口和校驗功能是捆綁的,所以必須要再進行一次校驗。不管是在bootloader還是在上層init中,所有校驗都是從vbmeta分區開始,然後讀取其中包含的各個分區的信息,然後依次循環讀取並校驗其他的各個分區,所有vbmeta.img中包含的分區都必須校驗通過才認爲是校驗成功。

bootloader

CONST CHAR8 *RequestedPartitionMission[] = {"boot", "dtbo", NULL};
CONST CHAR8 *RequestedPartitionRecovery[] = {"recovery", "dtbo", NULL};

if ((!Info->MultiSlotBoot) &&
         Info->BootIntoRecovery) {
   RequestedPartition = RequestedPartitionRecovery;
   NumRequestedPartition = ARRAY_SIZE (RequestedPartitionRecovery) - 1;
   if (Info->NumLoadedImages) {
     /* fastboot boot option, skip Index 0, as boot image already
      * loaded */
     RequestedPartition = &RequestedPartitionRecovery[1];
   }
} else {
   RequestedPartition = RequestedPartitionMission;
   NumRequestedPartition = ARRAY_SIZE (RequestedPartitionMission) - 1;
   if (Info->NumLoadedImages) {
     /* fastboot boot option, skip Index 0, as boot image already
      * loaded */
     RequestedPartition = &RequestedPartitionMission[1];
   }
}

if (Info->NumLoadedImages) {
  NumRequestedPartition--;
}

if (FixedPcdGetBool (AllowEio)) {
  VerityFlags = IsEnforcing () ? AVB_HASHTREE_ERROR_MODE_RESTART
                               : AVB_HASHTREE_ERROR_MODE_EIO;
} else {
  VerityFlags = AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE;
}

Result = avb_slot_verify (Ops, RequestedPartition, SlotSuffix, VerifyFlags,
                          VerityFlags, &SlotData);

avb校驗是所有分區都要進行校驗的,這裏傳入的RequestedPartition並不代表要校驗的分區,進一步跟進可以發現最終和RequestedPartition只會對Hash descriptor的解析有影響,其他類型的descriptor解析方法都是一樣的。
高通平臺的vbmeta.img包含如下信息:

 Minimum libavb version:   1.0

 Header Block:             256 bytes

 Authentication Block:     576 bytes

 Auxiliary Block:          3456 bytes

 Algorithm:                SHA256_RSA4096

 Rollback Index:           0

 Flags:                    0

 Release String:           'avbtool 1.1.0'

 Descriptors:

     Chain Partition descriptor:

       Partition Name:          system

       Rollback Index Location: 2

       Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d

     Chain Partition descriptor:

       Partition Name:          recovery

       Rollback Index Location: 1

       Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011

     Hash descriptor:

       Image Size:            35553280 bytes

       Hash Algorithm:        sha256

       Partition Name:        boot

       Salt:                  baa1ce5d7db69d1b3943a78b5b142ae4d77b4ed60b9885c8661e845172b29a13

       Digest:                ec7cb1ad89fed3104a03191434d5487b5b4acc78e6bfeb84d7178af40df7db75

       Flags:                 0

     Hashtree descriptor:

       Version of dm-verity:  1

       Image Size:            1056714752 bytes

       Tree Offset:           1056714752

       Tree Size:             8327168 bytes

       Data Block Size:       4096 bytes

       Hash Block Size:       4096 bytes

       FEC num roots:         2

       FEC offset:            1065041920

       FEC size:              8421376 bytes

       Hash Algorithm:        sha1

       Partition Name:        vendor

       Salt:                  abbf0829ed7bc08913b83f9a994a37ad2a85b5e9

       Root Digest:           39a22a035ebff2d339dc682603adedb91da01374

       Flags:                 0

     Hash descriptor:

       Image Size:            176641 bytes

       Hash Algorithm:        sha256

       Partition Name:        dtbo

       Salt:                  386837807aa5a7d9cbe51e7f768009f4e5fca5190af4b3e856a7c96a96c33e0a

       Digest:                dabdbe5be19c38a3428efd046182d215f8522ab7cd3804e84f196fe73e9052f7

       Flags:                 0

因此受到影響的只有boot/dtbo/recovery,recovery這裏是Chain Partition descriptor,但是解析recovery.img發現它校驗數據也是生成的Hash,而不是Hashtree的方式。 RequestedPartition代表的是需要被校驗的hash分區,只有其中包含的Hash分區,那麼纔會被校驗。因此根據boot mode方式的不同,需要校驗的hash分區也不同,正常啓動需要校驗:boot/dtbo,recovery啓動需要校驗:recovery/dtbo,而其他分區的校驗方式是一樣的。

init

針對init中的校驗,由於我們已經boot成功到上層了,雖然該函數依然會輪詢vbmeta包含的各個分區,但是此時kernel和dtbo都已經加載完畢了,沒有必要在對Hash分區進一步進行校驗了,所以我們可以傳入RequestedPartition爲空指針,這樣init當檢測到是Hash descriptor會直接return OK,並繼續下一個分區的校驗。
init中和AVB相關的主要是進行校驗和掛載分區的功能,終極目的還是爲了掛載,可以分開來看,有firststagemount和secondstagemount兩種方式,第一種方式是爲了掛載vendor分區,需要通過libavb對system/vendor進行校驗,system分區已經由kernel掛載過了,因此這裏只需要利用dm-verity參數掛載vendor分區,因爲vendor中包含了很多init rc文件,所以vendor必須要在這些rc執行之前被掛載上,所以必須要在第一階段把vendor分區掛載起來,然後在init的第二階段去執行init rc。第二種方式是其他分區的掛載,主要是通過讀取fstab中的掛載項進行掛載,這就和之前版本的android掛載方式一樣了。

需要注意的是,由於第一階段的掛載,我們沒有辦法通過fstab文件傳遞給init,因爲fstab在vendor中,而vendor還沒有被掛載起來,所以我們可以通過device tree的方式來傳遞fstab參數。這樣init在第一階段通過讀取device tree來獲取要在第一階段掛載的分區,也就是我們的vendor分區,由於avb和dm-verity是綁定的,所以還需要傳入對應的vbmeta節點,目的是開啓dm-verity功能,同時也會對vbmeta.img中存在的各個分區進行校驗。當然我們校驗是傳入RequestedPartition爲空指針,以跳過Hash分區的校驗,只進行其他分區的校驗。

這裏需要特別注意的是device tree中的vbmeta節點的配置,必須要和vbmeta.img中所包含的分區一致,因爲這個匹配校驗也會做,否則會報校驗失敗,從而無法啓動android。

firmware: firmware {
    android {
        compatible = "android,firmware";
        vbmeta {
            compatible = "android,vbmeta";
            parts = "vbmeta,boot,system,vendor,dtbo,recovery";
        };
        fstab {
            compatible = "android,fstab";
            vendor {
                compatible = "android,vendor";
                dev = "/dev/block/platform/soc/8804000.sdhci/by-name/vendor";
                type = "ext4";
                mnt_flags = "ro,barrier=1,discard";
                fsmgr_flags = "wait,avb";
                status = "ok";
            };
        };
    };
};

這個vbmeta中包含"vbmeta,boot,system,vendor,dtbo,recovery"這幾個分區,恰好和上面解析出來的一直,否則會報如下錯誤:

[ 11.254728] init: init first stage started!
[ 11.260572] init: Using Android DT directory /proc/device-tree/firmware/android/
[ 11.274173] init: [libfs_mgr]fs_mgr_read_fstab_default(): failed to find device default fstab
[ 11.463441] init: [libfs_mgr]by-name symlink not found for partition: 'recovery'
[ 11.471083] init: [libfs_mgr]avb_slot_verify failed, result: 2

這是一個去掉recovery分區的一個報錯實例。

init調用libavb庫的主入口函數如下:


AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,

                                                   AvbSlotVerifyFlags flags,

                                                   AvbSlotVerifyData** out_data) {

    // Invokes avb_slot_verify() to load and verify all vbmeta images.

    // Sets requested_partitions to nullptr as it's to copy the contents

    // of HASH partitions into handle>avb_slot_data_, which is not required as

    // fs_mgr only deals with HASHTREE partitions.

    const char* requested_partitions[] = {nullptr};

    // The |hashtree_error_mode| field doesn't matter as it only

    // influences the generated kernel cmdline parameters.

    return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,

                           AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);

}

可以看出,其傳入的requested_partitions確實是爲空nullptr,這樣就會跳過Hash分區校驗。
解析vbmeta數據中的Hash descriptor:


 switch (desc.tag) {
   case AVB_DESCRIPTOR_TAG_HASH: {
     AvbSlotVerifyResult sub_ret;
     sub_ret = load_and_verify_hash_partition(ops,
                                              requested_partitions,
                                              ab_suffix,
                                              allow_verification_error,
                                              descriptors[n],
                                              slot_data);
     if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
       ret = sub_ret;
       if (!allow_verification_error || !result_should_continue(ret)) {
         goto out;
       }
     }
   } break;

load_and_verify_hash_partition中判斷requested_partitions是否存在對應的分區名,不存在則返回OK跳轉:

  /* Don't bother loading or validating unless the partition was
   * requested in the first place.
   */
  found = avb_strv_find_str(requested_partitions,
                            (const char*)desc_partition_name,
                            hash_desc.partition_name_len);
  if (found == NULL) {
    ret = AVB_SLOT_VERIFY_RESULT_OK;
    goto out;
  }

禁止verification校驗功能

通過我的另一篇文章《Android P 如何掛載system鏡像到根目錄》的介紹,大家應該會有所瞭解,vbmeta.img中保存有一個flags:

typedef enum {
   AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0),
   AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = (1 << 1)
 } AvbVBMetaImageFlags;

該flags會有兩個bit,分別代表是否使能dm-verity和verification功能,本文介紹的AVB校驗功能就是verification使能的時候纔會進行。
我們可以通過修改此bit位來禁止AVB校驗功能,利用fastboot重新刷寫vbmeta.img:

fastboot --disable-verification flash vbmeta vbmeta.img

跳過AVB的代碼如下所示,在函數avb_slot_verify中:

if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {

  /* Since verification is disabled we didn't process any

   * descriptors and thus there's no cmdline... so set root= such

   * that the system partition is mounted.

   */

  avb_assert(slot_data->cmdline == NULL);

  slot_data->cmdline =

      avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)");

  if (slot_data->cmdline == NULL) {

    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;

    goto fail;

  }
} else {
  ...... //verification operation
}

這裏發現禁止校驗之後,函數就結束了,校驗操作在else中進行的。

禁止dm-verity功能

兩種方法:

adb disable-verity
fastboot --disable-verity flash vbmeta vbmeta.img

最終都是更新了vbmeta分區中保存的對應的flags來確定是否禁止dm-verity的。

 

備註:

     本文轉載:https://www.optbbs.com/thread-4907769-1-1.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章