diff --git a/README.expert.md b/README.expert.md index 91d98ae..3816f80 100644 --- a/README.expert.md +++ b/README.expert.md @@ -22,7 +22,7 @@ |--------------------------------+-------------------------| | | 4 | |--------------------------------+-------------------------| - | RESERVED | 4 | + |
| 4 | |--------------------------------+-------------------------| | | 4 | |--------------------------------+-------------------------| @@ -34,25 +34,59 @@ |--------------------------------+-------------------------| | | 1024 | |--------------------------------+-------------------------| - | | min(n * page_zie - 1632)| + | | 4 | + |--------------------------------+-------------------------| + | | 8 | + |--------------------------------+-------------------------| + |
| 4 | + |--------------------------------+-------------------------| + | | min(n * page_zie - 1648)| +----------------------------------------------------------+ - + ### 2. data part +----------------------------------------------------------+ | | kernel length | |--------------------------------+-------------------------| - | | | + | | min(n * page_zie - len)| +----------------------------------------------------------+ - + +--------------------------------+-------------------------+ | | ramdisk length | |--------------------------------+-------------------------| - | | | + | | min(n * page_zie - len)| +----------------------------------------------------------+ - + +--------------------------------+-------------------------+ | | second bootloader length| |--------------------------------+-------------------------| - | | | + | | min(n * page_zie - len)| + +----------------------------------------------------------+ + + +--------------------------------+-------------------------+ + | | recovery dtbo length | + |--------------------------------+-------------------------| + | | min(n * page_zie - len)| + +----------------------------------------------------------+ + +### 3. signature part + +#### 3.1 Boot Image Signature (VBoot 1.0) + + +--------------------------------+-------------------------+ + | | signature length | + |--------------------------------+-------------------------| + | | defined by boot_signer | + +----------------------------------------------------------+ + +#### 3.2 AVB Footer (VBoot 2.0) + + +--------------------------------+-------------------------+ + | | signature length | + |--------------------------------+-------------------------| + | ... | ... | + | | defined by avbtool, | + | | it will pad to requested| + | | image size | + | ... | ... | +----------------------------------------------------------+ diff --git a/README.md b/README.md index 65a54a0..9c9c4be 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,13 @@ This tool focuses on editing Android boot.img(also recovery.img and recovery-two #### Host OS requirement: Linux or Mac. +Also need python and java 8. #### Target Android requirement: -(1) Targeted boot.img(or recovery.img / recovery-two-step.img) MUST follows AOSP [verified boot flow](https://source.android.com/security/verifiedboot/index.html), which means it packs linux kernel, rootfs , and a optional second state bootloader, then sign it with OEM/USER keys. +(1) Target boot.img(or recovery.img / recovery-two-step.img) MUST follows AOSP verified boot flow, either [Boot image signature](https://source.android.com/security/verifiedboot/verified-boot#signature_format) in VBoot 1.0 or [AVB HASH footer](https://android.googlesource.com/platform/external/avb/+/master/README.md#The-VBMeta-struct) in VBoot 2.0. -(2) These utilities are known to work for Nexus (or Nexus compatible) boot.img(or recovery.img/recovery-two-step.img) for the following Android releases: +(2) These utilities are known to work for Nexus/Pixel (or Pixel compatible) boot.img(or recovery.img/recovery-two-step.img) for the following Android releases: - AOSP master - Lollipop (API Level 21,22) - Oreo (API Level 26,27) @@ -62,8 +63,8 @@ And you get recovery.img.signed An example boot.img has been placed at **src/test/resources/boot.img**, which is extracted from Nexus 5x(code: bullhead) factory images from [Google](https://dl.google.com/dl/android/aosp/bullhead-mda89e-factory-29247942.tgz), you can take it as a quick start. ## boot.img layout -Read [layout](https://github.com/cfig/Android_boot_image_editor/blob/master/README.expert.md) of Android boot.img. -We now support **os\_version** and **os\_patch\_level**. +Read [layout](README.expert.md) of Android boot.img. +We now support **os\_version** ,**os\_patch\_level**, **header_version** and **dtbo** ## References diff --git a/abootimg/build.gradle b/abootimg/build.gradle deleted file mode 100644 index 40867dc..0000000 --- a/abootimg/build.gradle +++ /dev/null @@ -1,69 +0,0 @@ -apply plugin: 'groovy' - -sourceCompatibility = 1.7 - -repositories { - mavenCentral() -} - -dependencies { - compile 'org.codehaus.groovy:groovy-all:2.4.7' - compile group: 'net.sf.jopt-simple', name: 'jopt-simple', version: '5.0.2' - testCompile group: 'junit', name: 'junit', version: '4.11' -} - -task abootimg(type: Jar, dependsOn:['build']) { - from files(sourceSets.main.output.classesDir) - from files("build/classes/groovy/main") - from configurations.runtime.asFileTree.files.collect { zipTree(it) } - - baseName = 'abootimg' - manifest { - attributes 'Main-Class': 'cfig.bootimg.abootimg' - } -} - -task mkbootimg(type: Jar, dependsOn:['build']) { - from files(sourceSets.main.output.classesDir) - from files("build/classes/groovy/main") - from configurations.runtime.asFileTree.files.collect { zipTree(it) } - - baseName = 'mkbootimg' - manifest { - attributes 'Main-Class': 'cfig.bootimg.mkbootimg' - } -} - -task repack(type: Jar, dependsOn:['build']) { - from files(sourceSets.main.output.classesDir) - from files("build/classes/groovy/main") - from configurations.runtime.asFileTree.files.collect { zipTree(it) } - - baseName = 'repack' - manifest { - attributes 'Main-Class': 'cfig.bootimg.repack' - } -} - -//call Google's 'mkbootimg' program -task pack_clear(type: JavaExec, dependsOn:[':pack_ramdisk_and_gz', 'build']) { - classpath = sourceSets.main.runtimeClasspath - main = "cfig.bootimg.repack_with_cmd" - args rootProject.outClearIMg, rootProject.rootWorkDir, rootProject.mkbootimgBin -} - -//call our 'mkbootimg.groovy' -task pack_clear2(type: JavaExec, dependsOn:[':pack_ramdisk_and_gz', 'mkbootimg']) { - classpath = sourceSets.main.runtimeClasspath - main = "cfig.bootimg.repack_with_cmd" - args rootProject.outClearIMg + "2" , rootProject.rootWorkDir, "java -jar build/libs/mkbootimg.jar" -} - -//directly calls engine of 'mkbootimg.groovy' from 'repack' -task pack_clear3(type: JavaExec, dependsOn: [':pack_ramdisk_and_gz', 'repack']) { - classpath = files("build/libs/repack.jar") - main = 'cfig.bootimg.repack' - maxHeapSize '512m' - args rootProject.outClearIMg + "3", rootProject.rootWorkDir -} - diff --git a/abootimg/src/main/groovy/cfig/bootimg/CArgs.groovy b/abootimg/src/main/groovy/cfig/bootimg/CArgs.groovy deleted file mode 100644 index 4d522f6..0000000 --- a/abootimg/src/main/groovy/cfig/bootimg/CArgs.groovy +++ /dev/null @@ -1,82 +0,0 @@ -package cfig.bootimg - -import groovy.transform.ToString - -/** - * Created by yu at 09:57 on 2016-06-17 - */ -@groovy.transform.TypeChecked -@ToString(includeNames = true, includeFields = true, excludes = "toCommandLine, CArgs") -class CArgs { - public String kernel; - public String ramdisk; - public String output; - public String cfg; - public String board; - public String second; - public String cmdline; - public String os_version; - public String os_patch_level; - public long base; - public long kernel_offset; - public long ramdisk_offset; - public long second_offset; - public int pagesize; - public long tags_offset; - public boolean id; - - public CArgs() { - kernel = "kernel"; - ramdisk = "ramdisk.img.gz"; - second = "second"; - output = "boot.img"; - cfg = "bootimg.json"; - } - - public List toCommandList() { - List ret = new ArrayList(); - ret.add("--base"); - ret.add("0x" + Long.toHexString(base)); - ret.add("--kernel"); - ret.add(kernel); - ret.add("--kernel_offset"); - ret.add("0x" + Long.toHexString(kernel_offset)); - if (null != ramdisk) { - ret.add("--ramdisk"); - ret.add(ramdisk); - } - ret.add("--ramdisk_offset"); - ret.add("0x" + Long.toHexString(ramdisk_offset)); - if (null != second) { - ret.add("--second"); - ret.add(second); - } - ret.add("--second_offset"); - ret.add("0x" + Long.toHexString(second_offset)); - if (null != board) { - ret.add("--board"); - ret.add(board); - } - ret.add("--pagesize"); - ret.add(Integer.toString(pagesize)); - ret.add("--cmdline"); - ret.add(cmdline); - if (null != os_version) { - ret.add("--os_version"); - ret.add(os_version); - } - if (null != os_patch_level) { - ret.add("--os_patch_level"); - ret.add(os_patch_level); - } - ret.add("--tags_offset"); - ret.add("0x" + Long.toHexString(tags_offset)); - if (id) { - ret.add("--id"); - } - ret.add("--output"); - ret.add(output); - - return ret; - } -} diff --git a/abootimg/src/main/groovy/cfig/bootimg/CImgInfo.groovy b/abootimg/src/main/groovy/cfig/bootimg/CImgInfo.groovy deleted file mode 100644 index 8b27794..0000000 --- a/abootimg/src/main/groovy/cfig/bootimg/CImgInfo.groovy +++ /dev/null @@ -1,99 +0,0 @@ -package cfig.bootimg - -import groovy.json.JsonSlurper -import groovy.json.JsonBuilder -import groovy.transform.ToString - -/** - * Created by yu at 09:58 on 2016-06-17 - */ -@ToString(includeNames=true, includeFields=true, includeSuper = true) -class CImgInfo extends CArgs { - public int kernel_len; - public int ramdisk_len; - public int second_len; - public int kernel_pos; - public int ramdisk_pos; - public int second_pos; - public byte[] hash; - - public static CImgInfo fromJson(String outFile, String workDir) { - CImgInfo aArg = new CImgInfo(); - //preset info - aArg.kernel = workDir + File.separator + aArg.kernel; - aArg.ramdisk = workDir + File.separator + aArg.ramdisk; - aArg.second = workDir + File.separator + aArg.second; - aArg.cfg = workDir + File.separator + aArg.cfg; - aArg.output = outFile; - - JsonSlurper jsonSlurper = new JsonSlurper() - Map result = jsonSlurper.parseText(new File(aArg.cfg).text); - - //arg info - aArg.board = result.bootimg.args.board; - aArg.cmdline = result.bootimg.args.cmdline; - aArg.base = Long.decode(result.bootimg.args.base); - aArg.kernel_offset = Long.decode(result.bootimg.args.kernel_offset); - aArg.ramdisk_offset = Long.decode(result.bootimg.args.ramdisk_offset); - aArg.second_offset = Long.decode(result.bootimg.args.second_offset); - aArg.tags_offset = Long.decode(result.bootimg.args.tags_offset); - aArg.id = true; - aArg.pagesize = result.bootimg.args.pagesize; - aArg.os_version = result.bootimg.args.os_version; - aArg.os_patch_level = result.bootimg.args.os_patch_level; - //image info - aArg.kernel_len = Integer.decode(result.bootimg.img.kernel_len); - aArg.ramdisk_len = Integer.decode(result.bootimg.img.ramdisk_len); - aArg.second_len = Integer.decode(result.bootimg.img.second_len); - //adjust preset info - if (0 == aArg.ramdisk_len) { - aArg.ramdisk = null; - } - if (0 == aArg.second_len) { - aArg.second = null; - } - - return aArg; - } - - private String bytes2String(byte[] inData) { - StringBuilder sb = new StringBuilder(""); - for (int i = 0; i < inData.length; i++) { - sb.append(Integer.toString((inData[i] & 0xff) + 0x100, 16).substring(1)); - } - return sb.toString(); - } - - public void toJson() { - JsonBuilder jb = new JsonBuilder(); - String hashString = bytes2String(this.hash); - jb.bootimg { - args { - base "0x" + Integer.toHexString((int)this.base); - kernel_offset "0x" + Integer.toHexString((int)this.kernel_offset); - ramdisk_offset "0x" + Integer.toHexString((int)this.ramdisk_offset); - second_offset "0x" + Integer.toHexString((int)this.second_offset); - tags_offset "0x" + Integer.toHexString((int)this.tags_offset); - pagesize this.pagesize; - board this.board; - cmdline this.cmdline; - os_version this.os_version; - os_patch_level this.os_patch_level; - id this.id; - } - img { - kernel_pos "0x" + Integer.toHexString(this.kernel_pos); - kernel_len "0x" + Integer.toHexString(this.kernel_len); - ramdisk_pos "0x" + Integer.toHexString(this.ramdisk_pos); - ramdisk_len "0x" + Integer.toHexString(this.ramdisk_len); - second_pos "0x" + Integer.toHexString(this.second_pos); - second_len "0x" + Integer.toHexString(this.second_len); - hash hashString; - } - } - FileWriter fw = new FileWriter(this.cfg); - fw.write(jb.toPrettyString()); - fw.flush(); - fw.close(); - } -} diff --git a/abootimg/src/main/groovy/cfig/bootimg/Packer.groovy b/abootimg/src/main/groovy/cfig/bootimg/Packer.groovy deleted file mode 100644 index a5e78ac..0000000 --- a/abootimg/src/main/groovy/cfig/bootimg/Packer.groovy +++ /dev/null @@ -1,353 +0,0 @@ -package cfig.bootimg - -import java.nio.ByteBuffer; -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import java.nio.channels.FileChannel; -import java.nio.ByteOrder; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.security.MessageDigest; - -/** - * Created by yu at 10:52 on 2016-06-18 - */ -@groovy.transform.CompileStatic -class Packer { - CArgs parse_cmdline(String[] inArgs) { - OptionParser parser = new OptionParser(); - parser.accepts("kernel", "path to the kernel").withRequiredArg(); - parser.accepts("ramdisk", "path to the ramdisk").withRequiredArg(); - parser.accepts("second", "path to the 2nd bootloader").withRequiredArg(); - parser.accepts("cmdline", "extra arguments to be passed on the kernel command line").withRequiredArg(); - parser.accepts("base", "base address").withRequiredArg(); - parser.accepts("kernel_offset", "kernel offset").withRequiredArg(); - parser.accepts("ramdisk_offset", "ramdisk offset").withRequiredArg(); - parser.accepts("second_offset", "2nd bootloader offset").withRequiredArg(); - parser.accepts("os_version", "operating system version").withRequiredArg(); - parser.accepts("os_patch_level", "operating system patch level").withRequiredArg(); - parser.accepts("tags_offset", "tags offset").withRequiredArg(); - parser.accepts("board", "board name").withRequiredArg(); - parser.accepts("pagesize", "page size").withRequiredArg(); - parser.accepts("id", "print the image ID on standard output"); - parser.accepts("output", "output file name").withRequiredArg(); - - OptionSet options = parser.parse(inArgs) - CArgs ret = new CArgs(); - - ret.kernel = options.valueOf("kernel") - - ret.output = options.valueOf("output") - - ret.ramdisk = options.valueOf("ramdisk") - - ret.second = options.valueOf("second") - - if (options.has("board")) { - ret.board = options.valueOf("board") - } else { - ret.board = "" - } - - ret.id = options.has("id") - - if (options.has("base")) { - ret.base = Long.decode(String.valueOf(options.valueOf("base"))) - } else { - ret.base = 0x10000000; - } - - if (options.has("kernel_offset")) { - ret.kernel_offset = Long.decode(String.valueOf(options.valueOf("kernel_offset"))) - } else { - ret.kernel_offset = 0x00008000; - } - - if (options.has("ramdisk_offset")) { - ret.ramdisk_offset = Long.decode(String.valueOf(options.valueOf("ramdisk_offset"))) - } else { - ret.ramdisk_offset = 0x01000000 - } - - ret.os_version = options.valueOf("os_version") - - ret.os_patch_level = options.valueOf("os_patch_level") - - if (options.has("second_offset")) { - ret.second_offset = Long.decode(String.valueOf(options.valueOf("second_offset"))) - } else { - ret.second_offset = 0x00f00000 - } - - if (options.has("tags_offset")) { - ret.tags_offset = Long.decode(String.valueOf(options.valueOf("tags_offset"))) - } else { - ret.tags_offset = 0x00000100 - } - - if (options.has("pagesize")) { - ret.pagesize = Integer.decode(String.valueOf(options.valueOf("pagesize"))) - } else { - ret.pagesize = 2048 - } - - if (options.has("cmdline")) { - ret.cmdline = String.valueOf(options.valueOf("cmdline")) - } else { - ret.cmdline = "" - } - - if (ret.cmdline.length() > 1536) { - println("cmdline length must <= 1536, current is " + ret.cmdline.length()); - printUsage(parser); - } - if (null == ret.kernel) { - println("kernel must not be empty"); - printUsage(parser); - } - if (null == ret.output) { - println("output file must not be empty"); - printUsage(parser); - } - if (ret.board.length() > 16) { - println("board name length must <= 16") - printUsage(parser); - } - - return ret; - } - - byte[] write_header(CArgs inArgs) { - ByteBuffer bf = ByteBuffer.allocate(1024 * 32); - bf.order(ByteOrder.LITTLE_ENDIAN); - - //header start - bf.put("ANDROID!".getBytes()) - bf.putInt((int) new File(inArgs.kernel).length()); - bf.putInt((int)(inArgs.base + inArgs.kernel_offset)); - - if (null == inArgs.ramdisk) { - bf.putInt(0) - } else { - bf.putInt((int) new File(inArgs.ramdisk).length()); - } - - bf.putInt((int)(inArgs.base + inArgs.ramdisk_offset)); - - if (null == inArgs.second) { - bf.putInt(0) - } else { - bf.putInt((int) new File(inArgs.second).length()); - } - - bf.putInt((int)(inArgs.base + inArgs.second_offset)); - bf.putInt((int)(inArgs.base + inArgs.tags_offset)); - bf.putInt(inArgs.pagesize) - bf.putInt(0); - bf.putInt((parse_os_version(inArgs.os_version) << 11) | parse_os_patch_level(inArgs.os_patch_level)) - - if (null == inArgs.board) { - bf.put(new byte[16]); - } else { - bf.put(inArgs.board.getBytes()) - bf.put(new byte[16 - inArgs.board.length()]) - } - - bf.put(inArgs.cmdline.substring(0, Math.min(512, inArgs.cmdline.length())).getBytes()) - bf.put(new byte[512 - Math.min(512, inArgs.cmdline.length())]) - byte[] img_id = hashFile(inArgs.kernel, inArgs.ramdisk, inArgs.second) - bf.put(img_id) - bf.put(new byte[32 - img_id.length]) - - if (inArgs.cmdline.length() > 512) { - bf.put(inArgs.cmdline.substring(512).getBytes()) - bf.put(new byte[1024 + 512 - inArgs.cmdline.length()]) - } else { - bf.put(new byte[1024]) - } - - //padding - pad_file(bf, inArgs.pagesize) - - //write - FileOutputStream fos = new FileOutputStream(inArgs.output, false); - fos.write(bf.array(), 0, bf.position()) - fos.close(); - - return img_id; - } - - void printUsage(OptionParser p) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - out.write("Usage: mkbootimg