mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-04-21 00:11:29 +00:00
Move magiskboot cli to argh
This commit is contained in:
parent
1f162b819d
commit
8ae7641e8a
@ -92,7 +92,6 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libboot-rs
|
libboot-rs
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
boot/main.cpp \
|
|
||||||
boot/bootimg.cpp \
|
boot/bootimg.cpp \
|
||||||
boot/compress.cpp \
|
boot/compress.cpp \
|
||||||
boot/format.cpp \
|
boot/format.cpp \
|
||||||
@ -102,7 +101,7 @@ LOCAL_LDFLAGS := -static
|
|||||||
|
|
||||||
ifdef B_CRT0
|
ifdef B_CRT0
|
||||||
LOCAL_STATIC_LIBRARIES += crt0
|
LOCAL_STATIC_LIBRARIES += crt0
|
||||||
LOCAL_LDFLAGS += -lm
|
LOCAL_LDFLAGS += -lm -Wl,--defsym=vfprintf=musl_vfprintf
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
include $(BUILD_EXECUTABLE)
|
||||||
|
397
native/src/boot/cli.rs
Normal file
397
native/src/boot/cli.rs
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
use crate::payload::extract_boot_from_payload;
|
||||||
|
use crate::sign::{sha1_hash, verify_boot_image};
|
||||||
|
use argh::FromArgs;
|
||||||
|
use base::{cmdline_logging, libc::umask, log_err, map_args, raw_cstr, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr};
|
||||||
|
use std::ffi::c_char;
|
||||||
|
use crate::cpio::cpio_commands;
|
||||||
|
use crate::dtb::dtb_commands;
|
||||||
|
use crate::ffi::{cleanup, compress, decompress_raw, formats, repack, sign, split_image_dtb, unpack, verify};
|
||||||
|
use crate::patch::hexpatch;
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
struct Cli {
|
||||||
|
#[argh(subcommand)]
|
||||||
|
action: Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand)]
|
||||||
|
enum Action {
|
||||||
|
Unpack(Unpack),
|
||||||
|
Repack(Repack),
|
||||||
|
Verify(Verify),
|
||||||
|
Sign(Sign),
|
||||||
|
Extract(Extract),
|
||||||
|
HexPatch(HexPatch),
|
||||||
|
Cpio(Cpio),
|
||||||
|
Dtb(Dtb),
|
||||||
|
Split(Split),
|
||||||
|
Sha1(Sha1),
|
||||||
|
Cleanup(Cleanup),
|
||||||
|
Compress(Compress),
|
||||||
|
Decompress(Decompress),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "unpack")]
|
||||||
|
struct Unpack {
|
||||||
|
#[argh(switch, short = 'n')]
|
||||||
|
no_decompress: bool,
|
||||||
|
#[argh(switch, short = 'h')]
|
||||||
|
dump_header: bool,
|
||||||
|
#[argh(positional)]
|
||||||
|
img: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "repack")]
|
||||||
|
struct Repack {
|
||||||
|
#[argh(switch, short = 'n')]
|
||||||
|
no_compress: bool,
|
||||||
|
#[argh(positional)]
|
||||||
|
img: String,
|
||||||
|
#[argh(positional, default = r#""new-boot.img".to_string()"#)]
|
||||||
|
out: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "verify")]
|
||||||
|
struct Verify {
|
||||||
|
#[argh(positional)]
|
||||||
|
img: String,
|
||||||
|
#[argh(positional)]
|
||||||
|
cert: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "sign")]
|
||||||
|
struct Sign {
|
||||||
|
#[argh(positional)]
|
||||||
|
img: String,
|
||||||
|
#[argh(positional)]
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "extract")]
|
||||||
|
struct Extract {
|
||||||
|
#[argh(positional)]
|
||||||
|
payload: String,
|
||||||
|
#[argh(positional)]
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "hexpatch")]
|
||||||
|
struct HexPatch {
|
||||||
|
#[argh(positional)]
|
||||||
|
file: String,
|
||||||
|
#[argh(positional)]
|
||||||
|
src: String,
|
||||||
|
#[argh(positional)]
|
||||||
|
dest: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "cpio")]
|
||||||
|
struct Cpio {
|
||||||
|
#[argh(positional)]
|
||||||
|
cmds: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "dtb")]
|
||||||
|
struct Dtb {
|
||||||
|
#[argh(positional)]
|
||||||
|
cmds: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "split")]
|
||||||
|
struct Split {
|
||||||
|
#[argh(switch, short = 'n')]
|
||||||
|
no_decompress: bool,
|
||||||
|
#[argh(positional)]
|
||||||
|
file: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "sha1")]
|
||||||
|
struct Sha1 {
|
||||||
|
#[argh(positional)]
|
||||||
|
file: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "cleanup")]
|
||||||
|
struct Cleanup {}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "compress")]
|
||||||
|
struct Compress {
|
||||||
|
#[argh(option, short = 'f', default = r#""gzip".to_string()"#)]
|
||||||
|
format: String,
|
||||||
|
#[argh(positional)]
|
||||||
|
file: String,
|
||||||
|
#[argh(positional)]
|
||||||
|
out: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand, name = "decompress")]
|
||||||
|
struct Decompress {
|
||||||
|
#[argh(positional)]
|
||||||
|
file: String,
|
||||||
|
#[argh(positional)]
|
||||||
|
out: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_usage(cmd: &str) {
|
||||||
|
eprintln!(
|
||||||
|
r#"MagiskBoot - Boot Image Modification Tool
|
||||||
|
|
||||||
|
Usage: {} <action> [args...]
|
||||||
|
|
||||||
|
Supported actions:
|
||||||
|
unpack [-n] [-h] <bootimg>
|
||||||
|
Unpack <bootimg> to its individual components, each component to
|
||||||
|
a file with its corresponding file name in the current directory.
|
||||||
|
Supported components: kernel, kernel_dtb, ramdisk.cpio, second,
|
||||||
|
dtb, extra, and recovery_dtbo.
|
||||||
|
By default, each component will be decompressed on-the-fly.
|
||||||
|
If '-n' is provided, all decompression operations will be skipped;
|
||||||
|
each component will remain untouched, dumped in its original format.
|
||||||
|
If '-h' is provided, the boot image header information will be
|
||||||
|
dumped to the file 'header', which can be used to modify header
|
||||||
|
configurations during repacking.
|
||||||
|
Return values:
|
||||||
|
0:valid 1:error 2:chromeos
|
||||||
|
|
||||||
|
repack [-n] <origbootimg> [outbootimg]
|
||||||
|
Repack boot image components using files from the current directory
|
||||||
|
to [outbootimg], or 'new-boot.img' if not specified. Current directory
|
||||||
|
should only contain required files for [outbootimg], or incorrect
|
||||||
|
[outbootimg] may be produced.
|
||||||
|
<origbootimg> is the original boot image used to unpack the components.
|
||||||
|
By default, each component will be automatically compressed using its
|
||||||
|
corresponding format detected in <origbootimg>. If a component file
|
||||||
|
in the current directory is already compressed, then no addition
|
||||||
|
compression will be performed for that specific component.
|
||||||
|
If '-n' is provided, all compression operations will be skipped.
|
||||||
|
If env variable PATCHVBMETAFLAG is set to true, all disable flags in
|
||||||
|
the boot image's vbmeta header will be set.
|
||||||
|
|
||||||
|
verify <bootimg> [x509.pem]
|
||||||
|
Check whether the boot image is signed with AVB 1.0 signature.
|
||||||
|
Optionally provide a certificate to verify whether the image is
|
||||||
|
signed by the public key certificate.
|
||||||
|
Return value:
|
||||||
|
0:valid 1:error
|
||||||
|
|
||||||
|
sign <bootimg> [name] [x509.pem pk8]
|
||||||
|
Sign <bootimg> with AVB 1.0 signature.
|
||||||
|
Optionally provide the name of the image (default: '/boot').
|
||||||
|
Optionally provide the certificate/private key pair for signing.
|
||||||
|
If the certificate/private key pair is not provided, the AOSP
|
||||||
|
verity key bundled in the executable will be used.
|
||||||
|
|
||||||
|
extract <payload.bin> [partition] [outfile]
|
||||||
|
Extract [partition] from <payload.bin> to [outfile].
|
||||||
|
If [outfile] is not specified, then output to '[partition].img'.
|
||||||
|
If [partition] is not specified, then attempt to extract either
|
||||||
|
'init_boot' or 'boot'. Which partition was chosen can be determined
|
||||||
|
by whichever 'init_boot.img' or 'boot.img' exists.
|
||||||
|
<payload.bin> can be '-' to be STDIN.
|
||||||
|
|
||||||
|
hexpatch <file> <hexpattern1> <hexpattern2>
|
||||||
|
Search <hexpattern1> in <file>, and replace it 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.
|
||||||
|
See "cpio --help" for supported commands.
|
||||||
|
|
||||||
|
dtb <file> <action> [args...]
|
||||||
|
Do dtb related actions to <file>.
|
||||||
|
See "dtb --help" for supported actions.
|
||||||
|
|
||||||
|
split [-n] <file>
|
||||||
|
Split image.*-dtb into kernel + kernel_dtb.
|
||||||
|
If '-n' is provided, decompression operations will be skipped;
|
||||||
|
the kernel will remain untouched, split in its original format.
|
||||||
|
|
||||||
|
sha1 <file>
|
||||||
|
Print the SHA1 checksum for <file>
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
Cleanup the current working directory
|
||||||
|
|
||||||
|
compress[=format] <infile> [outfile]
|
||||||
|
Compress <infile> with [format] to [outfile].
|
||||||
|
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||||
|
If [format] is not specified, then gzip will be used.
|
||||||
|
If [outfile] is not specified, then <infile> will be replaced
|
||||||
|
with another file suffixed with a matching file extension.
|
||||||
|
Supported formats:
|
||||||
|
|
||||||
|
{1}
|
||||||
|
|
||||||
|
decompress <infile> [outfile]
|
||||||
|
Detect format and decompress <infile> to [outfile].
|
||||||
|
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||||
|
If [outfile] is not specified, then <infile> will be replaced
|
||||||
|
with another file removing its archive format file extension.
|
||||||
|
Supported formats:
|
||||||
|
|
||||||
|
{1}
|
||||||
|
"#,
|
||||||
|
cmd, formats()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn main(
|
||||||
|
argc: i32,
|
||||||
|
argv: *const *const c_char,
|
||||||
|
_envp: *const *const c_char,
|
||||||
|
) -> i32 {
|
||||||
|
cmdline_logging();
|
||||||
|
umask(0);
|
||||||
|
let res: LoggedResult<()> = try {
|
||||||
|
let mut cmds = map_args(argc, argv)?;
|
||||||
|
if argc < 2 {
|
||||||
|
print_usage(cmds.first().unwrap_or(&"magiskboot"));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmds[1].starts_with("--") {
|
||||||
|
cmds[1] = &cmds[1][2..];
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(fmt) = str::strip_prefix(cmds[1], "compress=") {
|
||||||
|
cmds.insert(1, "compress");
|
||||||
|
cmds.insert(2, "-f");
|
||||||
|
cmds[3] = fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cli = Cli::from_args(&[cmds[0]], &cmds[1..]).on_early_exit(|| print_usage(cmds[0]));
|
||||||
|
match cli.action {
|
||||||
|
Action::Unpack(Unpack {
|
||||||
|
no_decompress,
|
||||||
|
dump_header,
|
||||||
|
ref mut img,
|
||||||
|
}) => {
|
||||||
|
return unpack(Utf8CStr::from_string(img).as_ptr(), no_decompress, dump_header);
|
||||||
|
}
|
||||||
|
Action::Repack(Repack {
|
||||||
|
no_compress,
|
||||||
|
ref mut img,
|
||||||
|
ref mut out,
|
||||||
|
}) => {
|
||||||
|
repack(
|
||||||
|
Utf8CStr::from_string(img).as_ptr(),
|
||||||
|
Utf8CStr::from_string(out).as_ptr(),
|
||||||
|
no_compress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Action::Verify(Verify {
|
||||||
|
ref mut img,
|
||||||
|
ref mut cert,
|
||||||
|
}) => {
|
||||||
|
return verify(Utf8CStr::from_string(img).as_ptr(), cert.as_mut().map(|x| Utf8CStr::from_string(x).as_ptr()).unwrap_or(std::ptr::null()));
|
||||||
|
}
|
||||||
|
Action::Sign(Sign {
|
||||||
|
ref mut img,
|
||||||
|
ref mut args,
|
||||||
|
}) => {
|
||||||
|
let (pem, pk8) = match args.get_mut(1..=2) {
|
||||||
|
Some([pem,pk8]) => (Utf8CStr::from_string(pem).as_ptr(), Utf8CStr::from_string(pk8).as_ptr()),
|
||||||
|
_ => (std::ptr::null(), std::ptr::null()),
|
||||||
|
};
|
||||||
|
return sign(
|
||||||
|
Utf8CStr::from_string(img).as_ptr(),
|
||||||
|
args.first_mut().map(|x| Utf8CStr::from_string(x).as_ptr()).unwrap_or(raw_cstr!("/boot")),
|
||||||
|
pem, pk8
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Action::Extract(Extract {
|
||||||
|
ref payload,
|
||||||
|
ref args,
|
||||||
|
}) => {
|
||||||
|
if args.len() > 2 {
|
||||||
|
Err(log_err!("Too many arguments"))?;
|
||||||
|
}
|
||||||
|
extract_boot_from_payload(
|
||||||
|
payload,
|
||||||
|
args.first().map(|x| x.as_str()),
|
||||||
|
args.get(1).map(|x| x.as_str()),
|
||||||
|
)
|
||||||
|
.log_with_msg(|w| w.write_str("Failed to extract from payload"))?;
|
||||||
|
}
|
||||||
|
Action::HexPatch(HexPatch {
|
||||||
|
ref mut file,
|
||||||
|
ref mut src,
|
||||||
|
ref mut dest,
|
||||||
|
}) => {
|
||||||
|
if !hexpatch(file, Utf8CStr::from_string(src), Utf8CStr::from_string(dest)) {
|
||||||
|
Err(log_err!("Failed to patch"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action::Cpio(Cpio {
|
||||||
|
ref cmds,
|
||||||
|
}) => {
|
||||||
|
return if cpio_commands(&cmds.iter().map(|x| x.as_str()).collect::<Vec<_>>()) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action::Dtb(Dtb {
|
||||||
|
ref cmds,
|
||||||
|
}) => {
|
||||||
|
return if dtb_commands(&cmds.iter().map(|x| x.as_str()).collect::<Vec<_>>()) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action::Split(Split {
|
||||||
|
no_decompress,
|
||||||
|
ref mut file,
|
||||||
|
}) => {
|
||||||
|
return split_image_dtb(Utf8CStr::from_string(file).as_ptr(), no_decompress);
|
||||||
|
}
|
||||||
|
Action::Sha1(Sha1 { ref mut file }) => {
|
||||||
|
let file = MappedFile::open(Utf8CStr::from_string(file))?;
|
||||||
|
let mut sha1 = [0u8; 20];
|
||||||
|
sha1_hash(file.as_ref(), &mut sha1);
|
||||||
|
for byte in &sha1 {
|
||||||
|
print!("{:02x}", byte);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
Action::Cleanup(_) => {
|
||||||
|
eprintln!("Cleaning up...");
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
Action::Decompress(Decompress {
|
||||||
|
ref mut file,
|
||||||
|
ref mut out,
|
||||||
|
}) => {
|
||||||
|
decompress_raw(Utf8CStr::from_string(file).as_mut_ptr(), out.as_mut().map(|x| Utf8CStr::from_string(x).as_ptr()).unwrap_or(std::ptr::null()));
|
||||||
|
}
|
||||||
|
Action::Compress(Compress {
|
||||||
|
ref mut file,
|
||||||
|
ref mut format,
|
||||||
|
ref mut out,
|
||||||
|
}) => {
|
||||||
|
compress(Utf8CStr::from_string(format).as_ptr(), Utf8CStr::from_string(file).as_ptr(), out.as_mut().map(|x| Utf8CStr::from_string(x).as_ptr()).unwrap_or(std::ptr::null()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if res.is_ok() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
@ -12,3 +12,13 @@ void decompress(char *infile, const char *outfile);
|
|||||||
bool decompress(rust::Slice<const uint8_t> buf, int fd);
|
bool decompress(rust::Slice<const uint8_t> buf, int fd);
|
||||||
bool xz(rust::Slice<const uint8_t> buf, rust::Vec<uint8_t> &out);
|
bool xz(rust::Slice<const uint8_t> buf, rust::Vec<uint8_t> &out);
|
||||||
bool unxz(rust::Slice<const uint8_t> buf, rust::Vec<uint8_t> &out);
|
bool unxz(rust::Slice<const uint8_t> buf, rust::Vec<uint8_t> &out);
|
||||||
|
|
||||||
|
inline ::rust::String formats() {
|
||||||
|
std::string s;
|
||||||
|
for (int fmt = GZIP; fmt < LZOP; ++fmt) {
|
||||||
|
s += fmt2name[(format_t) fmt];
|
||||||
|
s += ' ';
|
||||||
|
}
|
||||||
|
s.pop_back();
|
||||||
|
return {s};
|
||||||
|
}
|
||||||
|
@ -15,12 +15,12 @@ use num_traits::cast::AsPrimitive;
|
|||||||
use size::{Base, Size, Style};
|
use size::{Base, Size, Style};
|
||||||
|
|
||||||
use base::libc::{
|
use base::libc::{
|
||||||
c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, O_CLOEXEC, O_CREAT,
|
dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, O_CLOEXEC, O_CREAT,
|
||||||
O_RDONLY, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IRGRP,
|
O_RDONLY, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IRGRP,
|
||||||
S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
|
S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
|
||||||
};
|
};
|
||||||
use base::{
|
use base::{
|
||||||
cstr_buf, log_err, map_args, BytesExt, EarlyExitExt, FsPath, LoggedResult, MappedFile,
|
cstr_buf, log_err, BytesExt, EarlyExitExt, FsPath, LoggedResult, MappedFile,
|
||||||
ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
|
ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -753,14 +753,8 @@ impl Display for CpioEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
|
pub fn cpio_commands(cmds: &Vec<&str>) -> bool {
|
||||||
let res: LoggedResult<()> = try {
|
let res: LoggedResult<()> = try {
|
||||||
if argc < 1 {
|
|
||||||
Err(log_err!("No arguments"))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cmds = map_args(argc, argv)?;
|
|
||||||
|
|
||||||
let mut cli =
|
let mut cli =
|
||||||
CpioCli::from_args(&["magiskboot", "cpio"], &cmds).on_early_exit(print_cpio_usage);
|
CpioCli::from_args(&["magiskboot", "cpio"], &cmds).on_early_exit(print_cpio_usage);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use fdt::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use base::{
|
use base::{
|
||||||
libc::c_char, log_err, map_args, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr,
|
EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{check_env, patch::patch_verity};
|
use crate::{check_env, patch::patch_verity};
|
||||||
@ -274,13 +274,8 @@ fn dtb_patch(file: &Utf8CStr) -> LoggedResult<bool> {
|
|||||||
Ok(patched)
|
Ok(patched)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool {
|
pub fn dtb_commands(cmds: &Vec<&str>) -> bool {
|
||||||
let res: LoggedResult<()> = try {
|
let res: LoggedResult<()> = try {
|
||||||
if argc < 1 {
|
|
||||||
Err(log_err!("No arguments"))?;
|
|
||||||
}
|
|
||||||
let cmds = map_args(argc, argv)?;
|
|
||||||
|
|
||||||
let mut cli =
|
let mut cli =
|
||||||
DtbCli::from_args(&["magiskboot", "dtb"], &cmds).on_early_exit(print_dtb_usage);
|
DtbCli::from_args(&["magiskboot", "dtb"], &cmds).on_early_exit(print_dtb_usage);
|
||||||
|
|
||||||
|
@ -6,11 +6,7 @@
|
|||||||
pub use libz_rs_sys::*;
|
pub use libz_rs_sys::*;
|
||||||
pub use libbz2_rs_sys::*;
|
pub use libbz2_rs_sys::*;
|
||||||
pub use base;
|
pub use base;
|
||||||
use cpio::cpio_commands;
|
use sign::{get_sha, sha256_hash, sign_boot_image, verify_boot_image, SHA};
|
||||||
use dtb::dtb_commands;
|
|
||||||
use patch::hexpatch;
|
|
||||||
use payload::extract_boot_from_payload;
|
|
||||||
use sign::{get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image, SHA};
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
mod cpio;
|
mod cpio;
|
||||||
@ -21,6 +17,7 @@ mod payload;
|
|||||||
#[allow(warnings)]
|
#[allow(warnings)]
|
||||||
mod proto;
|
mod proto;
|
||||||
mod sign;
|
mod sign;
|
||||||
|
mod cli;
|
||||||
|
|
||||||
#[cxx::bridge]
|
#[cxx::bridge]
|
||||||
pub mod ffi {
|
pub mod ffi {
|
||||||
@ -35,16 +32,30 @@ pub mod ffi {
|
|||||||
unsafe extern "C++" {
|
unsafe extern "C++" {
|
||||||
include!("compress.hpp");
|
include!("compress.hpp");
|
||||||
fn decompress(buf: &[u8], fd: i32) -> bool;
|
fn decompress(buf: &[u8], fd: i32) -> bool;
|
||||||
|
#[cxx_name = "decompress"]
|
||||||
|
unsafe fn decompress_raw(infile: *mut c_char, outfile: *const c_char);
|
||||||
|
unsafe fn compress(format: *const c_char, infile: *const c_char, outfile: *const c_char);
|
||||||
fn xz(buf: &[u8], out: &mut Vec<u8>) -> bool;
|
fn xz(buf: &[u8], out: &mut Vec<u8>) -> bool;
|
||||||
fn unxz(buf: &[u8], out: &mut Vec<u8>) -> bool;
|
fn unxz(buf: &[u8], out: &mut Vec<u8>) -> bool;
|
||||||
|
|
||||||
include!("bootimg.hpp");
|
include!("bootimg.hpp");
|
||||||
|
include!("magiskboot.hpp");
|
||||||
#[cxx_name = "boot_img"]
|
#[cxx_name = "boot_img"]
|
||||||
type BootImage;
|
type BootImage;
|
||||||
#[cxx_name = "get_payload"]
|
#[cxx_name = "get_payload"]
|
||||||
fn payload(self: &BootImage) -> &[u8];
|
fn payload(self: &BootImage) -> &[u8];
|
||||||
#[cxx_name = "get_tail"]
|
#[cxx_name = "get_tail"]
|
||||||
fn tail(self: &BootImage) -> &[u8];
|
fn tail(self: &BootImage) -> &[u8];
|
||||||
|
|
||||||
|
fn cleanup();
|
||||||
|
|
||||||
|
unsafe fn unpack(image: *const c_char, skip_decomp: bool, hdr: bool) -> i32;
|
||||||
|
unsafe fn repack(src_img: *const c_char, out_img: *const c_char, skip_comp: bool);
|
||||||
|
unsafe fn verify(image: *const c_char, cert: *const c_char) -> i32;
|
||||||
|
unsafe fn sign(image: *const c_char, name: *const c_char, cert: *const c_char, key: *const c_char) -> i32;
|
||||||
|
unsafe fn split_image_dtb(filename: *const c_char, skip_decomp: bool) -> i32;
|
||||||
|
|
||||||
|
fn formats() -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
@ -53,29 +64,19 @@ pub mod ffi {
|
|||||||
fn update(self: &mut SHA, data: &[u8]);
|
fn update(self: &mut SHA, data: &[u8]);
|
||||||
fn finalize_into(self: &mut SHA, out: &mut [u8]);
|
fn finalize_into(self: &mut SHA, out: &mut [u8]);
|
||||||
fn output_size(self: &SHA) -> usize;
|
fn output_size(self: &SHA) -> usize;
|
||||||
fn sha1_hash(data: &[u8], out: &mut [u8]);
|
|
||||||
fn sha256_hash(data: &[u8], out: &mut [u8]);
|
fn sha256_hash(data: &[u8], out: &mut [u8]);
|
||||||
|
|
||||||
fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[namespace = "rust"]
|
#[namespace = "rust"]
|
||||||
#[allow(unused_unsafe)]
|
#[allow(unused_unsafe)]
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
fn extract_boot_from_payload(
|
|
||||||
partition: Utf8CStrRef,
|
|
||||||
in_path: Utf8CStrRef,
|
|
||||||
out_path: Utf8CStrRef,
|
|
||||||
) -> bool;
|
|
||||||
unsafe fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool;
|
|
||||||
unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool;
|
|
||||||
unsafe fn sign_boot_image(
|
unsafe fn sign_boot_image(
|
||||||
payload: &[u8],
|
payload: &[u8],
|
||||||
name: *const c_char,
|
name: *const c_char,
|
||||||
cert: *const c_char,
|
cert: *const c_char,
|
||||||
key: *const c_char,
|
key: *const c_char,
|
||||||
) -> Vec<u8>;
|
) -> Vec<u8>;
|
||||||
unsafe fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool;
|
unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,3 +28,16 @@ static inline bool check_env(const char *name) {
|
|||||||
const char *val = getenv(name);
|
const char *val = getenv(name);
|
||||||
return val != nullptr && val == "true"sv;
|
return val != nullptr && val == "true"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static 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);
|
||||||
|
}
|
@ -1,228 +0,0 @@
|
|||||||
#include <base.hpp>
|
|
||||||
|
|
||||||
#include "boot-rs.hpp"
|
|
||||||
#include "magiskboot.hpp"
|
|
||||||
#include "compress.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#ifdef USE_CRT0
|
|
||||||
__BEGIN_DECLS
|
|
||||||
int musl_vfprintf(FILE *stream, const char *format, va_list arg);
|
|
||||||
int vfprintf(FILE *stream, const char *format, va_list arg) {
|
|
||||||
return musl_vfprintf(stream, format, arg);
|
|
||||||
}
|
|
||||||
__END_DECLS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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 its individual components, each component to
|
|
||||||
a file with its corresponding file name in the current directory.
|
|
||||||
Supported components: kernel, kernel_dtb, ramdisk.cpio, second,
|
|
||||||
dtb, extra, and recovery_dtbo.
|
|
||||||
By default, each component will be decompressed on-the-fly.
|
|
||||||
If '-n' is provided, all decompression operations will be skipped;
|
|
||||||
each component will remain untouched, dumped in its original format.
|
|
||||||
If '-h' is provided, the boot image header information will be
|
|
||||||
dumped to the file 'header', which can be used to modify header
|
|
||||||
configurations during repacking.
|
|
||||||
Return values:
|
|
||||||
0:valid 1:error 2:chromeos
|
|
||||||
|
|
||||||
repack [-n] <origbootimg> [outbootimg]
|
|
||||||
Repack boot image components using files from the current directory
|
|
||||||
to [outbootimg], or 'new-boot.img' if not specified. Current directory
|
|
||||||
should only contain required files for [outbootimg], or incorrect
|
|
||||||
[outbootimg] may be produced.
|
|
||||||
<origbootimg> is the original boot image used to unpack the components.
|
|
||||||
By default, each component will be automatically compressed using its
|
|
||||||
corresponding format detected in <origbootimg>. If a component file
|
|
||||||
in the current directory is already compressed, then no addition
|
|
||||||
compression will be performed for that specific component.
|
|
||||||
If '-n' is provided, all compression operations will be skipped.
|
|
||||||
If env variable PATCHVBMETAFLAG is set to true, all disable flags in
|
|
||||||
the boot image's vbmeta header will be set.
|
|
||||||
|
|
||||||
verify <bootimg> [x509.pem]
|
|
||||||
Check whether the boot image is signed with AVB 1.0 signature.
|
|
||||||
Optionally provide a certificate to verify whether the image is
|
|
||||||
signed by the public key certificate.
|
|
||||||
Return value:
|
|
||||||
0:valid 1:error
|
|
||||||
|
|
||||||
sign <bootimg> [name] [x509.pem pk8]
|
|
||||||
Sign <bootimg> with AVB 1.0 signature.
|
|
||||||
Optionally provide the name of the image (default: '/boot').
|
|
||||||
Optionally provide the certificate/private key pair for signing.
|
|
||||||
If the certificate/private key pair is not provided, the AOSP
|
|
||||||
verity key bundled in the executable will be used.
|
|
||||||
|
|
||||||
extract <payload.bin> [partition] [outfile]
|
|
||||||
Extract [partition] from <payload.bin> to [outfile].
|
|
||||||
If [outfile] is not specified, then output to '[partition].img'.
|
|
||||||
If [partition] is not specified, then attempt to extract either
|
|
||||||
'init_boot' or 'boot'. Which partition was chosen can be determined
|
|
||||||
by whichever 'init_boot.img' or 'boot.img' exists.
|
|
||||||
<payload.bin> can be '-' to be STDIN.
|
|
||||||
|
|
||||||
hexpatch <file> <hexpattern1> <hexpattern2>
|
|
||||||
Search <hexpattern1> in <file>, and replace it 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.
|
|
||||||
See "cpio --help" for supported commands.
|
|
||||||
|
|
||||||
dtb <file> <action> [args...]
|
|
||||||
Do dtb related actions to <file>.
|
|
||||||
See "dtb --help" for supported actions.
|
|
||||||
|
|
||||||
split [-n] <file>
|
|
||||||
Split image.*-dtb into kernel + kernel_dtb.
|
|
||||||
If '-n' is provided, decompression operations will be skipped;
|
|
||||||
the kernel will remain untouched, split in its original format.
|
|
||||||
|
|
||||||
sha1 <file>
|
|
||||||
Print the SHA1 checksum for <file>
|
|
||||||
|
|
||||||
cleanup
|
|
||||||
Cleanup the current working directory
|
|
||||||
|
|
||||||
compress[=format] <infile> [outfile]
|
|
||||||
Compress <infile> with [format] to [outfile].
|
|
||||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
|
||||||
If [format] is not specified, then gzip will be used.
|
|
||||||
If [outfile] is not specified, then <infile> will be replaced
|
|
||||||
with another file suffixed with a matching file extension.
|
|
||||||
Supported formats: )EOF", arg0);
|
|
||||||
|
|
||||||
print_formats();
|
|
||||||
|
|
||||||
fprintf(stderr, R"EOF(
|
|
||||||
|
|
||||||
decompress <infile> [outfile]
|
|
||||||
Detect format and decompress <infile> to [outfile].
|
|
||||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
|
||||||
If [outfile] is not specified, then <infile> will be replaced
|
|
||||||
with another file removing its archive format file extension.
|
|
||||||
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);
|
|
||||||
unlink(BOOTCONFIG_FILE);
|
|
||||||
rm_rf(VND_RAMDISK_DIR);
|
|
||||||
} else if (argc > 2 && action == "sha1") {
|
|
||||||
uint8_t sha1[20];
|
|
||||||
{
|
|
||||||
mmap_data m(argv[2]);
|
|
||||||
sha1_hash(m, byte_data(sha1, sizeof(sha1)));
|
|
||||||
}
|
|
||||||
for (uint8_t i : sha1)
|
|
||||||
printf("%02x", i);
|
|
||||||
printf("\n");
|
|
||||||
} else if (argc > 2 && action == "split") {
|
|
||||||
if (argv[2] == "-n"sv) {
|
|
||||||
if (argc == 3)
|
|
||||||
usage(argv[0]);
|
|
||||||
return split_image_dtb(argv[3], true);
|
|
||||||
} else {
|
|
||||||
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 == "verify") {
|
|
||||||
return verify(argv[2], argv[3]);
|
|
||||||
} else if (argc > 2 && action == "sign") {
|
|
||||||
if (argc == 5) usage(argv[0]);
|
|
||||||
return sign(
|
|
||||||
argv[2],
|
|
||||||
argc > 3 ? argv[3] : "/boot",
|
|
||||||
argc > 5 ? argv[4] : nullptr,
|
|
||||||
argc > 5 ? argv[5] : nullptr);
|
|
||||||
} 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(byte_view(argv[2]), byte_view(argv[3]), byte_view(argv[4])) ? 0 : 1;
|
|
||||||
} else if (argc > 2 && action == "cpio") {
|
|
||||||
return rust::cpio_commands(argc - 2, argv + 2) ? 0 : 1;
|
|
||||||
} else if (argc > 2 && action == "dtb") {
|
|
||||||
return rust::dtb_commands(argc - 2, argv + 2) ? 0 : 1;
|
|
||||||
} else if (argc > 2 && action == "extract") {
|
|
||||||
return rust::extract_boot_from_payload(
|
|
||||||
argv[2],
|
|
||||||
argc > 3 ? argv[3] : "",
|
|
||||||
argc > 4 ? argv[4] : ""
|
|
||||||
) ? 0 : 1;
|
|
||||||
} else {
|
|
||||||
usage(argv[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -102,13 +102,9 @@ fn hex2byte(hex: &[u8]) -> Vec<u8> {
|
|||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool {
|
pub fn hexpatch(file: &mut String, from: &Utf8CStr, to: &Utf8CStr) -> bool {
|
||||||
let res: LoggedResult<bool> = try {
|
let res: LoggedResult<bool> = try {
|
||||||
let file = Utf8CStr::from_bytes(file)?;
|
let mut map = MappedFile::open_rw(Utf8CStr::from_string(file))?;
|
||||||
let from = Utf8CStr::from_bytes(from)?;
|
|
||||||
let to = Utf8CStr::from_bytes(to)?;
|
|
||||||
|
|
||||||
let mut map = MappedFile::open_rw(file)?;
|
|
||||||
let pattern = hex2byte(from.as_bytes());
|
let pattern = hex2byte(from.as_bytes());
|
||||||
let patch = hex2byte(to.as_bytes());
|
let patch = hex2byte(to.as_bytes());
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use crate::{
|
|||||||
proto::update_metadata::{mod_InstallOperation::Type, DeltaArchiveManifest},
|
proto::update_metadata::{mod_InstallOperation::Type, DeltaArchiveManifest},
|
||||||
};
|
};
|
||||||
use base::{
|
use base::{
|
||||||
error, ffi::Utf8CStrRef, LoggedError, LoggedResult, ReadSeekExt, ResultExt, Utf8CStr, WriteExt,
|
error, LoggedError, LoggedResult, ReadSeekExt, ResultExt, WriteExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! bad_payload {
|
macro_rules! bad_payload {
|
||||||
@ -28,10 +28,10 @@ macro_rules! bad_payload {
|
|||||||
|
|
||||||
const PAYLOAD_MAGIC: &str = "CrAU";
|
const PAYLOAD_MAGIC: &str = "CrAU";
|
||||||
|
|
||||||
fn do_extract_boot_from_payload(
|
pub fn extract_boot_from_payload(
|
||||||
in_path: &Utf8CStr,
|
in_path: &str,
|
||||||
partition_name: Option<&Utf8CStr>,
|
partition_name: Option<&str>,
|
||||||
out_path: Option<&Utf8CStr>,
|
out_path: Option<&str>,
|
||||||
) -> LoggedResult<()> {
|
) -> LoggedResult<()> {
|
||||||
let mut reader = BufReader::new(if in_path == "-" {
|
let mut reader = BufReader::new(if in_path == "-" {
|
||||||
unsafe { File::from_raw_fd(0) }
|
unsafe { File::from_raw_fd(0) }
|
||||||
@ -179,24 +179,3 @@ fn do_extract_boot_from_payload(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_boot_from_payload(
|
|
||||||
in_path: Utf8CStrRef,
|
|
||||||
partition: Utf8CStrRef,
|
|
||||||
out_path: Utf8CStrRef,
|
|
||||||
) -> bool {
|
|
||||||
let res: LoggedResult<()> = try {
|
|
||||||
let partition = if partition.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(partition)
|
|
||||||
};
|
|
||||||
let out_path = if out_path.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(out_path)
|
|
||||||
};
|
|
||||||
do_extract_boot_from_payload(in_path, partition, out_path)?
|
|
||||||
};
|
|
||||||
res.log_with_msg(|w| w.write_str("Failed to extract from payload"))
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user