Issue #145: staging

boot.img V3
unpack
java -jar bbootimg/build/libs/bbootimg.jar unpackInternal ../boot.img out
pack
java -jar bbootimg/build/libs/bbootimg.jar packInternal out ../x/boot.img.modified
dev
cfig 10 months ago
parent 979a9b9f77
commit 585e2fdcd5

@ -174,7 +174,8 @@ class Avb {
fun getJsonFileName(image_file: String): String {
val jsonFile = File(image_file).name.removeSuffix(".img") + ".avb.json"
return Helper.prop("workDir") + jsonFile
log.warn("XXXX: json file = " + Helper.joinPath(Helper.prop("workDir")!!, jsonFile))
return Helper.joinPath(Helper.prop("workDir")!!, jsonFile)
}
fun hasAvbFooter(fileName: String): Boolean {

@ -120,6 +120,7 @@ class Common {
fun dumpKernel(s: Helper.Slice) {
Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
log.warn("s.srcFile: ${s.srcFile}, s.dumpFile: ${s.dumpFile}, s.offset: ${s.offset}, s.length: ${s.length}")
parseKernelInfo(s.dumpFile)
}

@ -128,7 +128,7 @@ data class BootV2(
}
}
ret.kernel.let { theKernel ->
theKernel.file = "${workDir}kernel"
theKernel.file = File(workDir, "kernel").path
theKernel.size = bh2.kernelLength
theKernel.loadOffset = bh2.kernelOffset
theKernel.position = ret.getKernelPosition()
@ -138,28 +138,28 @@ data class BootV2(
theRamdisk.loadOffset = bh2.ramdiskOffset
theRamdisk.position = ret.getRamdiskPosition()
if (bh2.ramdiskLength > 0) {
theRamdisk.file = "${workDir}ramdisk.img"
theRamdisk.file = File(workDir, "ramdisk.img").path
}
}
if (bh2.secondBootloaderLength > 0) {
ret.secondBootloader = CommArgs()
ret.secondBootloader!!.size = bh2.secondBootloaderLength
ret.secondBootloader!!.loadOffset = bh2.secondBootloaderOffset
ret.secondBootloader!!.file = "${workDir}second"
ret.secondBootloader!!.file = File(workDir, "second").path
ret.secondBootloader!!.position = ret.getSecondBootloaderPosition()
}
if (bh2.recoveryDtboLength > 0) {
ret.recoveryDtbo = CommArgsLong()
ret.recoveryDtbo!!.size = bh2.recoveryDtboLength
ret.recoveryDtbo!!.loadOffset = bh2.recoveryDtboOffset //Q
ret.recoveryDtbo!!.file = "${workDir}recoveryDtbo"
ret.recoveryDtbo!!.file = File(workDir, "recoveryDtbo").path
ret.recoveryDtbo!!.position = ret.getRecoveryDtboPosition()
}
if (bh2.dtbLength > 0) {
ret.dtb = DtbArgsLong()
ret.dtb!!.size = bh2.dtbLength
ret.dtb!!.loadOffset = bh2.dtbOffset //Q
ret.dtb!!.file = "${workDir}dtb"
ret.dtb!!.file = File(workDir, "dtb").path
ret.dtb!!.position = ret.getDtbPosition()
}
}
@ -220,7 +220,7 @@ data class BootV2(
//ramdisk
if (this.ramdisk.size > 0) {
val fmt = C.dumpRamdisk(
Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), "${workDir}root"
Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), File(workDir, "root").path
)
this.ramdisk.file = this.ramdisk.file!! + ".$fmt"
if (fmt == "xz") {
@ -333,8 +333,8 @@ data class BootV2(
it.addRule()
it.addRow("ramdisk", this.ramdisk.file)
prints.add(Pair("ramdisk", this.ramdisk.file.toString()))
it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root")
prints.add(Pair("\\-- extracted ramdisk rootfs", "${workDir}root"))
it.addRow("\\-- extracted ramdisk rootfs", File(workDir, "root").path)
prints.add(Pair("\\-- extracted ramdisk rootfs", File(workDir, "root").path))
}
//second
this.secondBootloader?.let { theSecondBootloader ->
@ -440,7 +440,7 @@ data class BootV2(
File(this.ramdisk.file!!).deleleIfExists()
File(this.ramdisk.file!!.removeSuffix(".gz")).deleleIfExists()
//Common.packRootfs("${workDir}/root", this.ramdisk.file!!, Common.parseOsMajor(info.osVersion.toString()))
Common.packRootfs("${workDir}/root", this.ramdisk.file!!, this.ramdisk.xzFlags)
Common.packRootfs(File(workDir, "root").path, this.ramdisk.file!!, this.ramdisk.xzFlags)
}
this.ramdisk.size = File(this.ramdisk.file!!).length().toInt()
}

@ -49,14 +49,15 @@ data class BootV3(
private val log = LoggerFactory.getLogger(BootV3::class.java)
private val errLog = LoggerFactory.getLogger("uiderrors")
private val mapper = ObjectMapper()
private val workDir = Helper.prop("workDir")
private val workDir = Helper.prop("workDir")!!
fun parse(fileName: String): BootV3 {
val ret = BootV3()
FileInputStream(fileName).use { fis ->
val header = BootHeaderV3(fis)
//info
ret.info.output = File(fileName).name
ret.info.input = File(fileName).canonicalPath
ret.info.role = File(fileName).name
ret.info.json = File(fileName).name.removeSuffix(".img") + ".json"
ret.info.cmdline = header.cmdline.trim()
ret.info.headerSize = header.headerSize
@ -66,17 +67,17 @@ data class BootV3(
ret.info.pageSize = BootHeaderV3.pageSize
ret.info.signatureSize = header.signatureSize
//kernel
ret.kernel.file = workDir + "kernel"
ret.kernel.file = Helper.joinPath(workDir, "kernel")
ret.kernel.size = header.kernelSize
ret.kernel.position = BootHeaderV3.pageSize
//ramdisk
ret.ramdisk.file = workDir + "ramdisk.img"
ret.ramdisk.file = Helper.joinPath(workDir, "ramdisk.img")
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.file = Helper.joinPath(workDir, "bootsig")
ret.bootSignature.size = header.signatureSize
ret.bootSignature.position = ret.ramdisk.position + ret.ramdisk.size +
getPaddingSize(header.ramdiskSize, BootHeaderV3.pageSize)
@ -88,7 +89,8 @@ data class BootV3(
}
data class MiscInfo(
var output: String = "",
var input: String = "",
var role: String = "",
var json: String = "",
var headerVersion: Int = 0,
var headerSize: Int = 0,
@ -106,7 +108,7 @@ data class BootV3(
var size: Int = 0,
)
data class RamdiskArgs (
data class RamdiskArgs(
var file: String = "",
var position: Int = 0,
var size: Int = 0,
@ -118,22 +120,30 @@ data class BootV3(
this.kernel.size = File(this.kernel.file).length().toInt()
}
if (this.ramdisk.size > 0) {
if (File(this.ramdisk.file).exists() && !File(workDir + "root").exists()) {
if (File(this.ramdisk.file).exists() && !File(workDir, "root").exists()) {
//do nothing if we have ramdisk.img.gz but no /root
log.warn("Use prebuilt ramdisk file: ${this.ramdisk.file}")
} else {
File(this.ramdisk.file).deleleIfExists()
File(this.ramdisk.file.replaceFirst("[.][^.]+$", "")).deleleIfExists()
//TODO: remove cpio in C/C++
//C.packRootfs("$workDir/root", this.ramdisk.file, C.parseOsMajor(info.osVersion))
//C.packRootfs(Helper.joinPath($workDir, "root"), this.ramdisk.file, C.parseOsMajor(info.osVersion))
// enable advance JAVA cpio
C.packRootfs("$workDir/root", this.ramdisk.file, this.ramdisk.xzFlags)
C.packRootfs(Helper.joinPath(workDir, "root"), this.ramdisk.file, this.ramdisk.xzFlags)
}
this.ramdisk.size = File(this.ramdisk.file).length().toInt()
}
//header
FileOutputStream(this.info.output + ".clear", false).use { fos ->
val intermediateDir = Helper.joinPath(workDir, "intermediate")
File(intermediateDir).let {
if (!it.exists()) {
it.mkdir()
}
}
Helper.setProp("intermediateDir", intermediateDir)
val clearFile = Helper.joinPath(intermediateDir, this.info.role + ".clear")
FileOutputStream(clearFile, false).use { fos ->
//trim bootSig if it's not parsable
//https://github.com/cfig/Android_boot_image_editor/issues/88
File(Avb.getJsonFileName(this.bootSignature.file)).let { bootSigJson ->
@ -163,7 +173,7 @@ data class BootV3(
C.writePaddedFile(bf, this.ramdisk.file, this.info.pageSize)
}
//write V3 data
FileOutputStream("${this.info.output}.clear", true).use { fos ->
FileOutputStream(clearFile, true).use { fos ->
fos.write(bf.array(), 0, bf.position())
}
@ -178,12 +188,12 @@ data class BootV3(
//replace new pub key
readBackBootSig.auxBlob!!.pubkey!!.pubkey = AuxBlob.encodePubKey(alg)
//update hash and sig
readBackBootSig.auxBlob!!.hashDescriptors.get(0).update(this.info.output + ".clear")
readBackBootSig.auxBlob!!.hashDescriptors.get(0).update(this.info.role + ".clear")
bootSigBytes = readBackBootSig.encodePadded()
}
if (this.info.signatureSize > 0) {
//write V4 data
FileOutputStream("${this.info.output}.clear", true).use { fos ->
FileOutputStream(clearFile, true).use { fos ->
fos.write(bootSigBytes)
}
} else {
@ -192,22 +202,40 @@ data class BootV3(
}
//google way
this.toCommandLine().addArgument(this.info.output + ".google").let {
val googleClearFile = Helper.joinPath(intermediateDir, this.info.role + ".google")
this.toCommandLine().addArgument(googleClearFile).let {
log.info(it.toString())
DefaultExecutor().execute(it)
}
Helper.assertFileEquals(this.info.output + ".clear", this.info.output + ".google")
File(this.info.output + ".google").delete()
Helper.assertFileEquals(clearFile, googleClearFile)
File(googleClearFile).delete()
return this
}
fun sign(fileName: String): BootV3 {
if (File(Avb.getJsonFileName(info.output)).exists()) {
Signer.signAVB(fileName, this.info.imageSize, String.format(Helper.prop("avbtool")!!, "v1.2"))
log.warn("XXXX: sign $fileName")
if (File(Avb.getJsonFileName(info.role)).exists()) {
Signer.signAVB(
Helper.joinPath(Helper.prop("intermediateDir")!!, info.role),
this.info.imageSize,
String.format(Helper.prop("avbtool")!!, "v1.2")
)
} else {
log.warn("no AVB info found, assume it's clear image")
}
if (fileName != info.role) {
File(Helper.joinPath(Helper.prop("intermediateDir")!!, info.role + ".signed")).copyTo(File(fileName), true)
log.info("Signed image saved as $fileName")
} else {
File(
Helper.joinPath(
Helper.prop("intermediateDir")!!,
info.role + ".signed"
)
).copyTo(File(info.role + ".signed"), true)
log.info("Signed image saved as ${info.role}.signed")
}
return this
}
@ -227,35 +255,37 @@ data class BootV3(
fun extractImages(): BootV3 {
val workDir = Helper.prop("workDir")
//info
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir, this.info.json), this)
//kernel
if (kernel.size > 0) {
C.dumpKernel(Helper.Slice(info.output, kernel.position, kernel.size, kernel.file))
C.dumpKernel(Helper.Slice(info.input, kernel.position, kernel.size, kernel.file))
} else {
log.warn("${this.info.output} has no kernel")
log.warn("${this.info.role} has no kernel")
}
//ramdisk
if (ramdisk.size > 0) {
val fmt = C.dumpRamdisk(
Helper.Slice(info.output, ramdisk.position, ramdisk.size, ramdisk.file), "${workDir}root"
Helper.Slice(info.role, ramdisk.position, ramdisk.size, ramdisk.file), File(workDir, "root").toString()
)
this.ramdisk.file = this.ramdisk.file + ".$fmt"
if (fmt == "xz") {
val checkType = ZipHelper.xzStreamFlagCheckTypeToString(ZipHelper.parseStreamFlagCheckType(this.ramdisk.file))
val checkType =
ZipHelper.xzStreamFlagCheckTypeToString(ZipHelper.parseStreamFlagCheckType(this.ramdisk.file))
this.ramdisk.xzFlags = checkType
}
}
//bootsig
//dump info again
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir, this.info.json), this)
return this
}
fun extractVBMeta(): BootV3 {
// vbmeta in image
try {
val ai = AVBInfo.parseFrom(Dumpling(info.output)).dumpDefault(info.output)
log.warn("XXXX: info.output ${info.input}")
val ai = AVBInfo.parseFrom(Dumpling(info.input)).dumpDefault(info.role)
if (File("vbmeta.img").exists()) {
log.warn("Found vbmeta.img, parsing ...")
VBMetaParser().unpack("vbmeta.img")
@ -268,7 +298,7 @@ data class BootV3(
//GKI 1.0 bootsig
if (info.signatureSize > 0) {
log.info("GKI 1.0 signature")
Dumpling(info.output).readFully(Pair(this.bootSignature.position.toLong(), this.bootSignature.size))
Dumpling(info.role).readFully(Pair(this.bootSignature.position.toLong(), this.bootSignature.size))
.let { bootsigData ->
File(this.bootSignature.file).writeBytes(bootsigData)
if (bootsigData.any { it.toInt() != 0 }) {
@ -286,17 +316,17 @@ data class BootV3(
}
//GKI 2.0 bootsig
if (!File(Avb.getJsonFileName(info.output)).exists()) {
log.info("no AVB info found in ${info.output}")
if (!File(Avb.getJsonFileName(info.role)).exists()) {
log.info("no AVB info found in ${info.role}")
return this
}
log.info("probing 16KB boot signature ...")
val mainBlob = ObjectMapper().readValue(
File(Avb.getJsonFileName(info.output)),
File(Avb.getJsonFileName(info.role)),
AVBInfo::class.java
)
val bootSig16kData =
Dumpling(Dumpling(info.output).readFully(Pair(mainBlob.footer!!.originalImageSize - 16 * 1024, 16 * 1024)))
Dumpling(Dumpling(info.input).readFully(Pair(mainBlob.footer!!.originalImageSize - 16 * 1024, 16 * 1024)))
try {
val blob1 = AVBInfo.parseFrom(bootSig16kData)
.also { check(it.auxBlob!!.hashDescriptors[0].partition_name == "boot") }
@ -306,8 +336,8 @@ data class BootV3(
.also { check(it.auxBlob!!.hashDescriptors[0].partition_name == "generic_kernel") }
.also { it.dumpDefault("sig.kernel") }
val gkiAvbData = bootSig16kData.readFully(blob1.encode().size until bootSig16kData.getLength())
File("${workDir}kernel.img").let { gki ->
File("${workDir}kernel").copyTo(gki)
File(workDir, "kernel.img").let { gki ->
File(workDir, "kernel").copyTo(gki)
System.setProperty("more", workDir)
Avb.verify(blob2, Dumpling(gkiAvbData))
gki.delete()
@ -331,19 +361,26 @@ data class BootV3(
}
val tab = AsciiTable().let {
it.addRule()
it.addRow("image info", workDir + info.output.removeSuffix(".img") + ".json")
prints.add(Pair("image info", workDir + info.output.removeSuffix(".img") + ".json"))
it.addRow("image info", Helper.joinPath(workDir!!, info.role.removeSuffix(".img") + ".json"))
prints.add(Pair("image info", Helper.joinPath(workDir, info.role.removeSuffix(".img") + ".json")))
it.addRule()
if (this.kernel.size > 0) {
it.addRow("kernel", this.kernel.file)
prints.add(Pair("kernel", this.kernel.file))
File(Helper.prop("kernelVersionFile")).let { kernelVersionFile ->
File(Helper.joinPath(workDir, Helper.prop("kernelVersionStem")!!)).let { kernelVersionFile ->
log.warn("XXXX: kernelVersionFile ${kernelVersionFile.path}")
if (kernelVersionFile.exists()) {
it.addRow("\\-- version " + kernelVersionFile.readLines().toString(), kernelVersionFile.path)
prints.add(Pair("\\-- version " + kernelVersionFile.readLines().toString(), kernelVersionFile.path))
prints.add(
Pair(
"\\-- version " + kernelVersionFile.readLines().toString(),
kernelVersionFile.path
)
)
}
}
File(Helper.prop("kernelConfigFile")).let { kernelConfigFile ->
File(Helper.joinPath(workDir, Helper.prop("kernelConfigStem")!!)).let { kernelConfigFile ->
log.warn("XXXX: kernelConfigFile ${kernelConfigFile.path}")
if (kernelConfigFile.exists()) {
it.addRow("\\-- config", kernelConfigFile.path)
prints.add(Pair("\\-- config", kernelConfigFile.path))
@ -354,11 +391,11 @@ data class BootV3(
if (this.ramdisk.size > 0) {
//fancy
it.addRow("ramdisk", this.ramdisk.file)
it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root")
it.addRow("\\-- extracted ramdisk rootfs", Helper.joinPath(workDir, "root"))
it.addRule()
//basic
prints.add(Pair("ramdisk", this.ramdisk.file))
prints.add(Pair("\\-- extracted ramdisk rootfs", "${workDir}root"))
prints.add(Pair("\\-- extracted ramdisk rootfs", Helper.joinPath(workDir, "root")))
}
if (this.info.signatureSize > 0) {
it.addRow("GKI signature 1.0", this.bootSignature.file)
@ -368,7 +405,12 @@ data class BootV3(
prints.add(Pair("\\-- decoded boot signature", if (jsFile.exists()) jsFile.path else "N/A"))
if (jsFile.exists()) {
it.addRow("\\------ signing key", Avb.inspectKey(mapper.readValue(jsFile, AVBInfo::class.java)))
prints.add(Pair("\\------ signing key", Avb.inspectKey(mapper.readValue(jsFile, AVBInfo::class.java))))
prints.add(
Pair(
"\\------ signing key",
Avb.inspectKey(mapper.readValue(jsFile, AVBInfo::class.java))
)
)
}
}
it.addRule()
@ -383,7 +425,12 @@ data class BootV3(
//basic
prints.add(Pair("GKI signature 2.0", this.bootSignature.file))
prints.add(Pair("\\-- boot", jsonFile.path))
prints.add(Pair("\\------ signing key", Avb.inspectKey(mapper.readValue(jsonFile, AVBInfo::class.java))))
prints.add(
Pair(
"\\------ signing key",
Avb.inspectKey(mapper.readValue(jsonFile, AVBInfo::class.java))
)
)
}
}
File(Avb.getJsonFileName("sig.kernel")).let { jsonFile ->
@ -399,7 +446,7 @@ data class BootV3(
}
//AVB info
Avb.getJsonFileName(info.output).let { jsonFile ->
Avb.getJsonFileName(info.role).let { jsonFile ->
it.addRow("AVB info", if (File(jsonFile).exists()) jsonFile else "NONE")
prints.add(Pair("AVB info", if (File(jsonFile).exists()) jsonFile else "NONE"))
if (File(jsonFile).exists()) {
@ -428,20 +475,20 @@ data class BootV3(
log.info("\n" + Common.table2String(prints))
} else {
log.info(
"\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}",
"\n\t\t\tUnpack Summary of ${info.role}\n{}\n{}{}",
tableHeader.render(), tab.render(), tabVBMeta
)
}
return this
}
fun printPackSummary(): BootV3 {
Common.printPackSummary(info.output)
fun printPackSummary(fileName: String): BootV3 {
Common.printPackSummary(fileName)
return this
}
fun updateVbmeta(): BootV3 {
Avb.updateVbmeta(info.output)
Avb.updateVbmeta(info.role)
return this
}
@ -475,7 +522,8 @@ data class BootV3(
val alg = Algorithms.get(origSig.header!!.algorithm_type)!!
ret.addArgument("--gki_signing_algorithm").addArgument(alg.name)
ret.addArgument("--gki_signing_key").addArgument(alg.defaultKey)
ret.addArgument("--gki_signing_avbtool_path").addArgument(String.format(Helper.prop("avbtool")!!, "v1.2"))
ret.addArgument("--gki_signing_avbtool_path")
.addArgument(String.format(Helper.prop("avbtool")!!, "v1.2"))
}
ret.addArgument(" --id ")
ret.addArgument(" --output ")

@ -358,7 +358,7 @@ data class VendorBoot(
//ramdisk
//@formatter:off
val fmt = C.dumpRamdisk(
Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file), "${workDir}root",
Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file), File(workDir, "root").path,
this.ramdisk_table.ramdidks.isEmpty())
//@formatter:on
this.ramdisk.file = this.ramdisk.file + ".$fmt"
@ -423,16 +423,16 @@ data class VendorBoot(
this.ramdisk_table.ramdidks.forEachIndexed { index, entry ->
//fancy ascii
it.addRow("-- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file)
it.addRow("------- extracted rootfs", "${workDir}root.${index + 1}")
it.addRow("------- extracted rootfs", File(workDir, "root.${index + 1}").path)
//basic ascii
//@formatter:off
prints.add(Pair(" -- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file))
//@formatter:on
prints.add(Pair(" ------- extracted rootfs", "${workDir}root.${index + 1}"))
prints.add(Pair(" ------- extracted rootfs", File(workDir, "root.${index + 1}").path))
}
} else {
it.addRow("\\-- extracted ramdisk rootfs", "${workDir}root")
prints.add(Pair("\\-- extracted ramdisk rootfs", "${workDir}root"))
it.addRow("\\-- extracted ramdisk rootfs", File(workDir, "root").path)
prints.add(Pair("\\-- extracted ramdisk rootfs", File(workDir, "root").path))
}
it.addRule()
if (this.dtb.size > 0) {

@ -157,11 +157,11 @@ class Payload {
ManifestInfo.DynamicPartGroup(name = it.name, size = it.size, partName = it.partitionNamesList)
})
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File("$workDir/header.json"), this.header)
log.info(" header info dumped to ${workDir}header.json")
log.info(" header info dumped to " + File(workDir, "header.json").path)
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File("$workDir/manifest.json"), mi)
log.info(" manifest info dumped to ${workDir}manifest.json")
log.info(" manifest info dumped to " + File(workDir, "manifest.json").path)
val signatureFile = "${workDir}signatures.txt"
val signatureFile = File(workDir, "signatures.txt").path
FileOutputStream(signatureFile, false).use { fos ->
fos.writer().use { fWriter ->
fWriter.write("<Metadata> signatures: offset=" + this.header.manifestLen + ", size=" + this.header.metaSigLen + "\n")

@ -27,6 +27,7 @@ import java.io.File
import java.io.FileInputStream
import kotlin.io.path.Path
import kotlin.io.path.deleteIfExists
import kotlin.system.exitProcess
class BootImgParser : IPackable {
override val loopNo: Int
@ -34,11 +35,25 @@ class BootImgParser : IPackable {
override fun capabilities(): List<String> {
//ramdisk.img : Issue #122
return listOf("^boot(-debug)?\\.img$", "^recovery\\.img$", "^recovery-two-step\\.img$", "^init_boot\\.img$", "^ramdisk\\.img$")
return listOf(
"^boot(-debug)?\\.img$",
"^recovery\\.img$",
"^recovery-two-step\\.img$",
"^init_boot\\.img$",
"^ramdisk\\.img$"
)
}
override fun unpack(fileName: String) {
unpackInternal(fileName, fileName, outDir)
}
fun unpackInternal(targetFile: String, fileName: String, unpackDir: String) {
log.warn("Unpacking $fileName")
log.warn("fileName: $fileName, unpackDir: $unpackDir")
Helper.setProp("workDir", unpackDir)
clear()
File("$outDir/role").writeText(File(File(fileName).canonicalPath).name)
val hv = probeHeaderVersion(fileName)
log.info("header version $hv")
when (hv) {
@ -50,6 +65,7 @@ class BootImgParser : IPackable {
.printUnpackSummary()
log.debug(b2.toString())
}
in 3..4 -> {
val b3 = BootV3
.parse(fileName)
@ -58,6 +74,7 @@ class BootImgParser : IPackable {
.printUnpackSummary()
log.debug(b3.toString())
}
else -> {
val b2 = BootV2Dialects
.parse(fileName)
@ -69,8 +86,10 @@ class BootImgParser : IPackable {
}
}
override fun pack(fileName: String) {
val cfgFile = outDir + fileName.removeSuffix(".img") + ".json"
fun packInternal(targetFile: String, workspace: String, fileName: String) {
log.warn("XXXX: targetFile: $targetFile, fileName: $fileName, workspace: $workspace")
Helper.setProp("workDir", workspace)
val cfgFile = Helper.joinPath(outDir, targetFile.removeSuffix(".img") + ".json")
log.info("Loading config from $cfgFile")
if (!File(cfgFile).exists()) {
val tab = AsciiTable().let {
@ -82,23 +101,49 @@ class BootImgParser : IPackable {
log.info("\n{}", tab.render())
return
}
when (val hv = probeHeaderVersion(fileName)) {
0, 1, 2 ->
val worker =
try {
ObjectMapper().readValue(File(cfgFile), BootV2::class.java)
} catch (e: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException) {
try {
ObjectMapper().readValue(File(cfgFile), BootV3::class.java)
} catch (e: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException) {
null
}
}
if (worker == null) {
log.warn("XXXX: worker is null")
exitProcess(2)
}
when (worker) {
is BootV2 -> {
worker
.pack()
.sign()
.updateVbmeta()
.printPackSummary()
3, 4 ->
ObjectMapper().readValue(File(cfgFile), BootV3::class.java)
}
is BootV3 -> {
worker
.pack()
.sign(fileName)
.updateVbmeta()
.printPackSummary()
else -> throw IllegalArgumentException("do not support header version $hv")
.printPackSummary(fileName)
}
else -> {
log.error("unsupported boot image format")
exitProcess(2)
}
}
}
override fun pack(fileName: String) {
packInternal(fileName, outDir, fileName)
}
override fun flash(fileName: String, deviceName: String) {
val stem = fileName.substring(0, fileName.indexOf("."))
super.flash("$fileName.signed", stem)
@ -122,7 +167,7 @@ class BootImgParser : IPackable {
}
override fun `@verify`(fileName: String) {
File(Helper.prop("workDir")).let {
File(Helper.prop("workDir")!!).let {
if (!it.exists()) {
it.mkdirs()
}

@ -18,6 +18,7 @@ import rom.sparse.SparseImgParser
import org.slf4j.LoggerFactory
import packable.DeviceTreeParser
import java.io.File
import java.util.*
import java.util.regex.Pattern
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
@ -48,22 +49,48 @@ fun main(args: Array<String>) {
}
var targetFile: String? = null
var targetHandler: KClass<IPackable>? = null
log.info("XXXX: args: " + args.asList().toString())
run found@{
for (currentLoopNo in 0..1) { //currently we have only 2 loops
File(".").listFiles()!!.forEach { file ->
if (args.size > 1) { // manual mode
targetFile = if (File(args[1]).isFile) {
File(args[1]).canonicalPath
} else if (File(args[1] + "/role").isFile) {
File(args[1] + "/role").readText().trim()
} else {
log.error("Not sure of what to do: " + args.asList().toString())
exitProcess(1)
}
log.warn("manual mode: args= ${args[1]}, $targetFile")
packablePool
.filter { it.value.createInstance().loopNo == currentLoopNo }
.forEach { p ->
for (item in p.key) {
if (Pattern.compile(item).matcher(file.name).matches()) {
log.debug("Found: " + file.name + ", " + item)
targetFile = file.name
if (Pattern.compile(item).matcher(File(targetFile).name).matches()) {
log.info("Found: $targetFile, $item")
targetHandler = p.value
return@found
}
}
}
}//end-of-file-traversing
} else { // lazy mode
File(".").listFiles()!!.forEach { file ->
packablePool
.filter { it.value.createInstance().loopNo == currentLoopNo }
.forEach { p ->
for (item in p.key) {
if (Pattern.compile(item).matcher(file.name).matches()) {
log.debug("Found: " + file.name + ", " + item)
targetFile = file.name
targetHandler = p.value
return@found
}
}
}
}//end-of-file-traversing
}
}//end-of-range-loop
}//end-of-found@
@ -71,8 +98,9 @@ fun main(args: Array<String>) {
// /* 2 */ no-args & handler : help for Handler
// /* 3 */ args & no-handler: do nothing
// /* 4 */ args & handler : work
when (listOf(args.isEmpty(), targetHandler == null)) {
listOf(true, true) -> { /* 1 */
when (listOf(args.isNotEmpty(), targetHandler != null)) {
listOf(false, false) -> { /* 1 */
log.warn("args: ${args.size}, targetHandler: $targetHandler")
log.info("help:")
log.info("available IPackable subcommands are:")
IPackable::class.declaredFunctions.forEach {
@ -81,7 +109,8 @@ fun main(args: Array<String>) {
exitProcess(1)
}
listOf(true, false) -> {/* 2 */
listOf(false, true) -> {/* 2 */
log.warn("args: ${args.size}, targetHandler: $targetHandler")
log.info("available ${targetHandler!!.simpleName} subcommands are:")
targetHandler!!.declaredFunctions.forEach {
log.info("\t" + it.name)
@ -89,13 +118,15 @@ fun main(args: Array<String>) {
exitProcess(1)
}
listOf(false, true) -> {/* 3 */
listOf(true, false) -> {/* 3 */
log.warn("args: ${args.size}, targetHandler: $targetHandler")
log.warn("No handler is activated, DO NOTHING!")
exitProcess(2)
}
listOf(false, false) -> {/* 4 */
log.debug("continue ...")
listOf(true, true) -> {/* 4 */
log.warn("args: ${args.size}, targetHandler: $targetHandler")
log.info("continue ...")
}
}
@ -111,12 +142,20 @@ fun main(args: Array<String>) {
exitProcess(3)
}
log.warn("'${args[0]}' sequence initialized")
log.warn("XXXX: args.size: ${args.size}")
val convertedArgs = args.copyOf().apply { set(0, targetFile!!) }
functions[0].call(it.createInstance(), *convertedArgs)
/*
val reflectRet = when (functions[0].parameters.size) {
1 -> {
log.warn("1: call null")
functions[0].call(it.createInstance())
}
2 -> {
log.warn("2: call $targetFile")
functions[0].call(it.createInstance(), targetFile!!)
}
@ -141,6 +180,7 @@ fun main(args: Array<String>) {
if (functions[0].returnType.toString() != Unit.toString()) {
log.info("ret: $reflectRet")
}
*/
log.warn("'${args[0]}' sequence completed")
}
}

@ -30,7 +30,7 @@ class ErofsGenerator(inPartitionName: String) : BaseGenerator(inPartitionName, A
signingArgs = newArgs.toString()
val mkfsBin = "aosp/plugged/bin/mkfs.erofs"
val fc = "${workDir}file_contexts"
val fc = File(workDir, "file_contexts").path
val cmd = CommandLine.parse(mkfsBin).apply {
addArguments("-z lz4hc,9")
addArguments("--mount-point $mount_point")

@ -123,7 +123,7 @@ class Ext4Generator(inPartitionName: String = "NA") : BaseGenerator(partitionNam
addArguments("-e")
addArguments("-p out/target/product/shiba/system")
addArgument("-s")
addArguments("-S ${workDir}file_contexts.bin")
addArguments("-S " + File(workDir, "file_contexts.bin").path)
addArguments("-f " + Helper.prop("workDir") + "/$mount_point")
addArguments("-a /$mount_point")
addArgument(outFile)

@ -33,8 +33,8 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
readBackAi,
partName,
workDir,
workDir + File(info.output).nameWithoutExtension,
workDir + File(info.pulp).name + ".signed"
File(workDir, File(info.output).nameWithoutExtension).path,
File(workDir, File(info.pulp).name + ".signed").path
)
}
@ -43,8 +43,8 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
readBackAi,
partName,
workDir,
workDir + File(info.output).nameWithoutExtension,
workDir + "${info.output}.signed"
File(workDir, File(info.output).nameWithoutExtension).path,
File(workDir, "${info.output}.signed").path
)
}
@ -66,26 +66,26 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
addRow("What", "Where")
addRule()
addRow("image (${info.outerFsType})", fileName)
("${workDir}$stem.ext4").let { ext4 ->
File(workDir, "$stem.ext4").path.let { ext4 ->
if (File(ext4).exists()) {
addRule()
addRow("converted image (ext4)", ext4)
}
}
("${workDir}$stem.erofs").let {
File(workDir, "$stem.erofs").path.let {
if (File(it).exists()) {
addRule()
addRow("converted image (erofs)", it)
tail.addRule()
tail.addRow("sudo mount $it -o loop -t erofs ${workDir}mount")
tail.addRow("sudo mount $it -o loop -t erofs " + File(workDir, "mount").path)
tail.addRule()
} else if (info.innerFsType == "erofs") {
tail.addRule()
tail.addRow("sudo mount $fileName -o loop -t erofs ${workDir}mount")
tail.addRow("sudo mount $fileName -o loop -t erofs " + File(workDir, "mount").path)
tail.addRule()
}
}
("${workDir}$stem").let {
File(workDir, "$stem").path.let {
if (File(it).exists()) {
addRule()
if (File(it).isFile) {
@ -95,7 +95,7 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
}
}
}
("${workDir}$stem.log").let {
File(workDir, "$stem.log").path.let {
if (File(it).exists()) {
addRule()
addRow("log", it)
@ -103,7 +103,7 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
}
if (info.innerFsType == "erofs") {
addRule()
addRow("mount point", "${workDir}mount")
addRow("mount point", File(workDir, "mount").path)
}
addRule()
}
@ -117,7 +117,7 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
fun unwrap(): SparseImage {
if (info.outerFsType == "sparse") {
img2simg(workDir + File(info.output).name + ".signed", File(info.output).name + ".signed")
img2simg(File(workDir, (File(info.output).name + ".signed")).path, File(info.output).name + ".signed")
} else {
val s = info.pulp + ".signed"
val t = info.output + ".signed"

@ -32,8 +32,8 @@ class KernelExtractor {
fun run(fileName: String, workDir: File? = null): List<String> {
val ret: MutableList<String> = mutableListOf()
val kernelVersionFile = Helper.prop("kernelVersionFile")!!
val kernelConfigFile = Helper.prop("kernelConfigFile")!!
val kernelVersionFile = File(Helper.prop("workDir")!! , Helper.prop("kernelVersionStem")!!).toString()
val kernelConfigFile = File(Helper.prop("workDir")!! , Helper.prop("kernelConfigStem")!!).toString()
val cmdPrefix = if (EnvironmentVerifier().isWindows) "python " else ""
val cmd = CommandLine.parse(cmdPrefix + Helper.prop("kernelExtracter")).let {
it.addArgument("--input")

@ -6,6 +6,8 @@ verity_pk8 = aosp/security/verity.pk8
verity_pem = aosp/security/verity.x509.pem
kernelVersionFile = build/unzip_boot/kernel_version.txt
kernelConfigFile = build/unzip_boot/kernel_configs.txt
kernelVersionStem = kernel_version.txt
kernelConfigStem = kernel_configs.txt
kernelExtracter = aosp/make/tools/extract_kernel.py
mkbootimg = aosp/system/tools/mkbootimg/mkbootimg.py
dtboMaker = aosp/system/libufdt/utils/src/mkdtboimg.py

@ -21,9 +21,10 @@ should be compatible with "/usr/bin/env sh"
## TODO: command line usage
unpack
```
abe unpack boot.img
abe unpack boot.img out
```
pack
```
abe pack
abe pack out boot.img
```

@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory
import java.io.*
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.file.Paths
import java.nio.file.attribute.PosixFilePermission
import java.security.MessageDigest
import java.util.*
@ -47,6 +48,15 @@ class Helper {
return gcfg.getProperty(k)
}
fun setProp(k: String, v: String) {
gcfg.setProperty(k, v)
}
fun joinPath(vararg args: String): String {
val joinedPath = Paths.get("", *args)
return joinedPath.normalize().toString()
}
fun joinWithNulls(vararg source: ByteArray?): ByteArray {
val baos = ByteArrayOutputStream()
for (src in source) {

@ -22,6 +22,8 @@ fun main(args: Array<String>) {
println("bootchart: generate Android bootchart")
println("pidstat : given a pid, profile its CPU usage")
println("tracecmd : analyze trace-cmd report")
println("cpuinfo : get cpu info from /sys/devices/system/cpu/")
println("sysinfo : get overall system info from Android")
exitProcess(0)
}
if (args[0] == "cpuinfo") {

Loading…
Cancel
Save