Support extract boot image from payload.bin

This commit is contained in:
LoveSy 2022-12-13 11:33:16 +08:00 committed by John Wu
parent b136aba1e2
commit 7bf2e3875f
8 changed files with 5152 additions and 0 deletions

55
native/src/Cargo.lock generated
View File

@ -11,6 +11,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.79" version = "1.0.79"
@ -64,6 +70,9 @@ name = "magiskboot"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"base", "base",
"byteorder",
"cxx",
"protobuf",
] ]
[[package]] [[package]]
@ -81,6 +90,12 @@ dependencies = [
"base", "base",
] ]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.56" version = "1.0.56"
@ -90,6 +105,26 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "protobuf"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e"
dependencies = [
"once_cell",
"protobuf-support",
"thiserror",
]
[[package]]
name = "protobuf-support"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.26" version = "1.0.26"
@ -110,6 +145,26 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.8" version = "1.0.8"

View File

@ -9,3 +9,6 @@ path = "lib.rs"
[dependencies] [dependencies]
base = { path = "../base" } base = { path = "../base" }
cxx = { path = "../external/cxx-rs" }
protobuf = "3.2.0"
byteorder = "1"

View File

@ -9,6 +9,7 @@
#include "bootimg.hpp" #include "bootimg.hpp"
#include "magiskboot.hpp" #include "magiskboot.hpp"
#include "compress.hpp" #include "compress.hpp"
#include "boot-rs.hpp"
using namespace std; using namespace std;

View File

@ -290,6 +290,8 @@ private:
} }
if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out)) if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
return false; return false;
if (code == BZ_STREAM_END)
return true;
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
return true; return true;
} }
@ -723,3 +725,20 @@ void compress(const char *method, const char *infile, const char *outfile) {
if (rm_in) if (rm_in)
unlink(infile); unlink(infile);
} }
namespace rust {
bool decompress(const unsigned char *in, uint64_t in_size, int fd) {
format_t type = check_fmt(in, in_size);
if (!COMPRESSED(type)) {
LOGE("Input file is not a supported compressed type!\n");
return false;
}
auto strm = get_decoder(type, make_unique<fd_stream>(fd));
if (!strm->write(in, in_size)) {
return false;
}
return true;
}
}

View File

@ -11,3 +11,7 @@ filter_strm_ptr get_decoder(format_t type, stream_ptr &&base);
void compress(const char *method, const char *infile, const char *outfile); void compress(const char *method, const char *infile, const char *outfile);
void decompress(char *infile, const char *outfile); void decompress(char *infile, const char *outfile);
namespace rust {
bool decompress(const unsigned char *in, uint64_t in_size, int fd);
}

View File

