diff --git a/jni/magiskboot/Android.mk b/jni/magiskboot/Android.mk index 9976c4fdc..6e32b577e 100644 --- a/jni/magiskboot/Android.mk +++ b/jni/magiskboot/Android.mk @@ -9,7 +9,7 @@ LOCAL_C_INCLUDES := \ jni/ndk-compression/lz4/lib/ \ jni/ndk-compression/bzip2/ -LOCAL_SRC_FILES := main.c unpack.c repack.c hexpatch.c parseimg.c compress.c utils.c +LOCAL_SRC_FILES := main.c unpack.c repack.c hexpatch.c parseimg.c compress.c utils.c cpio.c LOCAL_CFLAGS += -DZLIB_CONST include $(BUILD_EXECUTABLE) diff --git a/jni/magiskboot/cpio.c b/jni/magiskboot/cpio.c new file mode 100644 index 000000000..343d72b76 --- /dev/null +++ b/jni/magiskboot/cpio.c @@ -0,0 +1,158 @@ +#include "magiskboot.h" +#include "cpio.h" + +static uint32_t x8u(char *hex) { + uint32_t val, inpos = 8, outpos; + char pattern[6]; + + while (*hex == '0') { + hex++; + if (!--inpos) return 0; + } + // Because scanf gratuitously treats %*X differently than printf does. + sprintf(pattern, "%%%dx%%n", inpos); + sscanf(hex, pattern, &val, &outpos); + if (inpos != outpos) error(1, "bad cpio header"); + + return val; +} + +static void cpio_vec_insert(vector *v, cpio_file *n) { + cpio_file *f, *t; + int shift = 0; + // Insert in alphabet order + vec_for_each(v, f) { + if (shift) { + vec_entry(v)[_i] = t; + t = f; + continue; + } + t = f; + if (strcmp(f->filename, n->filename) == 0) { + // Replace, then all is done + vec_entry(v)[_i] = n; + return; + } else if (strcmp(f->filename, n->filename) > 0) { + // Insert, then start shifting + vec_entry(v)[_i] = n; + t = f; + shift = 1; + } + } + vec_push_back(v, t); +} + +// Parse cpio file to a vector of cpio_file +void parse_cpio(const char *filename, vector *v) { + printf("\nLoading cpio: [%s]\n\n", filename); + int fd = open(filename, O_RDONLY); + if (fd < 0) + error(1, "Cannot open %s", filename); + cpio_newc_header header; + cpio_file *f; + while(read(fd, &header, 110) == 110) { + f = calloc(sizeof(*f), 1); + // f->ino = x8u(header.ino); + f->mode = x8u(header.mode); + f->uid = x8u(header.uid); + f->gid = x8u(header.gid); + // f->nlink = x8u(header.nlink); + // f->mtime = x8u(header.mtime); + f->filesize = x8u(header.filesize); + // f->devmajor = x8u(header.devmajor); + // f->devminor = x8u(header.devminor); + // f->rdevmajor = x8u(header.rdevmajor); + // f->rdevminor = x8u(header.rdevminor); + f->namesize = x8u(header.namesize); + // f->check = x8u(header.check); + f->filename = malloc(f->namesize); + read(fd, f->filename, f->namesize); + file_align(fd, 4, 0); + if (f->filesize) { + f->data = malloc(f->filesize); + read(fd, f->data, f->filesize); + file_align(fd, 4, 0); + } + vec_push_back(v, f); + } + close(fd); +} + +void dump_cpio(const char *filename, vector *v) { + printf("\nDump cpio: [%s]\n\n", filename); + int fd = open_new(filename); + unsigned inode = 300000; + char header[111]; + cpio_file *f; + vec_for_each(v, f) { + if (f->remove) continue; + sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", + inode++, // f->ino + f->mode, + f->uid, + f->gid, + 1, // f->nlink + 0, // f->mtime + f->filesize, + 0, // f->devmajor + 0, // f->devminor + 0, // f->rdevmajor + 0, // f->rdevminor + f->namesize, + 0 // f->check + ); + write(fd, header, 110); + write(fd, f->filename, f->namesize); + file_align(fd, 4, 1); + if (f->filesize) { + write(fd, f->data, f->filesize); + file_align(fd, 4, 1); + } + } +} + +void cpio_vec_destroy(vector *v) { + // Free each cpio_file + cpio_file *f; + vec_for_each(v, f) { + free(f->filename); + free(f->data); + free(f); + } + vec_destroy(v); +} + +void cpio_rm(int recursive, const char *entry, vector *v) { + cpio_file *f; + vec_for_each(v, f) { + if ((recursive && strncmp(f->filename, entry, strlen(entry)) == 0) + || (strcmp(f->filename, entry) == 0) ) { + f->remove = 1; + } + } +} + +void cpio_mkdir(mode_t mode, const char *entry, vector *v) { + cpio_file *f = calloc(sizeof(*f), 1); + f->mode = S_IFDIR | mode; + f->namesize = strlen(entry) + 1; + f->filename = malloc(f->namesize); + memcpy(f->filename, entry, f->namesize); + cpio_vec_insert(v, f); +} + +void cpio_add(mode_t mode, const char *entry, const char *filename, vector *v) { + int fd = open(filename, O_RDONLY); + if (fd < 0) + error(1, "Cannot open %s", filename); + cpio_file *f = calloc(sizeof(*f), 1); + f->mode = S_IFREG | mode; + f->namesize = strlen(entry) + 1; + f->filename = malloc(f->namesize); + memcpy(f->filename, entry, f->namesize); + f->filesize = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + f->data = malloc(f->filesize); + read(fd, f->data, f->filesize); + cpio_vec_insert(v, f); +} diff --git a/jni/magiskboot/cpio.h b/jni/magiskboot/cpio.h new file mode 100644 index 000000000..32a6935f0 --- /dev/null +++ b/jni/magiskboot/cpio.h @@ -0,0 +1,42 @@ +#ifndef _CPIO_H_ +#define _CPIO_H_ + +#include + +typedef struct cpio_file { + // uint32_t ino; + uint32_t mode; + uint32_t uid; + uint32_t gid; + // uint32_t nlink; + // uint32_t mtime; + uint32_t filesize; + // uint32_t devmajor; + // uint32_t devminor; + // uint32_t rdevmajor; + // uint32_t rdevminor; + uint32_t namesize; + // uint32_t check; + char *filename; + void *data; + int remove; +} cpio_file; + +typedef struct cpio_newc_header { + char magic[6]; + char ino[8]; + char mode[8]; + char uid[8]; + char gid[8]; + char nlink[8]; + char mtime[8]; + char filesize[8]; + char devmajor[8]; + char devminor[8]; + char rdevmajor[8]; + char rdevminor[8]; + char namesize[8]; + char check[8]; +} cpio_newc_header; + +#endif diff --git a/jni/magiskboot/hexpatch.c b/jni/magiskboot/hexpatch.c index 5c89df2db..3186c8eb6 100644 --- a/jni/magiskboot/hexpatch.c +++ b/jni/magiskboot/hexpatch.c @@ -1,27 +1,11 @@ #include "magiskboot.h" -static int hex2int(char c) { - int first = c / 16 - 3; - int second = c % 16; - int result = first * 10 + second; - if(result > 9) result--; - return result; -} - -static unsigned hex2ascii(char c, char d) { - int high = hex2int(c) * 16; - int low = hex2int(d); - return high + low; -} - -static void hexstr2str(const char *hex, unsigned char *str) { - char buf = 0; - for(int i = 0, length = strlen(hex); i < length; ++i){ - if(i % 2){ - str[i / 2] = hex2ascii(buf, hex[i]); - } else{ - buf = hex[i]; - } +static void hex2byte(const char *hex, unsigned char *str) { + char high, low; + for (int i = 0, length = strlen(hex); i < length; i += 2) { + high = toupper(hex[i]) - '0'; + low = toupper(hex[i + 1]) - '0'; + str[i / 2] = ((high > 9 ? high - 7 : high) << 4) + (low > 9 ? low - 7 : low); } } @@ -32,8 +16,8 @@ void hexpatch(const char *image, const char *from, const char *to) { mmap_rw(image, &file, &filesize); pattern = malloc(patternsize); patch = malloc(patchsize); - hexstr2str(from, pattern); - hexstr2str(to, patch); + hex2byte(from, pattern); + hex2byte(to, patch); for (size_t i = 0; i < filesize - patternsize; ++i) { if (memcmp(file + i, pattern, patternsize) == 0) { printf("Pattern %s found!\nPatching to %s\n", from, to); diff --git a/jni/magiskboot/magiskboot.h b/jni/magiskboot/magiskboot.h index 591cd3bdd..81ee859ee 100644 --- a/jni/magiskboot/magiskboot.h +++ b/jni/magiskboot/magiskboot.h @@ -3,7 +3,9 @@ #include #include +#include #include +#include #include #include #include @@ -48,12 +50,37 @@ typedef enum { QCDT, } file_t; +typedef enum { + NONE, + RM, + MKDIR, + ADD +} command_t; + extern char *SUP_EXT_LIST[SUP_NUM]; extern file_t SUP_TYPE_LIST[SUP_NUM]; // Cannot declare in header, but place a copy here for convenience // char *SUP_EXT_LIST[SUP_NUM] = { "gz", "xz", "lzma", "bz2", "lz4" }; // file_t SUP_TYPE_LIST[SUP_NUM] = { GZIP, XZ, LZMA, BZIP2, LZ4 }; +// Vector +typedef struct vector { + size_t size; + size_t cap; + void **data; +} vector; +void vec_init(vector *v); +void vec_push_back(vector *v, void *p); +void vec_destroy(vector *v); + +#define vec_size(v) (v)->size +#define vec_cap(v) (v)->cap +#define vec_entry(v) (v)->data +// vec_for_each(vector *v, void *e) +#define vec_for_each(v, e) \ + e = (v)->data[0]; \ + for (size_t _i = 0; _i < (v)->size; ++_i, e = (v)->data[_i]) + // Global variables extern unsigned char *kernel, *ramdisk, *second, *dtb; extern boot_img_hdr hdr; @@ -62,9 +89,8 @@ extern int mtk_kernel, mtk_ramdisk; // Main entries void unpack(const char *image); -void repack(const char *image); +void repack(const char* orig_image, const char* out_image); void hexpatch(const char *image, const char *from, const char *to); -void cpio(const char *filename); void error(int rc, const char *msg, ...); void parse_img(unsigned char *orig, size_t size); void cleanup(); @@ -79,12 +105,20 @@ void comp_file(const char *method, const char *from); int decomp(file_t type, const char *to, const unsigned char *from, size_t size); void decomp_file(char *from); +// CPIO +void parse_cpio(const char *filename, vector *v); +void dump_cpio(const char *filename, vector *v); +void cpio_vec_destroy(vector *v); +void cpio_rm(int recursive, const char *entry, vector *v); +void cpio_mkdir(mode_t mode, const char *entry, vector *v); +void cpio_add(mode_t mode, const char *entry, const char *filename, vector *v); + // Utils void mmap_ro(const char *filename, unsigned char **buf, size_t *size); void mmap_rw(const char *filename, unsigned char **buf, size_t *size); file_t check_type(const unsigned char *buf); void mem_align(size_t *pos, size_t align); -void file_align(int fd, size_t align); +void file_align(int fd, size_t align, int out); int open_new(const char *filename); void print_info(); diff --git a/jni/magiskboot/main.c b/jni/magiskboot/main.c index 1c3e289bb..7d55c9699 100644 --- a/jni/magiskboot/main.c +++ b/jni/magiskboot/main.c @@ -6,22 +6,36 @@ static void usage(char *arg0) { fprintf(stderr, "\n"); - fprintf(stderr, "%s --unpack \n", arg0); - fprintf(stderr, " Unpack to kernel, ramdisk.cpio, (second), (dtb) into the\n current directory\n\n"); - fprintf(stderr, "%s --repack \n", arg0); + fprintf(stderr, "%s --unpack \n", arg0); + fprintf(stderr, " Unpack to kernel, ramdisk.cpio, (second), (dtb) into the\n current directory\n\n"); + + fprintf(stderr, "%s --repack [outbootimg]\n", arg0); fprintf(stderr, " Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory\n"); - fprintf(stderr, " to new-image.img. is the original boot image you've just unpacked.\n"); - fprintf(stderr, " If file ramdisk.cpio exists, it will auto re-compress with the same method\n"); - fprintf(stderr, " used in , or it will attempt to find ramdisk.cpio.[ext], and repack\n"); - fprintf(stderr, " directly with the compressed file\n\n"); + fprintf(stderr, " to [outbootimg], or new-boot.img if not specified.\n"); + fprintf(stderr, " It will compress ramdisk.cpio with the same method used in \n"); + fprintf(stderr, " if exists, or attempt to find ramdisk.cpio.[ext], and repack\n"); + fprintf(stderr, " directly with the compressed ramdisk file\n\n"); + fprintf(stderr, "%s --hexpatch \n", arg0); fprintf(stderr, " Search in , and replace with \n\n"); + + fprintf(stderr, "%s --cpio- [flags...] [params...]\n", arg0); + fprintf(stderr, " Do cpio related cmds to and output to \n Supported commands:\n"); + fprintf(stderr, " --cpio-rm [-r] \n Remove entry from cpio, flag -r to remove recursively\n"); + fprintf(stderr, " --cpio-mkdir \n Create directory as an \n"); + fprintf(stderr, " --cpio-add \n Add as an ; will replace if already exists\n"); + fprintf(stderr, " e.g: the command to add /tmp/tmpfile as entry init.magisk.rc:\n"); + fprintf(stderr, " %s --cpio-add 0750 init.magisk.rc /tmp/tmpfile\n\n", arg0); + fprintf(stderr, "%s --compress[=method] \n", arg0); fprintf(stderr, " Compress with [method], or gzip if not specified.\n Supported methods: " SUP_LIST "\n\n"); + fprintf(stderr, "%s --decompress \n", arg0); fprintf(stderr, " Auto check file type, and decompress accordingly\n Supported methods: " SUP_LIST "\n\n"); + fprintf(stderr, "%s --cleanup\n", arg0); fprintf(stderr, " Cleanup the current working directory\n\n"); + exit(1); } @@ -36,33 +50,67 @@ void error(int rc, const char *msg, ...) { int main(int argc, char *argv[]) { printf("MagiskBoot (by topjohnwu) - Boot Image Modification Tool\n"); - if (argc < 3) { - if (strcmp(argv[1], "--cleanup") == 0) { - cleanup(); - } else { - usage(argv[0]); - } - } else { - if (strcmp(argv[1], "--unpack") == 0) { - unpack(argv[2]); - } else if (strcmp(argv[1], "--repack") == 0) { - repack(argv[2]); - } else if (strcmp(argv[1], "--hexpatch") == 0) { - if (argc < 5) - usage(argv[0]); - hexpatch(argv[2], argv[3], argv[4]); - } else if (strcmp(argv[1], "--decompress") == 0) { - decomp_file(argv[2]); - } else if (strstr(argv[1], "--compress") != NULL) { - char *method; - method = strchr(argv[1], '='); - if (method == NULL) method = "gzip"; - else method++; - comp_file(method, argv[2]); - } else { - usage(argv[0]); - } - } - return 0; + if (argc > 1 && strcmp(argv[1], "--cleanup") == 0) { + cleanup(); + } else if (argc > 2 && strcmp(argv[1], "--unpack") == 0) { + unpack(argv[2]); + } else if (argc > 2 && strcmp(argv[1], "--repack") == 0) { + repack(argv[2], argc > 3 ? argv[3] : NEW_BOOT); + } else if (argc > 2 && strcmp(argv[1], "--decompress") == 0) { + decomp_file(argv[2]); + } else if (argc > 2 && strncmp(argv[1], "--compress", 10) == 0) { + char *method; + method = strchr(argv[1], '='); + if (method == NULL) method = "gzip"; + else method++; + comp_file(method, argv[2]); + } else if (argc > 4 && strcmp(argv[1], "--hexpatch") == 0) { + hexpatch(argv[2], argv[3], argv[4]); + } else if (argc > 4 && strncmp(argv[1], "--cpio", 6) == 0) { + int recursive = 0; + char *command; + command_t cmd; + command = strchr(argv[1] + 2, '-'); + if (command == NULL) usage(argv[0]); + else command++; + if (strcmp(command, "rm") == 0) { + cmd = RM; + if (argc > 5 && strcmp(argv[4], "-r") == 0) { + recursive = 1; + argv[4] = argv[5]; + argc--; + } + } else if (argc > 5 && strcmp(command, "mkdir") == 0) { + cmd = MKDIR; + } else if (argc > 6 && strcmp(command, "add") == 0) { + cmd = ADD; + } else { + cmd = NONE; + usage(argv[0]); + } + vector v; + vec_init(&v); + parse_cpio(argv[2], &v); + switch(cmd) { + case RM: + cpio_rm(recursive, argv[4], &v); + break; + case MKDIR: + cpio_mkdir(strtoul(argv[4], NULL, 8), argv[5], &v); + break; + case ADD: + cpio_add(strtoul(argv[4], NULL, 8), argv[5], argv[6], &v); + break; + default: + // Never happen + break; + } + dump_cpio(argv[3], &v); + cpio_vec_destroy(&v); + } else { + usage(argv[0]); + } + + return 0; } diff --git a/jni/magiskboot/repack.c b/jni/magiskboot/repack.c index 5d834255f..b2ce58165 100644 --- a/jni/magiskboot/repack.c +++ b/jni/magiskboot/repack.c @@ -20,20 +20,20 @@ static void restore_buf(const void *buf, size_t size, int fd) { } } -void repack(const char* image) { +void repack(const char* orig_image, const char* out_image) { size_t size; unsigned char *orig; char name[PATH_MAX]; // Load original image - mmap_ro(image, &orig, &size); + mmap_ro(orig_image, &orig, &size); // Parse original image - printf("\nParsing boot image: [%s]\n\n", image); + printf("\nParsing boot image: [%s]\n\n", orig_image); parse_img(orig, size); // Create new image - int fd = open_new(NEW_BOOT); + int fd = open_new(out_image); // Set all sizes to 0 hdr.kernel_size = 0; @@ -51,7 +51,7 @@ void repack(const char* image) { hdr.kernel_size += 512; } hdr.kernel_size += restore(KERNEL_FILE, fd); - file_align(fd, hdr.page_size); + file_align(fd, hdr.page_size, 1); // Restore ramdisk if (mtk_ramdisk) { @@ -90,22 +90,22 @@ void repack(const char* image) { if (!found) error(1, "No ramdisk exists!"); hdr.ramdisk_size += restore(name, fd); - file_align(fd, hdr.page_size); + file_align(fd, hdr.page_size, 1); // Restore second if (access(SECOND_FILE, R_OK) == 0) { hdr.second_size += restore(SECOND_FILE, fd); - file_align(fd, hdr.page_size); + file_align(fd, hdr.page_size, 1); } // Restore dtb if (access(DTB_FILE, R_OK) == 0) { hdr.dt_size += restore(DTB_FILE, fd); - file_align(fd, hdr.page_size); + file_align(fd, hdr.page_size, 1); } // Write header back - printf("\nRepack to boot image: [%s]\n\n", NEW_BOOT); + printf("\nRepack to boot image: [%s]\n\n", out_image); print_info(); lseek(fd, 0, SEEK_SET); write(fd, &hdr, sizeof(hdr)); diff --git a/jni/magiskboot/utils.c b/jni/magiskboot/utils.c index 1c20ff1f1..8532b8861 100644 --- a/jni/magiskboot/utils.c +++ b/jni/magiskboot/utils.c @@ -62,13 +62,15 @@ void mem_align(size_t *pos, size_t align) { } } -void file_align(int fd, size_t align) { +void file_align(int fd, size_t align, int out) { size_t pos = lseek(fd, 0, SEEK_CUR); size_t mask = align - 1; if (pos & mask) { pos += align - (pos & mask); - ftruncate(fd, pos); - lseek(fd, 0, SEEK_END); + if (out) { + ftruncate(fd, pos); + } + lseek(fd, pos, SEEK_SET); } } @@ -138,3 +140,27 @@ void cleanup() { unlink(name); } } + +void vec_init(vector *v) { + vec_size(v) = 0; + vec_cap(v) = 1; + vec_entry(v) = malloc(sizeof(void*)); +} + +void vec_push_back(vector *v, void *p) { + if (v == NULL) return; + if (vec_size(v) == vec_cap(v)) { + vec_cap(v) *= 2; + vec_entry(v) = realloc(vec_entry(v), sizeof(void*) * vec_cap(v)); + } + vec_entry(v)[vec_size(v)] = p; + ++vec_size(v); +} + +void vec_destroy(vector *v) { + // Will not free each entry! + // Manually free each entry, then call this function + vec_size(v) = 0; + vec_cap(v) = 0; + free(v->data); +}