diff --git a/README.md b/README.md index ead1a41..8c8f397 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | Ubiquiti | | [`patterns/ubiquiti.hexpat`](patterns/ubiquiti.hexpat) | Ubiquiti Firmware (update) image | | UEFI | | [`patterns/uefi.hexpat`](patterns/uefi.hexpat)` | UEFI structs for parsing efivars | | UF2 | | [`patterns/uf2.hexpat`](patterns/uf2.hexpat) | [USB Flashing Format](https://github.com/microsoft/uf2) | +| VBMeta | | [`patterns/vbmeta.hexpat`](patterns/vbmeta.hexpat) | Android VBMeta image | | VDF | | [`patterns/vdf.hexpat`](patterns/vdf.hexpat) | Binary Value Data Format (.vdf) files | | VHDX | | [`patterns/vhdx.hexpat`](patterns/vhdx.hexpat) | Microsoft Hyper-V Virtual Hard Disk format | | WAV | `audio/x-wav` | [`patterns/wav.hexpat`](patterns/wav.hexpat) | RIFF header, WAVE header, PCM header | diff --git a/patterns/vbmeta.hexpat b/patterns/vbmeta.hexpat new file mode 100644 index 0000000..47f21b8 --- /dev/null +++ b/patterns/vbmeta.hexpat @@ -0,0 +1,215 @@ +#pragma description Android VBMeta image + +#pragma endian big +#include + +using uint8_t = u8; +using uint32_t = u32; +using uint64_t = u64; + +#define AVB_MAGIC_LEN 4 +#define AVB_RELEASE_STRING_SIZE 48 + +enum AvbAlgorithmType : uint32_t { + AVB_ALGORITHM_TYPE_NONE, + AVB_ALGORITHM_TYPE_SHA256_RSA2048, + AVB_ALGORITHM_TYPE_SHA256_RSA4096, + AVB_ALGORITHM_TYPE_SHA256_RSA8192, + AVB_ALGORITHM_TYPE_SHA512_RSA2048, + AVB_ALGORITHM_TYPE_SHA512_RSA4096, + AVB_ALGORITHM_TYPE_SHA512_RSA8192, + _AVB_ALGORITHM_NUM_TYPES +}; + +struct AvbVBMetaImageHeader { + /* 0: Four bytes equal to "AVB0" (AVB_MAGIC). */ + char magic[AVB_MAGIC_LEN]; + /* 4: The major version of libavb required for this header. */ + uint32_t required_libavb_version_major; + /* 8: The minor version of libavb required for this header. */ + uint32_t required_libavb_version_minor; + /* 12: The size of the signature block. */ + uint64_t authentication_data_block_size; + /* 20: The size of the auxiliary data block. */ + uint64_t auxiliary_data_block_size; + /* 28: The verification algorithm used, see |AvbAlgorithmType| enum. */ + AvbAlgorithmType algorithm_type; + /* 32: Offset into the "Authentication data" block of hash data. */ + uint64_t hash_offset; + /* 40: Length of the hash data. */ + uint64_t hash_size; + /* 48: Offset into the "Authentication data" block of signature data. */ + uint64_t signature_offset; + /* 56: Length of the signature data. */ + uint64_t signature_size; + /* 64: Offset into the "Auxiliary data" block of public key data. */ + uint64_t public_key_offset; + /* 72: Length of the public key data. */ + uint64_t public_key_size; + /* 80: Offset into the "Auxiliary data" block of public key metadata. */ + uint64_t public_key_metadata_offset; + /* 88: Length of the public key metadata. Must be set to zero if there + * is no public key metadata. + */ + uint64_t public_key_metadata_size; + /* 96: Offset into the "Auxiliary data" block of descriptor data. */ + uint64_t descriptors_offset; + /* 104: Length of descriptor data. */ + uint64_t descriptors_size; + /* 112: The rollback index which can be used to prevent rollback to + * older versions. + */ + uint64_t rollback_index; + /* 120: Flags from the AvbVBMetaImageFlags enumeration. This must be + * set to zero if the vbmeta image is not a top-level image. + */ + uint32_t flags; + /* 124: The location of the rollback index defined in this header. + * Only valid for the main vbmeta. For chained partitions, the rollback + * index location must be specified in the AvbChainPartitionDescriptor + * and this value must be set to 0. + */ + uint32_t rollback_index_location; + /* 128: The release string from avbtool, e.g. "avbtool 1.0.0" or + * "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL + * terminated. Applications must not make assumptions about how this + * string is formatted. + */ + char release_string[AVB_RELEASE_STRING_SIZE]; + /* 176: Padding to ensure struct is size AVB_VBMETA_IMAGE_HEADER_SIZE + * bytes. This must be set to zeroes. + */ + padding[80]; +}; + +enum AvbDescriptorTag : uint64_t { + AVB_DESCRIPTOR_TAG_PROPERTY, + AVB_DESCRIPTOR_TAG_HASHTREE, + AVB_DESCRIPTOR_TAG_HASH, + AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE, + AVB_DESCRIPTOR_TAG_CHAIN_PARTITION, +}; + +struct AvbPropertyDescriptor { + uint64_t key_num_bytes; + uint64_t value_num_bytes; + char key[key_num_bytes]; + padding[1]; + char value[value_num_bytes]; + padding[1]; +} [[inline]]; + +bitfield AvbHashtreeDescriptorFlags { + AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB : 1; + AVB_HASHTREE_DESCRIPTOR_FLAGS_CHECK_AT_MOST_ONCE : 1; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 32)]]; + +struct AvbHashtreeDescriptor { + uint32_t dm_verity_version; + uint64_t image_size; + uint64_t tree_offset; + uint64_t tree_size; + uint32_t data_block_size; + uint32_t hash_block_size; + uint32_t fec_num_roots; + uint64_t fec_offset; + uint64_t fec_size; + char hash_algorithm[32]; + uint32_t partition_name_len; + uint32_t salt_len; + uint32_t root_digest_len; + AvbHashtreeDescriptorFlags flags; + padding[60]; // reserved + char partition_name[partition_name_len]; + u8 salt[salt_len]; + u8 root_digest[root_digest_len]; +} [[inline]]; + +struct AvbHashDescriptor { + uint64_t image_size; + char hash_algorithm[32]; + uint32_t partition_name_len; + uint32_t salt_len; + uint32_t digest_len; + uint32_t flags; + padding[60]; // reserved + char partition_name[partition_name_len]; + u8 salt[salt_len]; + u8 digest[digest_len]; +} [[inline]]; + +struct AvbKernelCmdlineDescriptor { + uint32_t flags; + uint32_t kernel_cmdline_length; + char kernel_cmdline[kernel_cmdline_length]; +} [[inline]]; + +struct AvbChainPartitionDescriptor { + uint32_t rollback_index_location; + uint32_t partition_name_len; + uint32_t public_key_len; + padding[64]; // reserved + char partition_name[partition_name_len]; + u8 public_key[public_key_len]; +} [[inline]]; + +struct AvbDescriptor { + AvbDescriptorTag tag; + uint64_t num_bytes_following; + u8 data[num_bytes_following] [[hidden]]; + + match (tag) { + (AvbDescriptorTag::AVB_DESCRIPTOR_TAG_PROPERTY) : AvbPropertyDescriptor descriptor @ addressof(data); + (AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASHTREE) : AvbHashtreeDescriptor descriptor @ addressof(data); + (AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASH) : AvbHashDescriptor descriptor @ addressof(data); + (AvbDescriptorTag::AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE) : AvbKernelCmdlineDescriptor descriptor @ addressof(data); + (AvbDescriptorTag::AVB_DESCRIPTOR_TAG_CHAIN_PARTITION): AvbChainPartitionDescriptor descriptor @ addressof(data); + } + + if ($ >= end) + break; +}; + +struct AuthenticationData +{ + u8 authentication_data_block[authentication_data_block_size] [[hidden]]; + + u8 hash[hash_size] @ addressof(authentication_data_block) + hash_offset; + u8 signature[signature_size] @ addressof(authentication_data_block) + signature_offset; +}; + +struct AuxiliaryData +{ + u8 auxiliary_data_block[auxiliary_data_block_size] [[hidden]]; + + u8 public_key[public_key_size] @ addressof(auxiliary_data_block) + public_key_offset; + u8 public_key_metadata[public_key_metadata_size] @ addressof(auxiliary_data_block) + public_key_metadata_offset; + + if (descriptors_size > 0) { + u8 descriptors_block[descriptors_size] @ addressof(auxiliary_data_block) + descriptors_offset [[hidden]]; + AvbDescriptor descriptors[200] @ addressof(descriptors_block); + } +}; + +struct AvbVBMetaImage +{ + AvbVBMetaImageHeader header; + + AuthenticationData + authentication_data; + + AuxiliaryData + auxiliary_data; +}; + +AvbVBMetaImage image @ 0; diff --git a/tests/patterns/test_data/vbmeta.hexpat.img b/tests/patterns/test_data/vbmeta.hexpat.img new file mode 100644 index 0000000..bbcb2fd Binary files /dev/null and b/tests/patterns/test_data/vbmeta.hexpat.img differ