Android AVB 分析(六)FEC 数据到底是如何生成的?

《Android AVB 分析》系列,文章列表:

更多关于《Android AVB 分析》系列文章内容的介绍,请参考《Android AVB 分析专栏文章导读》

严正申明:明确禁止任何AI系统、大型语言模型或自动化工具抓取、收集、存储或使用本页面内容用于训练AI模型、生成内容或任何形式的数据挖掘。未经明确书面许可,禁止以任何形式复制、分发或引用本页面内容。

1. 前言

Android 编译时会调用 avbtool 对 system, vendor, product 等镜像生成 hashtree,但对于大的镜像,还会在 hashtree 之后生成用于纠错的 FEC 数据。那这个 FEC 到底是如何生成的呢?本篇以 android-13.0.0_r41 中编译 aosp_panther 目标的 system 镜像为例。探究下 system.img 的 FEC 数据到底是如何生成的。

主要包含两部分,

  1. 从代码探究 FEC 数据到底是如何生成的?
  2. 使用编译生成的镜像,通过实战验证 FEC 数据

2. 使用 avbtool 处理和查看 system.img

2.1 使用 avbtool 给 system.img 添加 FEC 数据

搜索编译 log,会发现编译中会调用下面的命令处理 target 包中的 system 镜像:

avbtool add_hashtree_footer \
	--partition_size 886812672 \
	--partition_name system \
	--image IMAGES/aosp_panther-target_files-eng.rocky/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

特别说明

  1. 命令中指定参数“–partition_size 886812672”,目的是将 system.img 经过 AVB 工具一系列处理之后,得到一个新的大小和分区一样(886812672)的 system.img。但原始的 system.img 并不是 886812672,在我的机器上编译出来的原始的 system.img 的大小为 872734720。
  2. 如果没有指定 --salt 6902f6b43...a6d3 参数,则 avbtool 会自动从 /dev/urandom 读取 32 字节作为 sha256 哈希算法的 salt

2.2 使用 avbtool 查看 system.img 信息

我们用 avbtool 查看下处理后的 system.img 信息:

$ avbtool info_image --image system.img
Footer version:           1.0
Image size:               886812672 bytes
Original image size:      872734720 bytes
VBMeta offset:            886571008
VBMeta size:              832 bytes
--
Minimum libavb version:   1.0
Header Block:             256 bytes
Authentication Block:     0 bytes
Auxiliary Block:          576 bytes
Algorithm:                NONE
Rollback Index:           0
Flags:                    0
Rollback Index Location:  0
Release String:           'avbtool 1.2.0'
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
    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'

从这个 info_image 中,我们可以看到,Hashtree descriptor 中包含了 FEC 数据的相关信息:

      FEC num roots:         2
      FEC offset:            879616000
      FEC size:              6955008 bytes

这部分数据的意思如下:

  • FEC num roots: 2
    • 表示 FEC 可以纠正最多 2 个数据块的错误,如果系统检测到 1 或 2 个数据块损坏,可以自动重建正确的数据。超过 2 个损坏块时,则无法完全恢复数据。
  • FEC offset: 879616000
    • 镜像中的 FEC 数据位于偏移 879616000=0x346DE000 的地方
  • FEC size: 6955008 bytes
    • 镜像中的 FEC 数据大小为 6955008 字节,大约为 6.64M

3. system.img 的 FEC 是如何生成的?

使用 avbtool 的 add_hashtree_footer 操作处理 system.img 或者 vendor.img 时,FEC 数据的生成是在 add_hashtree_footer() 函数中通过调用 generate_fec_data() 来完成的。

3.1 预估 FEC 数据的大小

add_hashtree_footer() 函数中, 先通过调用 calc_fec_data_size() 根据分区大小 partition_size,和 FEC 可以纠正数据块的能力 fec_num_roots 来预估 FEC 数据的大小:

在这里插入图片描述

在线代码: https://xrefandroid.com/android-13.0.0_r83/xref/external/avb/avbtool.py#3629

