mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-12-11 02:12:20 +00:00
Restructure native codebase
This commit is contained in:
775
native/jni/boot/bootimg.cpp
Normal file
775
native/jni/boot/bootimg.cpp
Normal file
@@ -0,0 +1,775 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <mincrypt/sha.h>
|
||||
#include <mincrypt/sha256.h>
|
||||
#include <base.hpp>
|
||||
|
||||
#include "bootimg.hpp"
|
||||
#include "magiskboot.hpp"
|
||||
#include "compress.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
uint32_t dyn_img_hdr::j32 = 0;
|
||||
uint64_t dyn_img_hdr::j64 = 0;
|
||||
|
||||
#define PADDING 15
|
||||
|
||||
static void decompress(format_t type, int fd, const void *in, size_t size) {
|
||||
auto ptr = get_decoder(type, make_unique<fd_stream>(fd));
|
||||
ptr->write(in, size, true);
|
||||
}
|
||||
|
||||
static off_t compress(format_t type, int fd, const void *in, size_t size) {
|
||||
auto prev = lseek(fd, 0, SEEK_CUR);
|
||||
{
|
||||
auto strm = get_encoder(type, make_unique<fd_stream>(fd));
|
||||
strm->write(in, size, true);
|
||||
}
|
||||
auto now = lseek(fd, 0, SEEK_CUR);
|
||||
return now - prev;
|
||||
}
|
||||
|
||||
static void dump(void *buf, size_t size, const char *filename) {
|
||||
if (size == 0)
|
||||
return;
|
||||
int fd = creat(filename, 0644);
|
||||
xwrite(fd, buf, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static size_t restore(int fd, const char *filename) {
|
||||
int ifd = xopen(filename, O_RDONLY);
|
||||
size_t size = lseek(ifd, 0, SEEK_END);
|
||||
lseek(ifd, 0, SEEK_SET);
|
||||
xsendfile(fd, ifd, nullptr, size);
|
||||
close(ifd);
|
||||
return size;
|
||||
}
|
||||
|
||||
void dyn_img_hdr::print() {
|
||||
uint32_t ver = header_version();
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "HEADER_VER", ver);
|
||||
if (!is_vendor)
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "KERNEL_SZ", kernel_size());
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "RAMDISK_SZ", ramdisk_size());
|
||||
if (ver < 3)
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "SECOND_SZ", second_size());
|
||||
if (ver == 0)
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "EXTRA_SZ", extra_size());
|
||||
if (ver == 1 || ver == 2)
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "RECOV_DTBO_SZ", recovery_dtbo_size());
|
||||
if (ver == 2 || is_vendor)
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "DTB_SZ", dtb_size());
|
||||
|
||||
if (uint32_t os_ver = os_version()) {
|
||||
int a,b,c,y,m = 0;
|
||||
int version = os_ver >> 11;
|
||||
int patch_level = os_ver & 0x7ff;
|
||||
|
||||
a = (version >> 14) & 0x7f;
|
||||
b = (version >> 7) & 0x7f;
|
||||
c = version & 0x7f;
|
||||
fprintf(stderr, "%-*s [%d.%d.%d]\n", PADDING, "OS_VERSION", a, b, c);
|
||||
|
||||
y = (patch_level >> 4) + 2000;
|
||||
m = patch_level & 0xf;
|
||||
fprintf(stderr, "%-*s [%d-%02d]\n", PADDING, "OS_PATCH_LEVEL", y, m);
|
||||
}
|
||||
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "PAGESIZE", page_size());
|
||||
if (char *n = name()) {
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "NAME", n);
|
||||
}
|
||||
fprintf(stderr, "%-*s [%.*s%.*s]\n", PADDING, "CMDLINE",
|
||||
BOOT_ARGS_SIZE, cmdline(), BOOT_EXTRA_ARGS_SIZE, extra_cmdline());
|
||||
if (char *checksum = id()) {
|
||||
fprintf(stderr, "%-*s [", PADDING, "CHECKSUM");
|
||||
for (int i = 0; i < SHA256_DIGEST_SIZE; ++i)
|
||||
fprintf(stderr, "%02hhx", checksum[i]);
|
||||
fprintf(stderr, "]\n");
|
||||
}
|
||||
}
|
||||
|
||||
void dyn_img_hdr::dump_hdr_file() {
|
||||
FILE *fp = xfopen(HEADER_FILE, "w");
|
||||
if (name())
|
||||
fprintf(fp, "name=%s\n", name());
|
||||
fprintf(fp, "cmdline=%.*s%.*s\n", BOOT_ARGS_SIZE, cmdline(), BOOT_EXTRA_ARGS_SIZE, extra_cmdline());
|
||||
uint32_t ver = os_version();
|
||||
if (ver) {
|
||||
int a, b, c, y, m;
|
||||
int version, patch_level;
|
||||
version = ver >> 11;
|
||||
patch_level = ver & 0x7ff;
|
||||
|
||||
a = (version >> 14) & 0x7f;
|
||||
b = (version >> 7) & 0x7f;
|
||||
c = version & 0x7f;
|
||||
fprintf(fp, "os_version=%d.%d.%d\n", a, b, c);
|
||||
|
||||
y = (patch_level >> 4) + 2000;
|
||||
m = patch_level & 0xf;
|
||||
fprintf(fp, "os_patch_level=%d-%02d\n", y, m);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void dyn_img_hdr::load_hdr_file() {
|
||||
parse_prop_file(HEADER_FILE, [=](string_view key, string_view value) -> bool {
|
||||
if (key == "name" && name()) {
|
||||
memset(name(), 0, 16);
|
||||
memcpy(name(), value.data(), value.length() > 15 ? 15 : value.length());
|
||||
} else if (key == "cmdline") {
|
||||
memset(cmdline(), 0, BOOT_ARGS_SIZE);
|
||||
memset(extra_cmdline(), 0, BOOT_EXTRA_ARGS_SIZE);
|
||||
if (value.length() > BOOT_ARGS_SIZE) {
|
||||
memcpy(cmdline(), value.data(), BOOT_ARGS_SIZE);
|
||||
auto len = std::min(value.length() - BOOT_ARGS_SIZE, (size_t) BOOT_EXTRA_ARGS_SIZE);
|
||||
memcpy(extra_cmdline(), &value[BOOT_ARGS_SIZE], len);
|
||||
} else {
|
||||
memcpy(cmdline(), value.data(), value.length());
|
||||
}
|
||||
} else if (key == "os_version") {
|
||||
int patch_level = os_version() & 0x7ff;
|
||||
int a, b, c;
|
||||
sscanf(value.data(), "%d.%d.%d", &a, &b, &c);
|
||||
os_version() = (((a << 14) | (b << 7) | c) << 11) | patch_level;
|
||||
} else if (key == "os_patch_level") {
|
||||
int os_ver = os_version() >> 11;
|
||||
int y, m;
|
||||
sscanf(value.data(), "%d-%d", &y, &m);
|
||||
y -= 2000;
|
||||
os_version() = (os_ver << 11) | (y << 4) | m;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
boot_img::boot_img(const char *image) : map(image) {
|
||||
fprintf(stderr, "Parsing boot image: [%s]\n", image);
|
||||
for (uint8_t *addr = map.buf; addr < map.buf + map.sz; ++addr) {
|
||||
format_t fmt = check_fmt(addr, map.sz);
|
||||
switch (fmt) {
|
||||
case CHROMEOS:
|
||||
// chromeos require external signing
|
||||
flags[CHROMEOS_FLAG] = true;
|
||||
addr += 65535;
|
||||
break;
|
||||
case DHTB:
|
||||
flags[DHTB_FLAG] = true;
|
||||
flags[SEANDROID_FLAG] = true;
|
||||
fprintf(stderr, "DHTB_HDR\n");
|
||||
addr += sizeof(dhtb_hdr) - 1;
|
||||
break;
|
||||
case BLOB:
|
||||
flags[BLOB_FLAG] = true;
|
||||
fprintf(stderr, "TEGRA_BLOB\n");
|
||||
addr += sizeof(blob_hdr) - 1;
|
||||
break;
|
||||
case AOSP:
|
||||
case AOSP_VENDOR:
|
||||
parse_image(addr, fmt);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
boot_img::~boot_img() {
|
||||
delete hdr;
|
||||
}
|
||||
|
||||
static int find_dtb_offset(uint8_t *buf, unsigned sz) {
|
||||
uint8_t * const end = buf + sz;
|
||||
|
||||
for (uint8_t *curr = buf; curr < end; curr += sizeof(fdt_header)) {
|
||||
curr = static_cast<uint8_t*>(memmem(curr, end - curr, DTB_MAGIC, sizeof(fdt32_t)));
|
||||
if (curr == nullptr)
|
||||
return -1;
|
||||
|
||||
auto fdt_hdr = reinterpret_cast<fdt_header *>(curr);
|
||||
|
||||
// Check that fdt_header.totalsize does not overflow kernel image size
|
||||
uint32_t totalsize = fdt32_to_cpu(fdt_hdr->totalsize);
|
||||
if (totalsize > end - curr)
|
||||
continue;
|
||||
|
||||
// Check that fdt_header.off_dt_struct does not overflow kernel image size
|
||||
uint32_t off_dt_struct = fdt32_to_cpu(fdt_hdr->off_dt_struct);
|
||||
if (off_dt_struct > end - curr)
|
||||
continue;
|
||||
|
||||
// Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE
|
||||
auto fdt_node_hdr = reinterpret_cast<fdt_node_header *>(curr + off_dt_struct);
|
||||
if (fdt32_to_cpu(fdt_node_hdr->tag) != FDT_BEGIN_NODE)
|
||||
continue;
|
||||
|
||||
return curr - buf;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static format_t check_fmt_lg(uint8_t *buf, unsigned sz) {
|
||||
format_t fmt = check_fmt(buf, sz);
|
||||
if (fmt == LZ4_LEGACY) {
|
||||
// We need to check if it is LZ4_LG
|
||||
uint32_t off = 4;
|
||||
uint32_t block_sz;
|
||||
while (off + sizeof(block_sz) <= sz) {
|
||||
memcpy(&block_sz, buf + off, sizeof(block_sz));
|
||||
off += sizeof(block_sz);
|
||||
if (off + block_sz > sz)
|
||||
return LZ4_LG;
|
||||
off += block_sz;
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
#define CMD_MATCH(s) BUFFER_MATCH(h->cmdline, s)
|
||||
|
||||
dyn_img_hdr *boot_img::create_hdr(uint8_t *addr, format_t type) {
|
||||
if (type == AOSP_VENDOR) {
|
||||
fprintf(stderr, "VENDOR_BOOT_HDR\n");
|
||||
auto h = reinterpret_cast<boot_img_hdr_vnd_v3*>(addr);
|
||||
hdr_addr = addr;
|
||||
switch (h->header_version) {
|
||||
case 4:
|
||||
return new dyn_img_vnd_v4(addr);
|
||||
default:
|
||||
return new dyn_img_vnd_v3(addr);
|
||||
}
|
||||
}
|
||||
|
||||
auto h = reinterpret_cast<boot_img_hdr_v0*>(addr);
|
||||
|
||||
if (h->page_size >= 0x02000000) {
|
||||
fprintf(stderr, "PXA_BOOT_HDR\n");
|
||||
hdr_addr = addr;
|
||||
return new dyn_img_pxa(addr);
|
||||
}
|
||||
|
||||
if (CMD_MATCH(NOOKHD_RL_MAGIC) ||
|
||||
CMD_MATCH(NOOKHD_GL_MAGIC) ||
|
||||
CMD_MATCH(NOOKHD_GR_MAGIC) ||
|
||||
CMD_MATCH(NOOKHD_EB_MAGIC) ||
|
||||
CMD_MATCH(NOOKHD_ER_MAGIC)) {
|
||||
flags[NOOKHD_FLAG] = true;
|
||||
fprintf(stderr, "NOOKHD_LOADER\n");
|
||||
addr += NOOKHD_PRE_HEADER_SZ;
|
||||
} else if (memcmp(h->name, ACCLAIM_MAGIC, 10) == 0) {
|
||||
flags[ACCLAIM_FLAG] = true;
|
||||
fprintf(stderr, "ACCLAIM_LOADER\n");
|
||||
addr += ACCLAIM_PRE_HEADER_SZ;
|
||||
}
|
||||
|
||||
// addr could be adjusted
|
||||
h = reinterpret_cast<boot_img_hdr_v0*>(addr);
|
||||
hdr_addr = addr;
|
||||
|
||||
switch (h->header_version) {
|
||||
case 1:
|
||||
return new dyn_img_v1(addr);
|
||||
case 2:
|
||||
return new dyn_img_v2(addr);
|
||||
case 3:
|
||||
return new dyn_img_v3(addr);
|
||||
case 4:
|
||||
return new dyn_img_v4(addr);
|
||||
default:
|
||||
return new dyn_img_v0(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#define get_block(name) \
|
||||
name = hdr_addr + off; \
|
||||
off += hdr->name##_size(); \
|
||||
off = align_to(off, hdr->page_size());
|
||||
|
||||
#define get_ignore(name) \
|
||||
if (hdr->name##_size()) { \
|
||||
auto blk_sz = align_to(hdr->name##_size(), hdr->page_size()); \
|
||||
ignore_size += blk_sz; \
|
||||
off += blk_sz; \
|
||||
}
|
||||
|
||||
void boot_img::parse_image(uint8_t *addr, format_t type) {
|
||||
hdr = create_hdr(addr, type);
|
||||
|
||||
if (char *id = hdr->id()) {
|
||||
for (int i = SHA_DIGEST_SIZE + 4; i < SHA256_DIGEST_SIZE; ++i) {
|
||||
if (id[i]) {
|
||||
flags[SHA256_FLAG] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hdr->print();
|
||||
|
||||
size_t off = hdr->hdr_space();
|
||||
get_block(kernel);
|
||||
get_block(ramdisk);
|
||||
get_block(second);
|
||||
get_block(extra);
|
||||
get_block(recovery_dtbo);
|
||||
get_block(dtb);
|
||||
|
||||
ignore = hdr_addr + off;
|
||||
get_ignore(signature)
|
||||
get_ignore(vendor_ramdisk_table)
|
||||
get_ignore(bootconfig)
|
||||
|
||||
if (auto size = hdr->kernel_size()) {
|
||||
if (int dtb_off = find_dtb_offset(kernel, size); dtb_off > 0) {
|
||||
kernel_dtb = kernel + dtb_off;
|
||||
hdr->kernel_dt_size = size - dtb_off;
|
||||
hdr->kernel_size() = dtb_off;
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "KERNEL_DTB_SZ", hdr->kernel_dt_size);
|
||||
}
|
||||
|
||||
k_fmt = check_fmt_lg(kernel, hdr->kernel_size());
|
||||
if (k_fmt == MTK) {
|
||||
fprintf(stderr, "MTK_KERNEL_HDR\n");
|
||||
flags[MTK_KERNEL] = true;
|
||||
k_hdr = reinterpret_cast<mtk_hdr *>(kernel);
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "SIZE", k_hdr->size);
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "NAME", k_hdr->name);
|
||||
kernel += sizeof(mtk_hdr);
|
||||
hdr->kernel_size() -= sizeof(mtk_hdr);
|
||||
k_fmt = check_fmt_lg(kernel, hdr->kernel_size());
|
||||
}
|
||||
if (k_fmt == ZIMAGE) {
|
||||
z_hdr = reinterpret_cast<zimage_hdr *>(kernel);
|
||||
if (void *gzip_offset = memmem(kernel, hdr->kernel_size(), GZIP1_MAGIC "\x08\x00", 4)) {
|
||||
fprintf(stderr, "ZIMAGE_KERNEL\n");
|
||||
z_info.hdr_sz = (uint8_t *) gzip_offset - kernel;
|
||||
|
||||
// Find end of piggy
|
||||
uint32_t zImage_size = z_hdr->end - z_hdr->start;
|
||||
uint32_t piggy_end = zImage_size;
|
||||
uint32_t offsets[16];
|
||||
memcpy(offsets, kernel + zImage_size - sizeof(offsets), sizeof(offsets));
|
||||
for (int i = 15; i >= 0; --i) {
|
||||
if (offsets[i] > (zImage_size - 0xFF) && offsets[i] < zImage_size) {
|
||||
piggy_end = offsets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (piggy_end == zImage_size) {
|
||||
fprintf(stderr, "! Could not find end of zImage piggy, keeping raw kernel\n");
|
||||
} else {
|
||||
flags[ZIMAGE_KERNEL] = true;
|
||||
z_info.tail = kernel + piggy_end;
|
||||
z_info.tail_sz = hdr->kernel_size() - piggy_end;
|
||||
kernel += z_info.hdr_sz;
|
||||
hdr->kernel_size() = piggy_end - z_info.hdr_sz;
|
||||
k_fmt = check_fmt_lg(kernel, hdr->kernel_size());
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "! Could not find zImage gzip piggy, keeping raw kernel\n");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name[k_fmt]);
|
||||
}
|
||||
if (auto size = hdr->ramdisk_size()) {
|
||||
if (hdr->is_vendor && hdr->header_version() >= 4) {
|
||||
// v4 vendor boot contains multiple ramdisks
|
||||
// Do not try to mess with it for now
|
||||
r_fmt = UNKNOWN;
|
||||
} else {
|
||||
r_fmt = check_fmt_lg(ramdisk, size);
|
||||
}
|
||||
if (r_fmt == MTK) {
|
||||
fprintf(stderr, "MTK_RAMDISK_HDR\n");
|
||||
flags[MTK_RAMDISK] = true;
|
||||
r_hdr = reinterpret_cast<mtk_hdr *>(ramdisk);
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "SIZE", r_hdr->size);
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "NAME", r_hdr->name);
|
||||
ramdisk += sizeof(mtk_hdr);
|
||||
hdr->ramdisk_size() -= sizeof(mtk_hdr);
|
||||
r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size());
|
||||
}
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name[r_fmt]);
|
||||
}
|
||||
if (auto size = hdr->extra_size()) {
|
||||
e_fmt = check_fmt_lg(extra, size);
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "EXTRA_FMT", fmt2name[e_fmt]);
|
||||
}
|
||||
|
||||
if (addr + off < map.buf + map.sz) {
|
||||
tail = addr + off;
|
||||
tail_size = map.buf + map.sz - tail;
|
||||
|
||||
// Check special flags
|
||||
if (tail_size >= 16 && BUFFER_MATCH(tail, SEANDROID_MAGIC)) {
|
||||
fprintf(stderr, "SAMSUNG_SEANDROID\n");
|
||||
flags[SEANDROID_FLAG] = true;
|
||||
} else if (tail_size >= 16 && BUFFER_MATCH(tail, LG_BUMP_MAGIC)) {
|
||||
fprintf(stderr, "LG_BUMP_IMAGE\n");
|
||||
flags[LG_BUMP_FLAG] = true;
|
||||
}
|
||||
|
||||
// Find AVB structures
|
||||
void *meta = memmem(tail, tail_size, AVB_MAGIC, AVB_MAGIC_LEN);
|
||||
if (meta) {
|
||||
// Double check if footer exists
|
||||
void *footer = tail + tail_size - sizeof(AvbFooter);
|
||||
if (BUFFER_MATCH(footer, AVB_FOOTER_MAGIC)) {
|
||||
fprintf(stderr, "VBMETA\n");
|
||||
flags[AVB_FLAG] = true;
|
||||
avb_meta = reinterpret_cast<AvbVBMetaImageHeader*>(meta);
|
||||
avb_footer = reinterpret_cast<AvbFooter*>(footer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int split_image_dtb(const char *filename) {
|
||||
auto img = mmap_data(filename);
|
||||
|
||||
if (int off = find_dtb_offset(img.buf, img.sz); off > 0) {
|
||||
format_t fmt = check_fmt_lg(img.buf, img.sz);
|
||||
if (COMPRESSED(fmt)) {
|
||||
int fd = creat(KERNEL_FILE, 0644);
|
||||
decompress(fmt, fd, img.buf, off);
|
||||
close(fd);
|
||||
} else {
|
||||
dump(img.buf, off, KERNEL_FILE);
|
||||
}
|
||||
dump(img.buf + off, img.sz - off, KER_DTB_FILE);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Cannot find DTB in %s\n", filename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int unpack(const char *image, bool skip_decomp, bool hdr) {
|
||||
boot_img boot(image);
|
||||
|
||||
if (hdr)
|
||||
boot.hdr->dump_hdr_file();
|
||||
|
||||
// Dump kernel
|
||||
if (!skip_decomp && COMPRESSED(boot.k_fmt)) {
|
||||
int fd = creat(KERNEL_FILE, 0644);
|
||||
decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size());
|
||||
close(fd);
|
||||
} else {
|
||||
dump(boot.kernel, boot.hdr->kernel_size(), KERNEL_FILE);
|
||||
}
|
||||
|
||||
// Dump kernel_dtb
|
||||
dump(boot.kernel_dtb, boot.hdr->kernel_dt_size, KER_DTB_FILE);
|
||||
|
||||
// Dump ramdisk
|
||||
if (!skip_decomp && COMPRESSED(boot.r_fmt)) {
|
||||
int fd = creat(RAMDISK_FILE, 0644);
|
||||
decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size());
|
||||
close(fd);
|
||||
} else {
|
||||
dump(boot.ramdisk, boot.hdr->ramdisk_size(), RAMDISK_FILE);
|
||||
}
|
||||
|
||||
// Dump second
|
||||
dump(boot.second, boot.hdr->second_size(), SECOND_FILE);
|
||||
|
||||
// Dump extra
|
||||
if (!skip_decomp && COMPRESSED(boot.e_fmt)) {
|
||||
int fd = creat(EXTRA_FILE, 0644);
|
||||
decompress(boot.e_fmt, fd, boot.extra, boot.hdr->extra_size());
|
||||
close(fd);
|
||||
} else {
|
||||
dump(boot.extra, boot.hdr->extra_size(), EXTRA_FILE);
|
||||
}
|
||||
|
||||
// Dump recovery_dtbo
|
||||
dump(boot.recovery_dtbo, boot.hdr->recovery_dtbo_size(), RECV_DTBO_FILE);
|
||||
|
||||
// Dump dtb
|
||||
dump(boot.dtb, boot.hdr->dtb_size(), DTB_FILE);
|
||||
|
||||
return boot.flags[CHROMEOS_FLAG] ? 2 : 0;
|
||||
}
|
||||
|
||||
#define file_align_with(page_size) \
|
||||
write_zero(fd, align_padding(lseek(fd, 0, SEEK_CUR) - off.header, page_size))
|
||||
|
||||
#define file_align() file_align_with(boot.hdr->page_size())
|
||||
|
||||
void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
const boot_img boot(src_img);
|
||||
fprintf(stderr, "Repack to boot image: [%s]\n", out_img);
|
||||
|
||||
struct {
|
||||
uint32_t header;
|
||||
uint32_t kernel;
|
||||
uint32_t ramdisk;
|
||||
uint32_t second;
|
||||
uint32_t extra;
|
||||
uint32_t dtb;
|
||||
uint32_t total;
|
||||
uint32_t vbmeta;
|
||||
} off{};
|
||||
|
||||
// Create a new boot header and reset sizes
|
||||
auto hdr = boot.hdr->clone();
|
||||
hdr->kernel_size() = 0;
|
||||
hdr->ramdisk_size() = 0;
|
||||
hdr->second_size() = 0;
|
||||
hdr->dtb_size() = 0;
|
||||
hdr->kernel_dt_size = 0;
|
||||
|
||||
if (access(HEADER_FILE, R_OK) == 0)
|
||||
hdr->load_hdr_file();
|
||||
|
||||
/***************
|
||||
* Write blocks
|
||||
***************/
|
||||
|
||||
// Create new image
|
||||
int fd = creat(out_img, 0644);
|
||||
|
||||
if (boot.flags[DHTB_FLAG]) {
|
||||
// Skip DHTB header
|
||||
write_zero(fd, sizeof(dhtb_hdr));
|
||||
} else if (boot.flags[BLOB_FLAG]) {
|
||||
xwrite(fd, boot.map.buf, sizeof(blob_hdr));
|
||||
} else if (boot.flags[NOOKHD_FLAG]) {
|
||||
xwrite(fd, boot.map.buf, NOOKHD_PRE_HEADER_SZ);
|
||||
} else if (boot.flags[ACCLAIM_FLAG]) {
|
||||
xwrite(fd, boot.map.buf, ACCLAIM_PRE_HEADER_SZ);
|
||||
}
|
||||
|
||||
// Copy raw header
|
||||
off.header = lseek(fd, 0, SEEK_CUR);
|
||||
xwrite(fd, boot.hdr_addr, hdr->hdr_space());
|
||||
|
||||
// kernel
|
||||
off.kernel = lseek(fd, 0, SEEK_CUR);
|
||||
if (boot.flags[MTK_KERNEL]) {
|
||||
// Copy MTK headers
|
||||
xwrite(fd, boot.k_hdr, sizeof(mtk_hdr));
|
||||
}
|
||||
if (boot.flags[ZIMAGE_KERNEL]) {
|
||||
// Copy zImage headers
|
||||
xwrite(fd, boot.z_hdr, boot.z_info.hdr_sz);
|
||||
}
|
||||
if (access(KERNEL_FILE, R_OK) == 0) {
|
||||
auto m = mmap_data(KERNEL_FILE);
|
||||
if (!COMPRESSED_ANY(check_fmt(m.buf, m.sz)) && COMPRESSED(boot.k_fmt)) {
|
||||
// Always use zopfli for zImage compression
|
||||
auto fmt = (boot.flags[ZIMAGE_KERNEL] && boot.k_fmt == GZIP) ? ZOPFLI : boot.k_fmt;
|
||||
hdr->kernel_size() = compress(fmt, fd, m.buf, m.sz);
|
||||
} else {
|
||||
hdr->kernel_size() = xwrite(fd, m.buf, m.sz);
|
||||
}
|
||||
|
||||
if (boot.flags[ZIMAGE_KERNEL]) {
|
||||
if (hdr->kernel_size() > boot.hdr->kernel_size()) {
|
||||
fprintf(stderr, "! Recompressed kernel is too large, using original kernel\n");
|
||||
ftruncate64(fd, lseek64(fd, - (off64_t) hdr->kernel_size(), SEEK_CUR));
|
||||
xwrite(fd, boot.kernel, boot.hdr->kernel_size());
|
||||
} else {
|
||||
// Pad zeros to make sure the zImage file size does not change
|
||||
// Also ensure the last 4 bytes are the uncompressed vmlinux size
|
||||
uint32_t sz = m.sz;
|
||||
write_zero(fd, boot.hdr->kernel_size() - hdr->kernel_size() - sizeof(sz));
|
||||
xwrite(fd, &sz, sizeof(sz));
|
||||
}
|
||||
|
||||
// zImage size shall remain the same
|
||||
hdr->kernel_size() = boot.hdr->kernel_size();
|
||||
}
|
||||
}
|
||||
if (boot.flags[ZIMAGE_KERNEL]) {
|
||||
// Copy zImage tail and adjust size accordingly
|
||||
hdr->kernel_size() += boot.z_info.hdr_sz;
|
||||
hdr->kernel_size() += xwrite(fd, boot.z_info.tail, boot.z_info.tail_sz);
|
||||
}
|
||||
|
||||
// kernel dtb
|
||||
if (access(KER_DTB_FILE, R_OK) == 0)
|
||||
hdr->kernel_size() += restore(fd, KER_DTB_FILE);
|
||||
file_align();
|
||||
|
||||
// ramdisk
|
||||
off.ramdisk = lseek(fd, 0, SEEK_CUR);
|
||||
if (boot.flags[MTK_RAMDISK]) {
|
||||
// Copy MTK headers
|
||||
xwrite(fd, boot.r_hdr, sizeof(mtk_hdr));
|
||||
}
|
||||
if (access(RAMDISK_FILE, R_OK) == 0) {
|
||||
auto m = mmap_data(RAMDISK_FILE);
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf, m.sz)) && COMPRESSED(boot.r_fmt)) {
|
||||
hdr->ramdisk_size() = compress(boot.r_fmt, fd, m.buf, m.sz);
|
||||
} else {
|
||||
hdr->ramdisk_size() = xwrite(fd, m.buf, m.sz);
|
||||
}
|
||||
file_align();
|
||||
}
|
||||
|
||||
// second
|
||||
off.second = lseek(fd, 0, SEEK_CUR);
|
||||
if (access(SECOND_FILE, R_OK) == 0) {
|
||||
hdr->second_size() = restore(fd, SECOND_FILE);
|
||||
file_align();
|
||||
}
|
||||
|
||||
// extra
|
||||
off.extra = lseek(fd, 0, SEEK_CUR);
|
||||
if (access(EXTRA_FILE, R_OK) == 0) {
|
||||
auto m = mmap_data(EXTRA_FILE);
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf, m.sz)) && COMPRESSED(boot.e_fmt)) {
|
||||
hdr->extra_size() = compress(boot.e_fmt, fd, m.buf, m.sz);
|
||||
} else {
|
||||
hdr->extra_size() = xwrite(fd, m.buf, m.sz);
|
||||
}
|
||||
file_align();
|
||||
}
|
||||
|
||||
// recovery_dtbo
|
||||
if (access(RECV_DTBO_FILE, R_OK) == 0) {
|
||||
hdr->recovery_dtbo_offset() = lseek(fd, 0, SEEK_CUR);
|
||||
hdr->recovery_dtbo_size() = restore(fd, RECV_DTBO_FILE);
|
||||
file_align();
|
||||
}
|
||||
|
||||
// dtb
|
||||
off.dtb = lseek(fd, 0, SEEK_CUR);
|
||||
if (access(DTB_FILE, R_OK) == 0) {
|
||||
hdr->dtb_size() = restore(fd, DTB_FILE);
|
||||
file_align();
|
||||
}
|
||||
|
||||
// Directly copy ignored blobs
|
||||
if (boot.ignore_size) {
|
||||
// ignore_size should already be aligned
|
||||
xwrite(fd, boot.ignore, boot.ignore_size);
|
||||
}
|
||||
|
||||
// Proprietary stuffs
|
||||
if (boot.flags[SEANDROID_FLAG]) {
|
||||
xwrite(fd, SEANDROID_MAGIC, 16);
|
||||
if (boot.flags[DHTB_FLAG]) {
|
||||
xwrite(fd, "\xFF\xFF\xFF\xFF", 4);
|
||||
}
|
||||
} else if (boot.flags[LG_BUMP_FLAG]) {
|
||||
xwrite(fd, LG_BUMP_MAGIC, 16);
|
||||
}
|
||||
|
||||
off.total = lseek(fd, 0, SEEK_CUR);
|
||||
file_align();
|
||||
|
||||
// vbmeta
|
||||
if (boot.flags[AVB_FLAG]) {
|
||||
// According to avbtool.py, if the input is not an Android sparse image
|
||||
// (which boot images are not), the default block size is 4096
|
||||
file_align_with(4096);
|
||||
off.vbmeta = lseek(fd, 0, SEEK_CUR);
|
||||
uint64_t vbmeta_size = __builtin_bswap64(boot.avb_footer->vbmeta_size);
|
||||
xwrite(fd, boot.avb_meta, vbmeta_size);
|
||||
}
|
||||
|
||||
// Pad image to original size if not chromeos (as it requires post processing)
|
||||
if (!boot.flags[CHROMEOS_FLAG]) {
|
||||
off_t current = lseek(fd, 0, SEEK_CUR);
|
||||
if (current < boot.map.sz) {
|
||||
write_zero(fd, boot.map.sz - current);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
/******************
|
||||
* Patch the image
|
||||
******************/
|
||||
|
||||
// Map output image as rw
|
||||
auto out = mmap_data(out_img, true);
|
||||
|
||||
// MTK headers
|
||||
if (boot.flags[MTK_KERNEL]) {
|
||||
auto m_hdr = reinterpret_cast<mtk_hdr *>(out.buf + off.kernel);
|
||||
m_hdr->size = hdr->kernel_size();
|
||||
hdr->kernel_size() += sizeof(mtk_hdr);
|
||||
}
|
||||
if (boot.flags[MTK_RAMDISK]) {
|
||||
auto m_hdr = reinterpret_cast<mtk_hdr *>(out.buf + off.ramdisk);
|
||||
m_hdr->size = hdr->ramdisk_size();
|
||||
hdr->ramdisk_size() += sizeof(mtk_hdr);
|
||||
}
|
||||
|
||||
// Make sure header size matches
|
||||
hdr->header_size() = hdr->hdr_size();
|
||||
|
||||
// Update checksum
|
||||
if (char *id = hdr->id()) {
|
||||
HASH_CTX ctx;
|
||||
boot.flags[SHA256_FLAG] ? SHA256_init(&ctx) : SHA_init(&ctx);
|
||||
uint32_t size = hdr->kernel_size();
|
||||
HASH_update(&ctx, out.buf + off.kernel, size);
|
||||
HASH_update(&ctx, &size, sizeof(size));
|
||||
size = hdr->ramdisk_size();
|
||||
HASH_update(&ctx, out.buf + off.ramdisk, size);
|
||||
HASH_update(&ctx, &size, sizeof(size));
|
||||
size = hdr->second_size();
|
||||
HASH_update(&ctx, out.buf + off.second, size);
|
||||
HASH_update(&ctx, &size, sizeof(size));
|
||||
size = hdr->extra_size();
|
||||
if (size) {
|
||||
HASH_update(&ctx, out.buf + off.extra, size);
|
||||
HASH_update(&ctx, &size, sizeof(size));
|
||||
}
|
||||
uint32_t ver = hdr->header_version();
|
||||
if (ver == 1 || ver == 2) {
|
||||
size = hdr->recovery_dtbo_size();
|
||||
HASH_update(&ctx, out.buf + hdr->recovery_dtbo_offset(), size);
|
||||
HASH_update(&ctx, &size, sizeof(size));
|
||||
}
|
||||
if (ver == 2) {
|
||||
size = hdr->dtb_size();
|
||||
HASH_update(&ctx, out.buf + off.dtb, size);
|
||||
HASH_update(&ctx, &size, sizeof(size));
|
||||
}
|
||||
memset(id, 0, BOOT_ID_SIZE);
|
||||
memcpy(id, HASH_final(&ctx), boot.flags[SHA256_FLAG] ? SHA256_DIGEST_SIZE : SHA_DIGEST_SIZE);
|
||||
}
|
||||
|
||||
// Print new header info
|
||||
hdr->print();
|
||||
|
||||
// Copy main header
|
||||
memcpy(out.buf + off.header, hdr->raw_hdr(), hdr->hdr_size());
|
||||
|
||||
if (boot.flags[AVB_FLAG]) {
|
||||
// Copy and patch AVB structures
|
||||
auto footer = reinterpret_cast<AvbFooter*>(out.buf + out.sz - sizeof(AvbFooter));
|
||||
auto vbmeta = reinterpret_cast<AvbVBMetaImageHeader*>(out.buf + off.vbmeta);
|
||||
memcpy(footer, boot.avb_footer, sizeof(AvbFooter));
|
||||
footer->original_image_size = __builtin_bswap64(off.total);
|
||||
footer->vbmeta_offset = __builtin_bswap64(off.vbmeta);
|
||||
if (check_env("PATCHVBMETAFLAG")) {
|
||||
vbmeta->flags = __builtin_bswap32(3);
|
||||
}
|
||||
}
|
||||
|
||||
if (boot.flags[DHTB_FLAG]) {
|
||||
// DHTB header
|
||||
auto d_hdr = reinterpret_cast<dhtb_hdr *>(out.buf);
|
||||
memcpy(d_hdr, DHTB_MAGIC, 8);
|
||||
d_hdr->size = off.total - sizeof(dhtb_hdr);
|
||||
SHA256_hash(out.buf + sizeof(dhtb_hdr), d_hdr->size, d_hdr->checksum);
|
||||
} else if (boot.flags[BLOB_FLAG]) {
|
||||
// Blob header
|
||||
auto b_hdr = reinterpret_cast<blob_hdr *>(out.buf);
|
||||
b_hdr->size = off.total - sizeof(blob_hdr);
|
||||
}
|
||||
}
|
||||
631
native/jni/boot/bootimg.hpp
Normal file
631
native/jni/boot/bootimg.hpp
Normal file
@@ -0,0 +1,631 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <utility>
|
||||
#include <bitset>
|
||||
#include "format.hpp"
|
||||
|
||||
/******************
|
||||
* Special Headers
|
||||
*****************/
|
||||
|
||||
struct mtk_hdr {
|
||||
uint32_t magic; /* MTK magic */
|
||||
uint32_t size; /* Size of the content */
|
||||
char name[32]; /* The type of the header */
|
||||
|
||||
char padding[472]; /* Padding to 512 bytes */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dhtb_hdr {
|
||||
char magic[8]; /* DHTB magic */
|
||||
uint8_t checksum[40]; /* Payload SHA256, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */
|
||||
uint32_t size; /* Payload size, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */
|
||||
|
||||
char padding[460]; /* Padding to 512 bytes */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct blob_hdr {
|
||||
char secure_magic[20]; /* "-SIGNED-BY-SIGNBLOB-" */
|
||||
uint32_t datalen; /* 0x00000000 */
|
||||
uint32_t signature; /* 0x00000000 */
|
||||
char magic[16]; /* "MSM-RADIO-UPDATE" */
|
||||
uint32_t hdr_version; /* 0x00010000 */
|
||||
uint32_t hdr_size; /* Size of header */
|
||||
uint32_t part_offset; /* Same as size */
|
||||
uint32_t num_parts; /* Number of partitions */
|
||||
uint32_t unknown[7]; /* All 0x00000000 */
|
||||
char name[4]; /* Name of partition */
|
||||
uint32_t offset; /* offset in blob where this partition starts */
|
||||
uint32_t size; /* Size of data */
|
||||
uint32_t version; /* 0x00000001 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct zimage_hdr {
|
||||
uint32_t code[9];
|
||||
uint32_t magic; /* zImage magic */
|
||||
uint32_t start; /* absolute load/run zImage address */
|
||||
uint32_t end; /* zImage end address */
|
||||
uint32_t endian; /* endianess flag */
|
||||
// There could be more fields, but we don't care
|
||||
} __attribute__((packed));
|
||||
|
||||
/**************
|
||||
* AVB Headers
|
||||
**************/
|
||||
|
||||
#define AVB_FOOTER_MAGIC_LEN 4
|
||||
#define AVB_MAGIC_LEN 4
|
||||
#define AVB_RELEASE_STRING_SIZE 48
|
||||
|
||||
// https://android.googlesource.com/platform/external/avb/+/refs/heads/android11-release/libavb/avb_footer.h
|
||||
struct AvbFooter {
|
||||
uint8_t magic[AVB_FOOTER_MAGIC_LEN];
|
||||
uint32_t version_major;
|
||||
uint32_t version_minor;
|
||||
uint64_t original_image_size;
|
||||
uint64_t vbmeta_offset;
|
||||
uint64_t vbmeta_size;
|
||||
uint8_t reserved[28];
|
||||
} __attribute__((packed));
|
||||
|
||||
// https://android.googlesource.com/platform/external/avb/+/refs/heads/android11-release/libavb/avb_vbmeta_image.h
|
||||
struct AvbVBMetaImageHeader {
|
||||
uint8_t magic[AVB_MAGIC_LEN];
|
||||
uint32_t required_libavb_version_major;
|
||||
uint32_t required_libavb_version_minor;
|
||||
uint64_t authentication_data_block_size;
|
||||
uint64_t auxiliary_data_block_size;
|
||||
uint32_t algorithm_type;
|
||||
uint64_t hash_offset;
|
||||
uint64_t hash_size;
|
||||
uint64_t signature_offset;
|
||||
uint64_t signature_size;
|
||||
uint64_t public_key_offset;
|
||||
uint64_t public_key_size;
|
||||
uint64_t public_key_metadata_offset;
|
||||
uint64_t public_key_metadata_size;
|
||||
uint64_t descriptors_offset;
|
||||
uint64_t descriptors_size;
|
||||
uint64_t rollback_index;
|
||||
uint32_t flags;
|
||||
uint32_t rollback_index_location;
|
||||
uint8_t release_string[AVB_RELEASE_STRING_SIZE];
|
||||
uint8_t reserved[80];
|
||||
} __attribute__((packed));
|
||||
|
||||
/*********************
|
||||
* Boot Image Headers
|
||||
*********************/
|
||||
|
||||
// https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/android12-release/include/bootimg/bootimg.h
|
||||
|
||||
#define BOOT_MAGIC_SIZE 8
|
||||
#define BOOT_NAME_SIZE 16
|
||||
#define BOOT_ID_SIZE 32
|
||||
#define BOOT_ARGS_SIZE 512
|
||||
#define BOOT_EXTRA_ARGS_SIZE 1024
|
||||
#define VENDOR_BOOT_ARGS_SIZE 2048
|
||||
#define VENDOR_RAMDISK_NAME_SIZE 32
|
||||
#define VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE 16
|
||||
|
||||
/* When the boot image header has a version of 0 - 2, the structure of the boot
|
||||
* image is as follows:
|
||||
*
|
||||
* +-----------------+
|
||||
* | boot header | 1 page
|
||||
* +-----------------+
|
||||
* | kernel | n pages
|
||||
* +-----------------+
|
||||
* | ramdisk | m pages
|
||||
* +-----------------+
|
||||
* | second stage | o pages
|
||||
* +-----------------+
|
||||
* | extra blob | x pages (non standard)
|
||||
* +-----------------+
|
||||
* | recovery dtbo | p pages
|
||||
* +-----------------+
|
||||
* | dtb | q 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 = (recovery_dtbo_size + page_size - 1) / page_size
|
||||
* q = (dtb_size + page_size - 1) / page_size
|
||||
* x = (extra_size + page_size - 1) / page_size
|
||||
*/
|
||||
|
||||
struct boot_img_hdr_v0_common {
|
||||
char magic[BOOT_MAGIC_SIZE];
|
||||
|
||||
uint32_t kernel_size; /* size in bytes */
|
||||
uint32_t kernel_addr; /* physical load addr */
|
||||
|
||||
uint32_t ramdisk_size; /* size in bytes */
|
||||
uint32_t ramdisk_addr; /* physical load addr */
|
||||
|
||||
uint32_t second_size; /* size in bytes */
|
||||
uint32_t second_addr; /* physical load addr */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct boot_img_hdr_v0 : public boot_img_hdr_v0_common {
|
||||
uint32_t tags_addr; /* physical addr for kernel tags */
|
||||
|
||||
// In AOSP headers, this field is used for page size.
|
||||
// For Samsung PXA headers, the use of this field is unknown;
|
||||
// however, its value is something unrealistic to be treated as page size.
|
||||
// We use this fact to determine whether this is an AOSP or PXA header.
|
||||
union {
|
||||
uint32_t unknown;
|
||||
uint32_t page_size; /* flash page size we assume */
|
||||
};
|
||||
|
||||
// In header v1, this field is used for header version
|
||||
// However, on some devices like Samsung, this field is used to store DTB
|
||||
// We treat this field differently based on its value
|
||||
union {
|
||||
uint32_t header_version; /* the version of the header */
|
||||
uint32_t extra_size; /* extra blob size in bytes */
|
||||
};
|
||||
|
||||
// Operating system version and security patch level.
|
||||
// For version "A.B.C" and patch level "Y-M-D":
|
||||
// (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M)
|
||||
// os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0]
|
||||
uint32_t os_version;
|
||||
|
||||
char name[BOOT_NAME_SIZE]; /* asciiz product name */
|
||||
char cmdline[BOOT_ARGS_SIZE];
|
||||
char id[BOOT_ID_SIZE]; /* timestamp / checksum / sha1 / etc */
|
||||
|
||||
// Supplemental command line data; kept here to maintain
|
||||
// binary compatibility with older versions of mkbootimg.
|
||||
char extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
|
||||
uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO/ACPIO image */
|
||||
uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
|
||||
uint32_t header_size;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct boot_img_hdr_v2 : public boot_img_hdr_v1 {
|
||||
uint32_t dtb_size; /* size in bytes for DTB image */
|
||||
uint64_t dtb_addr; /* physical load address for DTB image */
|
||||
} __attribute__((packed));
|
||||
|
||||
// Special Samsung header
|
||||
struct boot_img_hdr_pxa : public boot_img_hdr_v0_common {
|
||||
uint32_t extra_size; /* extra blob size in bytes */
|
||||
uint32_t unknown;
|
||||
uint32_t tags_addr; /* physical addr for kernel tags */
|
||||
uint32_t page_size; /* flash page size we assume */
|
||||
|
||||
char name[24]; /* asciiz product name */
|
||||
char cmdline[BOOT_ARGS_SIZE];
|
||||
char id[BOOT_ID_SIZE]; /* timestamp / checksum / sha1 / etc */
|
||||
|
||||
char extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* When the boot image header has a version of 3 - 4, the structure of the boot
|
||||
* image is as follows:
|
||||
*
|
||||
* +---------------------+
|
||||
* | boot header | 4096 bytes
|
||||
* +---------------------+
|
||||
* | kernel | m pages
|
||||
* +---------------------+
|
||||
* | ramdisk | n pages
|
||||
* +---------------------+
|
||||
* | boot signature | g pages
|
||||
* +---------------------+
|
||||
*
|
||||
* m = (kernel_size + 4096 - 1) / 4096
|
||||
* n = (ramdisk_size + 4096 - 1) / 4096
|
||||
* g = (signature_size + 4096 - 1) / 4096
|
||||
*
|
||||
* Page size is fixed at 4096 bytes.
|
||||
*
|
||||
* The structure of the vendor boot image is as follows:
|
||||
*
|
||||
* +------------------------+
|
||||
* | vendor boot header | o pages
|
||||
* +------------------------+
|
||||
* | vendor ramdisk section | p pages
|
||||
* +------------------------+
|
||||
* | dtb | q pages
|
||||
* +------------------------+
|
||||
* | vendor ramdisk table | r pages
|
||||
* +------------------------+
|
||||
* | bootconfig | s pages
|
||||
* +------------------------+
|
||||
*
|
||||
* o = (2128 + page_size - 1) / page_size
|
||||
* p = (vendor_ramdisk_size + page_size - 1) / page_size
|
||||
* q = (dtb_size + page_size - 1) / page_size
|
||||
* r = (vendor_ramdisk_table_size + page_size - 1) / page_size
|
||||
* s = (vendor_bootconfig_size + page_size - 1) / page_size
|
||||
*
|
||||
* Note that in version 4 of the vendor boot image, multiple vendor ramdisks can
|
||||
* be included in the vendor boot image. The bootloader can select a subset of
|
||||
* ramdisks to load at runtime. To help the bootloader select the ramdisks, each
|
||||
* ramdisk is tagged with a type tag and a set of hardware identifiers
|
||||
* describing the board, soc or platform that this ramdisk is intended for.
|
||||
*
|
||||
* The vendor ramdisk section is consist of multiple ramdisk images concatenated
|
||||
* one after another, and vendor_ramdisk_size is the size of the section, which
|
||||
* is the total size of all the ramdisks included in the vendor boot image.
|
||||
*
|
||||
* The vendor ramdisk table holds the size, offset, type, name and hardware
|
||||
* identifiers of each ramdisk. The type field denotes the type of its content.
|
||||
* The vendor ramdisk names are unique. The hardware identifiers are specified
|
||||
* in the board_id field in each table entry. The board_id field is consist of a
|
||||
* vector of unsigned integer words, and the encoding scheme is defined by the
|
||||
* hardware vendor.
|
||||
*
|
||||
* For the different type of ramdisks, there are:
|
||||
* - VENDOR_RAMDISK_TYPE_NONE indicates the value is unspecified.
|
||||
* - VENDOR_RAMDISK_TYPE_PLATFORM ramdisks contain platform specific bits, so
|
||||
* the bootloader should always load these into memory.
|
||||
* - VENDOR_RAMDISK_TYPE_RECOVERY ramdisks contain recovery resources, so
|
||||
* the bootloader should load these when booting into recovery.
|
||||
* - VENDOR_RAMDISK_TYPE_DLKM ramdisks contain dynamic loadable kernel
|
||||
* modules.
|
||||
*
|
||||
* Version 4 of the vendor boot image also adds a bootconfig section to the end
|
||||
* of the image. This section contains Boot Configuration parameters known at
|
||||
* build time. The bootloader is responsible for placing this section directly
|
||||
* after the generic ramdisk, followed by the bootconfig trailer, before
|
||||
* entering the kernel.
|
||||
*/
|
||||
|
||||
struct boot_img_hdr_v3 {
|
||||
uint8_t magic[BOOT_MAGIC_SIZE];
|
||||
|
||||
uint32_t kernel_size; /* size in bytes */
|
||||
uint32_t ramdisk_size; /* size in bytes */
|
||||
uint32_t os_version;
|
||||
uint32_t header_size;
|
||||
uint32_t reserved[4];
|
||||
|
||||
uint32_t header_version;
|
||||
|
||||
char cmdline[BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct boot_img_hdr_vnd_v3 {
|
||||
// Must be VENDOR_BOOT_MAGIC.
|
||||
uint8_t magic[BOOT_MAGIC_SIZE];
|
||||
// Version of the vendor boot image header.
|
||||
uint32_t header_version;
|
||||
uint32_t page_size; /* flash page size we assume */
|
||||
uint32_t kernel_addr; /* physical load addr */
|
||||
uint32_t ramdisk_addr; /* physical load addr */
|
||||
uint32_t ramdisk_size; /* size in bytes */
|
||||
char cmdline[VENDOR_BOOT_ARGS_SIZE];
|
||||
uint32_t tags_addr; /* physical addr for kernel tags (if required) */
|
||||
char name[BOOT_NAME_SIZE]; /* asciiz product name */
|
||||
uint32_t header_size;
|
||||
uint32_t dtb_size; /* size in bytes for DTB image */
|
||||
uint64_t dtb_addr; /* physical load address for DTB image */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct boot_img_hdr_v4 : public boot_img_hdr_v3 {
|
||||
uint32_t signature_size; /* size in bytes */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct boot_img_hdr_vnd_v4 : public boot_img_hdr_vnd_v3 {
|
||||
uint32_t vendor_ramdisk_table_size; /* size in bytes for the vendor ramdisk table */
|
||||
uint32_t vendor_ramdisk_table_entry_num; /* number of entries in the vendor ramdisk table */
|
||||
uint32_t vendor_ramdisk_table_entry_size; /* size in bytes for a vendor ramdisk table entry */
|
||||
uint32_t bootconfig_size; /* size in bytes for the bootconfig section */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct vendor_ramdisk_table_entry_v4 {
|
||||
uint32_t ramdisk_size; /* size in bytes for the ramdisk image */
|
||||
uint32_t ramdisk_offset; /* offset to the ramdisk image in vendor ramdisk section */
|
||||
uint32_t ramdisk_type; /* type of the ramdisk */
|
||||
uint8_t ramdisk_name[VENDOR_RAMDISK_NAME_SIZE]; /* asciiz ramdisk name */
|
||||
|
||||
// Hardware identifiers describing the board, soc or platform which this
|
||||
// ramdisk is intended to be loaded on.
|
||||
uint32_t board_id[VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
/*******************************
|
||||
* Polymorphic Universal Header
|
||||
*******************************/
|
||||
|
||||
#define decl_var(name, len) \
|
||||
virtual uint##len##_t &name() { j##len = 0; return j##len; }
|
||||
#define decl_val(name, type) \
|
||||
virtual type name() { return 0; }
|
||||
|
||||
struct dyn_img_hdr {
|
||||
|
||||
const bool is_vendor;
|
||||
|
||||
// Standard entries
|
||||
decl_var(kernel_size, 32)
|
||||
decl_var(ramdisk_size, 32)
|
||||
decl_var(second_size, 32)
|
||||
decl_val(page_size, uint32_t)
|
||||
decl_val(header_version, uint32_t)
|
||||
decl_var(extra_size, 32)
|
||||
decl_var(os_version, 32)
|
||||
decl_val(name, char *)
|
||||
decl_val(cmdline, char *)
|
||||
decl_val(id, char *)
|
||||
decl_val(extra_cmdline, char *)
|
||||
uint32_t kernel_dt_size = 0;
|
||||
|
||||
// v1/v2 specific
|
||||
decl_var(recovery_dtbo_size, 32)
|
||||
decl_var(recovery_dtbo_offset, 64)
|
||||
decl_var(header_size, 32)
|
||||
decl_var(dtb_size, 32)
|
||||
|
||||
// v4 specific
|
||||
decl_val(signature_size, uint32_t)
|
||||
decl_val(vendor_ramdisk_table_size, uint32_t)
|
||||
decl_val(bootconfig_size, uint32_t)
|
||||
|
||||
virtual ~dyn_img_hdr() {
|
||||
free(raw);
|
||||
}
|
||||
|
||||
virtual size_t hdr_size() = 0;
|
||||
virtual size_t hdr_space() { return page_size(); }
|
||||
virtual dyn_img_hdr *clone() = 0;
|
||||
|
||||
const void *raw_hdr() const { return raw; }
|
||||
void print();
|
||||
void dump_hdr_file();
|
||||
void load_hdr_file();
|
||||
|
||||
protected:
|
||||
union {
|
||||
boot_img_hdr_v2 *v2_hdr; /* AOSP v2 header */
|
||||
boot_img_hdr_v4 *v4_hdr; /* AOSP v4 header */
|
||||
boot_img_hdr_vnd_v4 *v4_vnd; /* AOSP vendor v4 header */
|
||||
boot_img_hdr_pxa *hdr_pxa; /* Samsung PXA header */
|
||||
void *raw; /* Raw pointer */
|
||||
};
|
||||
dyn_img_hdr(bool b) : is_vendor(b) {}
|
||||
|
||||
private:
|
||||
// Junk for references
|
||||
static uint32_t j32;
|
||||
static uint64_t j64;
|
||||
};
|
||||
|
||||
#undef decl_var
|
||||
#undef decl_val
|
||||
|
||||
#define __impl_cls(name, hdr) \
|
||||
protected: name() = default; \
|
||||
public: \
|
||||
name(void *ptr) { \
|
||||
raw = xmalloc(sizeof(hdr)); \
|
||||
memcpy(raw, ptr, sizeof(hdr)); \
|
||||
} \
|
||||
size_t hdr_size() override { \
|
||||
return sizeof(hdr); \
|
||||
} \
|
||||
dyn_img_hdr *clone() override { \
|
||||
auto p = new name(raw); \
|
||||
p->kernel_dt_size = kernel_dt_size; \
|
||||
return p; \
|
||||
};
|
||||
|
||||
#define __impl_val(name, hdr_name) \
|
||||
decltype(std::declval<dyn_img_hdr>().name()) name() override { return hdr_name->name; }
|
||||
|
||||
struct dyn_img_hdr_boot : public dyn_img_hdr {
|
||||
protected:
|
||||
dyn_img_hdr_boot() : dyn_img_hdr(false) {}
|
||||
};
|
||||
|
||||
#define impl_cls(ver) __impl_cls(dyn_img_##ver, boot_img_hdr_##ver)
|
||||
#define impl_val(name) __impl_val(name, v2_hdr)
|
||||
|
||||
struct dyn_img_common : public dyn_img_hdr_boot {
|
||||
impl_val(kernel_size)
|
||||
impl_val(ramdisk_size)
|
||||
impl_val(second_size)
|
||||
};
|
||||
|
||||
struct dyn_img_v0 : public dyn_img_common {
|
||||
impl_cls(v0)
|
||||
|
||||
impl_val(page_size)
|
||||
impl_val(extra_size)
|
||||
impl_val(os_version)
|
||||
impl_val(name)
|
||||
impl_val(cmdline)
|
||||
impl_val(id)
|
||||
impl_val(extra_cmdline)
|
||||
};
|
||||
|
||||
struct dyn_img_v1 : public dyn_img_v0 {
|
||||
impl_cls(v1)
|
||||
|
||||
impl_val(header_version)
|
||||
impl_val(recovery_dtbo_size)
|
||||
impl_val(recovery_dtbo_offset)
|
||||
impl_val(header_size)
|
||||
|
||||
uint32_t &extra_size() override { return dyn_img_hdr::extra_size(); }
|
||||
};
|
||||
|
||||
struct dyn_img_v2 : public dyn_img_v1 {
|
||||
impl_cls(v2)
|
||||
|
||||
impl_val(dtb_size)
|
||||
};
|
||||
|
||||
#undef impl_val
|
||||
#define impl_val(name) __impl_val(name, hdr_pxa)
|
||||
|
||||
struct dyn_img_pxa : public dyn_img_common {
|
||||
impl_cls(pxa)
|
||||
|
||||
impl_val(extra_size)
|
||||
impl_val(page_size)
|
||||
impl_val(name)
|
||||
impl_val(cmdline)
|
||||
impl_val(id)
|
||||
impl_val(extra_cmdline)
|
||||
};
|
||||
|
||||
#undef impl_val
|
||||
#define impl_val(name) __impl_val(name, v4_hdr)
|
||||
|
||||
struct dyn_img_v3 : public dyn_img_hdr_boot {
|
||||
impl_cls(v3)
|
||||
|
||||
impl_val(kernel_size)
|
||||
impl_val(ramdisk_size)
|
||||
impl_val(os_version)
|
||||
impl_val(header_size)
|
||||
impl_val(header_version)
|
||||
impl_val(cmdline)
|
||||
|
||||
// Make API compatible
|
||||
uint32_t page_size() override { return 4096; }
|
||||
char *extra_cmdline() override { return &v4_hdr->cmdline[BOOT_ARGS_SIZE]; }
|
||||
};
|
||||
|
||||
struct dyn_img_v4 : public dyn_img_v3 {
|
||||
impl_cls(v4)
|
||||
|
||||
impl_val(signature_size)
|
||||
};
|
||||
|
||||
struct dyn_img_hdr_vendor : public dyn_img_hdr {
|
||||
protected:
|
||||
dyn_img_hdr_vendor() : dyn_img_hdr(true) {}
|
||||
};
|
||||
|
||||
#undef impl_val
|
||||
#define impl_val(name) __impl_val(name, v4_vnd)
|
||||
|
||||
struct dyn_img_vnd_v3 : public dyn_img_hdr_vendor {
|
||||
impl_cls(vnd_v3)
|
||||
|
||||
impl_val(header_version)
|
||||
impl_val(page_size)
|
||||
impl_val(ramdisk_size)
|
||||
impl_val(cmdline)
|
||||
impl_val(name)
|
||||
impl_val(header_size)
|
||||
impl_val(dtb_size)
|
||||
|
||||
size_t hdr_space() override { return align_to(hdr_size(), page_size()); }
|
||||
|
||||
// Make API compatible
|
||||
char *extra_cmdline() override { return &v4_vnd->cmdline[BOOT_ARGS_SIZE]; }
|
||||
};
|
||||
|
||||
struct dyn_img_vnd_v4 : public dyn_img_vnd_v3 {
|
||||
impl_cls(vnd_v4)
|
||||
|
||||
impl_val(vendor_ramdisk_table_size)
|
||||
impl_val(bootconfig_size)
|
||||
};
|
||||
|
||||
#undef __impl_cls
|
||||
#undef __impl_val
|
||||
#undef impl_cls
|
||||
#undef impl_val
|
||||
|
||||
/******************
|
||||
* Full Boot Image
|
||||
******************/
|
||||
|
||||
enum {
|
||||
MTK_KERNEL,
|
||||
MTK_RAMDISK,
|
||||
CHROMEOS_FLAG,
|
||||
DHTB_FLAG,
|
||||
SEANDROID_FLAG,
|
||||
LG_BUMP_FLAG,
|
||||
SHA256_FLAG,
|
||||
BLOB_FLAG,
|
||||
NOOKHD_FLAG,
|
||||
ACCLAIM_FLAG,
|
||||
AVB_FLAG,
|
||||
ZIMAGE_KERNEL,
|
||||
BOOT_FLAGS_MAX
|
||||
};
|
||||
|
||||
struct boot_img {
|
||||
// Memory map of the whole image
|
||||
mmap_data map;
|
||||
|
||||
// Android image header
|
||||
dyn_img_hdr *hdr;
|
||||
|
||||
// Flags to indicate the state of current boot image
|
||||
std::bitset<BOOT_FLAGS_MAX> flags;
|
||||
|
||||
// The format of kernel, ramdisk and extra
|
||||
format_t k_fmt = UNKNOWN;
|
||||
format_t r_fmt = UNKNOWN;
|
||||
format_t e_fmt = UNKNOWN;
|
||||
|
||||
/***************************************************
|
||||
* Following pointers points within the mmap region
|
||||
***************************************************/
|
||||
|
||||
// MTK headers
|
||||
mtk_hdr *k_hdr;
|
||||
mtk_hdr *r_hdr;
|
||||
|
||||
// The pointers/values after parse_image
|
||||
// +---------------+
|
||||
// | z_hdr | z_info.hdr_sz
|
||||
// +---------------+
|
||||
// | kernel | hdr->kernel_size()
|
||||
// +---------------+
|
||||
// | z_info.tail | z_info.tail_sz
|
||||
// +---------------+
|
||||
zimage_hdr *z_hdr;
|
||||
struct {
|
||||
uint32_t hdr_sz;
|
||||
uint32_t tail_sz = 0;
|
||||
uint8_t *tail = nullptr;
|
||||
} z_info;
|
||||
|
||||
// Pointer to dtb that is embedded in kernel
|
||||
uint8_t *kernel_dtb;
|
||||
|
||||
// Pointer to end of image
|
||||
uint8_t *tail;
|
||||
size_t tail_size = 0;
|
||||
|
||||
// AVB structs
|
||||
AvbFooter *avb_footer;
|
||||
AvbVBMetaImageHeader *avb_meta;
|
||||
|
||||
// Pointers to blocks defined in header
|
||||
uint8_t *hdr_addr;
|
||||
uint8_t *kernel;
|
||||
uint8_t *ramdisk;
|
||||
uint8_t *second;
|
||||
uint8_t *extra;
|
||||
uint8_t *recovery_dtbo;
|
||||
uint8_t *dtb;
|
||||
|
||||
// Pointer to blocks defined in header, but we do not care
|
||||
uint8_t *ignore;
|
||||
size_t ignore_size = 0;
|
||||
|
||||
boot_img(const char *);
|
||||
~boot_img();
|
||||
|
||||
void parse_image(uint8_t *addr, format_t type);
|
||||
dyn_img_hdr *create_hdr(uint8_t *addr, format_t type);
|
||||
};
|
||||
675
native/jni/boot/compress.cpp
Normal file
675
native/jni/boot/compress.cpp
Normal file
@@ -0,0 +1,675 @@
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#include <zlib.h>
|
||||
#include <bzlib.h>
|
||||
#include <lzma.h>
|
||||
#include <lz4.h>
|
||||
#include <lz4frame.h>
|
||||
#include <lz4hc.h>
|
||||
#include <zopfli/util.h>
|
||||
#include <zopfli/deflate.h>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
#include "magiskboot.hpp"
|
||||
#include "compress.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define bwrite this->base->write
|
||||
|
||||
constexpr size_t CHUNK = 0x40000;
|
||||
constexpr size_t LZ4_UNCOMPRESSED = 0x800000;
|
||||
constexpr size_t LZ4_COMPRESSED = LZ4_COMPRESSBOUND(LZ4_UNCOMPRESSED);
|
||||
|
||||
class out_stream : public filter_stream {
|
||||
using filter_stream::filter_stream;
|
||||
using stream::read;
|
||||
};
|
||||
|
||||
class gz_strm : public out_stream {
|
||||
public:
|
||||
bool write(const void *buf, size_t len) override {
|
||||
return len == 0 || do_write(buf, len, Z_NO_FLUSH);
|
||||
}
|
||||
|
||||
~gz_strm() override {
|
||||
do_write(nullptr, 0, Z_FINISH);
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
inflateEnd(&strm);
|
||||
break;
|
||||
case ENCODE:
|
||||
deflateEnd(&strm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
enum mode_t {
|
||||
DECODE,
|
||||
ENCODE
|
||||
} mode;
|
||||
|
||||
gz_strm(mode_t mode, stream_ptr &&base) :
|
||||
out_stream(std::move(base)), mode(mode), strm{}, outbuf{0} {
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
inflateInit2(&strm, 15 | 16);
|
||||
break;
|
||||
case ENCODE:
|
||||
deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
z_stream strm;
|
||||
uint8_t outbuf[CHUNK];
|
||||
|
||||
bool do_write(const void *buf, size_t len, int flush) {
|
||||
strm.next_in = (Bytef *) buf;
|
||||
strm.avail_in = len;
|
||||
do {
|
||||
int code;
|
||||
strm.next_out = outbuf;
|
||||
strm.avail_out = sizeof(outbuf);
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
code = inflate(&strm, flush);
|
||||
break;
|
||||
case ENCODE:
|
||||
code = deflate(&strm, flush);
|
||||
break;
|
||||
}
|
||||
if (code == Z_STREAM_ERROR) {
|
||||
LOGW("gzip %s failed (%d)\n", mode ? "encode" : "decode", code);
|
||||
return false;
|
||||
}
|
||||
if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
|
||||
return false;
|
||||
} while (strm.avail_out == 0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class gz_decoder : public gz_strm {
|
||||
public:
|
||||
explicit gz_decoder(stream_ptr &&base) : gz_strm(DECODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class gz_encoder : public gz_strm {
|
||||
public:
|
||||
explicit gz_encoder(stream_ptr &&base) : gz_strm(ENCODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class zopfli_encoder : public chunk_out_stream {
|
||||
public:
|
||||
explicit zopfli_encoder(stream_ptr &&base) :
|
||||
chunk_out_stream(std::move(base), ZOPFLI_MASTER_BLOCK_SIZE),
|
||||
zo{}, out(nullptr), outsize(0), crc(crc32_z(0L, Z_NULL, 0)), in_total(0), bp(0) {
|
||||
ZopfliInitOptions(&zo);
|
||||
|
||||
// This config is already better than gzip -9
|
||||
zo.numiterations = 1;
|
||||
zo.blocksplitting = 0;
|
||||
|
||||
ZOPFLI_APPEND_DATA(31, &out, &outsize); /* ID1 */
|
||||
ZOPFLI_APPEND_DATA(139, &out, &outsize); /* ID2 */
|
||||
ZOPFLI_APPEND_DATA(8, &out, &outsize); /* CM */
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize); /* FLG */
|
||||
/* MTIME */
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize);
|
||||
|
||||
ZOPFLI_APPEND_DATA(2, &out, &outsize); /* XFL, 2 indicates best compression. */
|
||||
ZOPFLI_APPEND_DATA(3, &out, &outsize); /* OS follows Unix conventions. */
|
||||
}
|
||||
|
||||
~zopfli_encoder() override {
|
||||
finalize();
|
||||
|
||||
/* CRC */
|
||||
ZOPFLI_APPEND_DATA(crc % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((crc >> 8) % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((crc >> 16) % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((crc >> 24) % 256, &out, &outsize);
|
||||
|
||||
/* ISIZE */
|
||||
ZOPFLI_APPEND_DATA(in_total % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((in_total >> 8) % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((in_total >> 16) % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((in_total >> 24) % 256, &out, &outsize);
|
||||
|
||||
bwrite(out, outsize);
|
||||
free(out);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool write_chunk(const void *buf, size_t len, bool final) override {
|
||||
if (len == 0)
|
||||
return true;
|
||||
|
||||
auto in = static_cast<const unsigned char *>(buf);
|
||||
|
||||
in_total += len;
|
||||
crc = crc32_z(crc, in, len);
|
||||
|
||||
ZopfliDeflatePart(&zo, 2, final, in, 0, len, &bp, &out, &outsize);
|
||||
|
||||
// ZOPFLI_APPEND_DATA is extremely dumb, so we always preserve the
|
||||
// last byte to make sure that realloc is used instead of malloc
|
||||
if (!bwrite(out, outsize - 1))
|
||||
return false;
|
||||
out[0] = out[outsize - 1];
|
||||
outsize = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ZopfliOptions zo;
|
||||
unsigned char *out;
|
||||
size_t outsize;
|
||||
unsigned long crc;
|
||||
uint32_t in_total;
|
||||
unsigned char bp;
|
||||
};
|
||||
|
||||
class bz_strm : public out_stream {
|
||||
public:
|
||||
bool write(const void *buf, size_t len) override {
|
||||
return len == 0 || do_write(buf, len, BZ_RUN);
|
||||
}
|
||||
|
||||
~bz_strm() override {
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
BZ2_bzDecompressEnd(&strm);
|
||||
break;
|
||||
case ENCODE:
|
||||
do_write(nullptr, 0, BZ_FINISH);
|
||||
BZ2_bzCompressEnd(&strm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
enum mode_t {
|
||||
DECODE,
|
||||
ENCODE
|
||||
} mode;
|
||||
|
||||
bz_strm(mode_t mode, stream_ptr &&base) :
|
||||
out_stream(std::move(base)), mode(mode), strm{}, outbuf{0} {
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
BZ2_bzDecompressInit(&strm, 0, 0);
|
||||
break;
|
||||
case ENCODE:
|
||||
BZ2_bzCompressInit(&strm, 9, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bz_stream strm;
|
||||
char outbuf[CHUNK];
|
||||
|
||||
bool do_write(const void *buf, size_t len, int flush) {
|
||||
strm.next_in = (char *) buf;
|
||||
strm.avail_in = len;
|
||||
do {
|
||||
int code;
|
||||
strm.avail_out = sizeof(outbuf);
|
||||
strm.next_out = outbuf;
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
code = BZ2_bzDecompress(&strm);
|
||||
break;
|
||||
case ENCODE:
|
||||
code = BZ2_bzCompress(&strm, flush);
|
||||
break;
|
||||
}
|
||||
if (code < 0) {
|
||||
LOGW("bzip2 %s failed (%d)\n", mode ? "encode" : "decode", code);
|
||||
return false;
|
||||
}
|
||||
if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
|
||||
return false;
|
||||
} while (strm.avail_out == 0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class bz_decoder : public bz_strm {
|
||||
public:
|
||||
explicit bz_decoder(stream_ptr &&base) : bz_strm(DECODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class bz_encoder : public bz_strm {
|
||||
public:
|
||||
explicit bz_encoder(stream_ptr &&base) : bz_strm(ENCODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class lzma_strm : public out_stream {
|
||||
public:
|
||||
bool write(const void *buf, size_t len) override {
|
||||
return len == 0 || do_write(buf, len, LZMA_RUN);
|
||||
}
|
||||
|
||||
~lzma_strm() override {
|
||||
do_write(nullptr, 0, LZMA_FINISH);
|
||||
lzma_end(&strm);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum mode_t {
|
||||
DECODE,
|
||||
ENCODE_XZ,
|
||||
ENCODE_LZMA
|
||||
} mode;
|
||||
|
||||
lzma_strm(mode_t mode, stream_ptr &&base) :
|
||||
out_stream(std::move(base)), mode(mode), strm(LZMA_STREAM_INIT), outbuf{0} {
|
||||
lzma_options_lzma opt;
|
||||
|
||||
// Initialize preset
|
||||
lzma_lzma_preset(&opt, 9);
|
||||
lzma_filter filters[] = {
|
||||
{ .id = LZMA_FILTER_LZMA2, .options = &opt },
|
||||
{ .id = LZMA_VLI_UNKNOWN, .options = nullptr },
|
||||
};
|
||||
|
||||
lzma_ret code;
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
code = lzma_auto_decoder(&strm, UINT64_MAX, 0);
|
||||
break;
|
||||
case ENCODE_XZ:
|
||||
code = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC32);
|
||||
break;
|
||||
case ENCODE_LZMA:
|
||||
code = lzma_alone_encoder(&strm, &opt);
|
||||
break;
|
||||
}
|
||||
if (code != LZMA_OK) {
|
||||
LOGE("LZMA initialization failed (%d)\n", code);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
lzma_stream strm;
|
||||
uint8_t outbuf[CHUNK];
|
||||
|
||||
bool do_write(const void *buf, size_t len, lzma_action flush) {
|
||||
strm.next_in = (uint8_t *) buf;
|
||||
strm.avail_in = len;
|
||||
do {
|
||||
strm.avail_out = sizeof(outbuf);
|
||||
strm.next_out = outbuf;
|
||||
int code = lzma_code(&strm, flush);
|
||||
if (code != LZMA_OK && code != LZMA_STREAM_END) {
|
||||
LOGW("LZMA %s failed (%d)\n", mode ? "encode" : "decode", code);
|
||||
return false;
|
||||
}
|
||||
if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
|
||||
return false;
|
||||
} while (strm.avail_out == 0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class lzma_decoder : public lzma_strm {
|
||||
public:
|
||||
explicit lzma_decoder(stream_ptr &&base) : lzma_strm(DECODE, std::move(base)) {}
|
||||
};
|
||||
|
||||
class xz_encoder : public lzma_strm {
|
||||
public:
|
||||
explicit xz_encoder(stream_ptr &&base) : lzma_strm(ENCODE_XZ, std::move(base)) {}
|
||||
};
|
||||
|
||||
class lzma_encoder : public lzma_strm {
|
||||
public:
|
||||
explicit lzma_encoder(stream_ptr &&base) : lzma_strm(ENCODE_LZMA, std::move(base)) {}
|
||||
};
|
||||
|
||||
class LZ4F_decoder : public out_stream {
|
||||
public:
|
||||
explicit LZ4F_decoder(stream_ptr &&base) :
|
||||
out_stream(std::move(base)), ctx(nullptr), outbuf(nullptr), outCapacity(0) {
|
||||
LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
|
||||
}
|
||||
|
||||
~LZ4F_decoder() override {
|
||||
LZ4F_freeDecompressionContext(ctx);
|
||||
delete[] outbuf;
|
||||
}
|
||||
|
||||
bool write(const void *buf, size_t len) override {
|
||||
auto in = reinterpret_cast<const uint8_t *>(buf);
|
||||
if (!outbuf) {
|
||||
size_t read = len;
|
||||
LZ4F_frameInfo_t info;
|
||||
LZ4F_getFrameInfo(ctx, &info, in, &read);
|
||||
switch (info.blockSizeID) {
|
||||
case LZ4F_default:
|
||||
case LZ4F_max64KB: outCapacity = 1 << 16; break;
|
||||
case LZ4F_max256KB: outCapacity = 1 << 18; break;
|
||||
case LZ4F_max1MB: outCapacity = 1 << 20; break;
|
||||
case LZ4F_max4MB: outCapacity = 1 << 22; break;
|
||||
}
|
||||
outbuf = new uint8_t[outCapacity];
|
||||
in += read;
|
||||
len -= read;
|
||||
}
|
||||
size_t read, write;
|
||||
LZ4F_errorCode_t code;
|
||||
do {
|
||||
read = len;
|
||||
write = outCapacity;
|
||||
code = LZ4F_decompress(ctx, outbuf, &write, in, &read, nullptr);
|
||||
if (LZ4F_isError(code)) {
|
||||
LOGW("LZ4F decode error: %s\n", LZ4F_getErrorName(code));
|
||||
return false;
|
||||
}
|
||||
len -= read;
|
||||
in += read;
|
||||
if (!bwrite(outbuf, write))
|
||||
return false;
|
||||
} while (len != 0 || write != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
LZ4F_decompressionContext_t ctx;
|
||||
uint8_t *outbuf;
|
||||
size_t outCapacity;
|
||||
};
|
||||
|
||||
class LZ4F_encoder : public out_stream {
|
||||
public:
|
||||
explicit LZ4F_encoder(stream_ptr &&base) :
|
||||
out_stream(std::move(base)), ctx(nullptr), out_buf(nullptr), outCapacity(0) {
|
||||
LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
|
||||
}
|
||||
|
||||
bool write(const void *buf, size_t len) override {
|
||||
if (!out_buf) {
|
||||
LZ4F_preferences_t prefs {
|
||||
.frameInfo = {
|
||||
.blockSizeID = LZ4F_max4MB,
|
||||
.blockMode = LZ4F_blockIndependent,
|
||||
.contentChecksumFlag = LZ4F_contentChecksumEnabled,
|
||||
.blockChecksumFlag = LZ4F_noBlockChecksum,
|
||||
},
|
||||
.compressionLevel = 9,
|
||||
.autoFlush = 1,
|
||||
};
|
||||
outCapacity = LZ4F_compressBound(BLOCK_SZ, &prefs);
|
||||
out_buf = new uint8_t[outCapacity];
|
||||
size_t write = LZ4F_compressBegin(ctx, out_buf, outCapacity, &prefs);
|
||||
if (!bwrite(out_buf, write))
|
||||
return false;
|
||||
}
|
||||
if (len == 0)
|
||||
return true;
|
||||
|
||||
auto in = reinterpret_cast<const uint8_t *>(buf);
|
||||
size_t read, write;
|
||||
do {
|
||||
read = len > BLOCK_SZ ? BLOCK_SZ : len;
|
||||
write = LZ4F_compressUpdate(ctx, out_buf, outCapacity, in, read, nullptr);
|
||||
if (LZ4F_isError(write)) {
|
||||
LOGW("LZ4F encode error: %s\n", LZ4F_getErrorName(write));
|
||||
return false;
|
||||
}
|
||||
len -= read;
|
||||
in += read;
|
||||
if (!bwrite(out_buf, write))
|
||||
return false;
|
||||
} while (len != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
~LZ4F_encoder() override {
|
||||
size_t len = LZ4F_compressEnd(ctx, out_buf, outCapacity, nullptr);
|
||||
if (LZ4F_isError(len)) {
|
||||
LOGE("LZ4F end of frame error: %s\n", LZ4F_getErrorName(len));
|
||||
} else if (!bwrite(out_buf, len)) {
|
||||
LOGE("LZ4F end of frame error: I/O error\n");
|
||||
}
|
||||
LZ4F_freeCompressionContext(ctx);
|
||||
delete[] out_buf;
|
||||
}
|
||||
|
||||
private:
|
||||
LZ4F_compressionContext_t ctx;
|
||||
uint8_t *out_buf;
|
||||
size_t outCapacity;
|
||||
|
||||
static constexpr size_t BLOCK_SZ = 1 << 22;
|
||||
};
|
||||
|
||||
class LZ4_decoder : public chunk_out_stream {
|
||||
public:
|
||||
explicit LZ4_decoder(stream_ptr &&base) :
|
||||
chunk_out_stream(std::move(base), LZ4_COMPRESSED, sizeof(block_sz)),
|
||||
out_buf(new char[LZ4_UNCOMPRESSED]), block_sz(0) {}
|
||||
|
||||
~LZ4_decoder() override {
|
||||
finalize();
|
||||
delete[] out_buf;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool write_chunk(const void *buf, size_t len, bool final) override {
|
||||
// This is an error
|
||||
if (len != chunk_sz)
|
||||
return false;
|
||||
|
||||
auto in = reinterpret_cast<const char *>(buf);
|
||||
|
||||
if (block_sz == 0) {
|
||||
memcpy(&block_sz, in, sizeof(block_sz));
|
||||
if (block_sz == 0x184C2102) {
|
||||
// This is actually the lz4 magic, read the next 4 bytes
|
||||
block_sz = 0;
|
||||
chunk_sz = sizeof(block_sz);
|
||||
return true;
|
||||
}
|
||||
// Read the next block chunk
|
||||
chunk_sz = block_sz;
|
||||
return true;
|
||||
} else {
|
||||
int r = LZ4_decompress_safe(in, out_buf, block_sz, LZ4_UNCOMPRESSED);
|
||||
chunk_sz = sizeof(block_sz);
|
||||
block_sz = 0;
|
||||
if (r < 0) {
|
||||
LOGW("LZ4HC decompression failure (%d)\n", r);
|
||||
return false;
|
||||
}
|
||||
return bwrite(out_buf, r);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
char *out_buf;
|
||||
uint32_t block_sz;
|
||||
};
|
||||
|
||||
class LZ4_encoder : public chunk_out_stream {
|
||||
public:
|
||||
explicit LZ4_encoder(stream_ptr &&base, bool lg) :
|
||||
chunk_out_stream(std::move(base), LZ4_UNCOMPRESSED),
|
||||
out_buf(new char[LZ4_COMPRESSED]), lg(lg), in_total(0) {
|
||||
bwrite("\x02\x21\x4c\x18", 4);
|
||||
}
|
||||
|
||||
~LZ4_encoder() override {
|
||||
finalize();
|
||||
if (lg)
|
||||
bwrite(&in_total, sizeof(in_total));
|
||||
delete[] out_buf;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool write_chunk(const void *buf, size_t len, bool final) override {
|
||||
auto in = static_cast<const char *>(buf);
|
||||
uint32_t block_sz = LZ4_compress_HC(in, out_buf, len, LZ4_COMPRESSED, LZ4HC_CLEVEL_MAX);
|
||||
if (block_sz == 0) {
|
||||
LOGW("LZ4HC compression failure\n");
|
||||
return false;
|
||||
}
|
||||
if (bwrite(&block_sz, sizeof(block_sz)) && bwrite(out_buf, block_sz)) {
|
||||
in_total += len;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
char *out_buf;
|
||||
bool lg;
|
||||
uint32_t in_total;
|
||||
};
|
||||
|
||||
filter_strm_ptr get_encoder(format_t type, stream_ptr &&base) {
|
||||
switch (type) {
|
||||
case XZ:
|
||||
return make_unique<xz_encoder>(std::move(base));
|
||||
case LZMA:
|
||||
return make_unique<lzma_encoder>(std::move(base));
|
||||
case BZIP2:
|
||||
return make_unique<bz_encoder>(std::move(base));
|
||||
case LZ4:
|
||||
return make_unique<LZ4F_encoder>(std::move(base));
|
||||
case LZ4_LEGACY:
|
||||
return make_unique<LZ4_encoder>(std::move(base), false);
|
||||
case LZ4_LG:
|
||||
return make_unique<LZ4_encoder>(std::move(base), true);
|
||||
case ZOPFLI:
|
||||
return make_unique<zopfli_encoder>(std::move(base));
|
||||
case GZIP:
|
||||
default:
|
||||
return make_unique<gz_encoder>(std::move(base));
|
||||
}
|
||||
}
|
||||
|
||||
filter_strm_ptr get_decoder(format_t type, stream_ptr &&base) {
|
||||
switch (type) {
|
||||
case XZ:
|
||||
case LZMA:
|
||||
return make_unique<lzma_decoder>(std::move(base));
|
||||
case BZIP2:
|
||||
return make_unique<bz_decoder>(std::move(base));
|
||||
case LZ4:
|
||||
return make_unique<LZ4F_decoder>(std::move(base));
|
||||
case LZ4_LEGACY:
|
||||
case LZ4_LG:
|
||||
return make_unique<LZ4_decoder>(std::move(base));
|
||||
case ZOPFLI:
|
||||
case GZIP:
|
||||
default:
|
||||
return make_unique<gz_decoder>(std::move(base));
|
||||
}
|
||||
}
|
||||
|
||||
void decompress(char *infile, const char *outfile) {
|
||||
bool in_std = infile == "-"sv;
|
||||
bool rm_in = false;
|
||||
|
||||
FILE *in_fp = in_std ? stdin : xfopen(infile, "re");
|
||||
stream_ptr strm;
|
||||
|
||||
char buf[4096];
|
||||
size_t len;
|
||||
while ((len = fread(buf, 1, sizeof(buf), in_fp))) {
|
||||
if (!strm) {
|
||||
format_t type = check_fmt(buf, len);
|
||||
|
||||
fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]);
|
||||
|
||||
if (!COMPRESSED(type))
|
||||
LOGE("Input file is not a supported compressed type!\n");
|
||||
|
||||
/* If user does not provide outfile, infile has to be either
|
||||
* <path>.[ext], or '-'. Outfile will be either <path> or '-'.
|
||||
* If the input does not have proper format, abort */
|
||||
|
||||
char *ext = nullptr;
|
||||
if (outfile == nullptr) {
|
||||
outfile = infile;
|
||||
if (!in_std) {
|
||||
ext = strrchr(infile, '.');
|
||||
if (ext == nullptr || strcmp(ext, fmt2ext[type]) != 0)
|
||||
LOGE("Input file is not a supported type!\n");
|
||||
|
||||
// Strip out extension and remove input
|
||||
*ext = '\0';
|
||||
rm_in = true;
|
||||
fprintf(stderr, "Decompressing to [%s]\n", outfile);
|
||||
}
|
||||
}
|
||||
|
||||
FILE *out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we");
|
||||
strm = get_decoder(type, make_unique<fp_stream>(out_fp));
|
||||
if (ext) *ext = '.';
|
||||
}
|
||||
if (!strm->write(buf, len))
|
||||
LOGE("Decompression error!\n");
|
||||
}
|
||||
|
||||
strm.reset(nullptr);
|
||||
fclose(in_fp);
|
||||
|
||||
if (rm_in)
|
||||
unlink(infile);
|
||||
}
|
||||
|
||||
void compress(const char *method, const char *infile, const char *outfile) {
|
||||
format_t fmt = name2fmt[method];
|
||||
if (fmt == UNKNOWN)
|
||||
LOGE("Unknown compression method: [%s]\n", method);
|
||||
|
||||
bool in_std = infile == "-"sv;
|
||||
bool rm_in = false;
|
||||
|
||||
FILE *in_fp = in_std ? stdin : xfopen(infile, "re");
|
||||
FILE *out_fp;
|
||||
|
||||
if (outfile == nullptr) {
|
||||
if (in_std) {
|
||||
out_fp = stdout;
|
||||
} else {
|
||||
/* If user does not provide outfile and infile is not
|
||||
* STDIN, output to <infile>.[ext] */
|
||||
string tmp(infile);
|
||||
tmp += fmt2ext[fmt];
|
||||
out_fp = xfopen(tmp.data(), "we");
|
||||
fprintf(stderr, "Compressing to [%s]\n", tmp.data());
|
||||
rm_in = true;
|
||||
}
|
||||
} else {
|
||||
out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we");
|
||||
}
|
||||
|
||||
auto strm = get_encoder(fmt, make_unique<fp_stream>(out_fp));
|
||||
|
||||
char buf[4096];
|
||||
size_t len;
|
||||
while ((len = fread(buf, 1, sizeof(buf), in_fp))) {
|
||||
if (!strm->write(buf, len))
|
||||
LOGE("Compression error!\n");
|
||||
}
|
||||
|
||||
strm.reset(nullptr);
|
||||
fclose(in_fp);
|
||||
|
||||
if (rm_in)
|
||||
unlink(infile);
|
||||
}
|
||||
13
native/jni/boot/compress.hpp
Normal file
13
native/jni/boot/compress.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stream.hpp>
|
||||
|
||||
#include "format.hpp"
|
||||
|
||||
filter_strm_ptr get_encoder(format_t type, stream_ptr &&base);
|
||||
|
||||
filter_strm_ptr get_decoder(format_t type, stream_ptr &&base);
|
||||
|
||||
void compress(const char *method, const char *infile, const char *outfile);
|
||||
|
||||
void decompress(char *infile, const char *outfile);
|
||||
238
native/jni/boot/cpio.cpp
Normal file
238
native/jni/boot/cpio.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
#include "cpio.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
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];
|
||||
} __attribute__((packed));
|
||||
|
||||
static uint32_t x8u(const 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)
|
||||
LOGE("bad cpio header\n");
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
cpio_entry::cpio_entry(uint32_t mode) : mode(mode), uid(0), gid(0), filesize(0), data(nullptr) {}
|
||||
|
||||
cpio_entry::cpio_entry(const cpio_newc_header *h) :
|
||||
mode(x8u(h->mode)), uid(x8u(h->uid)), gid(x8u(h->gid)), filesize(x8u(h->filesize)), data(nullptr)
|
||||
{}
|
||||
|
||||
void cpio::dump(const char *file) {
|
||||
fprintf(stderr, "Dump cpio: [%s]\n", file);
|
||||
dump(xfopen(file, "we"));
|
||||
}
|
||||
|
||||
void cpio::rm(entry_map::iterator &it) {
|
||||
fprintf(stderr, "Remove [%s]\n", it->first.data());
|
||||
entries.erase(it);
|
||||
}
|
||||
|
||||
void cpio::rm(const char *name, bool r) {
|
||||
size_t len = strlen(name);
|
||||
for (auto it = entries.begin(); it != entries.end();) {
|
||||
if (it->first.compare(0, len, name) == 0 &&
|
||||
((r && it->first[len] == '/') || it->first[len] == '\0')) {
|
||||
auto tmp = it;
|
||||
++it;
|
||||
rm(tmp);
|
||||
if (!r) return;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cpio::extract_entry(const entry_map::value_type &e, const char *file) {
|
||||
fprintf(stderr, "Extract [%s] to [%s]\n", e.first.data(), file);
|
||||
unlink(file);
|
||||
rmdir(file);
|
||||
if (S_ISDIR(e.second->mode)) {
|
||||
::mkdir(file, e.second->mode & 0777);
|
||||
} else if (S_ISREG(e.second->mode)) {
|
||||
int fd = creat(file, e.second->mode & 0777);
|
||||
xwrite(fd, e.second->data, e.second->filesize);
|
||||
fchown(fd, e.second->uid, e.second->gid);
|
||||
close(fd);
|
||||
} else if (S_ISLNK(e.second->mode)) {
|
||||
auto target = strndup((char *) e.second->data, e.second->filesize);
|
||||
symlink(target, file);
|
||||
free(target);
|
||||
}
|
||||
}
|
||||
|
||||
void cpio::extract() {
|
||||
for (auto &e : entries)
|
||||
extract_entry(e, e.first.data());
|
||||
}
|
||||
|
||||
bool cpio::extract(const char *name, const char *file) {
|
||||
auto it = entries.find(name);
|
||||
if (it != entries.end()) {
|
||||
extract_entry(*it, file);
|
||||
return true;
|
||||
}
|
||||
fprintf(stderr, "Cannot find the file entry [%s]\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cpio::exists(const char *name) {
|
||||
return entries.count(name) != 0;
|
||||
}
|
||||
|
||||
#define do_out(buf, len) pos += fwrite(buf, 1, len, out);
|
||||
#define out_align() do_out(zeros, align_padding(pos, 4))
|
||||
void cpio::dump(FILE *out) {
|
||||
size_t pos = 0;
|
||||
unsigned inode = 300000;
|
||||
char header[111];
|
||||
char zeros[4] = {0};
|
||||
for (auto &e : entries) {
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
|
||||
inode++, // e->ino
|
||||
e.second->mode,
|
||||
e.second->uid,
|
||||
e.second->gid,
|
||||
1, // e->nlink
|
||||
0, // e->mtime
|
||||
e.second->filesize,
|
||||
0, // e->devmajor
|
||||
0, // e->devminor
|
||||
0, // e->rdevmajor
|
||||
0, // e->rdevminor
|
||||
(uint32_t) e.first.size() + 1,
|
||||
0 // e->check
|
||||
);
|
||||
do_out(header, 110);
|
||||
do_out(e.first.data(), e.first.size() + 1);
|
||||
out_align();
|
||||
if (e.second->filesize) {
|
||||
do_out(e.second->data, e.second->filesize);
|
||||
out_align();
|
||||
}
|
||||
}
|
||||
// Write trailer
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
|
||||
inode++, 0755, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0);
|
||||
do_out(header, 110);
|
||||
do_out("TRAILER!!!\0", 11);
|
||||
out_align();
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
void cpio::load_cpio(const char *file) {
|
||||
fprintf(stderr, "Loading cpio: [%s]\n", file);
|
||||
auto m = mmap_data(file);
|
||||
load_cpio(reinterpret_cast<char *>(m.buf), m.sz);
|
||||
}
|
||||
|
||||
void cpio::insert(string_view name, cpio_entry *e) {
|
||||
auto it = entries.find(name);
|
||||
if (it != entries.end()) {
|
||||
it->second.reset(e);
|
||||
} else {
|
||||
entries.emplace(name, e);
|
||||
}
|
||||
}
|
||||
|
||||
void cpio::add(mode_t mode, const char *name, const char *file) {
|
||||
auto m = mmap_data(file);
|
||||
auto e = new cpio_entry(S_IFREG | mode);
|
||||
e->filesize = m.sz;
|
||||
e->data = xmalloc(m.sz);
|
||||
memcpy(e->data, m.buf, m.sz);
|
||||
insert(name, e);
|
||||
fprintf(stderr, "Add entry [%s] (%04o)\n", name, mode);
|
||||
}
|
||||
|
||||
void cpio::mkdir(mode_t mode, const char *name) {
|
||||
insert(name, new cpio_entry(S_IFDIR | mode));
|
||||
fprintf(stderr, "Create directory [%s] (%04o)\n", name, mode);
|
||||
}
|
||||
|
||||
void cpio::ln(const char *target, const char *name) {
|
||||
auto e = new cpio_entry(S_IFLNK);
|
||||
e->filesize = strlen(target);
|
||||
e->data = strdup(target);
|
||||
insert(name, e);
|
||||
fprintf(stderr, "Create symlink [%s] -> [%s]\n", name, target);
|
||||
}
|
||||
|
||||
void cpio::mv(entry_map::iterator &it, const char *name) {
|
||||
fprintf(stderr, "Move [%s] -> [%s]\n", it->first.data(), name);
|
||||
auto e = it->second.release();
|
||||
entries.erase(it);
|
||||
insert(name, e);
|
||||
}
|
||||
|
||||
bool cpio::mv(const char *from, const char *to) {
|
||||
auto it = entries.find(from);
|
||||
if (it != entries.end()) {
|
||||
mv(it, to);
|
||||
return true;
|
||||
}
|
||||
fprintf(stderr, "Cannot find entry %s\n", from);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define pos_align(p) p = align_to(p, 4)
|
||||
|
||||
void cpio::load_cpio(const char *buf, size_t sz) {
|
||||
size_t pos = 0;
|
||||
while (pos < sz) {
|
||||
auto hdr = reinterpret_cast<const cpio_newc_header *>(buf + pos);
|
||||
if (memcmp(hdr->magic, "070701", 6) != 0)
|
||||
LOGE("bad cpio header\n");
|
||||
pos += sizeof(cpio_newc_header);
|
||||
string_view name(buf + pos);
|
||||
pos += x8u(hdr->namesize);
|
||||
pos_align(pos);
|
||||
if (name == "." || name == "..")
|
||||
continue;
|
||||
if (name == "TRAILER!!!") {
|
||||
// Android support multiple CPIO being concatenated
|
||||
// Search for the next cpio header
|
||||
auto next = static_cast<const char *>(memmem(buf + pos, sz - pos, "070701", 6));
|
||||
if (next == nullptr)
|
||||
break;
|
||||
pos = next - buf;
|
||||
continue;
|
||||
}
|
||||
auto entry = new cpio_entry(hdr);
|
||||
entry->data = xmalloc(entry->filesize);
|
||||
memcpy(entry->data, buf + pos, entry->filesize);
|
||||
pos += entry->filesize;
|
||||
insert(name, entry);
|
||||
pos_align(pos);
|
||||
}
|
||||
}
|
||||
55
native/jni/boot/cpio.hpp
Normal file
55
native/jni/boot/cpio.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
|
||||
struct cpio_newc_header;
|
||||
|
||||
struct cpio_entry {
|
||||
uint32_t mode;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t filesize;
|
||||
void *data;
|
||||
|
||||
explicit cpio_entry(uint32_t mode = 0);
|
||||
explicit cpio_entry(const cpio_newc_header *h);
|
||||
~cpio_entry() { free(data); }
|
||||
};
|
||||
|
||||
class cpio {
|
||||
public:
|
||||
struct StringCmp {
|
||||
using is_transparent = void;
|
||||
bool operator()(std::string_view a, std::string_view b) const {
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
using entry_map = std::map<std::string, std::unique_ptr<cpio_entry>, StringCmp>;
|
||||
|
||||
void load_cpio(const char *file);
|
||||
void dump(const char *file);
|
||||
void rm(const char *name, bool r = false);
|
||||
void extract();
|
||||
bool extract(const char *name, const char *file);
|
||||
bool exists(const char *name);
|
||||
void add(mode_t mode, const char *name, const char *file);
|
||||
void mkdir(mode_t mode, const char *name);
|
||||
void ln(const char *target, const char *name);
|
||||
bool mv(const char *from, const char *to);
|
||||
|
||||
protected:
|
||||
entry_map entries;
|
||||
|
||||
static void extract_entry(const entry_map::value_type &e, const char *file);
|
||||
void rm(entry_map::iterator &it);
|
||||
void mv(entry_map::iterator &it, const char *name);
|
||||
|
||||
private:
|
||||
void dump(FILE *out);
|
||||
void insert(std::string_view name, cpio_entry *e);
|
||||
void load_cpio(const char *buf, size_t sz);
|
||||
};
|
||||
428
native/jni/boot/dtb.cpp
Normal file
428
native/jni/boot/dtb.cpp
Normal file
@@ -0,0 +1,428 @@
|
||||
#include <bitset>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
#include "magiskboot.hpp"
|
||||
#include "dtb.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
constexpr int MAX_DEPTH = 32;
|
||||
static bitset<MAX_DEPTH> depth_set;
|
||||
|
||||
static void pretty_node(int depth) {
|
||||
if (depth == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < depth - 1; ++i)
|
||||
printf(depth_set[i] ? "│ " : " ");
|
||||
|
||||
printf(depth_set[depth - 1] ? "├── " : "└── ");
|
||||
}
|
||||
|
||||
static void pretty_prop(int depth) {
|
||||
for (int i = 0; i < depth; ++i)
|
||||
printf(depth_set[i] ? "│ " : " ");
|
||||
|
||||
printf(depth_set[depth] ? "│ " : " ");
|
||||
}
|
||||
|
||||
static void print_node(const void *fdt, int node = 0, int depth = 0) {
|
||||
// Print node itself
|
||||
pretty_node(depth);
|
||||
printf("#%d: %s\n", node, fdt_get_name(fdt, node, nullptr));
|
||||
|
||||
// Print properties
|
||||
depth_set[depth] = fdt_first_subnode(fdt, node) >= 0;
|
||||
int prop;
|
||||
fdt_for_each_property_offset(prop, fdt, node) {
|
||||
pretty_prop(depth);
|
||||
int size;
|
||||
const char *name;
|
||||
auto value = static_cast<const char *>(fdt_getprop_by_offset(fdt, prop, &name, &size));
|
||||
|
||||
bool is_str = !(size > 1 && value[0] == 0);
|
||||
if (is_str) {
|
||||
// Scan through value to see if printable
|
||||
for (int i = 0; i < size; ++i) {
|
||||
char c = value[i];
|
||||
if (i == size - 1) {
|
||||
// Make sure null terminate
|
||||
is_str = c == '\0';
|
||||
} else if ((c > 0 && c < 32) || c >= 127) {
|
||||
is_str = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_str) {
|
||||
printf("[%s]: [%s]\n", name, value);
|
||||
} else {
|
||||
printf("[%s]: <bytes>(%d)\n", name, size);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursive
|
||||
if (depth_set[depth]) {
|
||||
int child;
|
||||
int prev = -1;
|
||||
fdt_for_each_subnode(child, fdt, node) {
|
||||
if (prev >= 0)
|
||||
print_node(fdt, prev, depth + 1);
|
||||
prev = child;
|
||||
}
|
||||
depth_set[depth] = false;
|
||||
print_node(fdt, prev, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int find_fstab(const void *fdt, int node = 0) {
|
||||
if (auto name = fdt_get_name(fdt, node, nullptr); name && name == "fstab"sv)
|
||||
return node;
|
||||
int child;
|
||||
fdt_for_each_subnode(child, fdt, node) {
|
||||
int fstab = find_fstab(fdt, child);
|
||||
if (fstab >= 0)
|
||||
return fstab;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void dtb_print(const char *file, bool fstab) {
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
auto m = mmap_data(file);
|
||||
// Loop through all the dtbs
|
||||
int dtb_num = 0;
|
||||
uint8_t * const end = m.buf + m.sz;
|
||||
for (uint8_t *fdt = m.buf; fdt < end;) {
|
||||
fdt = static_cast<uint8_t*>(memmem(fdt, end - fdt, DTB_MAGIC, sizeof(fdt32_t)));
|
||||
if (fdt == nullptr)
|
||||
break;
|
||||
if (fstab) {
|
||||
int node = find_fstab(fdt);
|
||||
if (node >= 0) {
|
||||
fprintf(stderr, "Found fstab in buf.%04d\n", dtb_num);
|
||||
print_node(fdt, node);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Printing buf.%04d\n", dtb_num);
|
||||
print_node(fdt);
|
||||
}
|
||||
++dtb_num;
|
||||
fdt += fdt_totalsize(fdt);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static bool dtb_patch_rebuild(uint8_t *dtb, size_t dtb_sz, const char *file);
|
||||
|
||||
static bool dtb_patch(const char *file) {
|
||||
bool keep_verity = check_env("KEEPVERITY");
|
||||
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
auto m = mmap_data(file, true);
|
||||
|
||||
bool patched = false;
|
||||
uint8_t * const end = m.buf + m.sz;
|
||||
for (uint8_t *fdt = m.buf; fdt < end;) {
|
||||
fdt = static_cast<uint8_t*>(memmem(fdt, end - fdt, DTB_MAGIC, sizeof(fdt32_t)));
|
||||
if (fdt == nullptr)
|
||||
break;
|
||||
int node;
|
||||
// Patch the chosen node for bootargs
|
||||
fdt_for_each_subnode(node, fdt, 0) {
|
||||
if (auto name = fdt_get_name(fdt, node, nullptr); !name || name != "chosen"sv)
|
||||
continue;
|
||||
int len;
|
||||
if (auto value = fdt_getprop(fdt, node, "bootargs", &len)) {
|
||||
if (void *skip = memmem(value, len, "skip_initramfs", 14)) {
|
||||
fprintf(stderr, "Patch [skip_initramfs] -> [want_initramfs]\n");
|
||||
memcpy(skip, "want", 4);
|
||||
patched = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (int fstab = find_fstab(fdt); fstab >= 0) {
|
||||
fdt_for_each_subnode(node, fdt, fstab) {
|
||||
if (!keep_verity) {
|
||||
int len;
|
||||
char *value = (char *) fdt_getprop(fdt, node, "fsmgr_flags", &len);
|
||||
patched |= patch_verity(value, len) != len;
|
||||
}
|
||||
}
|
||||
}
|
||||
fdt += fdt_totalsize(fdt);
|
||||
}
|
||||
return patched;
|
||||
}
|
||||
|
||||
int dtb_commands(int argc, char *argv[]) {
|
||||
char *dtb = argv[0];
|
||||
++argv;
|
||||
--argc;
|
||||
|
||||
if (argv[0] == "print"sv) {
|
||||
dtb_print(dtb, argc > 1 && argv[1] == "-f"sv);
|
||||
return 0;
|
||||
} else if (argv[0] == "patch"sv) {
|
||||
if (!dtb_patch(dtb))
|
||||
exit(1);
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct fdt_blob {
|
||||
void *fdt;
|
||||
uint32_t offset;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static bool fdt_patch(void *fdt) {
|
||||
int fstab = find_fstab(fdt);
|
||||
if (fstab < 0)
|
||||
return false;
|
||||
bool modified = false;
|
||||
int node;
|
||||
fdt_for_each_subnode(node, fdt, fstab) {
|
||||
const char *name = fdt_get_name(fdt, node, nullptr);
|
||||
// Force remove AVB for 2SI since it may bootloop some devices
|
||||
int len;
|
||||
auto value = (const char *) fdt_getprop(fdt, node, "fsmgr_flags", &len);
|
||||
string copy(value, len);
|
||||
uint32_t new_len = patch_verity(copy.data(), len);
|
||||
if (new_len != len) {
|
||||
modified = true;
|
||||
fdt_setprop(fdt, node, "fsmgr_flags", copy.data(), new_len);
|
||||
}
|
||||
if (name == "system"sv) {
|
||||
fprintf(stderr, "Setting [mnt_point] to [/system_root]\n");
|
||||
fdt_setprop_string(fdt, node, "mnt_point", "/system_root");
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
#define MAX_FDT_GROWTH 256
|
||||
|
||||
template <class Table, class Header>
|
||||
static bool dt_table_patch(const Header *hdr, const char *out) {
|
||||
map<uint32_t, fdt_blob> dtb_map;
|
||||
auto buf = reinterpret_cast<const uint8_t *>(hdr);
|
||||
auto tables = reinterpret_cast<const Table *>(buf + sizeof(Header));
|
||||
|
||||
constexpr bool is_aosp = std::is_same_v<Header, dt_table_header>;
|
||||
|
||||
// AOSP DTB store ints in big endian
|
||||
using endian_conv = uint32_t (*)(uint32_t);
|
||||
endian_conv be_to_le;
|
||||
endian_conv le_to_be;
|
||||
if constexpr (is_aosp) {
|
||||
be_to_le = fdt32_to_cpu;
|
||||
le_to_be = cpu_to_fdt32;
|
||||
} else {
|
||||
be_to_le = le_to_be = [](uint32_t x) { return x; };
|
||||
}
|
||||
|
||||
// Collect all dtbs
|
||||
auto num_dtb = be_to_le(hdr->num_dtbs);
|
||||
for (int i = 0; i < num_dtb; ++i) {
|
||||
auto offset = be_to_le(tables[i].offset);
|
||||
if (dtb_map.count(offset) == 0) {
|
||||
auto blob = buf + offset;
|
||||
uint32_t size = fdt_totalsize(blob);
|
||||
auto fdt = xmalloc(size + MAX_FDT_GROWTH);
|
||||
memcpy(fdt, blob, size);
|
||||
fdt_open_into(fdt, fdt, size + MAX_FDT_GROWTH);
|
||||
dtb_map[offset] = { fdt, offset };
|
||||
}
|
||||
}
|
||||
if (dtb_map.empty())
|
||||
return false;
|
||||
|
||||
// Patch fdt
|
||||
bool modified = false;
|
||||
for (auto &[_, blob] : dtb_map)
|
||||
modified |= fdt_patch(blob.fdt);
|
||||
if (!modified)
|
||||
return false;
|
||||
|
||||
unlink(out);
|
||||
int fd = xopen(out, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
|
||||
|
||||
// This value is only used if AOSP DTB
|
||||
uint32_t total_size = 0;
|
||||
|
||||
// Copy headers and tables
|
||||
total_size += xwrite(fd, buf, dtb_map.begin()->first);
|
||||
|
||||
// mmap rw to patch table values retroactively
|
||||
auto mmap_sz = lseek(fd, 0, SEEK_CUR);
|
||||
auto addr = (uint8_t *) xmmap(nullptr, mmap_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
|
||||
// Guess alignment using gcd
|
||||
uint32_t align = 1;
|
||||
if constexpr (!is_aosp) {
|
||||
auto it = dtb_map.begin();
|
||||
align = (it++)->first;
|
||||
for (; it != dtb_map.end(); ++it)
|
||||
align = binary_gcd(align, it->first);
|
||||
}
|
||||
|
||||
// Write dtbs
|
||||
for (auto &val : dtb_map) {
|
||||
val.second.offset = lseek(fd, 0, SEEK_CUR);
|
||||
auto fdt = val.second.fdt;
|
||||
fdt_pack(fdt);
|
||||
auto size = fdt_totalsize(fdt);
|
||||
total_size += xwrite(fd, fdt, size);
|
||||
if constexpr (!is_aosp) {
|
||||
val.second.len = align_to(size, align);
|
||||
write_zero(fd, align_padding(lseek(fd, 0, SEEK_CUR), align));
|
||||
}
|
||||
free(fdt);
|
||||
}
|
||||
|
||||
// Patch headers
|
||||
if constexpr (is_aosp) {
|
||||
auto hdr_rw = reinterpret_cast<Header *>(addr);
|
||||
hdr_rw->total_size = le_to_be(total_size);
|
||||
}
|
||||
auto tables_rw = reinterpret_cast<Table *>(addr + sizeof(Header));
|
||||
for (int i = 0; i < num_dtb; ++i) {
|
||||
auto &blob = dtb_map[be_to_le(tables_rw[i].offset)];
|
||||
tables_rw[i].offset = le_to_be(blob.offset);
|
||||
tables_rw[i].len = le_to_be(blob.len);
|
||||
}
|
||||
|
||||
munmap(addr, mmap_sz);
|
||||
close(fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool blob_patch(uint8_t *dtb, size_t dtb_sz, const char *out) {
|
||||
vector<uint8_t *> fdt_list;
|
||||
vector<uint32_t> padding_list;
|
||||
|
||||
uint8_t * const end = dtb + dtb_sz;
|
||||
for (uint8_t *curr = dtb; curr < end;) {
|
||||
curr = static_cast<uint8_t*>(memmem(curr, end - curr, DTB_MAGIC, sizeof(fdt32_t)));
|
||||
if (curr == nullptr)
|
||||
break;
|
||||
auto len = fdt_totalsize(curr);
|
||||
auto fdt = static_cast<uint8_t *>(xmalloc(len + MAX_FDT_GROWTH));
|
||||
memcpy(fdt, curr, len);
|
||||
fdt_pack(fdt);
|
||||
uint32_t padding = len - fdt_totalsize(fdt);
|
||||
padding_list.push_back(padding);
|
||||
fdt_open_into(fdt, fdt, len + MAX_FDT_GROWTH);
|
||||
fdt_list.push_back(fdt);
|
||||
curr += len;
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
for (auto fdt : fdt_list)
|
||||
modified |= fdt_patch(fdt);
|
||||
if (!modified)
|
||||
return false;
|
||||
|
||||
unlink(out);
|
||||
int fd = xopen(out, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
|
||||
|
||||
for (int i = 0; i < fdt_list.size(); ++i) {
|
||||
auto fdt = fdt_list[i];
|
||||
fdt_pack(fdt);
|
||||
// Only add padding back if it is anything meaningful
|
||||
if (padding_list[i] > 4) {
|
||||
auto len = fdt_totalsize(fdt);
|
||||
fdt_set_totalsize(fdt, len + padding_list[i]);
|
||||
}
|
||||
xwrite(fd, fdt, fdt_totalsize(fdt));
|
||||
free(fdt);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define DTB_MATCH(s) BUFFER_MATCH(dtb, s)
|
||||
|
||||
static bool dtb_patch_rebuild(uint8_t *dtb, size_t dtb_sz, const char *file) {
|
||||
if (DTB_MATCH(QCDT_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<qcdt_hdr*>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 1:
|
||||
fprintf(stderr, "QCDT v1\n");
|
||||
return dt_table_patch<qctable_v1>(hdr, file);
|
||||
case 2:
|
||||
fprintf(stderr, "QCDT v2\n");
|
||||
return dt_table_patch<qctable_v2>(hdr, file);
|
||||
case 3:
|
||||
fprintf(stderr, "QCDT v3\n");
|
||||
return dt_table_patch<qctable_v3>(hdr, file);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (DTB_MATCH(DTBH_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<dtbh_hdr *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 2:
|
||||
fprintf(stderr, "DTBH v2\n");
|
||||
return dt_table_patch<bhtable_v2>(hdr, file);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (DTB_MATCH(PXADT_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<pxadt_hdr *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 1:
|
||||
fprintf(stderr, "PXA-DT v1\n");
|
||||
return dt_table_patch<pxatable_v1>(hdr, file);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (DTB_MATCH(PXA19xx_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<pxa19xx_hdr *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 1:
|
||||
fprintf(stderr, "PXA-19xx v1\n");
|
||||
return dt_table_patch<pxatable_v1>(hdr, file);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (DTB_MATCH(SPRD_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<sprd_hdr *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 1:
|
||||
fprintf(stderr, "SPRD v1\n");
|
||||
return dt_table_patch<sprdtable_v1>(hdr, file);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (DTB_MATCH(DT_TABLE_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<dt_table_header *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 0:
|
||||
fprintf(stderr, "DT_TABLE v0\n");
|
||||
return dt_table_patch<dt_table_entry>(hdr, file);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return blob_patch(dtb, dtb_sz, file);
|
||||
}
|
||||
}
|
||||
103
native/jni/boot/dtb.hpp
Normal file
103
native/jni/boot/dtb.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DT_TABLE_MAGIC "\xd7\xb7\xab\x1e"
|
||||
#define QCDT_MAGIC "QCDT"
|
||||
#define DTBH_MAGIC "DTBH"
|
||||
#define PXADT_MAGIC "PXA-DT"
|
||||
#define PXA19xx_MAGIC "PXA-19xx"
|
||||
#define SPRD_MAGIC "SPRD"
|
||||
|
||||
struct qcdt_hdr {
|
||||
char magic[4]; /* "QCDT" */
|
||||
uint32_t version; /* QCDT version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct qctable_v1 {
|
||||
uint32_t cpu_info[3]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in QCDT */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct qctable_v2 {
|
||||
uint32_t cpu_info[4]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in QCDT */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct qctable_v3 {
|
||||
uint32_t cpu_info[8]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in QCDT */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dtbh_hdr {
|
||||
char magic[4]; /* "DTBH" */
|
||||
uint32_t version; /* DTBH version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bhtable_v2 {
|
||||
uint32_t cpu_info[5]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in DTBH */
|
||||
uint32_t len; /* DTB size */
|
||||
uint32_t space; /* 0x00000020 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pxadt_hdr {
|
||||
char magic[6]; /* "PXA-DT" */
|
||||
uint32_t version; /* PXA-* version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pxa19xx_hdr {
|
||||
char magic[8]; /* "PXA-19xx" */
|
||||
uint32_t version; /* PXA-* version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pxatable_v1 {
|
||||
uint32_t cpu_info[2]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in PXA-* */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sprd_hdr {
|
||||
char magic[4]; /* "SPRD" */
|
||||
uint32_t version; /* SPRD version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sprdtable_v1 {
|
||||
uint32_t cpu_info[3]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in SPRD */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* AOSP DTB/DTBO partition layout */
|
||||
|
||||
struct dt_table_header {
|
||||
uint32_t magic; /* DT_TABLE_MAGIC */
|
||||
uint32_t total_size; /* includes dt_table_header + all dt_table_entry */
|
||||
uint32_t header_size; /* sizeof(dt_table_header) */
|
||||
|
||||
uint32_t dt_entry_size; /* sizeof(dt_table_entry) */
|
||||
uint32_t num_dtbs; /* number of dt_table_entry */
|
||||
uint32_t dt_entries_offset; /* offset to the first dt_table_entry */
|
||||
|
||||
uint32_t page_size; /* flash page size we assume */
|
||||
uint32_t version; /* DTBO image version */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dt_table_entry {
|
||||
uint32_t len; /* DTB size */
|
||||
uint32_t offset;
|
||||
|
||||
uint32_t id;
|
||||
uint32_t rev;
|
||||
uint32_t flags;
|
||||
|
||||
uint32_t custom[3];
|
||||
} __attribute__((packed));
|
||||
110
native/jni/boot/format.cpp
Normal file
110
native/jni/boot/format.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "format.hpp"
|
||||
|
||||
Name2Fmt name2fmt;
|
||||
Fmt2Name fmt2name;
|
||||
Fmt2Ext fmt2ext;
|
||||
|
||||
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
|
||||
|
||||
format_t check_fmt(const void *buf, size_t len) {
|
||||
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
|
||||
return CHROMEOS;
|
||||
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
|
||||
return AOSP;
|
||||
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
|
||||
return AOSP_VENDOR;
|
||||
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
|
||||
return GZIP;
|
||||
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
|
||||
return LZOP;
|
||||
} else if (CHECKED_MATCH(XZ_MAGIC)) {
|
||||
return XZ;
|
||||
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
|
||||
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
|
||||
return LZMA;
|
||||
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
|
||||
return BZIP2;
|
||||
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
|
||||
return LZ4;
|
||||
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
|
||||
return LZ4_LEGACY;
|
||||
} else if (CHECKED_MATCH(MTK_MAGIC)) {
|
||||
return MTK;
|
||||
} else if (CHECKED_MATCH(DTB_MAGIC)) {
|
||||
return DTB;
|
||||
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
|
||||
return DHTB;
|
||||
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
|
||||
return BLOB;
|
||||
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
|
||||
return ZIMAGE;
|
||||
} else {
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
const char *Fmt2Name::operator[](format_t fmt) {
|
||||
switch (fmt) {
|
||||
case GZIP:
|
||||
return "gzip";
|
||||
case ZOPFLI:
|
||||
return "zopfli";
|
||||
case LZOP:
|
||||
return "lzop";
|
||||
case XZ:
|
||||
return "xz";
|
||||
case LZMA:
|
||||
return "lzma";
|
||||
case BZIP2:
|
||||
return "bzip2";
|
||||
case LZ4:
|
||||
return "lz4";
|
||||
case LZ4_LEGACY:
|
||||
return "lz4_legacy";
|
||||
case LZ4_LG:
|
||||
return "lz4_lg";
|
||||
case DTB:
|
||||
return "dtb";
|
||||
case ZIMAGE:
|
||||
return "zimage";
|
||||
default:
|
||||
return "raw";
|
||||
}
|
||||
}
|
||||
|
||||
const char *Fmt2Ext::operator[](format_t fmt) {
|
||||
switch (fmt) {
|
||||
case GZIP:
|
||||
case ZOPFLI:
|
||||
return ".gz";
|
||||
case LZOP:
|
||||
return ".lzo";
|
||||
case XZ:
|
||||
return ".xz";
|
||||
case LZMA:
|
||||
return ".lzma";
|
||||
case BZIP2:
|
||||
return ".bz2";
|
||||
case LZ4:
|
||||
case LZ4_LEGACY:
|
||||
case LZ4_LG:
|
||||
return ".lz4";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK(s, f) else if (name == s) return f;
|
||||
|
||||
format_t Name2Fmt::operator[](std::string_view name) {
|
||||
if (0) {}
|
||||
CHECK("gzip", GZIP)
|
||||
CHECK("zopfli", ZOPFLI)
|
||||
CHECK("xz", XZ)
|
||||
CHECK("lzma", LZMA)
|
||||
CHECK("bzip2", BZIP2)
|
||||
CHECK("lz4", LZ4)
|
||||
CHECK("lz4_legacy", LZ4_LEGACY)
|
||||
CHECK("lz4_lg", LZ4_LG)
|
||||
else return UNKNOWN;
|
||||
}
|
||||
83
native/jni/boot/format.hpp
Normal file
83
native/jni/boot/format.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN,
|
||||
/* Boot formats */
|
||||
CHROMEOS,
|
||||
AOSP,
|
||||
AOSP_VENDOR,
|
||||
DHTB,
|
||||
BLOB,
|
||||
/* Compression formats */
|
||||
GZIP,
|
||||
ZOPFLI,
|
||||
XZ,
|
||||
LZMA,
|
||||
BZIP2,
|
||||
LZ4,
|
||||
LZ4_LEGACY,
|
||||
LZ4_LG,
|
||||
/* Unsupported compression */
|
||||
LZOP,
|
||||
/* Misc */
|
||||
MTK,
|
||||
DTB,
|
||||
ZIMAGE,
|
||||
} format_t;
|
||||
|
||||
#define COMPRESSED(fmt) ((fmt) >= GZIP && (fmt) < LZOP)
|
||||
#define COMPRESSED_ANY(fmt) ((fmt) >= GZIP && (fmt) <= LZOP)
|
||||
|
||||
#define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
|
||||
|
||||
#define BOOT_MAGIC "ANDROID!"
|
||||
#define VENDOR_BOOT_MAGIC "VNDRBOOT"
|
||||
#define CHROMEOS_MAGIC "CHROMEOS"
|
||||
#define GZIP1_MAGIC "\x1f\x8b"
|
||||
#define GZIP2_MAGIC "\x1f\x9e"
|
||||
#define LZOP_MAGIC "\x89""LZO"
|
||||
#define XZ_MAGIC "\xfd""7zXZ"
|
||||
#define BZIP_MAGIC "BZh"
|
||||
#define LZ4_LEG_MAGIC "\x02\x21\x4c\x18"
|
||||
#define LZ41_MAGIC "\x03\x21\x4c\x18"
|
||||
#define LZ42_MAGIC "\x04\x22\x4d\x18"
|
||||
#define MTK_MAGIC "\x88\x16\x88\x58"
|
||||
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
|
||||
#define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79"
|
||||
#define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00"
|
||||
#define SEANDROID_MAGIC "SEANDROIDENFORCE"
|
||||
#define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-"
|
||||
#define NOOKHD_RL_MAGIC "Red Loader"
|
||||
#define NOOKHD_GL_MAGIC "Green Loader"
|
||||
#define NOOKHD_GR_MAGIC "Green Recovery"
|
||||
#define NOOKHD_EB_MAGIC "eMMC boot.img+secondloader"
|
||||
#define NOOKHD_ER_MAGIC "eMMC recovery.img+secondloader"
|
||||
#define NOOKHD_PRE_HEADER_SZ 1048576
|
||||
#define ACCLAIM_MAGIC "BauwksBoot"
|
||||
#define ACCLAIM_PRE_HEADER_SZ 262144
|
||||
#define AVB_FOOTER_MAGIC "AVBf"
|
||||
#define AVB_MAGIC "AVB0"
|
||||
#define ZIMAGE_MAGIC "\x18\x28\x6f\x01"
|
||||
|
||||
class Fmt2Name {
|
||||
public:
|
||||
const char *operator[](format_t fmt);
|
||||
};
|
||||
|
||||
class Fmt2Ext {
|
||||
public:
|
||||
const char *operator[](format_t fmt);
|
||||
};
|
||||
|
||||
class Name2Fmt {
|
||||
public:
|
||||
format_t operator[](std::string_view name);
|
||||
};
|
||||
|
||||
format_t check_fmt(const void *buf, size_t len);
|
||||
|
||||
extern Name2Fmt name2fmt;
|
||||
extern Fmt2Name fmt2name;
|
||||
extern Fmt2Ext fmt2ext;
|
||||
41
native/jni/boot/hexpatch.cpp
Normal file
41
native/jni/boot/hexpatch.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
#include "magiskboot.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void hex2byte(const char *hex, uint8_t *buf) {
|
||||
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';
|
||||
buf[i / 2] = ((high > 9 ? high - 7 : high) << 4) + (low > 9 ? low - 7 : low);
|
||||
}
|
||||
}
|
||||
|
||||
int hexpatch(const char *file, const char *from, const char *to) {
|
||||
int patched = 1;
|
||||
|
||||
auto m = mmap_data(file, true);
|
||||
|
||||
vector<uint8_t> pattern(strlen(from) / 2);
|
||||
vector<uint8_t> patch(strlen(to) / 2);
|
||||
|
||||
hex2byte(from, pattern.data());
|
||||
hex2byte(to, patch.data());
|
||||
|
||||
uint8_t * const end = m.buf + m.sz;
|
||||
for (uint8_t *curr = m.buf; curr < end; curr += pattern.size()) {
|
||||
curr = static_cast<uint8_t*>(memmem(curr, end - curr, pattern.data(), pattern.size()));
|
||||
if (curr == nullptr)
|
||||
return patched;
|
||||
fprintf(stderr, "Patch @ %08X [%s] -> [%s]\n", (unsigned)(curr - m.buf), from, to);
|
||||
memset(curr, 0, pattern.size());
|
||||
memcpy(curr, patch.data(), patch.size());
|
||||
patched = 0;
|
||||
}
|
||||
|
||||
return patched;
|
||||
}
|
||||
24
native/jni/boot/magiskboot.hpp
Normal file
24
native/jni/boot/magiskboot.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define HEADER_FILE "header"
|
||||
#define KERNEL_FILE "kernel"
|
||||
#define RAMDISK_FILE "ramdisk.cpio"
|
||||
#define SECOND_FILE "second"
|
||||
#define EXTRA_FILE "extra"
|
||||
#define KER_DTB_FILE "kernel_dtb"
|
||||
#define RECV_DTBO_FILE "recovery_dtbo"
|
||||
#define DTB_FILE "dtb"
|
||||
#define NEW_BOOT "new-boot.img"
|
||||
|
||||
int unpack(const char *image, bool skip_decomp = false, bool hdr = false);
|
||||
void repack(const char *src_img, const char *out_img, bool skip_comp = false);
|
||||
int split_image_dtb(const char *filename);
|
||||
int hexpatch(const char *file, const char *from, const char *to);
|
||||
int cpio_commands(int argc, char *argv[]);
|
||||
int dtb_commands(int argc, char *argv[]);
|
||||
|
||||
uint32_t patch_verity(void *buf, uint32_t size);
|
||||
uint32_t patch_encryption(void *buf, uint32_t size);
|
||||
bool check_env(const char *name);
|
||||
192
native/jni/boot/main.cpp
Normal file
192
native/jni/boot/main.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
#include <mincrypt/sha.h>
|
||||
#include <base.hpp>
|
||||
|
||||
#include "magiskboot.hpp"
|
||||
#include "compress.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void print_formats() {
|
||||
for (int fmt = GZIP; fmt < LZOP; ++fmt) {
|
||||
fprintf(stderr, "%s ", fmt2name[(format_t) fmt]);
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(char *arg0) {
|
||||
fprintf(stderr,
|
||||
R"EOF(MagiskBoot - Boot Image Modification Tool
|
||||
|
||||
Usage: %s <action> [args...]
|
||||
|
||||
Supported actions:
|
||||
unpack [-n] [-h] <bootimg>
|
||||
Unpack <bootimg> to, if available, kernel, kernel_dtb, ramdisk.cpio,
|
||||
second, dtb, extra, and recovery_dtbo into current directory.
|
||||
If '-n' is provided, it will not attempt to decompress kernel or
|
||||
ramdisk.cpio from their original formats.
|
||||
If '-h' is provided, it will dump header info to 'header',
|
||||
which will be parsed when repacking.
|
||||
Return values:
|
||||
0:valid 1:error 2:chromeos
|
||||
|
||||
repack [-n] <origbootimg> [outbootimg]
|
||||
Repack boot image components from current directory
|
||||
to [outbootimg], or new-boot.img if not specified.
|
||||
If '-n' is provided, it will not attempt to recompress ramdisk.cpio,
|
||||
otherwise it will compress ramdisk.cpio and kernel with the same format
|
||||
as in <origbootimg> if the file provided is not already compressed.
|
||||
If env variable PATCHVBMETAFLAG is set to true, all disable flags will
|
||||
be set in the vbmeta header.
|
||||
|
||||
hexpatch <file> <hexpattern1> <hexpattern2>
|
||||
Search <hexpattern1> in <file>, and replace with <hexpattern2>
|
||||
|
||||
cpio <incpio> [commands...]
|
||||
Do cpio commands to <incpio> (modifications are done in-place)
|
||||
Each command is a single argument, add quotes for each command
|
||||
Supported commands:
|
||||
exists ENTRY
|
||||
Return 0 if ENTRY exists, else return 1
|
||||
rm [-r] ENTRY
|
||||
Remove ENTRY, specify [-r] to remove recursively
|
||||
mkdir MODE ENTRY
|
||||
Create directory ENTRY in permissions MODE
|
||||
ln TARGET ENTRY
|
||||
Create a symlink to TARGET with the name ENTRY
|
||||
mv SOURCE DEST
|
||||
Move SOURCE to DEST
|
||||
add MODE ENTRY INFILE
|
||||
Add INFILE as ENTRY in permissions MODE; replaces ENTRY if exists
|
||||
extract [ENTRY OUT]
|
||||
Extract ENTRY to OUT, or extract all entries to current directory
|
||||
test
|
||||
Test the current cpio's status
|
||||
Return value is 0 or bitwise or-ed of following values:
|
||||
0x1:Magisk 0x2:unsupported 0x4:Sony
|
||||
patch
|
||||
Apply ramdisk patches
|
||||
Configure with env variables: KEEPVERITY KEEPFORCEENCRYPT
|
||||
backup ORIG
|
||||
Create ramdisk backups from ORIG
|
||||
restore
|
||||
Restore ramdisk from ramdisk backup stored within incpio
|
||||
sha1
|
||||
Print stock boot SHA1 if previously backed up in ramdisk
|
||||
|
||||
dtb <input> <action> [args...]
|
||||
Do dtb related actions to <input>
|
||||
Supported actions:
|
||||
print [-f]
|
||||
Print all contents of dtb for debugging
|
||||
Specify [-f] to only print fstab nodes
|
||||
patch
|
||||
Search for fstab and remove verity/avb
|
||||
Modifications are done directly to the file in-place
|
||||
Configure with env variables: KEEPVERITY
|
||||
|
||||
split <input>
|
||||
Split image.*-dtb into kernel + kernel_dtb
|
||||
|
||||
sha1 <file>
|
||||
Print the SHA1 checksum for <file>
|
||||
|
||||
cleanup
|
||||
Cleanup the current working directory
|
||||
|
||||
compress[=format] <infile> [outfile]
|
||||
Compress <infile> with [format] (default: gzip), optionally to [outfile]
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT
|
||||
Supported formats: )EOF", arg0);
|
||||
|
||||
print_formats();
|
||||
|
||||
fprintf(stderr, R"EOF(
|
||||
|
||||
decompress <infile> [outfile]
|
||||
Detect format and decompress <infile>, optionally to [outfile]
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT
|
||||
Supported formats: )EOF");
|
||||
|
||||
print_formats();
|
||||
|
||||
fprintf(stderr, "\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
cmdline_logging();
|
||||
umask(0);
|
||||
|
||||
if (argc < 2)
|
||||
usage(argv[0]);
|
||||
|
||||
// Skip '--' for backwards compatibility
|
||||
string_view action(argv[1]);
|
||||
if (str_starts(action, "--"))
|
||||
action = argv[1] + 2;
|
||||
|
||||
if (action == "cleanup") {
|
||||
fprintf(stderr, "Cleaning up...\n");
|
||||
unlink(HEADER_FILE);
|
||||
unlink(KERNEL_FILE);
|
||||
unlink(RAMDISK_FILE);
|
||||
unlink(SECOND_FILE);
|
||||
unlink(KER_DTB_FILE);
|
||||
unlink(EXTRA_FILE);
|
||||
unlink(RECV_DTBO_FILE);
|
||||
unlink(DTB_FILE);
|
||||
} else if (argc > 2 && action == "sha1") {
|
||||
uint8_t sha1[SHA_DIGEST_SIZE];
|
||||
auto m = mmap_data(argv[2]);
|
||||
SHA_hash(m.buf, m.sz, sha1);
|
||||
for (uint8_t i : sha1)
|
||||
printf("%02x", i);
|
||||
printf("\n");
|
||||
} else if (argc > 2 && action == "split") {
|
||||
return split_image_dtb(argv[2]);
|
||||
} else if (argc > 2 && action == "unpack") {
|
||||
int idx = 2;
|
||||
bool nodecomp = false;
|
||||
bool hdr = false;
|
||||
for (;;) {
|
||||
if (idx >= argc)
|
||||
usage(argv[0]);
|
||||
if (argv[idx][0] != '-')
|
||||
break;
|
||||
for (char *flag = &argv[idx][1]; *flag; ++flag) {
|
||||
if (*flag == 'n')
|
||||
nodecomp = true;
|
||||
else if (*flag == 'h')
|
||||
hdr = true;
|
||||
else
|
||||
usage(argv[0]);
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return unpack(argv[idx], nodecomp, hdr);
|
||||
} else if (argc > 2 && action == "repack") {
|
||||
if (argv[2] == "-n"sv) {
|
||||
if (argc == 3)
|
||||
usage(argv[0]);
|
||||
repack(argv[3], argv[4] ? argv[4] : NEW_BOOT, true);
|
||||
} else {
|
||||
repack(argv[2], argv[3] ? argv[3] : NEW_BOOT);
|
||||
}
|
||||
} else if (argc > 2 && action == "decompress") {
|
||||
decompress(argv[2], argv[3]);
|
||||
} else if (argc > 2 && str_starts(action, "compress")) {
|
||||
compress(action[8] == '=' ? &action[9] : "gzip", argv[2], argv[3]);
|
||||
} else if (argc > 4 && action == "hexpatch") {
|
||||
return hexpatch(argv[2], argv[3], argv[4]);
|
||||
} else if (argc > 2 && action == "cpio"sv) {
|
||||
if (cpio_commands(argc - 2, argv + 2))
|
||||
usage(argv[0]);
|
||||
} else if (argc > 3 && action == "dtb") {
|
||||
if (dtb_commands(argc - 2, argv + 2))
|
||||
usage(argv[0]);
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
64
native/jni/boot/pattern.cpp
Normal file
64
native/jni/boot/pattern.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <base.hpp>
|
||||
|
||||
#include "magiskboot.hpp"
|
||||
|
||||
#define MATCH(p) else if (strncmp(s + skip, p, sizeof(p) - 1) == 0) skip += (sizeof(p) - 1)
|
||||
|
||||
static int skip_verity_pattern(const char *s) {
|
||||
int skip = s[0] == ',';
|
||||
|
||||
if (0) {}
|
||||
MATCH("verifyatboot");
|
||||
MATCH("verify");
|
||||
MATCH("avb_keys");
|
||||
MATCH("avb");
|
||||
MATCH("support_scfs");
|
||||
MATCH("fsverity");
|
||||
else return -1;
|
||||
|
||||
if (s[skip] == '=') {
|
||||
while (!strchr(" \n,", s[skip]))
|
||||
++skip;
|
||||
}
|
||||
return skip;
|
||||
}
|
||||
|
||||
static int skip_encryption_pattern(const char *s) {
|
||||
int skip = s[0] == ',';
|
||||
|
||||
if (0) {}
|
||||
MATCH("forceencrypt");
|
||||
MATCH("forcefdeorfbe");
|
||||
MATCH("fileencryption");
|
||||
else return -1;
|
||||
|
||||
if (s[skip] == '=') {
|
||||
while (!strchr(" \n,", s[skip]))
|
||||
++skip;
|
||||
}
|
||||
return skip;
|
||||
}
|
||||
|
||||
static uint32_t remove_pattern(char *src, uint32_t size, int(*pattern_skip)(const char *)) {
|
||||
int orig_sz = size;
|
||||
int write = 0;
|
||||
for (int read = 0; read < orig_sz;) {
|
||||
if (int skip = pattern_skip(src + read); skip > 0) {
|
||||
fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read);
|
||||
size -= skip;
|
||||
read += skip;
|
||||
} else {
|
||||
src[write++] = src[read++];
|
||||
}
|
||||
}
|
||||
memset(src + write, 0, orig_sz - write);
|
||||
return size;
|
||||
}
|
||||
|
||||
uint32_t patch_verity(void *buf, uint32_t size) {
|
||||
return remove_pattern(static_cast<char *>(buf), size, skip_verity_pattern);
|
||||
}
|
||||
|
||||
uint32_t patch_encryption(void *buf, uint32_t size) {
|
||||
return remove_pattern(static_cast<char *>(buf), size, skip_encryption_pattern);
|
||||
}
|
||||
288
native/jni/boot/ramdisk.cpp
Normal file
288
native/jni/boot/ramdisk.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
#include <base.hpp>
|
||||
|
||||
#include "cpio.hpp"
|
||||
#include "magiskboot.hpp"
|
||||
#include "compress.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const char *UNSUPPORT_LIST[] =
|
||||
{ "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc",
|
||||
"boot/sbin/launch_daemonsu.sh" };
|
||||
|
||||
static const char *MAGISK_LIST[] =
|
||||
{ ".backup/.magisk", "init.magisk.rc",
|
||||
"overlay/init.magisk.rc" };
|
||||
|
||||
class magisk_cpio : public cpio {
|
||||
public:
|
||||
void patch();
|
||||
int test();
|
||||
char *sha1();
|
||||
void restore();
|
||||
void backup(const char *orig);
|
||||
};
|
||||
|
||||
bool check_env(const char *name) {
|
||||
const char *val = getenv(name);
|
||||
return val != nullptr && val == "true"sv;
|
||||
}
|
||||
|
||||
void magisk_cpio::patch() {
|
||||
bool keepverity = check_env("KEEPVERITY");
|
||||
bool keepforceencrypt = check_env("KEEPFORCEENCRYPT");
|
||||
fprintf(stderr, "Patch with flag KEEPVERITY=[%s] KEEPFORCEENCRYPT=[%s]\n",
|
||||
keepverity ? "true" : "false", keepforceencrypt ? "true" : "false");
|
||||
|
||||
for (auto it = entries.begin(); it != entries.end();) {
|
||||
auto cur = it++;
|
||||
bool fstab = (!keepverity || !keepforceencrypt) &&
|
||||
S_ISREG(cur->second->mode) &&
|
||||
!str_starts(cur->first, ".backup") &&
|
||||
!str_contains(cur->first, "twrp") &&
|
||||
!str_contains(cur->first, "recovery") &&
|
||||
str_contains(cur->first, "fstab");
|
||||
if (!keepverity) {
|
||||
if (fstab) {
|
||||
fprintf(stderr, "Found fstab file [%s]\n", cur->first.data());
|
||||
cur->second->filesize = patch_verity(cur->second->data, cur->second->filesize);
|
||||
} else if (cur->first == "verity_key") {
|
||||
rm(cur);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!keepforceencrypt) {
|
||||
if (fstab) {
|
||||
cur->second->filesize = patch_encryption(cur->second->data, cur->second->filesize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define MAGISK_PATCHED (1 << 0)
|
||||
#define UNSUPPORTED_CPIO (1 << 1)
|
||||
#define SONY_INIT (1 << 2)
|
||||
|
||||
int magisk_cpio::test() {
|
||||
int ret = 0;
|
||||
for (auto file : UNSUPPORT_LIST) {
|
||||
if (exists(file)) {
|
||||
return UNSUPPORTED_CPIO;
|
||||
}
|
||||
}
|
||||
for (auto file : MAGISK_LIST) {
|
||||
if (exists(file)) {
|
||||
ret |= MAGISK_PATCHED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exists("init.real"))
|
||||
ret |= SONY_INIT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define for_each_line(line, buf, size) \
|
||||
for (line = (char *) buf; line < (char *) buf + size && line[0]; line = strchr(line + 1, '\n') + 1)
|
||||
|
||||
char *magisk_cpio::sha1() {
|
||||
char sha1[41];
|
||||
char *line;
|
||||
for (auto &e : entries) {
|
||||
if (e.first == "init.magisk.rc" || e.first == "overlay/init.magisk.rc") {
|
||||
for_each_line(line, e.second->data, e.second->filesize) {
|
||||
if (strncmp(line, "#STOCKSHA1=", 11) == 0) {
|
||||
strncpy(sha1, line + 12, 40);
|
||||
sha1[40] = '\0';
|
||||
return strdup(sha1);
|
||||
}
|
||||
}
|
||||
} else if (e.first == ".backup/.magisk") {
|
||||
for_each_line(line, e.second->data, e.second->filesize) {
|
||||
if (str_starts(line, "SHA1=")) {
|
||||
strncpy(sha1, line + 5, 40);
|
||||
sha1[40] = '\0';
|
||||
return strdup(sha1);
|
||||
}
|
||||
}
|
||||
} else if (e.first == ".backup/.sha1") {
|
||||
return (char *) e.second->data;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define for_each_str(str, buf, size) \
|
||||
for (str = (char *) buf; str < (char *) buf + size; str = str += strlen(str) + 1)
|
||||
|
||||
void magisk_cpio::restore() {
|
||||
if (auto it = entries.find(".backup/.rmlist"); it != entries.end()) {
|
||||
char *file;
|
||||
for_each_str(file, it->second->data, it->second->filesize) {
|
||||
rm(file);
|
||||
}
|
||||
rm(it);
|
||||
}
|
||||
|
||||
for (auto it = entries.begin(); it != entries.end();) {
|
||||
auto cur = it++;
|
||||
if (str_starts(cur->first, ".backup")) {
|
||||
if (cur->first.length() == 7 || &cur->first[8] == ".magisk"sv) {
|
||||
rm(cur);
|
||||
} else {
|
||||
mv(cur, &cur->first[8]);
|
||||
}
|
||||
} else if (str_starts(cur->first, "magisk") ||
|
||||
cur->first == "overlay/init.magisk.rc" ||
|
||||
cur->first == "sbin/magic_mask.sh" ||
|
||||
cur->first == "init.magisk.rc") {
|
||||
// Some known stuff we can remove
|
||||
rm(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void magisk_cpio::backup(const char *orig) {
|
||||
if (access(orig, R_OK))
|
||||
return;
|
||||
|
||||
entry_map backups;
|
||||
string rm_list;
|
||||
backups.emplace(".backup", new cpio_entry(S_IFDIR));
|
||||
|
||||
magisk_cpio o;
|
||||
o.load_cpio(orig);
|
||||
|
||||
// Remove existing backups in original ramdisk
|
||||
o.rm(".backup", true);
|
||||
rm(".backup", true);
|
||||
|
||||
auto lhs = o.entries.begin();
|
||||
auto rhs = entries.begin();
|
||||
|
||||
while (lhs != o.entries.end() || rhs != entries.end()) {
|
||||
int res;
|
||||
bool do_backup = false;
|
||||
if (lhs != o.entries.end() && rhs != entries.end()) {
|
||||
res = lhs->first.compare(rhs->first);
|
||||
} else if (lhs == o.entries.end()) {
|
||||
res = 1;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
// Something is missing in new ramdisk, do_backup!
|
||||
do_backup = true;
|
||||
fprintf(stderr, "Backup missing entry: ");
|
||||
} else if (res == 0) {
|
||||
if (lhs->second->filesize != rhs->second->filesize ||
|
||||
memcmp(lhs->second->data, rhs->second->data, lhs->second->filesize) != 0) {
|
||||
// Not the same!
|
||||
do_backup = true;
|
||||
fprintf(stderr, "Backup mismatch entry: ");
|
||||
}
|
||||
} else {
|
||||
// Something new in ramdisk
|
||||
rm_list += rhs->first;
|
||||
rm_list += (char) '\0';
|
||||
fprintf(stderr, "Record new entry: [%s] -> [.backup/.rmlist]\n", rhs->first.data());
|
||||
}
|
||||
|
||||
if (do_backup) {
|
||||
string name = ".backup/" + lhs->first;
|
||||
fprintf(stderr, "[%s] -> [%s]\n", lhs->first.data(), name.data());
|
||||
auto e = lhs->second.release();
|
||||
backups.emplace(name, e);
|
||||
}
|
||||
|
||||
// Increment positions
|
||||
if (res < 0) {
|
||||
++lhs;
|
||||
} else if (res == 0) {
|
||||
++lhs; ++rhs;
|
||||
} else {
|
||||
++rhs;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rm_list.empty()) {
|
||||
auto rm_list_file = new cpio_entry(S_IFREG);
|
||||
rm_list_file->filesize = rm_list.length();
|
||||
rm_list_file->data = xmalloc(rm_list.length());
|
||||
memcpy(rm_list_file->data, rm_list.data(), rm_list.length());
|
||||
backups.emplace(".backup/.rmlist", rm_list_file);
|
||||
}
|
||||
|
||||
if (backups.size() > 1)
|
||||
entries.merge(backups);
|
||||
}
|
||||
|
||||
int cpio_commands(int argc, char *argv[]) {
|
||||
char *incpio = argv[0];
|
||||
++argv;
|
||||
--argc;
|
||||
|
||||
magisk_cpio cpio;
|
||||
if (access(incpio, R_OK) == 0)
|
||||
cpio.load_cpio(incpio);
|
||||
|
||||
int cmdc;
|
||||
char *cmdv[6];
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
// Reset
|
||||
cmdc = 0;
|
||||
memset(cmdv, 0, sizeof(cmdv));
|
||||
|
||||
// Split the commands
|
||||
char *tok = strtok(argv[i], " ");
|
||||
while (tok && cmdc < std::size(cmdv)) {
|
||||
if (cmdc == 0 && tok[0] == '#')
|
||||
break;
|
||||
cmdv[cmdc++] = tok;
|
||||
tok = strtok(nullptr, " ");
|
||||
}
|
||||
|
||||
if (cmdc == 0)
|
||||
continue;
|
||||
|
||||
if (cmdv[0] == "test"sv) {
|
||||
exit(cpio.test());
|
||||
} else if (cmdv[0] == "restore"sv) {
|
||||
cpio.restore();
|
||||
} else if (cmdv[0] == "sha1"sv) {
|
||||
char *sha1 = cpio.sha1();
|
||||
if (sha1) printf("%s\n", sha1);
|
||||
return 0;
|
||||
} else if (cmdv[0] == "patch"sv) {
|
||||
cpio.patch();
|
||||
} else if (cmdc == 2 && cmdv[0] == "exists"sv) {
|
||||
exit(!cpio.exists(cmdv[1]));
|
||||
} else if (cmdc == 2 && cmdv[0] == "backup"sv) {
|
||||
cpio.backup(cmdv[1]);
|
||||
} else if (cmdc >= 2 && cmdv[0] == "rm"sv) {
|
||||
bool r = cmdc > 2 && cmdv[1] == "-r"sv;
|
||||
cpio.rm(cmdv[1 + r], r);
|
||||
} else if (cmdc == 3 && cmdv[0] == "mv"sv) {
|
||||
cpio.mv(cmdv[1], cmdv[2]);
|
||||
} else if (cmdv[0] == "extract"sv) {
|
||||
if (cmdc == 3) {
|
||||
return !cpio.extract(cmdv[1], cmdv[2]);
|
||||
} else {
|
||||
cpio.extract();
|
||||
return 0;
|
||||
}
|
||||
} else if (cmdc == 3 && cmdv[0] == "mkdir"sv) {
|
||||
cpio.mkdir(strtoul(cmdv[1], nullptr, 8), cmdv[2]);
|
||||
} else if (cmdc == 3 && cmdv[0] == "ln"sv) {
|
||||
cpio.ln(cmdv[1], cmdv[2]);
|
||||
} else if (cmdc == 4 && cmdv[0] == "add"sv) {
|
||||
cpio.add(strtoul(cmdv[1], nullptr, 8), cmdv[2], cmdv[3]);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
cpio.dump(incpio);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user