mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-27 10:07:54 +00:00
528601d25a
- Use ftruncate64 instead of ftruncate to workaround seccomp - Cast uint32_t to off64_t before making it negative Note: Using ftruncate with a modern NDK libc should actually be fine as the syscall wrapper in bionic will use ftruncate64 internally. However, since we are using the libc.a from r10e built for Gingerbread, seccomp wasn't a thing back then, and also the ftruncate64 symbol is missing; we have to create our own wrapper and call it instead on 32-bit ABIs. Props to @jnotuo for discovering the overflow bug and seccomp issue Fix #3703, close #4915
755 lines
25 KiB
C++
755 lines
25 KiB
C++
#include <functional>
|
|
#include <memory>
|
|
|
|
#include <libfdt.h>
|
|
#include <mincrypt/sha.h>
|
|
#include <mincrypt/sha256.h>
|
|
#include <utils.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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
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);
|
|
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");
|
|
fprintf(fp, "pagesize=%u\n", page_size());
|
|
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 == "page_size") {
|
|
page_size() = parse_int(value);
|
|
} else 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) {
|
|
mmap_ro(image, map_addr, map_size);
|
|
fprintf(stderr, "Parsing boot image: [%s]\n", image);
|
|
for (uint8_t *addr = map_addr; addr < map_addr + map_size; ++addr) {
|
|
format_t fmt = check_fmt(addr, map_size);
|
|
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() {
|
|
munmap(map_addr, map_size);
|
|
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
|
|
unsigned off = 4;
|
|
unsigned 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 get_block(name) {\
|
|
name = addr + off; \
|
|
off += hdr->name##_size(); \
|
|
off = do_align(off, hdr->page_size()); \
|
|
}
|
|
|
|
#define CMD_MATCH(s) BUFFER_MATCH(h->cmdline, s)
|
|
|
|
void boot_img::parse_image(uint8_t *addr, format_t type) {
|
|
auto h = reinterpret_cast<boot_img_hdr_v0*>(addr);
|
|
if (type == AOSP_VENDOR) {
|
|
fprintf(stderr, "VENDOR_BOOT_HDR\n");
|
|
switch (h->header_version) {
|
|
case 4:
|
|
hdr = new dyn_img_vnd_v4(addr);
|
|
break;
|
|
case 3:
|
|
default:
|
|
hdr = new dyn_img_vnd_v3(addr);
|
|
break;
|
|
}
|
|
} else if (h->page_size >= 0x02000000) {
|
|
fprintf(stderr, "PXA_BOOT_HDR\n");
|
|
hdr = new dyn_img_pxa(addr);
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
switch (h->header_version) {
|
|
case 1:
|
|
hdr = new dyn_img_v1(addr);
|
|
break;
|
|
case 2:
|
|
hdr = new dyn_img_v2(addr);
|
|
break;
|
|
case 3:
|
|
hdr = new dyn_img_v3(addr);
|
|
break;
|
|
case 4:
|
|
hdr = new dyn_img_v4(addr);
|
|
break;
|
|
default:
|
|
hdr = new dyn_img_v0(addr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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();
|
|
hdr_addr = addr;
|
|
get_block(kernel);
|
|
get_block(ramdisk);
|
|
get_block(second);
|
|
get_block(extra);
|
|
get_block(recovery_dtbo);
|
|
get_block(dtb);
|
|
|
|
if (int dtb_off = find_dtb_offset(kernel, hdr->kernel_size()); dtb_off > 0) {
|
|
kernel_dtb = kernel + dtb_off;
|
|
hdr->kernel_dt_size = hdr->kernel_size() - dtb_off;
|
|
hdr->kernel_size() = dtb_off;
|
|
fprintf(stderr, "%-*s [%u]\n", PADDING, "KERNEL_DTB_SZ", hdr->kernel_dt_size);
|
|
}
|
|
|
|
if (auto size = hdr->kernel_size()) {
|
|
k_fmt = check_fmt_lg(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);
|
|
uint32_t end = z_hdr->end_offset;
|
|
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;
|
|
uint8_t *end_addr = kernel + z_hdr->end_offset;
|
|
for (uint8_t *end_ptr = end_addr - 4; end_ptr >= end_addr - 64; end_ptr -= 4) {
|
|
uint32_t val;
|
|
memcpy(&val, end_ptr, sizeof(val));
|
|
if (z_hdr->end_offset - val < 0xFF && val < end) {
|
|
end = val;
|
|
}
|
|
}
|
|
if (end == z_hdr->end_offset) {
|
|
fprintf(stderr, "Could not find end of zImage gzip data, keeping raw kernel\n");
|
|
} else {
|
|
flags[ZIMAGE_KERNEL] = true;
|
|
z_info.tail = kernel + end;
|
|
z_info.tail_sz = hdr->kernel_size() - end;
|
|
kernel += z_info.hdr_sz;
|
|
hdr->kernel_size() = end - z_info.hdr_sz;
|
|
k_fmt = check_fmt_lg(kernel, hdr->kernel_size());
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Could not find zImage gzip data, keeping raw kernel\n");
|
|
}
|
|
}
|
|
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name[k_fmt]);
|
|
}
|
|
if (auto size = hdr->ramdisk_size()) {
|
|
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_addr + map_size) {
|
|
tail = addr + off;
|
|
tail_size = map_addr + map_size - 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) {
|
|
uint8_t *buf;
|
|
size_t sz;
|
|
mmap_ro(filename, buf, sz);
|
|
run_finally f([=]{ munmap(buf, sz); });
|
|
|
|
if (int off = find_dtb_offset(buf, sz); off > 0) {
|
|
format_t fmt = check_fmt_lg(buf, sz);
|
|
if (COMPRESSED(fmt)) {
|
|
int fd = creat(KERNEL_FILE, 0644);
|
|
decompress(fmt, fd, buf, off);
|
|
close(fd);
|
|
} else {
|
|
dump(buf, off, KERNEL_FILE);
|
|
}
|
|
dump(buf + off, 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() \
|
|
write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR) - off.header, 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_addr, sizeof(blob_hdr));
|
|
} else if (boot.flags[NOOKHD_FLAG]) {
|
|
xwrite(fd, boot.map_addr, NOOKHD_PRE_HEADER_SZ);
|
|
} else if (boot.flags[ACCLAIM_FLAG]) {
|
|
xwrite(fd, boot.map_addr, 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);
|
|
}
|
|
size_t raw_size;
|
|
if (access(KERNEL_FILE, R_OK) == 0) {
|
|
void *raw_buf;
|
|
mmap_ro(KERNEL_FILE, raw_buf, raw_size);
|
|
if (!COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.k_fmt)) {
|
|
hdr->kernel_size() = compress(boot.k_fmt, fd, raw_buf, raw_size);
|
|
} else {
|
|
hdr->kernel_size() = xwrite(fd, raw_buf, raw_size);
|
|
}
|
|
|
|
if (boot.flags[ZIMAGE_KERNEL] &&
|
|
boot.k_fmt == GZIP && hdr->kernel_size() > boot.hdr->kernel_size()) {
|
|
// Revert and try zipfoli
|
|
ftruncate64(fd, lseek64(fd, -(off64_t)hdr->kernel_size(), SEEK_CUR));
|
|
hdr->kernel_size() = compress(ZOPFLI, fd, raw_buf, raw_size);
|
|
}
|
|
|
|
munmap(raw_buf, raw_size);
|
|
}
|
|
if (boot.flags[ZIMAGE_KERNEL]) {
|
|
if (hdr->kernel_size() > boot.hdr->kernel_size()) {
|
|
LOGW("Recompressed kernel is too large, using original kernel\n");
|
|
ftruncate64(fd, lseek64(fd, -(off64_t)hdr->kernel_size(), SEEK_CUR));
|
|
hdr->kernel_size() = xwrite(fd, boot.z_info.tail - boot.hdr->kernel_size(), boot.hdr->kernel_size());
|
|
} else {
|
|
write_zero(fd, boot.hdr->kernel_size() - hdr->kernel_size() - 4);
|
|
uint32_t sz = raw_size;
|
|
xwrite(fd, &sz, sizeof(sz));
|
|
hdr->kernel_size() = boot.hdr->kernel_size();
|
|
}
|
|
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) {
|
|
size_t raw_size;
|
|
void *raw_buf;
|
|
mmap_ro(RAMDISK_FILE, raw_buf, raw_size);
|
|
if (!skip_comp && !COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.r_fmt)) {
|
|
hdr->ramdisk_size() = compress(boot.r_fmt, fd, raw_buf, raw_size);
|
|
} else {
|
|
hdr->ramdisk_size() = xwrite(fd, raw_buf, raw_size);
|
|
}
|
|
munmap(raw_buf, raw_size);
|
|
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) {
|
|
size_t raw_size;
|
|
void *raw_buf;
|
|
mmap_ro(EXTRA_FILE, raw_buf, raw_size);
|
|
if (!skip_comp && !COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.e_fmt)) {
|
|
hdr->extra_size() = compress(boot.e_fmt, fd, raw_buf, raw_size);
|
|
} else {
|
|
hdr->extra_size() = xwrite(fd, raw_buf, raw_size);
|
|
}
|
|
munmap(raw_buf, raw_size);
|
|
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();
|
|
}
|
|
|
|
// 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
|
|
off.vbmeta = lseek(fd, 0, SEEK_CUR);
|
|
if (boot.flags[AVB_FLAG]) {
|
|
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_size) {
|
|
write_zero(fd, boot.map_size - current);
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
|
|
/******************
|
|
* Patch the image
|
|
******************/
|
|
|
|
// Map output image as rw
|
|
uint8_t *new_addr;
|
|
size_t new_size;
|
|
mmap_rw(out_img, new_addr, new_size);
|
|
|
|
// MTK headers
|
|
if (boot.flags[MTK_KERNEL]) {
|
|
auto m_hdr = reinterpret_cast<mtk_hdr *>(new_addr + 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 *>(new_addr + 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, new_addr + off.kernel, size);
|
|
HASH_update(&ctx, &size, sizeof(size));
|
|
size = hdr->ramdisk_size();
|
|
HASH_update(&ctx, new_addr + off.ramdisk, size);
|
|
HASH_update(&ctx, &size, sizeof(size));
|
|
size = hdr->second_size();
|
|
HASH_update(&ctx, new_addr + off.second, size);
|
|
HASH_update(&ctx, &size, sizeof(size));
|
|
size = hdr->extra_size();
|
|
if (size) {
|
|
HASH_update(&ctx, new_addr + 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, new_addr + hdr->recovery_dtbo_offset(), size);
|
|
HASH_update(&ctx, &size, sizeof(size));
|
|
}
|
|
if (ver == 2) {
|
|
size = hdr->dtb_size();
|
|
HASH_update(&ctx, new_addr + 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(new_addr + off.header, hdr->raw_hdr(), hdr->hdr_size());
|
|
|
|
if (boot.flags[AVB_FLAG]) {
|
|
// Copy and patch AVB structures
|
|
auto footer = reinterpret_cast<AvbFooter*>(new_addr + new_size - sizeof(AvbFooter));
|
|
auto vbmeta = reinterpret_cast<AvbVBMetaImageHeader*>(new_addr + off.vbmeta);
|
|
memcpy(footer, boot.avb_footer, sizeof(AvbFooter));
|
|
footer->original_image_size = __builtin_bswap64(off.total);
|
|
footer->vbmeta_offset = __builtin_bswap64(off.vbmeta);
|
|
vbmeta->flags = __builtin_bswap32(3);
|
|
}
|
|
|
|
if (boot.flags[DHTB_FLAG]) {
|
|
// DHTB header
|
|
auto d_hdr = reinterpret_cast<dhtb_hdr *>(new_addr);
|
|
memcpy(d_hdr, DHTB_MAGIC, 8);
|
|
d_hdr->size = off.total - sizeof(dhtb_hdr);
|
|
SHA256_hash(new_addr + sizeof(dhtb_hdr), d_hdr->size, d_hdr->checksum);
|
|
} else if (boot.flags[BLOB_FLAG]) {
|
|
// Blob header
|
|
auto b_hdr = reinterpret_cast<blob_hdr *>(new_addr);
|
|
b_hdr->size = off.total - sizeof(blob_hdr);
|
|
}
|
|
|
|
munmap(new_addr, new_size);
|
|
}
|