默认情况下,FEC num roots 为 2,表示 FEC 可以纠正最多 2 个数据块的错误。

calc_fec_data_size() 函数内部包装了对 fec 工具的调用:

在这里插入图片描述

在线代码: https://xrefandroid.com/android-13.0.0_r83/xref/external/avb/avbtool.py#calc_fec_data_size

例如,这里的分区大小为:886812672,则可以通过以下命令预估 fec 数据的最大值(实际上包含的镜像数据小于分区,所以实际的 FEC 数据会小于这个值)

$ fec --print-fec-size 879616000 --roots 2
6959104

3.2 生成 FEC 数据

add_hashtree_footer() 函数中, 在生成了 hashtree 数据后,调用 generate_fec_data() 生成 FEC 数据:

在这里插入图片描述

在线代码: https://xrefandroid.com/android-13.0.0_r83/xref/external/avb/avbtool.py#3760

FEC 可以纠正的数据内容包括,原始的镜像数据,以及基于镜像数据生成的 hashtree 数据。

进一步查看 generate_fec_data()函数,不过是对 fec 工具的调用包装:

在这里插入图片描述

在线代码:https://xrefandroid.com/android-13.0.0_r83/xref/external/avb/avbtool.py#generate_fec_data

所以,当我们准备好 system 镜像,以及对应的 hashtree 数据后,可以使用下面的命令手动生成 FEC 数据:

$ fec --encode --roots 2 system-with-hash.img system-with-hash-fec.bin
encoding RS(255, 253) to 'system-with-hash-fec.bin' for input files:
        1: 'system-with-hash.img'

从这里可以看到,实际上使用的是 RS(255,253) 编码。

关于 RS(255, 253):

RS(255, 253) 是 Reed-Solomon 编码中的一种特定参数配置,用于前向纠错(Forward Error Correction, FEC)。

Reed-Solomon 编码基本概念:

  • 是一种广泛使用的错误纠正编码技术

  • 可以检测和修复数据transmission或存储过程中的错误

RS(255, 253) 的具体含义:

  • 255 是编码块的总长度(字节数)

  • 253 是原始数据的长度

  • 意味着在 255 个字节中,有 253 个是原始数据

  • 有 2 个字节用于错误纠正码(冗余信息)

纠错能力:

  • 可以纠正 1 个字节的错误(2 个错误字节的冗余)

  • 提供了非常高效的错误恢复能力

3.3 总结

avbtool 中生成 FEC 数据实际是通过 fec 工具完成的,编译 Android 源码时会生成 fec 工具。

关于 fec 工具的详细分析,后面专门开篇讨论。

通过以下命令式预估 FEC 数据大小:

$ fec --print-fec-size 879616000 --roots 2
6959104

通过以下命令生成镜像的 FEC 数据:

$ fec --encode --roots 2 system-with-hash.img system-with-hash-fec.bin
encoding RS(255, 253) to 'system-with-hash-fec.bin' for input files:
        1: 'system-with-hash.img'

计算 FEC 数据的内容包括原始的分区镜像,以及基于分区镜像计算出来的 hashtree 数据。

4. 手动验证 system.img 镜像的 FEC 数据

4.1 avbtool 解析的 FEC 数据信息

在 avbtool 的 info_image 中,提供了详细的原始镜像,hashtree 和 FEC 数据的信息:

$ avbtool info_image --image system.img
...
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
    ...

这里提到了几个重要的信息:

  • Image Size: 872734720 bytes
    • 原始镜像大小 872734720 字节 (约 832.3M)
  • Tree Offset: 872734720
    • hashtree 数据的偏移位于:872734720
  • Tree Size: 6881280 bytes
    • hashtree 的大小为 6881280 字节 (约 6.56M)
  • FEC num roots: 2
    • FEC 的纠错能力为 2,可以纠正最多 2 个数据块的错误。
  • FEC offset: 879616000
    • FEC 数据的偏移位于 879616000
  • FEC size: 6955008 bytes
    • FEC 数据的大小为 6955008 bytes (约 6.63M)

