From ee2a30470a1f9df5aa3931e3518d9af1c21fc38a Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 24 Feb 2017 07:45:48 +0800 Subject: [PATCH] Boot IMG tools complete re-write Fix #27, Fix #35, Fix #68, Fix #70, Fix #71, Fix #72, Fix #75, Fix #87 --- jni/bootimgtools/bootimg.h | 16 ++- jni/bootimgtools/extract.c | 207 ++++++++++++++++------------------ jni/bootimgtools/repack.c | 225 ++++++++++++++++++++----------------- 3 files changed, 226 insertions(+), 222 deletions(-) diff --git a/jni/bootimgtools/bootimg.h b/jni/bootimgtools/bootimg.h index a1d7e77fa..65f81b34e 100644 --- a/jni/bootimgtools/bootimg.h +++ b/jni/bootimgtools/bootimg.h @@ -28,6 +28,9 @@ typedef struct boot_img_hdr boot_img_hdr; #define BOOT_ARGS_SIZE 512 #define BOOT_EXTRA_ARGS_SIZE 1024 +#define CHROMEOS_MAGIC "CHROMEOS" +#define CHROMEOS_MAGIC_SIZE 8 + struct boot_img_hdr { uint8_t magic[BOOT_MAGIC_SIZE]; @@ -43,7 +46,14 @@ struct boot_img_hdr uint32_t tags_addr; /* physical addr for kernel tags */ uint32_t page_size; /* flash page size we assume */ - uint32_t unused[2]; /* future expansion: should be 0 */ + uint32_t dt_size; /* device tree in bytes */ + + /* operating system version and security patch level; for + * version "A.B.C" and patch level "Y-M-D": + * ver = A << 14 | B << 7 | C (7 bits for each of A, B, C) + * lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M) + * os_version = ver << 11 | lvl */ + uint32_t os_version; uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */ @@ -83,8 +93,8 @@ struct boot_img_hdr ** else: jump to kernel_addr */ -int extract(char *image); -int repack(char *image); +int extract(const char *image); +int repack(const char *image); int hexpatch(char *image, char *from, char *to); #endif diff --git a/jni/bootimgtools/extract.c b/jni/bootimgtools/extract.c index 2dff91127..71735781c 100644 --- a/jni/bootimgtools/extract.c +++ b/jni/bootimgtools/extract.c @@ -11,139 +11,120 @@ #include "bootimg.h" -void dump(uint8_t *ptr, size_t size, char* filename) { +// Global pointer of current positions +unsigned char *base, *pos; + +static void dump(size_t size, const char *filename) { unlink(filename); - int ofd = open(filename, O_WRONLY|O_CREAT, 0644); + int ofd = open(filename, O_WRONLY | O_CREAT, 0644); assert(ofd >= 0); - int ret = write(ofd, ptr, size); + int ret = write(ofd, pos, size); assert(ret == size); close(ofd); + pos += size; } -//TODO: Search for other header types -void dump_ramdisk(uint8_t *ptr, size_t size) { - //GZip header - if(memcmp(ptr, "\x1f\x8b\x08\x00", 4) == 0) { - dump(ptr, size, "ramdisk.gz"); - //MTK header - } else if(memcmp(ptr, "\x88\x16\x88\x58", 4) == 0) { - if(memcmp(ptr+8, "RECOVERY", 8)==0) { - dump(ptr, 0, "ramdisk-mtk-recovery"); - } else if(memcmp(ptr+8, "ROOTFS\0\0", 8)==0) { - dump(ptr, 0, "ramdisk-mtk-boot"); - } else { - exit(1); - } - dump(ptr, 0, "ramdisk-mtk"); //Create an mtk flag - dump_ramdisk(ptr+512, size-512); +static void page_align(uint32_t pagesize) { + uint32_t itemsize = pos - base, pagemask = pagesize - 1L; + if (itemsize & pagemask) { + pos += pagesize - (itemsize & pagemask); + } +} + +static int aosp() { + printf("AOSP Boot Image Detected\n"); + + char name[PATH_MAX], *ext; + + // Read the header + struct boot_img_hdr hdr; + memcpy(&hdr, base, sizeof(hdr)); + + pos = base + hdr.page_size; + + // Dump zImage + if (memcmp(pos, "\x88\x16\x88\x58", 4) == 0) { + printf("MTK header found in zImage\n"); + pos += 512; + hdr.kernel_size -= 512; + } + dump(hdr.kernel_size, "kernel"); + page_align(hdr.page_size); + + // Dump ramdisk + if (memcmp(pos, "\x88\x16\x88\x58", 4) == 0) { + printf("MTK header found in ramdisk\n"); + pos += 512; + hdr.ramdisk_size -= 512; + } + // Compression detection + if (memcmp(pos, "\x1f\x8b\x08\x00", 4) == 0) { + // gzip header + printf("gzip ramdisk format detected!\n"); + ext = "gz"; + } else if (memcmp(pos, "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a", 9) == 0) { + // lzop header + printf("lzop ramdisk format detected!\n"); + ext = "lzo"; + } else if (memcmp(pos, "\xfd""7zXZ\x00", 6) == 0) { + // xz header + printf("xz ramdisk format detected!\n"); + ext = "xz"; + } else if (memcmp(pos, "\x5d\x00\x00", 3) == 0 + && (pos[12] == (unsigned char) '\xff' || pos[12] == (unsigned char) '\x00')) { + // lzma header + printf("lzma ramdisk format detected!\n"); + ext = "lzma"; + } else if (memcmp(pos, "BZh", 3) == 0) { + // bzip2 header + printf("bzip2 ramdisk format detected!\n"); + ext = "bz2"; + } else if ( ( memcmp(pos, "\x04\x22\x4d\x18", 4) == 0 + || memcmp(pos, "\x03\x21\x4c\x18", 4) == 0) + || memcmp(pos, "\x02\x21\x4c\x18", 4) == 0) { + // lz4 header + printf("lz4 ramdisk format detected!\n"); + ext = "lz4"; } else { - //Since our first aim is to extract/repack ramdisk - //Stop if we can't find it - //Still dump it for debug purposes - dump(ptr, size, "ramdisk"); - - fprintf(stderr, "Unknown ramdisk type\n"); - abort(); - } -} - -void search_security_hdr(uint8_t *buf, size_t size) { - if(memcmp(buf, "CHROMEOS", 8) == 0) { - dump(buf, 0, "chromeos"); - return; - } -} - -int search_security(uint8_t *buf, size_t size, int pos) { - //Rockchip signature - if(memcmp(buf+1024, "SIGN", 4) == 0) { - //Rockchip signature AT LEAST means the bootloader will check the crc - dump(buf, 0, "rkcrc"); //Create an flag to tell it - - //And it's possible there is a security too + fprintf(stderr, "Unknown ramdisk format!\n"); return 1; } + sprintf(name, "%s.%s", "ramdisk", ext); + dump(hdr.ramdisk_size, name); + page_align(hdr.page_size); - //If we didn't parse the whole file, it is highly likely there is a boot signature - if(pos < size) { - return 1; + if (hdr.second_size) { + // Dump second + dump(hdr.second_size, "second"); + page_align(hdr.page_size); + } + + if (hdr.dt_size) { + // Dump dtb + dump(hdr.dt_size, "dtb"); + page_align(hdr.page_size); } return 0; } -/* - * TODO: - * - At the moment we dump kernel + ramdisk + second + DT, it's likely we only want ramdisk - * - Error-handling via assert() is perhaps not the best - */ -int extract(char *image) { - - int fd = open(image, O_RDONLY); - off_t size = lseek(fd, 0, SEEK_END); +int extract(const char* image) { + int fd = open(image, O_RDONLY), ret = 0; + size_t size = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); - uint8_t *orig = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - uint8_t *base = orig; - assert(base); + unsigned char *orig = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - search_security_hdr(base, size); - - //We're searching for the header in the whole file, we could stop earlier. - //At least HTC and nVidia have a signature header - while(base<(orig+size)) { - if(memcmp(base, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) + // Check headers + for(base = orig; base < (orig + size); base += 256) { + if (memcmp(base, CHROMEOS_MAGIC, CHROMEOS_MAGIC_SIZE) == 0) { + dump(0, "chromeos"); + } else if (memcmp(base, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) { + ret = aosp(); break; - //We're searching every 256bytes, is it ok? - base += 256; - } - assert(base < (orig+size)); - - struct boot_img_hdr *hdr = (struct boot_img_hdr*) base; - assert( - hdr->page_size == 2048 || - hdr->page_size == 4096 || - hdr->page_size == 16384 - ); - - long pos = hdr->page_size; - dump(base+pos, hdr->kernel_size, "kernel"); - pos += hdr->kernel_size + hdr->page_size-1; - pos &= ~(hdr->page_size-1L); - - dump_ramdisk(base+pos, hdr->ramdisk_size); - pos += hdr->ramdisk_size + hdr->page_size-1; - pos &= ~(hdr->page_size-1L); - - if(hdr->second_size) { - assert( (pos+hdr->second_size) <= size); - dump(base+pos, hdr->second_size, "second"); - pos += hdr->second_size + hdr->page_size-1; - pos &= ~(hdr->page_size-1L); - } - - //This is non-standard, so we triple check - if( hdr->unused[0] && - pos < size && - (pos+hdr->unused[0]) <= size) { - - if(memcmp(base+pos, "QCDT", 4) == 0 || - memcmp(base+pos, "SPRD", 4) == 0 || - memcmp(base+pos, "DTBH", 4) == 0 || - memcmp(base+pos, "\xD0\x0D\xFE\xED", 4) == 0 - ) { - dump(base+pos, hdr->unused[0], "dt"); - pos += hdr->unused[0] + hdr->page_size-1; - pos &= ~(hdr->page_size-1L); } } - - //If we think we find some security-related infos in the boot.img - //create a "secure" flag to warn the user it is dangerous - if(search_security(base, size, pos)) { - dump(base, 0, "secure"); - } - munmap(orig, size); close(fd); - return 0; + return ret; } + diff --git a/jni/bootimgtools/repack.c b/jni/bootimgtools/repack.c index bb9732e8b..9c3ac2123 100644 --- a/jni/bootimgtools/repack.c +++ b/jni/bootimgtools/repack.c @@ -11,134 +11,147 @@ #include "bootimg.h" -off_t file_size(char *filename) { - struct stat st; - if(stat(filename, &st)) - exit(1); - return st.st_size; -} +// Global pointer of current positions +void *ibase, *ipos; +int ofd, opos; -int append_file(int ofd, char *filename, off_t pos) { - lseek(ofd, pos, SEEK_SET); +static size_t dump(const char *filename) { int fd = open(filename, O_RDONLY); - int size = lseek(fd, 0, SEEK_END); + if (fd < 0) { + fprintf(stderr, "Cannot open %s\n", filename); + exit(1); + } + size_t size = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); - sendfile(ofd, fd, NULL, size); + if (sendfile(ofd, fd, NULL, size) < 0) { + fprintf(stderr, "Cannot write %s\n", filename); + exit(1); + } close(fd); + opos += size; return size; } -int append_ramdisk(int ofd, off_t pos) { - if(access("ramdisk-mtk", R_OK) == 0) { - char buf[512]; - off_t size = file_size("ramdisk.gz"); - memcpy(buf, "\x88\x16\x88\x58", 4); - uint32_t v = size; - memcpy(buf+4, &v, sizeof(v)); //Should convert to LE - - //TODO: RECOVERY OR ROOTFS? - char str[32]; - memset(str, 0, sizeof(str)); - if(access("ramdisk-mtk-boot", R_OK)==0) { - strcpy(str, "ROOTFS"); - } else if(access("ramdisk-mtk-recovery", R_OK)==0) { - strcpy(str, "RECOVERY"); - } else { - exit(1); - } - memcpy(buf+8, str, sizeof(str)); - - memset(buf+8+sizeof(str), 0xff, 512-8-sizeof(str)); - - pwrite(ofd, buf, sizeof(buf), pos); - - return append_file(ofd, "ramdisk.gz", pos + 512) + 512; - } else if(access("ramdisk.gz", R_OK) == 0) { - return append_file(ofd, "ramdisk.gz", pos); - } else { - return append_file(ofd, "ramdisk", pos); - } -} - -void post_process(struct boot_img_hdr *hdr, int ofd, int pos) { - if(access("rkcrc", R_OK) == 0) { - fprintf(stderr, "Rockchip CRCs not supported yet\n"); +static void dump_buf(size_t size, const void *buf) { + if (write(ofd, buf, size) < 0) { + fprintf(stderr, "Cannot dump from input file\n"); exit(1); } - //Round up the file size - ftruncate(ofd, pos); + opos += size; } -int repack(char *image) { - - //TODO: Merge with extract.c? - //{ - int ifd = open(image, O_RDONLY); - off_t isize = lseek(ifd, 0, SEEK_END); - lseek(ifd, 0, SEEK_SET); - uint8_t *iorig = mmap(NULL, isize, PROT_READ, MAP_SHARED, ifd, 0); - uint8_t *ibase = iorig; - assert(ibase); - - while(ibase<(iorig+isize)) { - if(memcmp(ibase, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) - break; - ibase += 256; +static void in_page_align(uint32_t pagesize) { + uint32_t itemsize = ipos - ibase, pagemask = pagesize - 1L; + if (itemsize & pagemask) { + ipos += pagesize - (itemsize & pagemask); } - assert(ibase < (iorig+isize)); - //} - // - struct boot_img_hdr *ihdr = (struct boot_img_hdr*) ibase; - assert( - ihdr->page_size == 2048 || - ihdr->page_size == 4096 || - ihdr->page_size == 16384 - ); +} - unlink("new-boot.img"); - int ofd = open("new-boot.img", O_RDWR|O_CREAT, 0644); - ftruncate(ofd, ihdr->page_size); - //Write back original header, we'll change it later - write(ofd, ihdr, sizeof(*ihdr)); +static void out_page_align(uint32_t pagesize) { + uint32_t pagemask = pagesize - 1L; + if (opos & pagemask) { + opos += pagesize - (opos & pagemask); + } + ftruncate(ofd, opos); + lseek(ofd, 0, SEEK_END); +} - struct boot_img_hdr *hdr = mmap(NULL, sizeof(*ihdr), PROT_READ|PROT_WRITE, MAP_SHARED, ofd, 0); - //First set everything to zero, so we know where we are at. - hdr->kernel_size = 0; - hdr->ramdisk_size = 0; - hdr->second_size = 0; - hdr->unused[0] = 0; - memset(hdr->id, 0, sizeof(hdr->id)); //Setting id to 0 might be wrong? +static int aosp() { + printf("AOSP Boot Image Detected\n"); - int pos = hdr->page_size; - int size = 0; + char *name; + struct boot_img_hdr hdr, ihdr; - size = append_file(ofd, "kernel", pos); - pos += size + hdr->page_size - 1; - pos &= ~(hdr->page_size-1); - hdr->kernel_size = size; + // Read the original header + memcpy(&ihdr, ibase, sizeof(ihdr)); + hdr = ihdr; - size = append_ramdisk(ofd, pos); - pos += size + hdr->page_size - 1; - pos &= ~(hdr->page_size-1); - hdr->ramdisk_size = size; + // Set all sizes to 0 + hdr.kernel_size = 0; + hdr.ramdisk_size = 0; + hdr.second_size = 0; + hdr.dt_size = 0; - if(access("second", R_OK) == 0) { - size = append_file(ofd, "second", pos); - pos += size + hdr->page_size - 1; - pos &= ~(hdr->page_size-1); - hdr->second_size = size; + // Skip a page + ftruncate(ofd, hdr.page_size); + lseek(ofd, 0, SEEK_END); + opos += hdr.page_size; + ipos = ibase + hdr.page_size; + + // Dump zImage + if (memcmp(ipos, "\x88\x16\x88\x58", 4) == 0) { + printf("Dumping MTK header back to zImage\n"); + dump_buf(512, ipos); + hdr.kernel_size += 512; + } + hdr.kernel_size += dump("kernel"); + ipos += ihdr.kernel_size; + in_page_align(hdr.page_size); + out_page_align(hdr.page_size); + + // Dump ramdisk + if (memcmp(ipos, "\x88\x16\x88\x58", 4) == 0) { + printf("Dumping MTK header back to ramdisk\n"); + dump_buf(512, ipos); + hdr.ramdisk_size += 512; + } + if (access("ramdisk.gz", R_OK) == 0) { + name = "ramdisk.gz"; + } else if (access("ramdisk.lzo", R_OK) == 0) { + name = "ramdisk.lzo"; + } else if (access("ramdisk.xz", R_OK) == 0) { + name = "ramdisk.xz"; + } else if (access("ramdisk.lzma", R_OK) == 0) { + name = "ramdisk.lzma"; + } else if (access("ramdisk.bz2", R_OK) == 0) { + name = "ramdisk.bz2"; + } else if (access("ramdisk.lz4", R_OK) == 0) { + name = "ramdisk.lz4"; + } else { + fprintf(stderr, "Ramdisk file doesn't exist!\n"); + return 1; + } + hdr.ramdisk_size += dump(name); + out_page_align(hdr.page_size); + + // Dump second + if (access("second", R_OK) == 0) { + hdr.second_size += dump("second"); + out_page_align(hdr.page_size); } - if(access("dt", R_OK) == 0) { - size = append_file(ofd, "dt", pos); - pos += size + hdr->page_size - 1; - pos &= ~(hdr->page_size-1); - hdr->unused[0] = size; + // Dump dtb + if (access("dtb", R_OK) == 0) { + hdr.dt_size += dump("dtb"); + out_page_align(hdr.page_size); } - post_process(hdr, ofd, pos); - munmap(hdr, sizeof(*ihdr)); - close(ofd); + // Write header back + lseek(ofd, 0, SEEK_SET); + write(ofd, &hdr, sizeof(hdr)); return 0; } + +int repack(const char* image) { + // Load original boot + int ifd = open(image, O_RDONLY), ret = -1; + size_t isize = lseek(ifd, 0, SEEK_END); + lseek(ifd, 0, SEEK_SET); + void *orig = mmap(NULL, isize, PROT_READ, MAP_SHARED, ifd, 0); + + // Create new boot image + unlink("new-boot.img"); + ofd = open("new-boot.img", O_RDWR | O_CREAT, 0644); + + // Check headers + for(ibase = orig; ibase < (orig + isize); ibase += 256) { + if (memcmp(ibase, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) { + ret = aosp(); + break; + } + } + munmap(orig, isize); + close(ifd); + return ret; +}