diff --git a/jni/bootimgtools/Android.mk b/jni/bootimgtools/Android.mk index 733220bc8..23bc7bdde 100644 --- a/jni/bootimgtools/Android.mk +++ b/jni/bootimgtools/Android.mk @@ -3,6 +3,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := bootimgtools LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := main.c unpack.c repack.c hexpatch.c parseimg.c +LOCAL_SRC_FILES := main.c unpack.c repack.c hexpatch.c parseimg.c compress.c LOCAL_CFLAGS += -std=gnu11 +LOCAL_LDLIBS += -lz include $(BUILD_EXECUTABLE) diff --git a/jni/bootimgtools/bootimg.h b/jni/bootimgtools/bootimg.h index 48b08fa14..2f157bc82 100644 --- a/jni/bootimgtools/bootimg.h +++ b/jni/bootimgtools/bootimg.h @@ -28,9 +28,6 @@ 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]; @@ -76,10 +73,13 @@ struct boot_img_hdr ** +-----------------+ ** | second stage | o pages ** +-----------------+ +** | device tree | p pages +** +-----------------+ ** ** n = (kernel_size + page_size - 1) / page_size ** m = (ramdisk_size + page_size - 1) / page_size ** o = (second_size + page_size - 1) / page_size +** p = (dt_size + page_size - 1) / page_size ** ** 0. all entities are page_size aligned in flash ** 1. kernel and ramdisk are required (size != 0) @@ -93,20 +93,4 @@ struct boot_img_hdr ** else: jump to kernel_addr */ - -/* Self defined stuffs */ - -// Global pointers to mmap -unsigned char *base, *kernel, *ramdisk, *second, *dtb; -// Parsed header -boot_img_hdr hdr; - -int unpack(const char *image); -int repack(const char *image); -int hexpatch(char *image, char *from, char *to); -void error(int rc, const char *msg, ...); -void print_header(); -void parse_elf(); -void parse_aosp(); - #endif diff --git a/jni/bootimgtools/compress.c b/jni/bootimgtools/compress.c new file mode 100644 index 000000000..b10f8a5c6 --- /dev/null +++ b/jni/bootimgtools/compress.c @@ -0,0 +1,59 @@ +#include "magiskboot.h" + +void gzip(int dec, const char* filename, unsigned char* buf, size_t size) { + int ret, flush, have, pos = 0; + z_stream strm; + unsigned char out[CHUNK]; + + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + if (fd < 0) + error(1, "Unable to create %s", filename); + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + if (dec) { + ret = inflateInit2(&strm, windowBits | ZLIB_GZIP); + } else { + ret = deflateInit2(&strm, 9, Z_DEFLATED, windowBits | ZLIB_GZIP, memLevel, Z_DEFAULT_STRATEGY); + } + + if (ret != Z_OK) + error(1, "Unable to init zlib"); + + do { + strm.next_in = buf + pos; + if (pos + CHUNK > size) { + strm.avail_in = size - pos; + pos = size; + flush = Z_FINISH; + } else { + strm.avail_in = CHUNK; + pos += CHUNK; + flush = Z_NO_FLUSH; + } + + do { + strm.avail_out = CHUNK; + strm.next_out = out; + if (dec) { + inflate(&strm, flush); + } else { + deflate(&strm, flush); + } + have = CHUNK - strm.avail_out; + if (write(fd, out, have) != have) + error(1, "Error in writing %s", filename); + } while (strm.avail_out == 0); + + } while(pos < size); + + if (dec) { + inflateEnd(&strm); + } else { + deflateEnd(&strm); + } + close(fd); +} diff --git a/jni/bootimgtools/elf.h b/jni/bootimgtools/elf.h index 447209528..9e16726f7 100644 --- a/jni/bootimgtools/elf.h +++ b/jni/bootimgtools/elf.h @@ -22,6 +22,8 @@ ** +-----------------+ ** | program header | dtb info ** +-----------------+ +** | program header | (possible) cmdline info +** +-----------------+ ** ~ ** +-----------------+ ** | section header | cmdline info diff --git a/jni/bootimgtools/hexpatch.c b/jni/bootimgtools/hexpatch.c index e48e84c97..dc768dc8c 100644 --- a/jni/bootimgtools/hexpatch.c +++ b/jni/bootimgtools/hexpatch.c @@ -1,14 +1,4 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bootimg.h" +#include "magiskboot.h" static int hex2int(char c) { int first = c / 16 - 3; @@ -35,7 +25,7 @@ static void hexstr2str(char *hex, unsigned char *str) { } } -int hexpatch(char * image, char *from, char *to) { +void hexpatch(char * image, char *from, char *to) { int fd = open(image, O_RDWR), patternsize = strlen(from) / 2, patchsize = strlen(to) / 2; size_t filesize = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); @@ -57,5 +47,4 @@ int hexpatch(char * image, char *from, char *to) { free(pattern); free(patch); close(fd); - return 0; -} \ No newline at end of file +} diff --git a/jni/bootimgtools/magiskboot.h b/jni/bootimgtools/magiskboot.h new file mode 100644 index 000000000..99d68cd64 --- /dev/null +++ b/jni/bootimgtools/magiskboot.h @@ -0,0 +1,62 @@ +#ifndef _ARCHIVE_H_ +#define _ARCHIVE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bootimg.h" + +#define windowBits 15 +#define ZLIB_GZIP 16 +#define memLevel 8 +#define CHUNK 0x40000 + +#define CHROMEOS_MAGIC "CHROMEOS" +#define CHROMEOS_MAGIC_SIZE 8 + +#define KERNEL_FILE "kernel" +#define RAMDISK_FILE "ramdisk.cpio" +#define SECOND_FILE "second" +#define DTB_FILE "dtb" + +typedef enum { + DONTCARE, + CHROMEOS, + AOSP, + ELF, + GZIP, + LZOP, + XZ, + LZMA, + BZIP2, + LZ4, + QCDT, +} file_t; + +// Global variables +extern unsigned char *base, *kernel, *ramdisk, *second, *dtb; +extern boot_img_hdr hdr; +extern file_t boot_type, ramdisk_type, dtb_type; +extern int mtk_kernel, mtk_ramdisk; + +// Main entries +void unpack(const char *image); +void repack(const char *image); +void hexpatch(char *image, char *from, char *to); +void error(int rc, const char *msg, ...); + +// Parse image +void parse_img(unsigned char *orig, size_t size); + +// Compressions +void gzip(int dec, const char* filename, unsigned char* buf, size_t size); + +#endif diff --git a/jni/bootimgtools/main.c b/jni/bootimgtools/main.c index 9eed10c86..ba24a3cf1 100644 --- a/jni/bootimgtools/main.c +++ b/jni/bootimgtools/main.c @@ -3,7 +3,7 @@ #include #include -#include "bootimg.h" +#include "magiskboot.h" /******************** Patch Boot Image diff --git a/jni/bootimgtools/parseimg.c b/jni/bootimgtools/parseimg.c index 4230c2fe9..9790a6787 100644 --- a/jni/bootimgtools/parseimg.c +++ b/jni/bootimgtools/parseimg.c @@ -6,8 +6,14 @@ #include "bootimg.h" #include "elf.h" +#include "magiskboot.h" -void print_header() { +unsigned char *base, *kernel, *ramdisk, *second, *dtb; +boot_img_hdr hdr; +int mtk_kernel = 0, mtk_ramdisk = 0; +file_t boot_type, ramdisk_type, dtb_type; + +static void check_headers() { printf("KERNEL [%d] @ 0x%08x\n", hdr.kernel_size, hdr.kernel_addr); printf("RAMDISK [%d] @ 0x%08x\n", hdr.ramdisk_size, hdr.ramdisk_addr); printf("SECOND [%d] @ 0x%08x\n", hdr.second_size, hdr.second_addr); @@ -30,6 +36,58 @@ void print_header() { } printf("NAME [%s]\n", hdr.name); printf("CMDLINE [%s]\n", hdr.cmdline); + + // Check compression + if (memcmp(ramdisk, "\x1f\x8b\x08\x00", 4) == 0) { + // gzip header + printf("COMPRESSION [gzip]\n"); + ramdisk_type = GZIP; + } else if (memcmp(ramdisk, "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a", 9) == 0) { + // lzop header + printf("COMPRESSION [lzop]\n"); + ramdisk_type = LZOP; + } else if (memcmp(ramdisk, "\xfd""7zXZ\x00", 6) == 0) { + // xz header + printf("COMPRESSION [xz]\n"); + ramdisk_type = XZ; + } else if (memcmp(ramdisk, "\x5d\x00\x00", 3) == 0 + && (ramdisk[12] == (unsigned char) '\xff' || ramdisk[12] == (unsigned char) '\x00')) { + // lzma header + printf("COMPRESSION [lzma]\n"); + ramdisk_type = LZMA; + } else if (memcmp(ramdisk, "BZh", 3) == 0) { + // bzip2 header + printf("COMPRESSION [bzip2]\n"); + ramdisk_type = BZIP2; + } else if ( ( memcmp(ramdisk, "\x04\x22\x4d\x18", 4) == 0 + || memcmp(ramdisk, "\x03\x21\x4c\x18", 4) == 0) + || memcmp(ramdisk, "\x02\x21\x4c\x18", 4) == 0) { + // lz4 header + printf("COMPRESSION [lz4]\n"); + ramdisk_type = LZ4; + + } else { + error(1, "Unknown ramdisk format!"); + } + + // Check MTK + if (memcmp(kernel, "\x88\x16\x88\x58", 4) == 0) { + printf("MTK header found in kernel\n"); + mtk_kernel = 1; + } + if (memcmp(ramdisk, "\x88\x16\x88\x58", 4) == 0) { + printf("MTK header found in ramdisk\n"); + mtk_kernel = 1; + } + + // Check dtb + if (boot_type == ELF && hdr.dt_size) { + if (memcmp(dtb, "QCDT", 4) == 0) { + dtb_type = QCDT; + } else if (memcmp(dtb, ELF_MAGIC, ELF_MAGIC_SIZE) == 0) { + dtb_type = ELF; + } + } } static void page_align(unsigned char **pos) { @@ -120,7 +178,7 @@ static void elf_set(int i, size_t size, size_t offset, size_t addr) { } } -void parse_elf() { +static void parse_elf() { // Reset boot image header memset(&hdr, 0, sizeof(hdr)); @@ -191,10 +249,10 @@ void parse_elf() { error(1, "ELF format error!"); } - print_header(); + check_headers(); } -void parse_aosp() { +static void parse_aosp() { printf("IMG [AOSP]\n"); @@ -228,5 +286,21 @@ void parse_aosp() { page_align(&pos); } - print_header(); -} \ No newline at end of file + check_headers(); +} + +void parse_img(unsigned char *orig, size_t size) { + for(base = orig; base < (orig + size); base += 256) { + if (memcmp(base, CHROMEOS_MAGIC, CHROMEOS_MAGIC_SIZE) == 0) { + boot_type = CHROMEOS; + } else if (memcmp(base, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) { + if (boot_type != CHROMEOS) boot_type = AOSP; + parse_aosp(); + break; + } else if (memcmp(base, ELF_MAGIC, ELF_MAGIC_SIZE) == 0) { + boot_type = ELF; + parse_elf(); + break; + } + } +} diff --git a/jni/bootimgtools/repack.c b/jni/bootimgtools/repack.c index 09cf5a737..c2267148a 100644 --- a/jni/bootimgtools/repack.c +++ b/jni/bootimgtools/repack.c @@ -1,18 +1,6 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "magiskboot.h" -#include "bootimg.h" -#include "elf.h" - -// Global pointer of current positions +// Global pointer of output static int ofd, opos; static size_t restore(const char *filename) { @@ -49,34 +37,24 @@ static void page_align() { lseek(ofd, 0, SEEK_END); } -int repack(const char* image) { - // Load original boot - int ifd = open(image, O_RDONLY), ret = -1; +void repack(const char* image) { + // Load original image + int ifd = open(image, O_RDONLY); if (ifd < 0) error(1, "Cannot open %s", image); - size_t isize = lseek(ifd, 0, SEEK_END); lseek(ifd, 0, SEEK_SET); unsigned char *orig = mmap(NULL, isize, PROT_READ, MAP_SHARED, ifd, 0); + // Parse original image + parse_img(orig, isize); + // Create new boot image - unlink("new-boot.img"); - ofd = open("new-boot.img", O_RDWR | O_CREAT, 0644); + ofd = open("new-boot.img", O_RDWR | O_CREAT | O_TRUNC, 0644); - // Parse images - for(base = orig; base < (orig + isize); base += 256) { - if (memcmp(base, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) { - parse_aosp(); - break; - } else if (memcmp(base, ELF_MAGIC, ELF_MAGIC_SIZE) == 0) { - parse_elf(); - break; - } - } - - printf("\n"); - - char *name; + char name[PATH_MAX]; + #define EXT_NUM 6 + char *ext_list[EXT_NUM] = { "gz", "lzo", "xz", "lzma", "bz2", "lz4" }; // Set all sizes to 0 hdr.kernel_size = 0; @@ -90,52 +68,87 @@ int repack(const char* image) { opos += hdr.page_size; // Restore kernel - if (memcmp(kernel, "\x88\x16\x88\x58", 4) == 0) { - printf("Dumping MTK header back to kernel\n"); + if (mtk_kernel) { restore_buf(512, kernel); hdr.kernel_size += 512; } - hdr.kernel_size += restore("kernel"); + hdr.kernel_size += restore(KERNEL_FILE); page_align(); - // Dump ramdisk - if (memcmp(ramdisk, "\x88\x16\x88\x58", 4) == 0) { - printf("Dumping MTK header back to ramdisk\n"); + // Restore ramdisk + if (mtk_ramdisk) { restore_buf(512, ramdisk); 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"; + + if (access(RAMDISK_FILE, R_OK) == 0) { + // If we found raw cpio, recompress to original format + int rfd = open(RAMDISK_FILE, O_RDONLY); + if (rfd < 0) + error(1, "Cannot open " RAMDISK_FILE); + + size_t cpio_size = lseek(rfd, 0, SEEK_END); + lseek(rfd, 0, SEEK_SET); + unsigned char *cpio = mmap(NULL, cpio_size, PROT_READ, MAP_SHARED, rfd, 0); + + switch (ramdisk_type) { + case GZIP: + sprintf(name, "%s.%s", RAMDISK_FILE, "gz"); + gzip(0, name, cpio, cpio_size); + break; + case LZOP: + sprintf(name, "%s.%s", RAMDISK_FILE, "lzo"); + break; + case XZ: + sprintf(name, "%s.%s", RAMDISK_FILE, "xz"); + break; + case LZMA: + sprintf(name, "%s.%s", RAMDISK_FILE, "lzma"); + break; + case BZIP2: + sprintf(name, "%s.%s", RAMDISK_FILE, "bz2"); + break; + case LZ4: + sprintf(name, "%s.%s", RAMDISK_FILE, "lz4"); + break; + default: + // Never happens + break; + } + + printf("Re-compressed %s to %s\n", RAMDISK_FILE, name); + munmap(cpio, cpio_size); + close(rfd); } else { - error(1, "Ramdisk file doesn't exist!"); + // If no raw cpio found, find compressed ones + int found = 0; + for (int i = 0; i < EXT_NUM; ++i) { + sprintf(name, "%s.%s", RAMDISK_FILE, ext_list[i]); + if (access(name, R_OK) == 0) { + found = 1; + break; + } + } + if (!found) { + error(1, "No ramdisk exists!"); + } } + hdr.ramdisk_size += restore(name); page_align(); - // Dump second - if (access("second", R_OK) == 0) { - hdr.second_size += restore("second"); + // Restore second + if (access(SECOND_FILE, R_OK) == 0) { + hdr.second_size += restore(SECOND_FILE); page_align(); } - // Dump dtb - if (access("dtb", R_OK) == 0) { - hdr.dt_size += restore("dtb"); + // Restore dtb + if (access(DTB_FILE, R_OK) == 0) { + hdr.dt_size += restore(DTB_FILE); page_align(); } - print_header(); - // Write header back lseek(ofd, 0, SEEK_SET); write(ofd, &hdr, sizeof(hdr)); @@ -143,5 +156,7 @@ int repack(const char* image) { munmap(orig, isize); close(ifd); close(ofd); - return ret; + if (opos > isize) { + error(2, "Boot partition too small!"); + } } diff --git a/jni/bootimgtools/unpack.c b/jni/bootimgtools/unpack.c index 996ed8646..8d7961055 100644 --- a/jni/bootimgtools/unpack.c +++ b/jni/bootimgtools/unpack.c @@ -1,20 +1,7 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bootimg.h" -#include "elf.h" +#include "magiskboot.h" static void dump(unsigned char *buf, 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 | O_TRUNC, 0644); if (ofd < 0) error(1, "Cannot open %s", filename); if (write(ofd, buf, size) != size) @@ -22,8 +9,8 @@ static void dump(unsigned char *buf, size_t size, const char *filename) { close(ofd); } -int unpack(const char* image) { - int fd = open(image, O_RDONLY), ret = 0; +void unpack(const char* image) { + int fd = open(image, O_RDONLY); if (fd < 0) error(1, "Cannot open %s", image); @@ -31,81 +18,75 @@ int unpack(const char* image) { lseek(fd, 0, SEEK_SET); unsigned char *orig = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - // Parse images - for(base = orig; base < (orig + size); base += 256) { - if (memcmp(base, CHROMEOS_MAGIC, CHROMEOS_MAGIC_SIZE) == 0) { - dump(base, 0, "chromeos"); - } else if (memcmp(base, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) { - parse_aosp(); - break; - } else if (memcmp(base, ELF_MAGIC, ELF_MAGIC_SIZE) == 0) { - parse_elf(); - break; - } + // Parse image + parse_img(orig, size); + + if (boot_type == CHROMEOS) { + // The caller should know it's chromeos, as it needs additional signing + dump(base, 0, "chromeos"); } - char name[PATH_MAX], *ext; + char name[PATH_MAX]; // Dump kernel - if (memcmp(kernel, "\x88\x16\x88\x58", 4) == 0) { - printf("MTK header found in kernel\n"); + if (mtk_kernel) { kernel += 512; hdr.kernel_size -= 512; } - dump(kernel, hdr.kernel_size, "kernel"); + dump(kernel, hdr.kernel_size, KERNEL_FILE); // Dump ramdisk - if (memcmp(ramdisk, "\x88\x16\x88\x58", 4) == 0) { - printf("MTK header found in ramdisk\n"); + if (mtk_ramdisk) { ramdisk += 512; hdr.ramdisk_size -= 512; } - // Compression detection - if (memcmp(ramdisk, "\x1f\x8b\x08\x00", 4) == 0) { - // gzip header - printf("COMPRESSION [gzip]\n"); - ext = "gz"; - } else if (memcmp(ramdisk, "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a", 9) == 0) { - // lzop header - printf("COMPRESSION [lzop]\n"); - ext = "lzo"; - } else if (memcmp(ramdisk, "\xfd""7zXZ\x00", 6) == 0) { - // xz header - printf("COMPRESSION [xz]\n"); - ext = "xz"; - } else if (memcmp(ramdisk, "\x5d\x00\x00", 3) == 0 - && (ramdisk[12] == (unsigned char) '\xff' || ramdisk[12] == (unsigned char) '\x00')) { - // lzma header - printf("COMPRESSION [lzma]\n"); - ext = "lzma"; - } else if (memcmp(ramdisk, "BZh", 3) == 0) { - // bzip2 header - printf("COMPRESSION [bzip2]\n"); - ext = "bz2"; - } else if ( ( memcmp(ramdisk, "\x04\x22\x4d\x18", 4) == 0 - || memcmp(ramdisk, "\x03\x21\x4c\x18", 4) == 0) - || memcmp(ramdisk, "\x02\x21\x4c\x18", 4) == 0) { - // lz4 header - printf("COMPRESSION [lz4]\n"); - ext = "lz4"; - } else { - error(1, "Unknown ramdisk format!"); + + switch (ramdisk_type) { + case GZIP: + sprintf(name, "%s.%s", RAMDISK_FILE, "gz"); + gzip(1, RAMDISK_FILE, ramdisk, hdr.ramdisk_size); + break; + case LZOP: + sprintf(name, "%s.%s", RAMDISK_FILE, "lzo"); + break; + case XZ: + sprintf(name, "%s.%s", RAMDISK_FILE, "xz"); + break; + case LZMA: + sprintf(name, "%s.%s", RAMDISK_FILE, "lzma"); + break; + case BZIP2: + sprintf(name, "%s.%s", RAMDISK_FILE, "bz2"); + break; + case LZ4: + sprintf(name, "%s.%s", RAMDISK_FILE, "lz4"); + break; + default: + // Never happens + break; } - sprintf(name, "%s.%s", "ramdisk", ext); + // Dump the compressed ramdisk, just in case dump(ramdisk, hdr.ramdisk_size, name); if (hdr.second_size) { // Dump second - dump(second, hdr.second_size, "second"); + dump(second, hdr.second_size, SECOND_FILE); } if (hdr.dt_size) { - // Dump dtb - dump(dtb, hdr.dt_size, "dtb"); + if (boot_type == ELF && (dtb_type != QCDT && dtb_type != ELF )) { + printf("Non QC dtb found in ELF kernel, recreate kernel\n"); + gzip(0, KERNEL_FILE, kernel, hdr.kernel_size); + int kfp = open(KERNEL_FILE, O_WRONLY | O_APPEND); + write(kfp, dtb, hdr.dt_size); + close(kfp); + } else { + // Dump dtb + dump(dtb, hdr.dt_size, DTB_FILE); + } } munmap(orig, size); close(fd); - return ret; }