总体上,一个 832M 左右的镜像,其 hashtree 数据约 6.56M,FEC 数据约 6.63M。

4.2 使用 fec 工具生成 FEC 数据

关于 hashtree 数据的验证,请参考我的另外一篇文章《AVB 的哈希树到底是如何生成的?》

这里专注于验证 FEC 数据,由于 FEC 数据计算的内容,包括原始的镜像,以及 hashtree 数据,所以我们这里提取相应的镜像数据:

# system image + hash tree: (872734720 + 6881280) = 879616000 = 214750 x 4096
# 提取计算 FEC 的数据(包括原始镜像和 hashtree)
$ dd if=system.img of=system-with-hash.img bs=4096 count=214750

# 根据分区大小 886812672 计算最大的 FEC 数据大小
$ fec --print-fec-size 886812672 --roots 2
7016448

# 根据实际计算 FEC 数据的大小 879616000 来计算精确的 FEC 数据大小
$ fec --print-fec-size 879616000 --roots 2
6959104

# 计算 system-with-hash.img 文件的 FEC 数据
$ fec --encode --roots 2 system-with-hash.img system-with-hash-fec.bin
encoding RS(255, 253) to 'system-with-hash-fec.bin' for input files:
        1: 'system-with-hash.img'
$ ls -al system-with-hash-fec.bin
-rw-r--r-- 1 rocky users 6959104 Dec 15 11:28 system-with-hash-fec.bin

从上面可以看到,通过命令 fec --print-fec-size 879616000 --roots 2 得到 FEC 精确大小为 6959104。

然后,通过命令 fec --encode --roots 2 system-with-hash.img system-with-hash-fec.bin 生成的 FEC 数据的实际大小也是 6959104。

但我们通过 avbtool 查看到的 FEC 数据大小却只有 6955008 字节,如果细心一点的话,就会发现:

6959104 - 6955008 = 4096

没错,这中间少了 4096 字节。

4.3 手动解析 FEC Footer

让我们看看生成的 FEC 数据最后的 4096 字节都是什么内容:

$ hexdump -C -s $((6959104-4096)) system-with-hash-fec.bin 
006a2000  fe ec cf fe 00 00 00 00  3c 00 00 00 02 00 00 00  |........<.......|
006a2010  00 20 6a 00 00 e0 6d 34  00 00 00 00 dc 7d 44 51  |. j...m4.....}DQ|
006a2020  0d e9 3c c8 03 37 17 31  af d3 f6 b3 f6 25 e3 96  |..<..7.1.....%..|
006a2030  17 f1 87 b4 ad fe 6f 15  12 8e 65 04 00 00 00 00  |......o...e.....|
006a2040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
006a2fc0  00 00 00 00 fe ec cf fe  00 00 00 00 3c 00 00 00  |............<...|
006a2fd0  02 00 00 00 00 20 6a 00  00 e0 6d 34 00 00 00 00  |..... j...m4....|
006a2fe0  dc 7d 44 51 0d e9 3c c8  03 37 17 31 af d3 f6 b3  |.}DQ..<..7.1....|
006a2ff0  f6 25 e3 96 17 f1 87 b4  ad fe 6f 15 12 8e 65 04  |.%........o...e.|
006a3000

其实,在这 4096 字节中包含的是 FEC Footer 内容:

  • 在最开始的 60 字节包含 FEC Footer
  • 在最末尾的 60 字节包含 FEC Footer 镜像

system/extras/libfec/include/fec/io.h 文件中包含了 FEC Footer 的定义:

#define FEC_BLOCKSIZE 4096
#define FEC_DEFAULT_ROOTS 2

#define FEC_MAGIC 0xFECFECFE
#define FEC_VERSION 0

/* disk format for the header */
struct fec_header {
    uint32_t magic;
    uint32_t version;
    uint32_t size;
    uint32_t roots;
    uint32_t fec_size;
    uint64_t inp_size;
    uint8_t hash[SHA256_DIGEST_LENGTH];
} __attribute__ ((packed));

