Boot IMG tools complete re-write

Fix #27, Fix #35, Fix #68, Fix #70, Fix #71, Fix #72, Fix #75, Fix #87
This commit is contained in:
topjohnwu 2017-02-24 07:45:48 +08:00
parent e11fb2c09e
commit ee2a30470a
3 changed files with 226 additions and 222 deletions

View File

@ -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

View File

@ -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);
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");
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 {
exit(1);
}
dump(ptr, 0, "ramdisk-mtk"); //Create an mtk flag
dump_ramdisk(ptr+512, size-512);
} 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;
}

View File

@ -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 {
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);
}
memcpy(buf+8, str, sizeof(str));
opos += size;
}
memset(buf+8+sizeof(str), 0xff, 512-8-sizeof(str));
static void in_page_align(uint32_t pagesize) {
uint32_t itemsize = ipos - ibase, pagemask = pagesize - 1L;
if (itemsize & pagemask) {
ipos += pagesize - (itemsize & pagemask);
}
}
pwrite(ofd, buf, sizeof(buf), pos);
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);
}
return append_file(ofd, "ramdisk.gz", pos + 512) + 512;
} else if(access("ramdisk.gz", R_OK) == 0) {
return append_file(ofd, "ramdisk.gz", pos);
static int aosp() {
printf("AOSP Boot Image Detected\n");
char *name;
struct boot_img_hdr hdr, ihdr;
// Read the original header
memcpy(&ihdr, ibase, sizeof(ihdr));
hdr = ihdr;
// Set all sizes to 0
hdr.kernel_size = 0;
hdr.ramdisk_size = 0;
hdr.second_size = 0;
hdr.dt_size = 0;
// 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 {
return append_file(ofd, "ramdisk", pos);
}
fprintf(stderr, "Ramdisk file doesn't exist!\n");
return 1;
}
hdr.ramdisk_size += dump(name);
out_page_align(hdr.page_size);
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");
exit(1);
}
//Round up the file size
ftruncate(ofd, pos);
}
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;
}
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));
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?
int pos = hdr->page_size;
int size = 0;
size = append_file(ofd, "kernel", pos);
pos += size + hdr->page_size - 1;
pos &= ~(hdr->page_size-1);
hdr->kernel_size = size;
size = append_ramdisk(ofd, pos);
pos += size + hdr->page_size - 1;
pos &= ~(hdr->page_size-1);
hdr->ramdisk_size = size;
// Dump second
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;
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;
}