diff --git a/README.md b/README.md index 93047d7..abae94b 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,24 @@ Your boot.img.signed and vbmeta.img.signd will be updated together, then you can +
+ working with vendor_boot.img + vbmeta.img (Pixel 5 etc.) +Most devices include hash descriptor of vendor_boot.img in vbmeta.img, so if you need to modify vendor_boot.img, you need to update vbmeta.img together. + +```bash +rm *.img +cp vendor_boot.img +cp vbmeta.img +./gradlew unpack +./gradlew pack +./gradlew flash +``` + +Please note that to use 'gradle flash', your host machine must be connectted to your DUT with adb, and you already 'adb root'. + +
+ +
How to disable AVB verification @@ -158,6 +176,8 @@ boot\_signer
https://android.googlesource.com/platform/system/extras
mkbootimg
https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/
+boot header definition
+https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h
kernel info extractor
https://android.googlesource.com/platform/build/+/refs/heads/master/tools/extract_kernel.py
mkdtboimg
diff --git a/aosp/avb/avbtool.v1.2.py b/aosp/avb/avbtool.v1.2.py index 1211df3..8647b29 100755 --- a/aosp/avb/avbtool.v1.2.py +++ b/aosp/avb/avbtool.v1.2.py @@ -642,6 +642,15 @@ def verify_vbmeta_signature(vbmeta_header, vbmeta_blob): return True +def create_avb_hashtree_hasher(algorithm, salt): + """Create the hasher for AVB hashtree based on the input algorithm.""" + + if algorithm.lower() == 'blake2b-256': + return hashlib.new('blake2b', salt, digest_size=32) + + return hashlib.new(algorithm, salt) + + class ImageChunk(object): """Data structure used for representing chunks in Android sparse files. @@ -1406,7 +1415,8 @@ class AvbHashtreeDescriptor(AvbDescriptor): self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)] o += salt_len self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)] - if root_digest_len != len(hashlib.new(self.hash_algorithm).digest()): + + if root_digest_len != self._hashtree_digest_size(): if root_digest_len != 0: raise LookupError('root_digest_len doesn\'t match hash algorithm') @@ -1426,6 +1436,9 @@ class AvbHashtreeDescriptor(AvbDescriptor): self.root_digest = b'' self.flags = 0 + def _hashtree_digest_size(self): + return len(create_avb_hashtree_hasher(self.hash_algorithm, b'').digest()) + def print_desc(self, o): """Print the descriptor. @@ -1496,7 +1509,7 @@ class AvbHashtreeDescriptor(AvbDescriptor): image_filename = os.path.join(image_dir, self.partition_name + image_ext) image = ImageHandler(image_filename, read_only=True) # Generate the hashtree and checks that it matches what's in the file. - digest_size = len(hashlib.new(self.hash_algorithm).digest()) + digest_size = self._hashtree_digest_size() digest_padding = round_to_pow2(digest_size) - digest_size (hash_level_offsets, tree_size) = calc_hash_level_offsets( self.image_size, self.data_block_size, digest_size + digest_padding) @@ -3579,7 +3592,8 @@ class Avb(object): print('1.{}'.format(required_libavb_version_minor)) return - digest_size = len(hashlib.new(hash_algorithm).digest()) + 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 @@ -4064,7 +4078,7 @@ def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt, level_output_list = [] remaining = hash_src_size while remaining > 0: - hasher = hashlib.new(hash_alg_name, salt) + hasher = create_avb_hashtree_hasher(hash_alg_name, salt) # Only read from the file for the first level - for subsequent # levels, access the array we're building. if level_num == 0: @@ -4096,7 +4110,7 @@ def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt, hash_src_size = len(level_output) level_num += 1 - hasher = hashlib.new(hash_alg_name, salt) + hasher = create_avb_hashtree_hasher(hash_alg_name, salt) hasher.update(level_output) return hasher.digest(), bytes(hash_ret) diff --git a/bbootimg/src/main/kotlin/bootimg/Common.kt b/bbootimg/src/main/kotlin/bootimg/Common.kt index 831725d..8a158aa 100644 --- a/bbootimg/src/main/kotlin/bootimg/Common.kt +++ b/bbootimg/src/main/kotlin/bootimg/Common.kt @@ -23,7 +23,6 @@ import java.nio.ByteOrder import java.security.MessageDigest import java.util.regex.Pattern - @OptIn(ExperimentalUnsignedTypes::class) class Common { data class VeritySignature( @@ -34,13 +33,6 @@ class Common { var jarPath: String = "" ) - data class Slice( - var srcFile: String, - var offset: Int, - var length: Int, - var dumpFile: String - ) - companion object { private val log = LoggerFactory.getLogger(Common::class.java) private const val MAX_ANDROID_VER = 11 @@ -111,12 +103,12 @@ class Common { return listOf() } - fun dumpKernel(s: Slice) { + fun dumpKernel(s: Helper.Slice) { Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) parseKernelInfo(s.dumpFile) } - fun dumpRamdisk(s: Slice, root: String): String { + fun dumpRamdisk(s: Helper.Slice, root: String): String { var ret = "gz" Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) when { @@ -162,7 +154,7 @@ class Common { return ret } - fun dumpDtb(s: Slice) { + fun dumpDtb(s: Helper.Slice) { Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) //extract DTB if (EnvironmentVerifier().hasDtc) { diff --git a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt index e253e4a..da089ee 100644 --- a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt +++ b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt @@ -4,7 +4,6 @@ import cfig.Avb import cfig.EnvironmentVerifier import cfig.bootimg.Common import cfig.bootimg.Common.Companion.deleleIfExists -import cfig.bootimg.Common.Slice import cfig.bootimg.Signer import cfig.helper.Helper import cfig.packable.VBMetaParser @@ -169,12 +168,11 @@ data class BootV2( //info ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this) //kernel - Common.dumpKernel(Slice(info.output, kernel.position.toInt(), kernel.size, kernel.file!!)) + Common.dumpKernel(Helper.Slice(info.output, kernel.position.toInt(), kernel.size, kernel.file!!)) //ramdisk if (this.ramdisk.size > 0) { val fmt = Common.dumpRamdisk( - Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), - "${workDir}root" + Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), "${workDir}root" ) this.ramdisk.file = this.ramdisk.file!! + ".$fmt" //dump info again @@ -200,7 +198,7 @@ data class BootV2( } //dtb this.dtb?.let { _ -> - Common.dumpDtb(Slice(info.output, dtb!!.position.toInt(), dtb!!.size, dtb!!.file!!)) + Common.dumpDtb(Helper.Slice(info.output, dtb!!.position.toInt(), dtb!!.size, dtb!!.file!!)) } return this diff --git a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt index ffab8f8..b9cfa93 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt @@ -20,9 +20,11 @@ import java.nio.ByteOrder import cfig.bootimg.Common as C @OptIn(ExperimentalUnsignedTypes::class) -data class BootV3(var info: MiscInfo = MiscInfo(), - var kernel: CommArgs = CommArgs(), - val ramdisk: CommArgs = CommArgs() +data class BootV3( + var info: MiscInfo = MiscInfo(), + var kernel: CommArgs = CommArgs(), + val ramdisk: CommArgs = CommArgs(), + var bootSignature: CommArgs = CommArgs() ) { companion object { private val log = LoggerFactory.getLogger(BootV3::class.java) @@ -51,6 +53,13 @@ data class BootV3(var info: MiscInfo = MiscInfo(), ret.ramdisk.size = header.ramdiskSize ret.ramdisk.position = ret.kernel.position + header.kernelSize + getPaddingSize(header.kernelSize, BootHeaderV3.pageSize) + //boot signature + if (header.signatureSize > 0) { + ret.bootSignature.file = workDir + "bootsig" + ret.bootSignature.size = header.signatureSize + ret.bootSignature.position = ret.ramdisk.position + ret.ramdisk.size + + getPaddingSize(header.ramdiskSize, BootHeaderV3.pageSize) + } } ret.info.imageSize = File(fileName).length() return ret @@ -58,22 +67,23 @@ data class BootV3(var info: MiscInfo = MiscInfo(), } data class MiscInfo( - var output: String = "", - var json: String = "", - var headerVersion: Int = 0, - var headerSize: Int = 0, - var pageSize: Int = 0, - var cmdline: String = "", - var osVersion: String = "", - var osPatchLevel: String = "", - var imageSize: Long = 0, - var signatureSize: Int = 0 + var output: String = "", + var json: String = "", + var headerVersion: Int = 0, + var headerSize: Int = 0, + var pageSize: Int = 0, + var cmdline: String = "", + var osVersion: String = "", + var osPatchLevel: String = "", + var imageSize: Long = 0, + var signatureSize: Int = 0 ) data class CommArgs( - var file: String = "", - var position: Int = 0, - var size: Int = 0) + var file: String = "", + var position: Int = 0, + var size: Int = 0 + ) fun pack(): BootV3 { if (File(this.ramdisk.file).exists() && !File(workDir + "root").exists()) { @@ -94,9 +104,9 @@ data class BootV3(var info: MiscInfo = MiscInfo(), FileOutputStream(this.info.output + ".clear", false).use { fos -> val encodedHeader = this.toHeader().encode() fos.write(encodedHeader) - fos.write(ByteArray( - Helper.round_to_multiple(encodedHeader.size, - this.info.pageSize) - encodedHeader.size)) + fos.write( + ByteArray(Helper.round_to_multiple(encodedHeader.size, this.info.pageSize) - encodedHeader.size) + ) } //data @@ -128,13 +138,14 @@ data class BootV3(var info: MiscInfo = MiscInfo(), private fun toHeader(): BootHeaderV3 { return BootHeaderV3( - kernelSize = kernel.size, - ramdiskSize = ramdisk.size, - headerVersion = info.headerVersion, - osVersion = info.osVersion, - osPatchLevel = info.osPatchLevel, - headerSize = info.headerSize, - cmdline = info.cmdline) + kernelSize = kernel.size, + ramdiskSize = ramdisk.size, + headerVersion = info.headerVersion, + osVersion = info.osVersion, + osPatchLevel = info.osPatchLevel, + headerSize = info.headerSize, + cmdline = info.cmdline + ) } fun extractImages(): BootV3 { @@ -142,11 +153,21 @@ data class BootV3(var info: MiscInfo = MiscInfo(), //info ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) //kernel - C.dumpKernel(C.Slice(info.output, kernel.position, kernel.size, kernel.file)) + C.dumpKernel(Helper.Slice(info.output, kernel.position, kernel.size, kernel.file)) //ramdisk val fmt = C.dumpRamdisk( - C.Slice(info.output, ramdisk.position, ramdisk.size, ramdisk.file), "${workDir}root") + Helper.Slice(info.output, ramdisk.position, ramdisk.size, ramdisk.file), "${workDir}root" + ) this.ramdisk.file = this.ramdisk.file + ".$fmt" + //bootsig + if (info.signatureSize > 0) { + Helper.extractFile( + info.output, this.bootSignature.file, + this.bootSignature.position.toLong(), this.bootSignature.size + ) + Avb().parseVbMeta(this.bootSignature.file) + } + //dump info again ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) return this @@ -187,6 +208,13 @@ data class BootV3(var info: MiscInfo = MiscInfo(), it.addRow("ramdisk", this.ramdisk.file) it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root") it.addRule() + + if (this.info.signatureSize > 0) { + it.addRow("boot signature", this.bootSignature.file) + it.addRow("\\-- decoded boot signature", Avb.getJsonFileName(this.bootSignature.file)) + it.addRule() + } + it.addRow("AVB info", Avb.getJsonFileName(info.output)) it.addRule() it @@ -201,8 +229,10 @@ data class BootV3(var info: MiscInfo = MiscInfo(), "" } } - log.info("\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}", - tableHeader.render(), tab.render(), tabVBMeta) + log.info( + "\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}", + tableHeader.render(), tab.render(), tabVBMeta + ) return this } diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt index 4ad4b83..b8d9e9c 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt @@ -17,30 +17,100 @@ import java.nio.ByteBuffer import java.nio.ByteOrder import cfig.bootimg.Common as C import cfig.EnvironmentVerifier +import cfig.io.Struct3 +import java.io.InputStream @OptIn(ExperimentalUnsignedTypes::class) -data class VendorBoot(var info: MiscInfo = MiscInfo(), - var ramdisk: CommArgs = CommArgs(), - var dtb: CommArgs = CommArgs()) { +data class VendorBoot( + var info: MiscInfo = MiscInfo(), + var ramdisk: CommArgs = CommArgs(), + var dtb: CommArgs = CommArgs(), + var ramdisk_table: Vrt = Vrt(), + var bootconfig: CommArgs = CommArgs() +) { data class CommArgs( - var file: String = "", - var position: Long = 0, - var size: Int = 0, - var loadAddr: Long = 0) + var file: String = "", + var position: Long = 0, + var size: Int = 0, + var loadAddr: Long = 0 + ) data class MiscInfo( - var output: String = "", - var json: String = "", - var headerVersion: Int = 0, - var product: String = "", - var headerSize: Int = 0, - var pageSize: Int = 0, - var cmdline: String = "", - var tagsLoadAddr: Long = 0, - var kernelLoadAddr: Long = 0, - var imageSize: Long = 0 + var output: String = "", + var json: String = "", + var headerVersion: Int = 0, + var product: String = "", + var headerSize: Int = 0, + var pageSize: Int = 0, + var cmdline: String = "", + var tagsLoadAddr: Long = 0, + var kernelLoadAddr: Long = 0, + var imageSize: Long = 0 + ) + + enum class VrtType { + NONE, + PLATFORM, + RECOVERY, + DLKM; + + companion object { + fun fromInt(value: Int): VrtType { + return when (value) { + NONE.ordinal -> NONE + PLATFORM.ordinal -> PLATFORM + RECOVERY.ordinal -> RECOVERY + DLKM.ordinal -> DLKM + else -> throw IllegalArgumentException() + } + } + } + } + + class Vrt( + var size: Int = 0, + var position: Long = 0, + var ramdidks: MutableList = mutableListOf() ) + class VrtEntry( + var size: Int = 0, + var offset: Int = 0, + var type: VrtType = VrtType.NONE, + var name: String = "", //32s + var boardId: Array = arrayOf(), //16I + var file: String = "" + ) { + companion object { + private val log = LoggerFactory.getLogger(VrtEntry::class.java) + const val VENDOR_RAMDISK_NAME_SIZE = 32 + const val VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16 + const val FORMAT_STRING = "3I${VENDOR_RAMDISK_NAME_SIZE}s${VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I" + + init { + log.info(Struct3(FORMAT_STRING).calcSize().toString()) + } + } + + constructor(iS: InputStream?, dumpFile: String) : this() { + if (iS == null) { + return + } + val info = Struct3(FORMAT_STRING).unpack(iS) + assert((3 + 1 + VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE) == info.size) + this.size = (info[0] as UInt).toInt() + this.offset = (info[1] as UInt).toInt() + this.type = VrtType.fromInt((info[2] as UInt).toInt()) + this.name = info[3] as String + this.file = dumpFile + } + + override fun toString(): String { + return "VrtEntry(ramdiskSize=$size, ramdiskOffset=$offset, ramdiskType=$type, ramdiskName='$name', boardId=${boardId.contentToString()})" + } + + } + companion object { private val log = LoggerFactory.getLogger(VendorBoot::class.java) fun parse(fileName: String): VendorBoot { @@ -59,27 +129,44 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), ret.info.headerVersion = header.headerVersion //ramdisk ret.ramdisk.file = workDir + "ramdisk.img" - ret.ramdisk.size = header.vndRamdiskSize + ret.ramdisk.size = header.vndRamdiskTotalSize ret.ramdisk.loadAddr = header.ramdiskLoadAddr ret.ramdisk.position = Helper.round_to_multiple( - VendorBootHeader.VENDOR_BOOT_IMAGE_HEADER_V3_SIZE.toLong(), - header.pageSize) + VendorBootHeader.VENDOR_BOOT_IMAGE_HEADER_V3_SIZE.toLong(), header.pageSize + ) //dtb ret.dtb.file = workDir + "dtb" ret.dtb.size = header.dtbSize ret.dtb.loadAddr = header.dtbLoadAddr - ret.dtb.position = ret.ramdisk.position + - Helper.round_to_multiple(ret.ramdisk.size, header.pageSize) + ret.dtb.position = ret.ramdisk.position + Helper.round_to_multiple(ret.ramdisk.size, header.pageSize) + //vrt + if (header.vrtSize > 0) { + ret.ramdisk_table.size = header.vrtSize + ret.ramdisk_table.position = + ret.dtb.position + Helper.round_to_multiple(ret.ramdisk_table.size, header.pageSize) + FileInputStream(ret.info.output).use { + it.skip(ret.ramdisk_table.position) + for (item in 0 until header.vrtEntryNum) { + ret.ramdisk_table.ramdidks.add(VrtEntry(it, workDir + "ramdisk.${item + 1}")) + } + } + ret.ramdisk_table.ramdidks.forEach { + log.warn(it.toString()) + } + } + //bootconfig + if (header.bootconfigSize > 0) { + ret.bootconfig.file = workDir + "bootconfig" + ret.bootconfig.size = header.bootconfigSize + ret.bootconfig.position = + ret.ramdisk_table.position + Helper.round_to_multiple(ret.bootconfig.size, header.pageSize) + } } ret.info.imageSize = File(fileName).length() return ret } } - private fun parseOsMajor(): Int { - return 11 - } - fun pack(): VendorBoot { val workDir = Helper.prop("workDir") if (File(workDir + this.ramdisk.file).exists() && !File(workDir + "root").exists()) { @@ -88,7 +175,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), } else { File(this.ramdisk.file).deleleIfExists() File(this.ramdisk.file.removeSuffix(".gz")).deleleIfExists() - //TODO: remove cpio in C/C++ + //Fixed: remove cpio in C/C++ //C.packRootfs("$workDir/root", this.ramdisk.file, parseOsMajor()) //enable advance JAVA cpio C.packRootfs("$workDir/root", this.ramdisk.file) @@ -99,9 +186,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), FileOutputStream(this.info.output + ".clear", false).use { fos -> val encodedHeader = this.toHeader().encode() fos.write(encodedHeader) - fos.write(ByteArray( - Helper.round_to_multiple(encodedHeader.size, - this.info.pageSize) - encodedHeader.size)) + fos.write(ByteArray(Helper.round_to_multiple(encodedHeader.size, this.info.pageSize) - encodedHeader.size)) } //data log.info("Writing data ...") @@ -110,7 +195,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), it.order(ByteOrder.LITTLE_ENDIAN) C.writePaddedFile(it, this.ramdisk.file, this.info.pageSize) C.writePaddedFile(it, this.dtb.file, this.info.pageSize) - it + it } //write FileOutputStream("${this.info.output}.clear", true).use { fos -> @@ -135,17 +220,17 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), private fun toHeader(): VendorBootHeader { return VendorBootHeader( - headerVersion = info.headerVersion, - pageSize = info.pageSize, - kernelLoadAddr = info.kernelLoadAddr, - ramdiskLoadAddr = ramdisk.loadAddr, - vndRamdiskSize = ramdisk.size, - cmdline = info.cmdline, - tagsLoadAddr = info.tagsLoadAddr, - product = info.product, - headerSize = info.headerSize, - dtbSize = dtb.size, - dtbLoadAddr = dtb.loadAddr + headerVersion = info.headerVersion, + pageSize = info.pageSize, + kernelLoadAddr = info.kernelLoadAddr, + ramdiskLoadAddr = ramdisk.loadAddr, + vndRamdiskTotalSize = ramdisk.size, + cmdline = info.cmdline, + tagsLoadAddr = info.tagsLoadAddr, + product = info.product, + headerSize = info.headerSize, + dtbSize = dtb.size, + dtbLoadAddr = dtb.loadAddr ) } @@ -154,13 +239,27 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), //header ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) //ramdisk - val fmt = C.dumpRamdisk(C.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file), - "${workDir}root") + //@formatter:off + val fmt = C.dumpRamdisk( + Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file), "${workDir}root") + //@formatter:on this.ramdisk.file = this.ramdisk.file + ".$fmt" //dump info again ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) //dtb - C.dumpDtb(C.Slice(info.output, dtb.position.toInt(), dtb.size, dtb.file)) + C.dumpDtb(Helper.Slice(info.output, dtb.position.toInt(), dtb.size, dtb.file)) + //vrt + this.ramdisk_table.ramdidks.forEachIndexed { index, it -> + log.info("dumping vendor ramdisk ${index + 1}/${this.ramdisk_table.ramdidks.size} ...") + val s = Helper.Slice(ramdisk.file, it.offset, it.size, it.file) + C.dumpRamdisk(s, workDir + "root.${index + 1}") + } + //bootconfig + if (bootconfig.size > 0) { + Helper.Slice(info.output, bootconfig.position.toInt(), bootconfig.size, bootconfig.file).let { s -> + Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) + } + } return this } @@ -189,12 +288,23 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), it.addRow("image info", workDir + info.output.removeSuffix(".img") + ".json") it.addRule() it.addRow("ramdisk", this.ramdisk.file) - it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root") + if (this.ramdisk_table.size > 0) { + this.ramdisk_table.ramdidks.forEachIndexed { index, entry -> + it.addRow("-- ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file) + it.addRow("------- extracted rootfs", "${workDir}root.${index + 1}") + } + } else { + it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root") + } it.addRule() it.addRow("dtb", this.dtb.file) if (File(this.dtb.file + ".src").exists()) { it.addRow("\\-- decompiled dts", dtb.file + ".src") } + if (this.bootconfig.size > 0) { + it.addRule() + it.addRow("bootconfig", this.bootconfig.file) + } it.addRule() it.addRow("AVB info", Avb.getJsonFileName(info.output)) it.addRule() @@ -210,25 +320,24 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), "" } } - log.info("\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}", - tableHeader.render(), tab.render(), tabVBMeta) + log.info("\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}", tableHeader.render(), tab.render(), tabVBMeta) return this } private fun toCommandLine(): CommandLine { val cmdPrefix = if (EnvironmentVerifier().isWindows) "python " else "" return CommandLine.parse(cmdPrefix + Helper.prop("mkbootimg")).apply { - addArgument("--vendor_ramdisk").addArgument(ramdisk.file) - addArgument("--dtb").addArgument(dtb.file) - addArgument("--vendor_cmdline").addArgument(info.cmdline, false) - addArgument("--header_version").addArgument(info.headerVersion.toString()) - addArgument("--base").addArgument("0") - addArgument("--tags_offset").addArgument(info.tagsLoadAddr.toString()) - addArgument("--kernel_offset").addArgument(info.kernelLoadAddr.toString()) - addArgument("--ramdisk_offset").addArgument(ramdisk.loadAddr.toString()) - addArgument("--dtb_offset").addArgument(dtb.loadAddr.toString()) - addArgument("--pagesize").addArgument(info.pageSize.toString()) - addArgument("--vendor_boot") + addArgument("--vendor_ramdisk").addArgument(ramdisk.file) + addArgument("--dtb").addArgument(dtb.file) + addArgument("--vendor_cmdline").addArgument(info.cmdline, false) + addArgument("--header_version").addArgument(info.headerVersion.toString()) + addArgument("--base").addArgument("0") + addArgument("--tags_offset").addArgument(info.tagsLoadAddr.toString()) + addArgument("--kernel_offset").addArgument(info.kernelLoadAddr.toString()) + addArgument("--ramdisk_offset").addArgument(ramdisk.loadAddr.toString()) + addArgument("--dtb_offset").addArgument(dtb.loadAddr.toString()) + addArgument("--pagesize").addArgument(info.pageSize.toString()) + addArgument("--vendor_boot") } } } diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBootHeader.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBootHeader.kt index 0b06fa3..90dae45 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBootHeader.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBootHeader.kt @@ -10,7 +10,7 @@ class VendorBootHeader( var pageSize: Int = 0, var kernelLoadAddr: Long = 0, var ramdiskLoadAddr: Long = 0, - var vndRamdiskSize: Int = 0, + var vndRamdiskTotalSize: Int = 0, var cmdline: String = "", var tagsLoadAddr: Long = 0, var product: String = "", @@ -37,7 +37,7 @@ class VendorBootHeader( this.pageSize = (info[2] as UInt).toInt() this.kernelLoadAddr = (info[3] as UInt).toLong() this.ramdiskLoadAddr = (info[4] as UInt).toLong() - this.vndRamdiskSize = (info[5] as UInt).toInt() + this.vndRamdiskTotalSize = (info[5] as UInt).toInt() this.cmdline = info[6] as String this.tagsLoadAddr = (info[7] as UInt).toLong() this.product = info[8] as String @@ -64,7 +64,7 @@ class VendorBootHeader( pageSize, kernelLoadAddr, ramdiskLoadAddr, - vndRamdiskSize, + vndRamdiskTotalSize, cmdline, tagsLoadAddr, product, @@ -106,7 +106,7 @@ class VendorBootHeader( } override fun toString(): String { - return "VendorBootHeader(headerVersion=$headerVersion, pageSize=$pageSize, kernelLoadAddr=$kernelLoadAddr, ramdiskLoadAddr=$ramdiskLoadAddr, vndRamdiskSize=$vndRamdiskSize, cmdline='$cmdline', tagsLoadAddr=$tagsLoadAddr, product='$product', headerSize=$headerSize, dtbSize=$dtbSize, dtbLoadAddr=$dtbLoadAddr, vrtSize=$vrtSize, vrtEntryNum=$vrtEntryNum, vrtEntrySize=$vrtEntrySize, bootconfigSize=$bootconfigSize)" + return "VendorBootHeader(headerVersion=$headerVersion, pageSize=$pageSize, kernelLoadAddr=$kernelLoadAddr, ramdiskLoadAddr=$ramdiskLoadAddr, vndRamdiskSize=$vndRamdiskTotalSize, cmdline='$cmdline', tagsLoadAddr=$tagsLoadAddr, product='$product', headerSize=$headerSize, dtbSize=$dtbSize, dtbLoadAddr=$dtbLoadAddr, vrtSize=$vrtSize, vrtEntryNum=$vrtEntryNum, vrtEntrySize=$vrtEntrySize, bootconfigSize=$bootconfigSize)" } diff --git a/doc/additional_tricks.md b/doc/additional_tricks.md index 76d42ed..2fac77a 100644 --- a/doc/additional_tricks.md +++ b/doc/additional_tricks.md @@ -77,3 +77,14 @@ Avoid using this feature on Windows, create regular file instead. * remember to close File streams to avoid any potential problems +## Boot image signature in BootImage V4 +"boot signature" is designed for GKI, it's to be verified by VTS, not bootloader, so this part can be seen as part of the raw boot.img for bootloader. + +Emulate creating GKI image: +``` +out/host/linux-x86/bin/mkbootimg --kernel out/target/product/vsoc_arm64/kernel --ramdisk out/target/product/vsoc_arm64/ramdisk.img --gki_signing_key external/avb/test/data/testkey_rsa4096.pem --gki_signing_algorithm SHA256_RSA4096 --os_version 11 --os_patch_level 2021-03-05 --header_version 4 --output out/target/product/vsoc_arm64/boot.img +out/host/linux-x86/bin/avbtool add_hash_footer --image out/target/product/vsoc_arm64/boot.img --partition_size 67108864 --partition_name boot --algorithm SHA256_RSA2048 --key external/avb/test/data/testkey_rsa2048.pem --prop com.android.build.boot.fingerprint:nicefinger --prop com.android.build.boot.os_version:11 --rollback_index 1614902400 +``` +As it's only used for GKI verification, I don't want to spend too much time on any special steps in 'gradle pack' flow, as long as DUT can boot up properly. + + diff --git a/doc/layout.md b/doc/layout.md index 2423b9a..f870e7f 100644 --- a/doc/layout.md +++ b/doc/layout.md @@ -125,7 +125,6 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi ### data -``` +-----------------------------------------------------------+ --> pagesize | | kernel length | +-----------------------------------------------------------+ @@ -136,7 +135,6 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi padding calculation: | | min(n * page_size - len) | -``` @@ -156,7 +154,7 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi |--------------------------------+--------------------------| --> 20 | | 4 | |--------------------------------+--------------------------| --> 24 - | | 4 | + | | 4 | |--------------------------------+--------------------------| --> 28 | | 2048 | |--------------------------------+--------------------------| --> 2076 @@ -184,17 +182,28 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi ### data -``` - +-----------------------------------------------------------+ --> pagesize - | | padded len | - +--------------------------------+--------------------------+ + + +------------------+-------------+--------------------------+ --> pagesize + | | ramdisk 1 | | + | +-------------+ | + | | ramdisk 2 | | + | +-------------+ padded len | + | | ramdisk n | | + | +-------------+ | --> pagesize + vendor_ramdisk_total_size + | | padding | | + +--------------------------------+--------------------------+ --> pagesize + vendor_ramdisk_total_size + padding | | padded len | + +--------------------------------+--------------------------+ --> dtb offset + dtb size + padding + | | entry 1 | | + | table> +-------------+ | + | | entry 2 | padded len | + | +-------------+ | + | | entry n | | + | (v4) +-------------+ | + | | padding | | + +-----------------------------------------------------------+ --> vrt offset + vrt size + padding + | (v4) | padded len | +--------------------------------+--------------------------+ - | | padded len | - +-----------------------------------------------------------+ - | | padded len | - +--------------------------------+--------------------------+ -``` diff --git a/helper/src/main/kotlin/cfig/helper/Helper.kt b/helper/src/main/kotlin/cfig/helper/Helper.kt index a930bec..1652b75 100644 --- a/helper/src/main/kotlin/cfig/helper/Helper.kt +++ b/helper/src/main/kotlin/cfig/helper/Helper.kt @@ -18,6 +18,13 @@ import java.text.CharacterIterator @OptIn(ExperimentalUnsignedTypes::class) class Helper { + data class Slice( + var srcFile: String, + var offset: Int, + var length: Int, + var dumpFile: String + ) + companion object { private val gcfg: Properties = Properties().apply { load(Helper::class.java.classLoader.getResourceAsStream("general.cfg")) @@ -85,6 +92,10 @@ class Helper { return data } + fun extractFile(s: Slice) { + return extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) + } + fun extractFile(fileName: String, outImgName: String, offset: Long, length: Int) { if (0 == length) { return