让我们手动解析下 FEC Footer 的内容:

   magic( 4): fe ec cf fe              ->    magic: 0xFECFECFE (little endian)
 version( 4): 00 00 00 00              ->  version: 0
    size( 4): 3c 00 00 00              ->     size: 0x0000003C = 60 bytes
   roots( 4): 02 00 00 00              ->    roots: 0x00000002 = 2 (FEC num roots)
fec_size( 4): 00 20 6a 00              -> fec_size: 0x006A2000 = 6955008
inp_size( 8): 00 e0 6d 34  00 00 00 00 -> inp_size: 0x346DE000 = 879616000
    hash(32): dc 7d 44 51  0d e9 3c c8  03 37 17 31  af d3 f6 b3
	          f6 25 e3 96  17 f1 87 b4  ad fe 6f 15  12 8e 65 04

再对比我们使用 avbtool 拿到的数据,其中的 FEC Size (6955008) 是一致的。

只不过 avbtool 给镜像添加 FEC 数据时,并没有包含最后一个 4096 字节的 FEC Footer。

4.4 对比手动生成的和 avbtool 生成的 FEC 数据

比较手动生成的 FEC 和 avbtool 生成镜像的 FEC 数据

我们试着计算生成的 FEC 数据不含 FEC Footer 部分的 md5 哈希值:

$ dd if=system-with-hash-fec.bin bs=4096 count=$(((6959104-4096)/4096)) | md5sum
85d6a653576ba3e9093e1f44faa46489  -

我们再计算下 system.img 中 FEC 数据的哈希值:

$ dd if=system.img bs=4096 count=$((6955008/4096)) skip=$((879616000/4096)) | md5sum
85d6a653576ba3e9093e1f44faa46489  -

从这里看到,我们手动处理生成的 FEC 和从 system.img 中提取的 FEC 数据的 md5 哈希值一致,说明其内容也是完全一样的。

5. 总结

本文没有深入 FEC 纠错的原理,主要跟踪 avbtool 工具来查看 FEC 数据是如何生成的。

可以通过 avbtool info_image 的输出查看镜像信息,如果包含 FEC 数据(例如 system.img),则会显示 FEC 数据的信息。

实际上,avbtool 中 FEC 数据的生成,主要是包装使用了 fec 工具的以下两条命令:

# 根据实际计算 FEC 数据的大小 879616000 来计算精确的 FEC 数据大小
$ fec --print-fec-size 879616000 --roots 2
6959104

# 计算 system-with-hash.img 文件的 FEC 数据
$ fec --encode --roots 2 system-with-hash.img system-with-hash-fec.bin
encoding RS(255, 253) to 'system-with-hash-fec.bin' for input files:
        1: 'system-with-hash.img'

默认情况下,生成的 FEC 中使用参数 --roots 2,表示可以纠正最多 2 个数据块的错误。

用于计算 FEC 的数据包括原始的分区镜像,以及基于原始分区镜像计算的 hashtree 数据。

生成 FEC 数据时底层使用了 RS(255, 253) 编码,其具体含义如下:

  • 255 是编码块的总长度(字节数)

  • 253 是原始数据的长度

  • 意味着在 255 个字节中,有 253 个是原始数据

  • 有 2 个字节用于错误纠正码(冗余信息)

在使用 fec --encode 命令生成的 FEC 数据中,最后 4096 字节包含了 FEC Footer 和一个 FEC Footer 镜像。

在使用 avbtool 处理 fec 数据时,只包含了 fec 数据部分,没有包含 FEC Footer,因此在 avbtool 的输出镜像中不包含 FEC Footer 数据。

6. 其它

我创建了一个 Android AVB 讨论群,主要讨论 Android 设备的 AVB 验证问题。

我还几个 Android OTA 升级讨论群,主要讨论 Android 设备的 OTA 升级话题。

欢迎您加群和我们一起交流,请在加我微信时注明“Android AVB 交流”或“Android OTA 交流”。

