Android AVB 分析(十九)Android 镜像中的 FEC 到底能纠正多少错误?

好的,我们来分析您的问题。您想通过修改 `system.img` 文件来让 FEC 无法纠错。这涉及到对 dm-verity 镜像结构的深入理解。根据您的提问,我们可以将问题解构为以下核心步骤: 1. **理解机制**:FEC(前向纠错)如何与 dm-verity 协同工作,以及它的纠错能力边界在哪里。 2. **定位目标**:FEC 数据存放在哪里,以及它是如何与原始数据块关联的。 3. **设计攻击**:修改哪些内容,如何修改,才能突破 FEC纠错能力,使其失效。 下面,我将结合参考资料,一步步推演并生成具体的方案。 ### 一、 FECdm-verity 中的工作机制与平衡点 首先,理解 FECdm-verity 的关系是设计攻击的基础。 | 机制 | 核心目标 | 对用户问题的启示 | | :--- | :--- | :--- | | **dm-verity** | **完整性验证**。为每个数据块计算哈希,并构建哈希树。启动时验证哈希,确保数据未被篡改。其默认行为是:**检测到错误即失败** [ref_1]。 | 这是第一道防线。任何对数据块的修改,都会导致哈希校验失败。 | | **FEC (前向纠错)** | **容错与恢复**。在原始数据块基础上,生成额外的冗余校验数据(Reed-Solomon码)。当 dm-verity 检测到哈希错误时,会**优先尝试使用FEC数据来修复损坏的块**,修复失败后才报告I/O错误 [ref_1]。 | 这是第二道防线。要让FEC失效,必须制造超出其纠错能力的损坏。 | **安全与容错的平衡点** 正在于此:**FEC纠错能力是有限的**。它被设计为纠正**少量、随机**的位错误(例如由存储介质老化、宇宙射线等引起),而不是应对**大面积、系统性**的篡改[ref_1]。dm-verity 的哈希树保证了篡改必然被检测到,而FEC则提供了一个“容错窗口”。如果篡改程度超出了这个窗口,FEC将无法纠正,系统便会退回到dm-verity的严格模式——启动失败,从而保证了安全。 ### 二、 修改 `system.img` 以使 FEC 失效的具体方案 要让FEC纠错不了,核心思路是**制造超出FEC算法纠错能力的损坏**。这通常需要对FEC编码的结构有一定了解。以下是几种可行的路径: #### 方案一:破坏 FEC 校验数据本身(最直接的方法) FEC纠错能力依赖于**数据块****校验块**的完整性。如果校验数据本身被损坏或无效,那么整个纠错机制就失去了依据。 1. **定位 FEC 数据区域**:在构建了 FECdm-verity 镜像中,FEC 校验数据通常存储在镜像文件的**末尾**或一个**独立的数据区域**。您需要知道镜像的精确布局。例如,在使用 `veritysetup` 或 `build_verity_tree` 等工具时,可以通过参数或输出信息确定 FEC 数据的位置大小。 2. **修改或擦除 FEC 数据**:使用十六进制编辑器或 `dd` 命令,直接对 `system.img` 文件中存放 FEC 校验数据的部分进行**大面积随机写入**或**清零**。 * **原理**:这相当于破坏了“方程”中的“已知条件”(即 [ref_1] 中例子里的 `135` `203`)。解码器没有足够的有效校验信息来解出正确的原始数据,纠错必然失败。 * **操作示例(概念性)**: ```bash # 假设已知 FEC 数据区域在 system.img 的偏移量 0x10000000 开始,大小为 0x100000 字节 # 使用 dd 命令将该区域填充为随机数据,彻底破坏 FEC 信息 dd if=/dev/urandom of=system.img bs=1 seek=$((0x10000000)) count=$((0x100000)) conv=notrunc ``` *注释:此命令会从指定偏移开始,用随机数据覆盖指定大小的区域,从而破坏FEC校验数据 [ref_1]。* #### 方案二:对原始数据块进行协同、大面积篡改 根据 Reed-Solomon 码的原理,其纠错能力有一个上限,例如能纠正 `t` 个符号的错误。如果我们制造的损坏符号数大于 `t`,纠错就会失败。 1. **计算并超越纠错阈值**:首先需要了解该镜像FEC 的配置参数(如 `roots` 参数,它决定了校验符号的数量 `m`,进而决定了纠错能力 `t ≈ m/2`)。 2. **实施协同攻击**:不是只修改一个文件,而是**在同一个 FEC 码字(Code Word)内,修改多个不相关的数据块**。一个 FEC 码字包含 `k` 个数据符号 `m` 个校验符号[ref_1]。如果我们在同一个码字内,损坏的数据符号数量超过了 `t`,那么即使校验数据完好,解码器也无法计算出唯一正确的解。 * **操作难点**:这需要精确了解文件系统(如 ext4)的块分配与 FEC 码字符号之间的映射关系,技术门槛较高。通常需要逆向工程构建镜像的工具链。 #### 方案三:破坏哈希树与数据的关联性 dm-verity 的工作流程是:先读数据 -> 计算哈希 -> 与哈希树中的值比对。FEC 仅在哈希比对失败时触发[ref_1]。我们可以尝试制造一种矛盾状态。 1. **“正确”的哈希,“错误”的数据**:理论上,如果能在修改数据块后,**同步修改哈希树中对应节点的哈希值**,使其与篡改后的数据匹配,那么 dm-verity 的校验就会通过,FEC 根本不会被触发。 2. **“错误”的哈希,“不可纠正”的数据**:更接近您目标的做法是,修改数据块,但**不修改**对应的哈希值。这样 dm-verity 会检测到错误并调用 FEC。此时,如果您按照**方案二**的方法,在受影响的码字内制造足够多的错误FEC 就会纠正失败,系统最终因 I/O 错误而无法启动。 ### 三、 总结与风险提示 要让 FEC 无法纠错,最直接且易于实施的方法是 **方案一:直接破坏 FEC 校验数据本身**。这相当于抽掉了容错机制的基石。方案二在理论上更精巧,但实现复杂。方案三则涉及对 dm-verity 完整性的更深层攻击。 **重要警告**: * 上述所有操作都是对系统核心镜像的破坏性修改,**仅用于安全研究、教学或测试环境**。 * 在实际设备上,此类操作极大概率会导致设备**无法启动(变砖)**。 * 现代设备通常还结合了 **AVBAndroid Verified Boot)** 等机制,会对整个镜像(包括哈希树可能的FEC元数据)进行签名验证。仅仅破坏FEC数据可能导致签名校验失败,在启动的早期阶段就被拒绝,从而无法进入dm-verity阶段[ref_3]。 总而言之,通过挂载 `system.img`(或以其他二进制方式修改),定位并破坏其内部的 FEC 校验数据区域,是实现“让 FEC 不能纠错”这一目标最有效的技术路径。其根本原理在于使 FEC 解码器失去用于数学重建的必要冗余信息[ref_1]。
def add_hashtree_footer(self, image_filename, partition_size, partition_name, generate_fec, fec_num_roots, hash_algorithm, block_size, salt, chain_partitions, algorithm_name, key_path, public_key_metadata_path, rollback_index, flags, rollback_index_location, props, props_from_file, kernel_cmdlines, setup_rootfs_from_kernel, setup_as_rootfs_from_kernel, include_descriptors_from_image, calc_max_image_size, signing_helper, signing_helper_with_files, release_string, append_to_release_string, output_vbmeta_image, do_not_append_vbmeta_image, print_required_libavb_version, use_persistent_root_digest, do_not_use_ab, no_hashtree, check_at_most_once): required_libavb_version_minor = 0 if use_persistent_root_digest or do_not_use_ab or check_at_most_once: required_libavb_version_minor = 1 if rollback_index_location > 0: required_libavb_version_minor = 2 # If we're asked to calculate minimum required libavb version, we're done. if print_required_libavb_version: print('1.{}'.format(required_libavb_version_minor)) return digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'') .digest()) digest_padding = round_to_pow2(digest_size) - digest_size # If |partition_size| is given (e.g. not 0), calculate the maximum image # size such that an image this size + the hashtree + metadata (footer + # vbmeta struct) fits in |partition_size|. We use very conservative figures # for metadata. if partition_size > 0: max_tree_size = 0 max_fec_size = 0 if not no_hashtree: (_, max_tree_size) = calc_hash_level_offsets( partition_size, block_size, digest_size + digest_padding) if generate_fec: max_fec_size = calc_fec_data_size(partition_size, fec_num_roots) max_metadata_size = (max_fec_size + max_tree_size + self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE) max_image_size = partition_size - max_metadata_size else: max_image_size = 0 # If we're asked to only calculate the maximum image size, we're done. if calc_max_image_size: print('{}'.format(max_image_size)) return image = ImageHandler(image_filename) if partition_size > 0: if partition_size % image.block_size != 0: raise AvbError('Partition size of {} is not a multiple of the image ' 'block size {}.'.format(partition_size, image.block_size)) elif image.image_size % image.block_size != 0: raise AvbError('File size of {} is not a multiple of the image ' 'block size {}.'.format(image.image_size, image.block_size)) # If there's already a footer, truncate the image to its original # size. This way 'avbtool add_hashtree_footer' is idempotent # (modulo salts). if image.image_size >= AvbFooter.SIZE: image.seek(image.image_size - AvbFooter.SIZE) try: footer = AvbFooter(image.read(AvbFooter.SIZE)) # Existing footer found. Just truncate. original_image_size = footer.original_image_size image.truncate(footer.original_image_size) except (LookupError, struct.error): original_image_size = image.image_size else: # Image size is too small to possibly contain a footer. original_image_size = image.image_size # If anything goes wrong from here-on, restore the image back to # its original size. try: # Ensure image is multiple of block_size. rounded_image_size = round_to_multiple(image.image_size, block_size) if rounded_image_size > image.image_size: # If we need to round up the image size, it means the length of the # data to append is not a multiple of block size. # Setting multiple_block_size to false, so append_raw() will not # require it. image.append_raw(b'\0' * (rounded_image_size - image.image_size), multiple_block_size=False) # If image size exceeds the maximum image size, fail. if partition_size > 0: if image.image_size > max_image_size: raise AvbError('Image size of {} exceeds maximum image ' 'size of {} in order to fit in a partition ' 'size of {}.'.format(image.image_size, max_image_size, partition_size)) if salt: salt = binascii.unhexlify(salt) elif salt is None and not use_persistent_root_digest: # If salt is not explicitly specified, choose a hash that's the same # size as the hash size. Don't populate a random salt if this # descriptor is being created to use a persistent digest on device. hash_size = digest_size with open('/dev/urandom', 'rb') as f: salt = f.read(hash_size) else: salt = b'' # Hashes are stored upside down so we need to calculate hash # offsets in advance. (hash_level_offsets, tree_size) = calc_hash_level_offsets( image.image_size, block_size, digest_size + digest_padding) # If the image isn't sparse, its size might not be a multiple of # the block size. This will screw up padding later so just grow it. if image.image_size % image.block_size != 0: assert not image.is_sparse padding_needed = image.block_size - (image.image_size%image.block_size) image.truncate(image.image_size + padding_needed) # Generate the tree and add padding as needed. tree_offset = image.image_size root_digest, hash_tree = generate_hash_tree(image, image.image_size, block_size, hash_algorithm, salt, digest_padding, hash_level_offsets, tree_size) # Generate HashtreeDescriptor with details about the tree we # just generated. if no_hashtree: tree_size = 0 hash_tree = b'' ht_desc = AvbHashtreeDescriptor() ht_desc.dm_verity_version = 1 ht_desc.image_size = image.image_size ht_desc.tree_offset = tree_offset ht_desc.tree_size = tree_size ht_desc.data_block_size = block_size ht_desc.hash_block_size = block_size ht_desc.hash_algorithm = hash_algorithm ht_desc.partition_name = partition_name ht_desc.salt = salt if do_not_use_ab: ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_DO_NOT_USE_AB if not use_persistent_root_digest: ht_desc.root_digest = root_digest if check_at_most_once: ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE # Write the hash tree padding_needed = (round_to_multiple(len(hash_tree), image.block_size) - len(hash_tree)) hash_tree_with_padding = hash_tree + b'\0' * padding_needed #ifdef OPLUS_FEATURE_SECURITY_COMMON #Zhiwei.Qu@BSP.Security.Basic, 2023/02/03, skip appending tree chunk if the size is zero. if len(hash_tree_with_padding) > 0: image.append_raw(hash_tree_with_padding) #endif /*OPLUS_FEATURE_SECURITY_COMMON*/ len_hashtree_and_fec = len(hash_tree_with_padding) # Generate FEC codes, if requested. if generate_fec: if no_hashtree: fec_data = b'' else: fec_data = generate_fec_data(image_filename, fec_num_roots) padding_needed = (round_to_multiple(len(fec_data), image.block_size) - len(fec_data)) fec_data_with_padding = fec_data + b'\0' * padding_needed fec_offset = image.image_size image.append_raw(fec_data_with_padding) len_hashtree_and_fec += len(fec_data_with_padding) # Update the hashtree descriptor. ht_desc.fec_num_roots = fec_num_roots ht_desc.fec_offset = fec_offset ht_desc.fec_size = len(fec_data) ht_desc_to_setup = None if setup_as_rootfs_from_kernel: ht_desc_to_setup = ht_desc # Generate the VBMeta footer and add padding as needed. vbmeta_offset = tree_offset + len_hashtree_and_fec vbmeta_blob = self._generate_vbmeta_blob( algorithm_name, key_path, public_key_metadata_path, [ht_desc], chain_partitions, rollback_index, flags, rollback_index_location, props, props_from_file, kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup, include_descriptors_from_image, signing_helper, signing_helper_with_files, release_string, append_to_release_string, required_libavb_version_minor) padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) - len(vbmeta_blob)) vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed # Write vbmeta blob, if requested. if output_vbmeta_image: output_vbmeta_image.write(vbmeta_blob) # Append vbmeta blob and footer, unless requested not to. if not do_not_append_vbmeta_image: image.append_raw(vbmeta_blob_with_padding) # Now insert a DONT_CARE chunk with enough bytes such that the # final Footer block is at the end of partition_size.. if partition_size > 0: image.append_dont_care(partition_size - image.image_size - 1 * image.block_size) # Generate the Footer that tells where the VBMeta footer # is. Also put enough padding in the front of the footer since # we'll write out an entire block. footer = AvbFooter() footer.original_image_size = original_image_size footer.vbmeta_offset = vbmeta_offset footer.vbmeta_size = len(vbmeta_blob) footer_blob = footer.encode() footer_blob_with_padding = ( b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob) image.append_raw(footer_blob_with_padding) except Exception as e: # Truncate back to original size, then re-raise. image.truncate(original_image_size) raise AvbError('Adding hashtree_footer failed: {}.'.format(e)) from e详细解析avbtool中的add_hashtree_footer方法
10-17
<think>我们正在详细分析avbtool中的add_hashtree_footer方法。根据上下文,该方法用于在分区镜像(如system.img)上添加AVBAndroid Verified Boot)页脚,包括构建哈希树、生成FEC(前向纠错)数据以及添加vbmeta结构等。 我们将按照函数执行流程逐步分析: ### 1. 参数解析 函数参数众多,主要分为: - **镜像相关**:`image_filename`(镜像文件路径)、`partition_size`(分区大小)、`partition_name`(分区名) - **哈希树配置**:`hash_algorithm`(哈希算法)、`block_size`(块大小)、`salt`(盐值)、`no_hashtree`(是否跳过哈希树生成) - **FEC配置**:`generate_fec`(是否生成FEC)、`fec_num_roots`(FEC根数) - **签名与验证**:`algorithm_name`(签名算法)、`key_path`(私钥路径)、`public_key_metadata_path`(公钥元数据路径) - **描述符属性**:`props`(属性列表)、`props_from_file`(从文件读取属性)、`kernel_cmdlines`(内核命令行) - **高级功能**:`use_persistent_root_digest`(使用持久化根摘要)、`do_not_use_ab`(禁用A/B更新)、`check_at_most_once`(启用一次性检查) - **输出控制**:`output_vbmeta_image`(输出vbmeta文件)、`calc_max_image_size`(仅计算最大镜像尺寸) ### 2. 计算所需libavb版本 ```python required_libavb_version_minor = 0 if use_persistent_root_digest or do_not_use_ab or check_at_most_once: required_libavb_version_minor = 1 if rollback_index_location > 0: required_libavb_version_minor = 2 ``` - 若启用持久化根摘要/禁用A/B/一次性检查,则要求libavb >= 1.1 - 若指定了回滚索引位置(`rollback_index_location>0`),则要求libavb >= 1.2 ### 3. 计算摘要尺寸与填充 ```python digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'').digest()) digest_padding = round_to_pow2(digest_size) - digest_size ``` - `digest_size`:所选哈希算法的摘要长度(如SHA256为32字节) - `digest_padding`:将摘要长度对齐到下一个2的幂所需的填充(如32字节无需填充) ### 4. 分区空间计算 若分区大小`partition_size>0`,需预留空间存储哈希树、FEC元数据: ```python if partition_size > 0: max_tree_size = 0 max_fec_size = 0 if not no_hashtree: (_, max_tree_size) = calc_hash_level_offsets(partition_size, block_size, digest_size + digest_padding) if generate_fec: max_fec_size = calc_fec_data_size(partition_size, fec_num_roots) max_metadata_size = max_fec_size + max_tree_size + self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE max_image_size = partition_size - max_metadata_size ``` - `calc_hash_level_offsets`:计算哈希树的最大尺寸(基于分区尺寸块大小) - `calc_fec_data_size`:计算FEC数据尺寸(公式参考引用[3]) ### 5. 镜像预处理 - 若镜像已存在AVB页脚,则截断至原始数据大小 - 确保镜像大小为`block_size`的倍数(必要时填充0) ### 6. 哈希树生成 ```python # 计算哈希树布局 (hash_level_offsets, tree_size) = calc_hash_level_offsets( image.image_size, block_size, digest_size + digest_padding) # 生成哈希树 root_digest, hash_tree = generate_hash_tree(...) ``` - `generate_hash_tree`:逐块读取镜像数据,构建Merkle树 - 哈希树结构:自底向上,每层哈希由子节点哈希连接后计算得出 ### 7. 构建并写入哈希树 ```python ht_desc = AvbHashtreeDescriptor() # 设置描述符属性(分区名、块大小、盐值等) image.append_raw(hash_tree_with_padding) # 写入镜像 ``` 描述符关键字段: - `root_digest`:Merkle树根摘要(除非使用持久化根摘要) - `flags`:标志位(如禁用A/B、一次性检查) ### 8. FEC生成(可选) ```python if generate_fec: fec_data = generate_fec_data(image_filename, fec_num_roots) image.append_raw(fec_data_with_padding) ht_desc.fec_num_roots = fec_num_roots # 更新描述符 ``` - FEC基于Reed-Solomon码,用于纠正哈希树中的错误[^3] ### 9. 生成vbmeta结构 ```python vbmeta_blob = self._generate_vbmeta_blob(...) ``` - 整合哈希树描述符、链式分区、内核命令行等 - 使用指定私钥签名 ### 10. 写入vbmeta与页脚 ```python # 写入vbmeta(可选输出到独立文件) if output_vbmeta_image: output_vbmeta_image.write(vbmeta_blob) # 追加vbmeta到镜像 image.append_raw(vbmeta_blob_with_padding) # 添加AVB页脚 footer = AvbFooter() footer.original_image_size = original_image_size footer.vbmeta_offset = vbmeta_offset footer.vbmeta_size = len(vbmeta_blob) image.append_raw(footer_blob_with_padding) ``` 页脚结构固定512字节,位于分区末尾,包含: - 原始镜像大小 - vbmeta结构的位置大小 - 魔数(标识AVB格式) ### 11. 异常处理 若过程中出现异常,镜像将被截断回原始大小,避免残留无效数据。 ### 实例验证(引用[2][4]) 在Android编译中调用示例: ```bash avbtool add_hashtree_footer \ --partition_size 886812672 \ --partition_name system \ --image system.img \ --salt 6902...a6d3 \ --hash_algorithm sha256 \ ...其他参数... ``` 输出结果可通过`avbtool info_image`验证: - `Root Digest`与生成时一致[^4] - 包含哈希树/FEC的精确偏移量 ### 相关问题 1. 如何选择适合的哈希算法块大小以平衡安全性与性能? 2. 在启用`use_persistent_root_digest`时,根摘要如何安全存储? 3. 如何验证已添加页脚的镜像是否符合AVB规范? 4. FEC根数(`fec_num_roots`)对纠错能力有何影响?[^3] 5. 为什么在某些场景下需要禁用A/B更新(`do_not_use_ab`)? [^1]:该变量 BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS 可用于指定传递给 avbtool add_hashtree_footer 用于 system.img 的附加选项。此处通常使用的选项包括 --hash_algorithm 、 --salt 、 --block_size --do_not_generate_fec 。 [^2]:为了显示方便,整理一下具体的处理命令,如下: avbtool add_hashtree_footer \ --partition_size 886812672 \ --partition_name system \ --image aosp_panther-target_files-eng.rocky/IMAGES/system.img \ --salt 6902f6b436dd8f08a2ecd512d4576a03325e14db8e6b1bb72b68d22f20a6a6d3 \ --hash_algorithm sha256 \ --prop com.android.build.system.os_version:13 \ --prop com.android.build.system.fingerprint:Android/aosp_panther/panther:13/TQ2A.230405.003.E1/rocky12021421:userdebug/test-keys \ --prop com.android.build.system.security_patch:2023-04-05 [^3]:在 add_hashtree_footer() 函数中, 先通过调用 calc_fec_data_size() 根据分区大小 partition_size, FEC 可以纠正数据块的能力 fec_num_roots 来预估 FEC 数据的大小: [^4]:对比本文一开始使用 avbtool 查看 system.img 得到的信息中的 Root Digest,二者一致: Descriptors: Hashtree descriptor: Version of dm-verity: 1 Image Size: 872734720 bytes Tree Offset: 872734720 Tree Size: 6881280 bytes Data Block Size: 4096 bytes Hash Block Size: 4096 bytes FEC num roots: 2 FEC offset: 879616000 FEC size: 6955008 bytes Hash Algorithm: sha256 Partition Name: system Salt: 6902f6b436dd8f08a2ecd512d4576a03325e14db8e6b1bb72b68d22f20a6a6d3 Root Digest: e2b0749496127b3b0dd589ea54bf6ccb113fa05d587b1e361a55d3bc0ea6f068 Flags: 0 </think>### AVB的`add_hashtree_footer`方法详细解析 `add_hashtree_footer`是avbtool的核心方法,用于为Android分区镜像添加AVB(Android Verified Boot)验证结构。主要流程如下: #### 1. 参数校验与版本计算 ```python # 计算最小libavb版本要求(基于功能标志) required_libavb_version_minor = 0 if use_persistent_root_digest or do_not_use_ab or check_at_most_once: required_libavb_version_minor = 1 if rollback_index_location > 0: required_libavb_version_minor = 2 # [^1] ``` #### 2. 空间预计算 方法处理两种特殊场景: ```python if print_required_libavb_version: # 仅输出最小版本号 print('1.{}'.format(required_libavb_version_minor)) return if calc_max_image_size: # 仅计算最大镜像尺寸 max_metadata_size = max_fec_size + max_tree_size + self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE max_image_size = partition_size - max_metadata_size print('{}'.format(max_image_size)) return # [^2][^3] ``` #### 3. 镜像预处理 ```python # 检测并移除现有AVB页脚 if image.image_size >= AvbFooter.SIZE: image.seek(image.image_size - AvbFooter.SIZE) try: footer = AvbFooter(image.read(AvbFooter.SIZE)) image.truncate(footer.original_image_size) # 截断至原始大小 except: pass # 无有效页脚则保持原样 ``` #### 4. 盐值生成与哈希树构建 ```python # 盐值处理逻辑 if salt: salt = binascii.unhexlify(salt) # 使用指定盐值 elif salt is None and not use_persistent_root_digest: with open('/dev/urandom', 'rb') as f: salt = f.read(hash_size) # 生成随机盐值 else: salt = b'' # 使用持久化摘要时无需盐值 # 哈希树生成核心 (hash_level_offsets, tree_size) = calc_hash_level_offsets( image.image_size, block_size, digest_size + digest_padding ) root_digest, hash_tree = generate_hash_tree( image, image.image_size, block_size, hash_algorithm, salt, digest_padding, hash_level_offsets, tree_size ) # [^4] ``` #### 5. 哈希树描述符构建 ```python ht_desc = AvbHashtreeDescriptor() ht_desc.dm_verity_version = 1 ht_desc.image_size = image.image_size ht_desc.tree_offset = tree_offset ... if do_not_use_ab: ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_DO_NOT_USE_AB # 禁用A/B标志 if check_at_most_once: ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE # 一次性验证 ``` #### 6. FEC(前向纠错)生成 ```python if generate_fec: fec_data = generate_fec_data(image_filename, fec_num_roots) ... ht_desc.fec_num_roots = fec_num_roots # 设置FEC根数 ht_desc.fec_offset = fec_offset ht_desc.fec_size = len(fec_data) # [^3] ``` #### 7. VBMeta结构生成与签名 ```python vbmeta_blob = self._generate_vbmeta_blob( algorithm_name, key_path, public_key_metadata_path, [ht_desc], ... # 包含所有链式分区属性 signing_helper, signing_helper_with_files, required_libavb_version_minor ) # [^2] ``` #### 8. 结构写入与页脚添加 ```python # 写入哈希树FEC image.append_raw(hash_tree_with_padding) if generate_fec: image.append_raw(fec_data_with_padding) # 添加VBMeta页脚 if not do_not_append_vbmeta_image: image.append_raw(vbmeta_blob_with_padding) footer = AvbFooter() footer.original_image_size = original_image_size footer.vbmeta_offset = vbmeta_offset footer.vbmeta_size = len(vbmeta_blob) image.append_raw(footer_blob_with_padding) # [^4] ``` #### 9. 安全恢复机制 ```python try: # 所有写入操作 except Exception as e: image.truncate(original_image_size) # 出错时恢复原始镜像 raise AvbError('Adding hashtree_footer failed') from e ``` ### 关键数据结构 #### 哈希树描述符(AvbHashtreeDescriptor) | 字段 | 说明 | 示例值 | |------|------|--------| | `partition_name` | 分区标识 | `system` | | `hash_algorithm` | 哈希算法 | `sha256` | | `root_digest` | Merkle根摘要 | 64字节十六进制串 | | `salt` | 盐值 | 随机32字节 | | `flags` | 特性标志 | `FLAGS_CHECK_AT_MOST_ONCE` | #### AVB页脚(AvbFooter) ```c struct AvbFooter { uint64_t magic; // 魔数'AVBf' (0x0141566246) uint64_t original_size; // 原始镜像大小 uint64_t vbmeta_offset; // VBMeta结构偏移量 uint64_t vbmeta_size; // VBMeta结构大小 }; // 固定512字节 ``` ### 相关问题 1. 盐值(salt)AVB验证中如何增强安全性?[^2] 2. FEC根数(`fec_num_roots`)的选择对纠错能力有何影响?[^3] 3. 如何验证已添加页脚的镜像是否符合AVB规范?[^4] 4. 在Android构建系统中如何定制`add_hashtree_footer`参数?[^1] 5. 启用`check_at_most_once`会对设备启动性能产生什么影响? [^1]:该变量 BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS 可用于指定传递给 avbtool add_hashtree_footer 用于 system.img 的附加选项 [^2]:--salt 6902f6b436dd8f08a2ecd512d4576a03325e14db8e6b1bb72b68d22f20a6a6d3 [^3]:在 add_hashtree_footer() 函数中, 先通过调用 calc_fec_data_size() 根据分区大小 partition_size FEC 可以纠正数据块的能力 fec_num_roots 来预估 FEC 数据的大小 [^4]:Descriptors: Hashtree descriptor: Version of dm-verity: 1 ... Root Digest: e2b0749496127b3b0dd589ea54bf6ccb113fa05d587b1e361a55d3bc0ea6f068
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洛奇看世界

一分也是爱~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值