Magisk/native/src/boot/patch.rs
2023-06-21 16:47:20 -07:00

123 lines
3.6 KiB
Rust

use base::{MappedFile, MutBytesExt, ResultExt, Utf8CStr};
// SAFETY: assert(buf.len() >= 1) && assert(len <= buf.len())
macro_rules! match_patterns {
($buf:ident, $($str:literal), *) => {{
let mut len = if *$buf.get_unchecked(0) == b',' { 1 } else { 0 };
let b = $buf.get_unchecked(len..);
let found = if b.is_empty() {
false
}
$(
else if b.starts_with($str) {
len += $str.len();
true
}
)*
else {
false
};
if found {
let b = $buf.get_unchecked(len..);
if !b.is_empty() && b[0] == b'=' {
for c in b.iter() {
if b" \n\0".contains(c) {
break;
}
len += 1;
}
}
Some(len)
} else {
None
}
}};
}
fn remove_pattern(buf: &mut [u8], pattern_matcher: unsafe fn(&[u8]) -> Option<usize>) -> usize {
let mut write = 0_usize;
let mut read = 0_usize;
let mut sz = buf.len();
// SAFETY: assert(write <= read) && assert(read <= buf.len())
unsafe {
while read < buf.len() {
if let Some(len) = pattern_matcher(buf.get_unchecked(read..)) {
let skipped = buf.get_unchecked(read..(read + len));
// SAFETY: all matching patterns are ASCII bytes
let skipped = std::str::from_utf8_unchecked(skipped);
eprintln!("Remove pattern [{}]", skipped);
sz -= len;
read += len;
} else {
*buf.get_unchecked_mut(write) = *buf.get_unchecked(read);
write += 1;
read += 1;
}
}
}
if let Some(buf) = buf.get_mut(write..) {
buf.fill(0);
}
sz
}
pub fn patch_verity(buf: &mut [u8]) -> usize {
unsafe fn match_verity_pattern(buf: &[u8]) -> Option<usize> {
match_patterns!(
buf,
b"verifyatboot",
b"verify",
b"avb_keys",
b"avb",
b"support_scfs",
b"fsverity"
)
}
remove_pattern(buf, match_verity_pattern)
}
pub fn patch_encryption(buf: &mut [u8]) -> usize {
unsafe fn match_encryption_pattern(buf: &[u8]) -> Option<usize> {
match_patterns!(buf, b"forceencrypt", b"forcefdeorfbe", b"fileencryption")
}
remove_pattern(buf, match_encryption_pattern)
}
fn hex2byte(hex: &[u8]) -> Vec<u8> {
let mut v = Vec::new();
v.reserve(hex.len() / 2);
for bytes in hex.chunks(2) {
if bytes.len() != 2 {
break;
}
let high = bytes[0].to_ascii_uppercase() - b'0';
let low = bytes[1].to_ascii_uppercase() - b'0';
let h = if high > 9 { high - 7 } else { high };
let l = if low > 9 { low - 7 } else { low };
v.push(h << 4 | l);
}
v
}
pub fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool {
fn inner(file: &[u8], from: &[u8], to: &[u8]) -> anyhow::Result<bool> {
let file = Utf8CStr::from_bytes(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 patch = hex2byte(to.as_bytes());
let v = map.patch(pattern.as_slice(), patch.as_slice());
for off in &v {
eprintln!("Patch @ {:#010X} [{}] -> [{}]", off, from, to);
}
Ok(!v.is_empty())
}
inner(file, from, to).log().unwrap_or(false)
}