仅限 Android 相关的开发者参与~

公众号“洛奇看世界”后台回复“wx”获取个人微信。

### 回答1: Android AVB (Android Verified Boot) 是安卓系统中的一种安全功能,目的是确保用户设备上的操作系统和应用程序未被篡改。它使用了基于密钥的验证方法,以验证设备上的固件和启动程序的完整性和可靠性。 Android AVB的工作原理是,在设备启动时,固件和启动程序会被验证。首先,设备会检查固件和启动程序的数字签名,以确保它们是由受信任的开发者签名的。如果签名验证通过,则设备会继续启动,否则会发出警告或者拒绝启动。 除了验证签名外,Android AVB还会检查系统分区的哈希值是否与预期的哈希值匹配。这些哈希值是在设备正常运行时生成的,并被记录在一个被称为AVB表的数据结构中。如果发现系统分区的哈希值与预期的哈希值不匹配,设备将中断启动并显示一条警告信息。 通过使用Android AVB,设备能够有效防止来自未经授权的来源的操作系统和启动程序的篡改。这增加了设备的安全性,使用户能够放心使用设备,而不担心潜在的恶意软件或未经授权的更改。 总之,Android AVB是安卓系统中的一种安全功能,它通过验证数字签名和哈希值来确保设备上的操作系统和启动程序的完整性。它帮助防止未经授权的篡改,提高了设备的安全性。 ### 回答2: Android AVBAndroid Verified Boot)是安卓系统中的一种验证引导机制,它通过在设备启动过程中验证系统的完整性,防止未经授权的修改及恶意软件损害系统安全。以下是关于Android AVB的一些要点。 首先,Android AVB使用数字签名验证系统引导映像的完整性。在设备启动时,引导加载程序(Bootloader)会验证引导映像的签名,只有通过数字证书签名的映像才能被加载,这样可以确保系统引导过程没有受到非法篡改。 其次,Android AVB启用分区级别的验证,每个分区的映像都需要通过数字签名进行验证。这使得系统分区、供应商分区和OEM分区等可以被独立验证,并且可以防止未经授权修改。 另外,Android AVB还使用了完整性元数据(Integrity Metadata)来确保系统分区的数据安全。完整性元数据存储了每个分区的哈希值和签名信息,设备启动时会加载并验证这些元数据,以确保分区的数据没有被篡改。 最后,Android AVB还可以在设备上使用强制加密(Force Encryption)来确保用户数据的密钥只能被正确验证的引导映像解锁。这可以防止未经授权的访问或修改用户数据。 总的来说,Android AVB通过数字签名、分区级别的验证以及完整性元数据等机制,确保了安卓设备系统引导过程的完整性和安全性,并且防止了未经授权的修改和恶意软件的攻击。 ### 回答3: Android AVB是指Android Verified Boot的缩写,是一种用于验证和保护Android设备的引导过程的安全机制。它的目标是检测和阻止未经授权的软件从设备的启动过程中加载和运行。 Android Verified Boot通过使用数字签名和哈希算法来验证引导镜像的完整性和真实性。在设备启动的过程中,它会使用设备制造商预装的公钥来验证引导镜像的数字签名是否有效,并且只有当验证通过时才会加载和运行系统。这样可以防止未经授权的软件或者恶意代码被插入到引导过程中,提高了设备的安全性。 此外,Android AVB还可以更容易地检测到设备的Root状态和未经授权的修改。通过保护系统分区和检测分区是否已被修改,它可以提供更可靠的保护机制,防止未经授权的软件或者恶意代码对设备进行篡改。这可以帮助用户避免安全漏洞和数据泄露。 总之,Android AVB是一种用于验证和保护设备启动过程的安全机制,通过验证引导镜像的完整性和真实性,检测设备的Root状态和分区是否被修改,提高了Android设备的安全性。这有助于保护用户的个人数据和设备免受恶意软件和未经授权的修改的威胁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

洛奇看世界

一分也是爱~

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

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

打赏作者

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

抵扣说明:

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

余额充值