@ -1 +1,197 @@
#![feature(format_args_nl)]
mod update_metadata;
use crate::update_metadata::install_operation::Type;
use crate::update_metadata::DeltaArchiveManifest;
pub use base; pub use base;
use base::error;
use base::libc::c_char;
use byteorder::{BigEndian, ReadBytesExt};
use protobuf::{EnumFull, Message};
use std::ffi::CStr;
use std::fs::File;
use std::io::{BufReader, ErrorKind, Read, Seek, SeekFrom};
use std::io::{Error as IOError, Write};
use std::os::unix::io::AsRawFd;
#[cxx::bridge(namespace = "rust")]
mod ffi {
unsafe extern "C++" {
pub unsafe fn decompress(in_: *const u8, in_size: u64, fd: i32) -> bool;
}
extern "Rust" {
unsafe fn extract_boot_from_payload(
in_path: *const c_char,
out_path: *const c_char,
) -> bool;
}
}
#[macro_export]
macro_rules! data_err {
($fmt:expr) => { IOError::new(ErrorKind::InvalidData, format!($fmt)) };
($fmt:expr, $($args:tt)*) => { IOError::new(ErrorKind::InvalidData, format!($fmt, $($args)*)) };
}
static PAYLOAD_MAGIC: &str = "CrAU";
pub fn do_extract_boot_from_payload(in_path: &str, out_path: &str) -> Result<(), IOError> {
// let mut file = File::create(out_path).unwrap();
let in_file = File::open(in_path)?;
let mut reader = BufReader::new(in_file);
let buf = &mut [0u8; 4];
reader.read_exact(buf)?;
if buf != PAYLOAD_MAGIC.as_bytes() {
return Err(data_err!("invalid payload magic"));
}
let version = reader.read_u64::<BigEndian>()?;
if version != 2 {
return Err(data_err!("unsupported version {}", version));
}
let manifest_len = reader.read_u64::<BigEndian>()?;
if manifest_len == 0 {
return Err(data_err!("manifest length is zero"));
}
let manifest_sig_len = reader.read_u32::<BigEndian>()?;
if manifest_sig_len == 0 {
return Err(data_err!("manifest signature length is zero"));
}
let mut buf = vec![0; manifest_len as usize];
reader.read_exact(&mut buf)?;
let manifest = DeltaArchiveManifest::parse_from_bytes(&buf)?;
if !manifest.has_minor_version() || manifest.minor_version() != 0 {
return Err(data_err!(
"delta payloads are not supported, please use a full payload file"
));
}
if !manifest.has_block_size() {
return Err(data_err!("block size not found"));
}
let boot = manifest.partitions.iter().find(|partition| {
partition.has_partition_name() && partition.partition_name() == "init_boot"
});
let boot = match boot {
Some(boot) => Some(boot),
None => manifest.partitions.iter().find(|partition| {
partition.has_partition_name() && partition.partition_name() == "boot"
}),
};
let boot = boot.ok_or(data_err!("boot partition not found"))?;
let base_offset = reader.stream_position()?;
let base_offset = base_offset + manifest_sig_len as u64;
let block_size = manifest
.block_size
.ok_or(data_err!("block size not found"))? as u64;
let mut out_file = File::create(out_path)?;
for operation in boot.operations.iter() {
let data_len = operation
.data_length
.ok_or(data_err!("data length not found"))?;
let data_offset = operation
.data_offset
.ok_or(data_err!("data offset not found"))?;
let data_type = operation
.type_
.ok_or(data_err!("operation type not found"))?;
let data_type = data_type
.enum_value()
.map_err(|_| data_err!("operation type not valid"))?;
let mut buf = vec![0; data_len as usize];
reader.seek(SeekFrom::Start(base_offset + data_offset))?;
reader.read_exact(&mut buf)?;
let out_offset = operation
.dst_extents
.get(0)
.ok_or(data_err!("dst extents not found"))?
.start_block
.ok_or(data_err!("start block not found"))?
* block_size;
match data_type {
Type::REPLACE => {
out_file.seek(SeekFrom::Start(out_offset))?;
out_file.write_all(&buf)?;
}
Type::ZERO => {
for ext in operation.dst_extents.iter() {
let out_seek =
ext.start_block.ok_or(data_err!("start block not found"))? * block_size;
let num_blocks = ext.num_blocks.ok_or(data_err!("num blocks not found"))?;
out_file.seek(SeekFrom::Start(out_seek))?;
out_file.write_all(&vec![0; num_blocks as usize])?;
}
}
Type::REPLACE_BZ | Type::REPLACE_XZ => {
out_file.seek(SeekFrom::Start(out_offset))?;
unsafe {
if !ffi::decompress(buf.as_ptr(), buf.len() as u64, out_file.as_raw_fd()) {
return Err(data_err!("decompression failed"));
}
}
}
_ => {
return Err(data_err!(
"unsupported operation type: {}",
data_type.descriptor().name()
));
}
};
}
Ok(())
}
fn extract_boot_from_payload(in_path: *const c_char, out_path: *const c_char) -> bool {
let in_path = unsafe { CStr::from_ptr(in_path) };
let out_path = unsafe { CStr::from_ptr(out_path) };
let in_path = match in_path.to_str() {
Ok(path) => path,
Err(_) => {
error!("Failed to extract boot from payload: input path invalid");
return false;
}
};
let out_path = match out_path.to_str() {
Ok(path) => path,
Err(_) => {
error!("Failed to extract boot from payload: output path invalid");
return false;
}
};
match do_extract_boot_from_payload(in_path, out_path) {
Ok(_) => true,
Err(err) => {
error!("Failed to extract boot from payload: \"{}\"", err);
false
}
}
}

View File

@ -4,6 +4,8 @@
#include "magiskboot.hpp" #include "magiskboot.hpp"
#include "compress.hpp" #include "compress.hpp"
#include "boot-rs.cpp"
using namespace std; using namespace std;
static void print_formats() { static void print_formats() {
@ -46,6 +48,9 @@ Supported actions:
If env variable PATCHVBMETAFLAG is set to true, all disable flags in If env variable PATCHVBMETAFLAG is set to true, all disable flags in
the boot image's vbmeta header will be set. the boot image's vbmeta header will be set.
extract <payload.bin> <outbootimg>
Extract the boot image from payload.bin to <outbootimg>.
hexpatch <file> <hexpattern1> <hexpattern2> hexpatch <file> <hexpattern1> <hexpattern2>
Search <hexpattern1> in <file>, and replace it with <hexpattern2> Search <hexpattern1> in <file>, and replace it with <hexpattern2>
@ -201,6 +206,8 @@ int main(int argc, char *argv[]) {
} else if (argc > 3 && action == "dtb") { } else if (argc > 3 && action == "dtb") {
if (dtb_commands(argc - 2, argv + 2)) if (dtb_commands(argc - 2, argv + 2))
usage(argv[0]); usage(argv[0]);
} else if (argc > 3 && action == "extract") {
return rust::extract_boot_from_payload(argv[2], argv[3]) ? 0 : 1;
} else { } else {
usage(argv[0]); usage(argv[0]);
} }

File diff suppressed because it is too large Load Diff