mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-10-28 11:05:09 +00:00
Simplify magiskboot FFI
This commit is contained in:
@@ -87,7 +87,6 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
boot/bootimg.cpp \
|
boot/bootimg.cpp \
|
||||||
boot/format.cpp \
|
|
||||||
boot/boot-rs.cpp
|
boot/boot-rs.cpp
|
||||||
|
|
||||||
LOCAL_LDFLAGS := -static
|
LOCAL_LDFLAGS := -static
|
||||||
|
|||||||
@@ -48,6 +48,43 @@ static bool check_env(const char *name) {
|
|||||||
return val != nullptr && val == "true"sv;
|
return val != nullptr && val == "true"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileFormat check_fmt(const void *buf, size_t len) {
|
||||||
|
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
|
||||||
|
return FileFormat::CHROMEOS;
|
||||||
|
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
|
||||||
|
return FileFormat::AOSP;
|
||||||
|
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
|
||||||
|
return FileFormat::AOSP_VENDOR;
|
||||||
|
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
|
||||||
|
return FileFormat::GZIP;
|
||||||
|
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
|
||||||
|
return FileFormat::LZOP;
|
||||||
|
} else if (CHECKED_MATCH(XZ_MAGIC)) {
|
||||||
|
return FileFormat::XZ;
|
||||||
|
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
|
||||||
|
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
|
||||||
|
return FileFormat::LZMA;
|
||||||
|
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
|
||||||
|
return FileFormat::BZIP2;
|
||||||
|
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
|
||||||
|
return FileFormat::LZ4;
|
||||||
|
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
|
||||||
|
return FileFormat::LZ4_LEGACY;
|
||||||
|
} else if (CHECKED_MATCH(MTK_MAGIC)) {
|
||||||
|
return FileFormat::MTK;
|
||||||
|
} else if (CHECKED_MATCH(DTB_MAGIC)) {
|
||||||
|
return FileFormat::DTB;
|
||||||
|
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
|
||||||
|
return FileFormat::DHTB;
|
||||||
|
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
|
||||||
|
return FileFormat::BLOB;
|
||||||
|
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
|
||||||
|
return FileFormat::ZIMAGE;
|
||||||
|
} else {
|
||||||
|
return FileFormat::UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void dyn_img_hdr::print() const {
|
void dyn_img_hdr::print() const {
|
||||||
uint32_t ver = header_version();
|
uint32_t ver = header_version();
|
||||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "HEADER_VER", ver);
|
fprintf(stderr, "%-*s [%u]\n", PADDING, "HEADER_VER", ver);
|
||||||
@@ -472,7 +509,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
|||||||
fprintf(stderr, "! Could not find zImage piggy, keeping raw kernel\n");
|
fprintf(stderr, "! Could not find zImage piggy, keeping raw kernel\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name[k_fmt]);
|
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name(k_fmt));
|
||||||
}
|
}
|
||||||
if (auto size = hdr->ramdisk_size()) {
|
if (auto size = hdr->ramdisk_size()) {
|
||||||
if (hdr->vendor_ramdisk_table_size()) {
|
if (hdr->vendor_ramdisk_table_size()) {
|
||||||
@@ -493,7 +530,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK",
|
"%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK",
|
||||||
it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type),
|
it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type),
|
||||||
it.ramdisk_size, fmt2name[fmt]);
|
it.ramdisk_size, fmt2name(fmt));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
r_fmt = check_fmt_lg(ramdisk, size);
|
r_fmt = check_fmt_lg(ramdisk, size);
|
||||||
@@ -507,12 +544,12 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
|||||||
hdr->ramdisk_size() -= sizeof(mtk_hdr);
|
hdr->ramdisk_size() -= sizeof(mtk_hdr);
|
||||||
r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size());
|
r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size());
|
||||||
}
|
}
|
||||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name[r_fmt]);
|
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name(r_fmt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto size = hdr->extra_size()) {
|
if (auto size = hdr->extra_size()) {
|
||||||
e_fmt = check_fmt_lg(extra, size);
|
e_fmt = check_fmt_lg(extra, size);
|
||||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "EXTRA_FMT", fmt2name[e_fmt]);
|
fprintf(stderr, "%-*s [%s]\n", PADDING, "EXTRA_FMT", fmt2name(e_fmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tail.sz()) {
|
if (tail.sz()) {
|
||||||
@@ -523,8 +560,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
|||||||
} else if (tail.sz() >= 16 && BUFFER_MATCH(tail.buf(), LG_BUMP_MAGIC)) {
|
} else if (tail.sz() >= 16 && BUFFER_MATCH(tail.buf(), LG_BUMP_MAGIC)) {
|
||||||
fprintf(stderr, "LG_BUMP_IMAGE\n");
|
fprintf(stderr, "LG_BUMP_IMAGE\n");
|
||||||
flags[LG_BUMP_FLAG] = true;
|
flags[LG_BUMP_FLAG] = true;
|
||||||
} else if (!(tail.sz() >= 4 && BUFFER_MATCH(tail.buf(), AVB_MAGIC)) && verify()) {
|
} else if (verify()) {
|
||||||
// Check if the image is avb 1.0 signed
|
|
||||||
fprintf(stderr, "AVB1_SIGNED\n");
|
fprintf(stderr, "AVB1_SIGNED\n");
|
||||||
flags[AVB1_SIGNED_FLAG] = true;
|
flags[AVB1_SIGNED_FLAG] = true;
|
||||||
}
|
}
|
||||||
@@ -532,13 +568,13 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
|||||||
// Find AVB footer
|
// Find AVB footer
|
||||||
const void *footer = tail.buf() + tail.sz() - sizeof(AvbFooter);
|
const void *footer = tail.buf() + tail.sz() - sizeof(AvbFooter);
|
||||||
if (BUFFER_MATCH(footer, AVB_FOOTER_MAGIC)) {
|
if (BUFFER_MATCH(footer, AVB_FOOTER_MAGIC)) {
|
||||||
avb_footer = reinterpret_cast<const AvbFooter*>(footer);
|
avb_footer = static_cast<const AvbFooter*>(footer);
|
||||||
// Double check if meta header exists
|
// Double check if meta header exists
|
||||||
const void *meta = base_addr + __builtin_bswap64(avb_footer->vbmeta_offset);
|
const void *meta = base_addr + __builtin_bswap64(avb_footer->vbmeta_offset);
|
||||||
if (BUFFER_MATCH(meta, AVB_MAGIC)) {
|
if (BUFFER_MATCH(meta, AVB_MAGIC)) {
|
||||||
fprintf(stderr, "VBMETA\n");
|
fprintf(stderr, "VBMETA\n");
|
||||||
flags[AVB_FLAG] = true;
|
flags[AVB_FLAG] = true;
|
||||||
vbmeta = reinterpret_cast<const AvbVBMetaImageHeader*>(meta);
|
vbmeta = static_cast<const AvbVBMetaImageHeader*>(meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,10 +583,6 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool boot_img::verify(const char *cert) const {
|
|
||||||
return rust::verify_boot_image(*this, cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) {
|
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) {
|
||||||
mmap_data img(filename.data());
|
mmap_data img(filename.data());
|
||||||
|
|
||||||
@@ -801,7 +833,7 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
|
|||||||
// A v4 boot image ramdisk will have to be merged with other vendor ramdisks,
|
// A v4 boot image ramdisk will have to be merged with other vendor ramdisks,
|
||||||
// and they have to use the exact same compression method. v4 GKIs are required to
|
// and they have to use the exact same compression method. v4 GKIs are required to
|
||||||
// use lz4 (legacy), so hardcode the format here.
|
// use lz4 (legacy), so hardcode the format here.
|
||||||
fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name[r_fmt], fmt2name[FileFormat::LZ4_LEGACY]);
|
fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name(r_fmt), fmt2name(FileFormat::LZ4_LEGACY));
|
||||||
r_fmt = FileFormat::LZ4_LEGACY;
|
r_fmt = FileFormat::LZ4_LEGACY;
|
||||||
}
|
}
|
||||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(r_fmt)) {
|
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(r_fmt)) {
|
||||||
@@ -987,7 +1019,7 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
|
|||||||
// Sign the image after we finish patching the boot image
|
// Sign the image after we finish patching the boot image
|
||||||
if (boot.flags[AVB1_SIGNED_FLAG]) {
|
if (boot.flags[AVB1_SIGNED_FLAG]) {
|
||||||
byte_view payload(out.buf() + off.header, off.total - off.header);
|
byte_view payload(out.buf() + off.header, off.total - off.header);
|
||||||
auto sig = rust::sign_boot_image(payload, "/boot", nullptr, nullptr);
|
auto sig = sign_payload(payload);
|
||||||
if (!sig.empty()) {
|
if (!sig.empty()) {
|
||||||
lseek(fd, off.total, SEEK_SET);
|
lseek(fd, off.total, SEEK_SET);
|
||||||
xwrite(fd, sig.data(), sig.size());
|
xwrite(fd, sig.data(), sig.size());
|
||||||
@@ -997,33 +1029,15 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
int verify(rust::Utf8CStr image, const char *cert) {
|
void cleanup() {
|
||||||
const boot_img boot(image.data());
|
unlink(HEADER_FILE);
|
||||||
if (cert == nullptr) {
|
unlink(KERNEL_FILE);
|
||||||
// Boot image parsing already checks if the image is signed
|
unlink(RAMDISK_FILE);
|
||||||
return boot.flags[AVB1_SIGNED_FLAG] ? 0 : 1;
|
unlink(SECOND_FILE);
|
||||||
} else {
|
unlink(KER_DTB_FILE);
|
||||||
// Provide a custom certificate and re-verify
|
unlink(EXTRA_FILE);
|
||||||
return boot.verify(cert) ? 0 : 1;
|
unlink(RECV_DTBO_FILE);
|
||||||
}
|
unlink(DTB_FILE);
|
||||||
}
|
unlink(BOOTCONFIG_FILE);
|
||||||
|
rm_rf(VND_RAMDISK_DIR);
|
||||||
int sign(rust::Utf8CStr image, rust::Utf8CStr name, const char *cert, const char *key) {
|
|
||||||
const boot_img boot(image.data());
|
|
||||||
auto sig = rust::sign_boot_image(boot.payload, name.data(), cert, key);
|
|
||||||
if (sig.empty())
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
auto eof = boot.tail.buf() - boot.map.buf();
|
|
||||||
int fd = xopen(image.data(), O_WRONLY | O_CLOEXEC);
|
|
||||||
if (lseek(fd, eof, SEEK_SET) != eof || xwrite(fd, sig.data(), sig.size()) != sig.size()) {
|
|
||||||
close(fd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (auto off = lseek(fd, 0, SEEK_CUR); off < boot.map.sz()) {
|
|
||||||
// Wipe out rest of tail
|
|
||||||
write_zero(fd, boot.map.sz() - off);
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -676,7 +676,12 @@ struct boot_img {
|
|||||||
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type);
|
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type);
|
||||||
|
|
||||||
// Rust FFI
|
// Rust FFI
|
||||||
|
static std::unique_ptr<boot_img> create(rust::Utf8CStr name) { return std::make_unique<boot_img>(name.c_str()); }
|
||||||
rust::Slice<const uint8_t> get_payload() const { return payload; }
|
rust::Slice<const uint8_t> get_payload() const { return payload; }
|
||||||
rust::Slice<const uint8_t> get_tail() const { return tail; }
|
rust::Slice<const uint8_t> get_tail() const { return tail; }
|
||||||
bool verify(const char *cert = nullptr) const;
|
bool is_signed() const { return flags[AVB1_SIGNED_FLAG]; }
|
||||||
|
uint64_t tail_off() const { return tail.buf() - map.buf(); }
|
||||||
|
|
||||||
|
// Implemented in Rust
|
||||||
|
bool verify() const noexcept;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
use crate::compress::{compress, decompress};
|
use crate::compress::{compress, decompress};
|
||||||
use crate::cpio::{cpio_commands, print_cpio_usage};
|
use crate::cpio::{cpio_commands, print_cpio_usage};
|
||||||
use crate::dtb::{DtbAction, dtb_commands, print_dtb_usage};
|
use crate::dtb::{DtbAction, dtb_commands, print_dtb_usage};
|
||||||
use crate::ffi::{FileFormat, cleanup, repack, sign, split_image_dtb, unpack, verify};
|
use crate::ffi::{BootImage, FileFormat, cleanup, repack, split_image_dtb, unpack};
|
||||||
use crate::patch::hexpatch;
|
use crate::patch::hexpatch;
|
||||||
use crate::payload::extract_boot_from_payload;
|
use crate::payload::extract_boot_from_payload;
|
||||||
use crate::sign::sha1_hash;
|
use crate::sign::{sha1_hash, sign_boot_image};
|
||||||
use argh::FromArgs;
|
use argh::FromArgs;
|
||||||
use base::{
|
use base::{
|
||||||
CmdArgs, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, cmdline_logging, cstr,
|
CmdArgs, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, WriteExt,
|
||||||
libc::umask, log_err,
|
cmdline_logging, cstr, libc, libc::umask, log_err,
|
||||||
};
|
};
|
||||||
use std::ffi::c_char;
|
use std::ffi::c_char;
|
||||||
|
use std::io::{Seek, SeekFrom, Write};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(FromArgs)]
|
#[derive(FromArgs)]
|
||||||
@@ -159,7 +160,7 @@ fn print_usage(cmd: &str) {
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
r#"MagiskBoot - Boot Image Modification Tool
|
r#"MagiskBoot - Boot Image Modification Tool
|
||||||
|
|
||||||
Usage: {} <action> [args...]
|
Usage: {0} <action> [args...]
|
||||||
|
|
||||||
Supported actions:
|
Supported actions:
|
||||||
unpack [-n] [-h] <bootimg>
|
unpack [-n] [-h] <bootimg>
|
||||||
@@ -259,6 +260,44 @@ Supported actions:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_cmd(image: &Utf8CStr, cert: Option<&Utf8CStr>) -> bool {
|
||||||
|
let image = BootImage::new(image);
|
||||||
|
match cert {
|
||||||
|
None => {
|
||||||
|
// Boot image parsing already checks if the image is signed
|
||||||
|
image.is_signed()
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
// Provide a custom certificate and re-verify
|
||||||
|
image.verify(cert).is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign_cmd(
|
||||||
|
image: &Utf8CStr,
|
||||||
|
name: Option<&Utf8CStr>,
|
||||||
|
cert: Option<&Utf8CStr>,
|
||||||
|
key: Option<&Utf8CStr>,
|
||||||
|
) -> LoggedResult<()> {
|
||||||
|
let img = BootImage::new(image);
|
||||||
|
let name = name.unwrap_or(cstr!("/boot"));
|
||||||
|
let sig = sign_boot_image(img.payload(), name, cert, key)?;
|
||||||
|
let tail_off = img.tail_off();
|
||||||
|
drop(img);
|
||||||
|
let mut fd = image.open(libc::O_WRONLY | libc::O_CLOEXEC)?;
|
||||||
|
fd.seek(SeekFrom::Start(tail_off))?;
|
||||||
|
fd.write_all(&sig)?;
|
||||||
|
let current = fd.stream_position()?;
|
||||||
|
let eof = fd.seek(SeekFrom::End(0))?;
|
||||||
|
if eof > current {
|
||||||
|
// Zero out rest of the file
|
||||||
|
fd.seek(SeekFrom::Start(current))?;
|
||||||
|
fd.write_zeros((eof - current) as usize)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 {
|
pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 {
|
||||||
cmdline_logging();
|
cmdline_logging();
|
||||||
@@ -302,40 +341,24 @@ pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *con
|
|||||||
no_compress,
|
no_compress,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Action::Verify(Verify {
|
Action::Verify(Verify { mut img, mut cert }) => {
|
||||||
ref mut img,
|
return if verify_cmd(
|
||||||
ref mut cert,
|
Utf8CStr::from_string(&mut img),
|
||||||
}) => {
|
cert.as_mut().map(Utf8CStr::from_string),
|
||||||
return unsafe {
|
) {
|
||||||
verify(
|
0
|
||||||
Utf8CStr::from_string(img),
|
} else {
|
||||||
cert.as_mut()
|
1
|
||||||
.map(|x| Utf8CStr::from_string(x).as_ptr())
|
|
||||||
.unwrap_or(std::ptr::null()),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Action::Sign(Sign {
|
Action::Sign(Sign { mut img, mut args }) => {
|
||||||
ref mut img,
|
let mut iter = args.iter_mut();
|
||||||
ref mut args,
|
sign_cmd(
|
||||||
}) => {
|
Utf8CStr::from_string(&mut img),
|
||||||
let (pem, pk8) = match args.get_mut(1..=2) {
|
iter.next().map(Utf8CStr::from_string),
|
||||||
Some([pem, pk8]) => (
|
iter.next().map(Utf8CStr::from_string),
|
||||||
Utf8CStr::from_string(pem).as_ptr(),
|
iter.next().map(Utf8CStr::from_string),
|
||||||
Utf8CStr::from_string(pk8).as_ptr(),
|
)?;
|
||||||
),
|
|
||||||
_ => (std::ptr::null(), std::ptr::null()),
|
|
||||||
};
|
|
||||||
return unsafe {
|
|
||||||
sign(
|
|
||||||
Utf8CStr::from_string(img),
|
|
||||||
args.first_mut()
|
|
||||||
.map(Utf8CStr::from_string)
|
|
||||||
.unwrap_or(cstr!("/boot")),
|
|
||||||
pem,
|
|
||||||
pk8,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Action::Extract(Extract {
|
Action::Extract(Extract {
|
||||||
ref payload,
|
ref payload,
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
#include "boot-rs.hpp"
|
|
||||||
#include "format.hpp"
|
|
||||||
|
|
||||||
Fmt2Name fmt2name;
|
|
||||||
|
|
||||||
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
|
|
||||||
|
|
||||||
FileFormat check_fmt(const void *buf, size_t len) {
|
|
||||||
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
|
|
||||||
return FileFormat::CHROMEOS;
|
|
||||||
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
|
|
||||||
return FileFormat::AOSP;
|
|
||||||
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
|
|
||||||
return FileFormat::AOSP_VENDOR;
|
|
||||||
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
|
|
||||||
return FileFormat::GZIP;
|
|
||||||
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
|
|
||||||
return FileFormat::LZOP;
|
|
||||||
} else if (CHECKED_MATCH(XZ_MAGIC)) {
|
|
||||||
return FileFormat::XZ;
|
|
||||||
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
|
|
||||||
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
|
|
||||||
return FileFormat::LZMA;
|
|
||||||
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
|
|
||||||
return FileFormat::BZIP2;
|
|
||||||
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
|
|
||||||
return FileFormat::LZ4;
|
|
||||||
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
|
|
||||||
return FileFormat::LZ4_LEGACY;
|
|
||||||
} else if (CHECKED_MATCH(MTK_MAGIC)) {
|
|
||||||
return FileFormat::MTK;
|
|
||||||
} else if (CHECKED_MATCH(DTB_MAGIC)) {
|
|
||||||
return FileFormat::DTB;
|
|
||||||
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
|
|
||||||
return FileFormat::DHTB;
|
|
||||||
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
|
|
||||||
return FileFormat::BLOB;
|
|
||||||
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
|
|
||||||
return FileFormat::ZIMAGE;
|
|
||||||
} else {
|
|
||||||
return FileFormat::UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *Fmt2Name::operator[](FileFormat fmt) {
|
|
||||||
switch (fmt) {
|
|
||||||
case FileFormat::GZIP:
|
|
||||||
return "gzip";
|
|
||||||
case FileFormat::ZOPFLI:
|
|
||||||
return "zopfli";
|
|
||||||
case FileFormat::LZOP:
|
|
||||||
return "lzop";
|
|
||||||
case FileFormat::XZ:
|
|
||||||
return "xz";
|
|
||||||
case FileFormat::LZMA:
|
|
||||||
return "lzma";
|
|
||||||
case FileFormat::BZIP2:
|
|
||||||
return "bzip2";
|
|
||||||
case FileFormat::LZ4:
|
|
||||||
return "lz4";
|
|
||||||
case FileFormat::LZ4_LEGACY:
|
|
||||||
return "lz4_legacy";
|
|
||||||
case FileFormat::LZ4_LG:
|
|
||||||
return "lz4_lg";
|
|
||||||
case FileFormat::DTB:
|
|
||||||
return "dtb";
|
|
||||||
case FileFormat::ZIMAGE:
|
|
||||||
return "zimage";
|
|
||||||
default:
|
|
||||||
return "raw";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
enum class FileFormat : ::std::uint8_t;
|
enum class FileFormat : ::std::uint8_t;
|
||||||
|
|
||||||
#define COMPRESSED(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) < +FileFormat::LZOP)
|
#define COMPRESSED(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) < +FileFormat::LZOP)
|
||||||
@@ -9,6 +7,7 @@ enum class FileFormat : ::std::uint8_t;
|
|||||||
|
|
||||||
#define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
|
#define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
|
||||||
#define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr)
|
#define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr)
|
||||||
|
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
|
||||||
|
|
||||||
#define BOOT_MAGIC "ANDROID!"
|
#define BOOT_MAGIC "ANDROID!"
|
||||||
#define VENDOR_BOOT_MAGIC "VNDRBOOT"
|
#define VENDOR_BOOT_MAGIC "VNDRBOOT"
|
||||||
@@ -41,20 +40,8 @@ enum class FileFormat : ::std::uint8_t;
|
|||||||
#define AVB_MAGIC "AVB0"
|
#define AVB_MAGIC "AVB0"
|
||||||
#define ZIMAGE_MAGIC "\x18\x28\x6f\x01"
|
#define ZIMAGE_MAGIC "\x18\x28\x6f\x01"
|
||||||
|
|
||||||
class Fmt2Name {
|
|
||||||
public:
|
|
||||||
const char *operator[](FileFormat fmt);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Name2Fmt {
|
|
||||||
public:
|
|
||||||
FileFormat operator[](std::string_view name);
|
|
||||||
};
|
|
||||||
|
|
||||||
FileFormat check_fmt(const void *buf, size_t len);
|
FileFormat check_fmt(const void *buf, size_t len);
|
||||||
|
|
||||||
static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) {
|
static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) {
|
||||||
return check_fmt(bytes.data(), bytes.size());
|
return check_fmt(bytes.data(), bytes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
extern Fmt2Name fmt2name;
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::ffi::FileFormat;
|
use crate::ffi::FileFormat;
|
||||||
|
use base::{Utf8CStr, cstr, libc};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@@ -22,23 +23,33 @@ impl FromStr for FileFormat {
|
|||||||
|
|
||||||
impl Display for FileFormat {
|
impl Display for FileFormat {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(self.to_cstr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileFormat {
|
||||||
|
fn to_cstr(&self) -> &'static Utf8CStr {
|
||||||
match *self {
|
match *self {
|
||||||
Self::GZIP => write!(f, "gzip"),
|
Self::GZIP => cstr!("gzip"),
|
||||||
Self::ZOPFLI => write!(f, "zopfli"),
|
Self::ZOPFLI => cstr!("zopfli"),
|
||||||
Self::LZOP => write!(f, "lzop"),
|
Self::LZOP => cstr!("lzop"),
|
||||||
Self::XZ => write!(f, "xz"),
|
Self::XZ => cstr!("xz"),
|
||||||
Self::LZMA => write!(f, "lzma"),
|
Self::LZMA => cstr!("lzma"),
|
||||||
Self::BZIP2 => write!(f, "bzip2"),
|
Self::BZIP2 => cstr!("bzip2"),
|
||||||
Self::LZ4 => write!(f, "lz4"),
|
Self::LZ4 => cstr!("lz4"),
|
||||||
Self::LZ4_LEGACY => write!(f, "lz4_legacy"),
|
Self::LZ4_LEGACY => cstr!("lz4_legacy"),
|
||||||
Self::LZ4_LG => write!(f, "lz4_lg"),
|
Self::LZ4_LG => cstr!("lz4_lg"),
|
||||||
Self::DTB => write!(f, "dtb"),
|
Self::DTB => cstr!("dtb"),
|
||||||
Self::ZIMAGE => write!(f, "zimage"),
|
Self::ZIMAGE => cstr!("zimage"),
|
||||||
_ => write!(f, "raw"),
|
_ => cstr!("raw"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fmt2name(fmt: FileFormat) -> *const libc::c_char {
|
||||||
|
fmt.to_cstr().as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
impl FileFormat {
|
impl FileFormat {
|
||||||
pub fn ext(&self) -> &'static str {
|
pub fn ext(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
pub use base;
|
pub use base;
|
||||||
use compress::{compress_bytes, decompress_bytes};
|
use compress::{compress_bytes, decompress_bytes};
|
||||||
use sign::{SHA, get_sha, sha256_hash, sign_boot_image, verify_boot_image};
|
use format::fmt2name;
|
||||||
|
use sign::{SHA, get_sha, sha256_hash, sign_payload_for_cxx};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
mod compress;
|
mod compress;
|
||||||
@@ -59,25 +60,10 @@ pub mod ffi {
|
|||||||
include!("format.hpp");
|
include!("format.hpp");
|
||||||
fn check_fmt(buf: &[u8]) -> FileFormat;
|
fn check_fmt(buf: &[u8]) -> FileFormat;
|
||||||
|
|
||||||
include!("bootimg.hpp");
|
|
||||||
#[cxx_name = "boot_img"]
|
|
||||||
type BootImage;
|
|
||||||
#[cxx_name = "get_payload"]
|
|
||||||
fn payload(self: &BootImage) -> &[u8];
|
|
||||||
#[cxx_name = "get_tail"]
|
|
||||||
fn tail(self: &BootImage) -> &[u8];
|
|
||||||
|
|
||||||
include!("magiskboot.hpp");
|
include!("magiskboot.hpp");
|
||||||
fn cleanup();
|
fn cleanup();
|
||||||
fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32;
|
fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32;
|
||||||
fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool);
|
fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool);
|
||||||
unsafe fn verify(image: Utf8CStrRef, cert: *const c_char) -> i32;
|
|
||||||
unsafe fn sign(
|
|
||||||
image: Utf8CStrRef,
|
|
||||||
name: Utf8CStrRef,
|
|
||||||
cert: *const c_char,
|
|
||||||
key: *const c_char,
|
|
||||||
) -> i32;
|
|
||||||
fn split_image_dtb(filename: Utf8CStrRef, skip_decomp: bool) -> i32;
|
fn split_image_dtb(filename: Utf8CStrRef, skip_decomp: bool) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,18 +77,32 @@ pub mod ffi {
|
|||||||
|
|
||||||
fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
|
fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
|
||||||
fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
|
fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
|
||||||
|
fn fmt2name(fmt: FileFormat) -> *const c_char;
|
||||||
|
|
||||||
|
#[cxx_name = "sign_payload"]
|
||||||
|
fn sign_payload_for_cxx(payload: &[u8]) -> Vec<u8>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[namespace = "rust"]
|
// BootImage FFI
|
||||||
#[allow(unused_unsafe)]
|
unsafe extern "C++" {
|
||||||
|
include!("bootimg.hpp");
|
||||||
|
#[cxx_name = "boot_img"]
|
||||||
|
type BootImage;
|
||||||
|
|
||||||
|
#[cxx_name = "get_payload"]
|
||||||
|
fn payload(self: &BootImage) -> &[u8];
|
||||||
|
#[cxx_name = "get_tail"]
|
||||||
|
fn tail(self: &BootImage) -> &[u8];
|
||||||
|
fn is_signed(self: &BootImage) -> bool;
|
||||||
|
fn tail_off(self: &BootImage) -> u64;
|
||||||
|
|
||||||
|
#[Self = BootImage]
|
||||||
|
#[cxx_name = "create"]
|
||||||
|
fn new(img: Utf8CStrRef) -> UniquePtr<BootImage>;
|
||||||
|
}
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
unsafe fn sign_boot_image(
|
#[cxx_name = "verify"]
|
||||||
payload: &[u8],
|
fn verify_for_cxx(self: &BootImage) -> bool;
|
||||||
name: *const c_char,
|
|
||||||
cert: *const c_char,
|
|
||||||
key: *const c_char,
|
|
||||||
) -> Vec<u8>;
|
|
||||||
unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,19 +14,5 @@
|
|||||||
|
|
||||||
int unpack(rust::Utf8CStr image, bool skip_decomp = false, bool hdr = false);
|
int unpack(rust::Utf8CStr image, bool skip_decomp = false, bool hdr = false);
|
||||||
void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp = false);
|
void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp = false);
|
||||||
int verify(rust::Utf8CStr image, const char *cert);
|
|
||||||
int sign(rust::Utf8CStr image, rust::Utf8CStr name, const char *cert, const char *key);
|
|
||||||
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp = false);
|
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp = false);
|
||||||
|
void cleanup();
|
||||||
inline void cleanup() {
|
|
||||||
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);
|
|
||||||
unlink(BOOTCONFIG_FILE);
|
|
||||||
rm_rf(VND_RAMDISK_DIR);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ use x509_cert::der::Any;
|
|||||||
use x509_cert::der::asn1::{OctetString, PrintableString};
|
use x509_cert::der::asn1::{OctetString, PrintableString};
|
||||||
use x509_cert::spki::AlgorithmIdentifier;
|
use x509_cert::spki::AlgorithmIdentifier;
|
||||||
|
|
||||||
use base::libc::c_char;
|
use base::{LoggedError, LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, log_err};
|
||||||
use base::{LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr, log_err};
|
|
||||||
|
|
||||||
use crate::ffi::BootImage;
|
use crate::ffi::BootImage;
|
||||||
|
|
||||||
@@ -265,23 +264,27 @@ impl BootSignature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool {
|
impl BootImage {
|
||||||
let res: LoggedResult<()> = try {
|
pub fn verify(&self, cert: Option<&Utf8CStr>) -> LoggedResult<()> {
|
||||||
let tail = img.tail();
|
let tail = self.tail();
|
||||||
|
if tail.starts_with(b"AVB0") {
|
||||||
|
return Err(LoggedError::default());
|
||||||
|
}
|
||||||
|
|
||||||
// Don't use BootSignature::from_der because tail might have trailing zeros
|
// Don't use BootSignature::from_der because tail might have trailing zeros
|
||||||
let mut reader = SliceReader::new(tail)?;
|
let mut reader = SliceReader::new(tail)?;
|
||||||
let mut sig = BootSignature::decode(&mut reader)?;
|
let mut sig = BootSignature::decode(&mut reader)?;
|
||||||
match unsafe { Utf8CStr::from_ptr(cert) } {
|
if let Some(s) = cert {
|
||||||
Ok(s) => {
|
let pem = MappedFile::open(s)?;
|
||||||
let pem = MappedFile::open(s)?;
|
sig.certificate = Certificate::from_pem(pem)?;
|
||||||
sig.certificate = Certificate::from_pem(pem)?;
|
|
||||||
}
|
|
||||||
Err(StrErr::NullPointerError) => {}
|
|
||||||
Err(e) => Err(e)?,
|
|
||||||
};
|
};
|
||||||
sig.verify(img.payload())?;
|
|
||||||
};
|
sig.verify(self.payload()).log()
|
||||||
res.is_ok()
|
}
|
||||||
|
|
||||||
|
pub fn verify_for_cxx(&self) -> bool {
|
||||||
|
self.verify(None).is_ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Bytes {
|
enum Bytes {
|
||||||
@@ -303,47 +306,44 @@ const VERITY_PK8: &[u8] = include_bytes!("../../../tools/keys/verity.pk8");
|
|||||||
|
|
||||||
pub fn sign_boot_image(
|
pub fn sign_boot_image(
|
||||||
payload: &[u8],
|
payload: &[u8],
|
||||||
name: *const c_char,
|
name: &Utf8CStr,
|
||||||
cert: *const c_char,
|
cert: Option<&Utf8CStr>,
|
||||||
key: *const c_char,
|
key: Option<&Utf8CStr>,
|
||||||
) -> Vec<u8> {
|
) -> LoggedResult<Vec<u8>> {
|
||||||
let res: LoggedResult<Vec<u8>> = try {
|
let cert = match cert {
|
||||||
// Process arguments
|
Some(s) => Bytes::Mapped(MappedFile::open(s)?),
|
||||||
let name = unsafe { Utf8CStr::from_ptr(name) }?;
|
None => Bytes::Slice(VERITY_PEM),
|
||||||
let cert = match unsafe { Utf8CStr::from_ptr(cert) } {
|
|
||||||
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
|
|
||||||
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PEM),
|
|
||||||
Err(e) => Err(e)?,
|
|
||||||
};
|
|
||||||
let key = match unsafe { Utf8CStr::from_ptr(key) } {
|
|
||||||
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
|
|
||||||
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PK8),
|
|
||||||
Err(e) => Err(e)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse cert and private key
|
|
||||||
let cert = Certificate::from_pem(cert)?;
|
|
||||||
let mut signer = Signer::from_private_key(key.as_ref())?;
|
|
||||||
|
|
||||||
// Sign image
|
|
||||||
let attr = AuthenticatedAttributes {
|
|
||||||
target: PrintableString::new(name.as_bytes())?,
|
|
||||||
length: payload.len() as u64,
|
|
||||||
};
|
|
||||||
signer.update(payload);
|
|
||||||
signer.update(attr.to_der()?.as_slice());
|
|
||||||
let sig = signer.sign()?;
|
|
||||||
|
|
||||||
// Create BootSignature DER
|
|
||||||
let alg_id = cert.signature_algorithm().clone();
|
|
||||||
let sig = BootSignature {
|
|
||||||
format_version: 1,
|
|
||||||
certificate: cert,
|
|
||||||
algorithm_identifier: alg_id,
|
|
||||||
authenticated_attributes: attr,
|
|
||||||
signature: OctetString::new(sig)?,
|
|
||||||
};
|
|
||||||
sig.to_der()?
|
|
||||||
};
|
};
|
||||||
res.unwrap_or_default()
|
let key = match key {
|
||||||
|
Some(s) => Bytes::Mapped(MappedFile::open(s)?),
|
||||||
|
None => Bytes::Slice(VERITY_PK8),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse cert and private key
|
||||||
|
let cert = Certificate::from_pem(cert)?;
|
||||||
|
let mut signer = Signer::from_private_key(key.as_ref())?;
|
||||||
|
|
||||||
|
// Sign image
|
||||||
|
let attr = AuthenticatedAttributes {
|
||||||
|
target: PrintableString::new(name.as_bytes())?,
|
||||||
|
length: payload.len() as u64,
|
||||||
|
};
|
||||||
|
signer.update(payload);
|
||||||
|
signer.update(attr.to_der()?.as_slice());
|
||||||
|
let sig = signer.sign()?;
|
||||||
|
|
||||||
|
// Create BootSignature DER
|
||||||
|
let alg_id = cert.signature_algorithm().clone();
|
||||||
|
let sig = BootSignature {
|
||||||
|
format_version: 1,
|
||||||
|
certificate: cert,
|
||||||
|
algorithm_identifier: alg_id,
|
||||||
|
authenticated_attributes: attr,
|
||||||
|
signature: OctetString::new(sig)?,
|
||||||
|
};
|
||||||
|
sig.to_der().log()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign_payload_for_cxx(payload: &[u8]) -> Vec<u8> {
|
||||||
|
sign_boot_image(payload, cstr!("/boot"), None, None).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user