mirror of
https://github.com/Momoko-Ayase/Senbei.git
synced 2026-09-20 06:18:01 -04:00
fix(android): carry v24 runtime context across package scans
This commit is contained in:
@@ -145,7 +145,21 @@ fn restore_so_file_with_context(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore_so_file(input: &Path, dest: &Path, verbose: bool) -> Result<Option<Vec<u8>>> {
|
pub fn restore_so_file(input: &Path, dest: &Path, verbose: bool) -> Result<Option<Vec<u8>>> {
|
||||||
Ok(restore_so_file_with_context(input, dest, verbose)?.embedded_metadata)
|
let mut method_index_module = None;
|
||||||
|
restore_so_file_with_method_index_module(input, dest, verbose, &mut method_index_module)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn restore_so_file_with_method_index_module(
|
||||||
|
input: &Path,
|
||||||
|
dest: &Path,
|
||||||
|
verbose: bool,
|
||||||
|
method_index_module: &mut Option<Vec<u8>>,
|
||||||
|
) -> Result<Option<Vec<u8>>> {
|
||||||
|
let restored = restore_so_file_with_context(input, dest, verbose)?;
|
||||||
|
if let Some(module) = restored.method_index_module {
|
||||||
|
*method_index_module = Some(module);
|
||||||
|
}
|
||||||
|
Ok(restored.embedded_metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Content identity for cross-source deduplication: the same library may
|
/// Content identity for cross-source deduplication: the same library may
|
||||||
@@ -173,7 +187,7 @@ pub fn restore_metadata_bytes(data: &[u8]) -> anyhow::Result<(Vec<u8>, senbei_me
|
|||||||
restore_metadata_bytes_with_module(data, None)
|
restore_metadata_bytes_with_module(data, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_metadata_bytes_with_module(
|
pub(crate) fn restore_metadata_bytes_with_module(
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
method_index_module: Option<&[u8]>,
|
method_index_module: Option<&[u8]>,
|
||||||
) -> anyhow::Result<(Vec<u8>, senbei_metadata::Report)> {
|
) -> anyhow::Result<(Vec<u8>, senbei_metadata::Report)> {
|
||||||
@@ -261,15 +275,15 @@ pub enum EntryStatus {
|
|||||||
Failed(anyhow::Error),
|
Failed(anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PackageRestoreContext {
|
struct PackageRestoreContext<'a> {
|
||||||
method_index_module: Option<Vec<u8>>,
|
method_index_module: &'a mut Option<Vec<u8>>,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackageRestoreContext {
|
impl<'a> PackageRestoreContext<'a> {
|
||||||
fn new(verbose: bool) -> Self {
|
fn new(verbose: bool, method_index_module: &'a mut Option<Vec<u8>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
method_index_module: None,
|
method_index_module,
|
||||||
verbose,
|
verbose,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,6 +306,25 @@ pub fn restore_package(
|
|||||||
out_root: &Path,
|
out_root: &Path,
|
||||||
seen: &mut HashSet<String>,
|
seen: &mut HashSet<String>,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
) -> Result<Vec<EntryOutcome>> {
|
||||||
|
let mut method_index_module = None;
|
||||||
|
restore_package_with_method_index_module(
|
||||||
|
package,
|
||||||
|
rel,
|
||||||
|
out_root,
|
||||||
|
seen,
|
||||||
|
verbose,
|
||||||
|
&mut method_index_module,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn restore_package_with_method_index_module(
|
||||||
|
package: &Path,
|
||||||
|
rel: &Path,
|
||||||
|
out_root: &Path,
|
||||||
|
seen: &mut HashSet<String>,
|
||||||
|
verbose: bool,
|
||||||
|
method_index_module: &mut Option<Vec<u8>>,
|
||||||
) -> Result<Vec<EntryOutcome>> {
|
) -> Result<Vec<EntryOutcome>> {
|
||||||
let bundle = package
|
let bundle = package
|
||||||
.extension()
|
.extension()
|
||||||
@@ -302,7 +335,7 @@ pub fn restore_package(
|
|||||||
let mut archive = open_package(package)?;
|
let mut archive = open_package(package)?;
|
||||||
let temporary = tempfile::tempdir().context("create package workspace")?;
|
let temporary = tempfile::tempdir().context("create package workspace")?;
|
||||||
let mut outcomes = Vec::new();
|
let mut outcomes = Vec::new();
|
||||||
let mut context = PackageRestoreContext::new(verbose);
|
let mut context = PackageRestoreContext::new(verbose, method_index_module);
|
||||||
|
|
||||||
let mut direct = Vec::new();
|
let mut direct = Vec::new();
|
||||||
let mut nested = Vec::new();
|
let mut nested = Vec::new();
|
||||||
@@ -347,8 +380,15 @@ pub fn restore_package(
|
|||||||
.with_context(|| format!("extract `{label}`"))?;
|
.with_context(|| format!("extract `{label}`"))?;
|
||||||
outcomes.append(&mut entry_outcomes);
|
outcomes.append(&mut entry_outcomes);
|
||||||
}
|
}
|
||||||
|
struct NestedPackage {
|
||||||
|
label: PathBuf,
|
||||||
|
path: PathBuf,
|
||||||
|
base: PathBuf,
|
||||||
|
entries: Vec<(usize, PathBuf)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nested_packages = Vec::with_capacity(nested.len());
|
||||||
for (index, name) in nested {
|
for (index, name) in nested {
|
||||||
let mut nested_context = PackageRestoreContext::new(verbose);
|
|
||||||
let nested_label = rel.join(&name);
|
let nested_label = rel.join(&name);
|
||||||
let nested_path = extract_entry(&mut archive, index, &temporary, &nested_label)
|
let nested_path = extract_entry(&mut archive, index, &temporary, &nested_label)
|
||||||
.with_context(|| format!("extract `{}`", nested_label.display()))?;
|
.with_context(|| format!("extract `{}`", nested_label.display()))?;
|
||||||
@@ -368,26 +408,45 @@ pub fn restore_package(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Restore the protected library before metadata so v24.1 metadata can
|
nested_packages.push(NestedPackage {
|
||||||
// consume the runtime profile recovered from module 0x0C.
|
label: nested_label,
|
||||||
entries.sort_by_key(|(_, entry_name)| package_entry_priority(entry_name));
|
path: nested_path,
|
||||||
// Keep the nested package's stem in the output layout so two splits
|
base: rel.join(name.with_extension("")),
|
||||||
// carrying same-named entries cannot collide.
|
entries,
|
||||||
let base = rel.join(name.with_extension(""));
|
});
|
||||||
for (nested_index, entry_name) in entries {
|
}
|
||||||
let label = format!("{}::{}", nested_label.display(), entry_name.display());
|
|
||||||
let dest = out_root.join(&base).join(crate::job::out_name(&entry_name));
|
// A bundle can put libil2cpp.so in an ABI split while metadata stays in
|
||||||
let mut entry_outcomes = restore_package_entry(
|
// base.apk. Share one context across the whole bundle and process all SOs
|
||||||
&mut nested_archive,
|
// before any metadata, regardless of which nested APK owns each entry.
|
||||||
nested_index,
|
for priority in [0_u8, 1_u8] {
|
||||||
&label,
|
for nested_package in &nested_packages {
|
||||||
&dest,
|
let mut nested_archive = open_package(&nested_package.path)?;
|
||||||
&temporary,
|
for (nested_index, entry_name) in nested_package
|
||||||
seen,
|
.entries
|
||||||
&mut nested_context,
|
.iter()
|
||||||
)
|
.filter(|(_, entry_name)| package_entry_priority(entry_name) == priority)
|
||||||
.with_context(|| format!("extract `{label}`"))?;
|
{
|
||||||
outcomes.append(&mut entry_outcomes);
|
let label = format!(
|
||||||
|
"{}::{}",
|
||||||
|
nested_package.label.display(),
|
||||||
|
entry_name.display()
|
||||||
|
);
|
||||||
|
let dest = out_root
|
||||||
|
.join(&nested_package.base)
|
||||||
|
.join(crate::job::out_name(entry_name));
|
||||||
|
let mut entry_outcomes = restore_package_entry(
|
||||||
|
&mut nested_archive,
|
||||||
|
*nested_index,
|
||||||
|
&label,
|
||||||
|
&dest,
|
||||||
|
&temporary,
|
||||||
|
seen,
|
||||||
|
&mut context,
|
||||||
|
)
|
||||||
|
.with_context(|| format!("extract `{label}`"))?;
|
||||||
|
outcomes.append(&mut entry_outcomes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(outcomes)
|
Ok(outcomes)
|
||||||
@@ -403,7 +462,7 @@ fn restore_package_entry<R: Read + Seek>(
|
|||||||
dest: &Path,
|
dest: &Path,
|
||||||
temporary: &tempfile::TempDir,
|
temporary: &tempfile::TempDir,
|
||||||
seen: &mut HashSet<String>,
|
seen: &mut HashSet<String>,
|
||||||
context: &mut PackageRestoreContext,
|
context: &mut PackageRestoreContext<'_>,
|
||||||
) -> Result<Vec<EntryOutcome>> {
|
) -> Result<Vec<EntryOutcome>> {
|
||||||
let entry_path = extract_entry(archive, index, temporary, Path::new(label))?;
|
let entry_path = extract_entry(archive, index, temporary, Path::new(label))?;
|
||||||
let entry_file =
|
let entry_file =
|
||||||
@@ -437,7 +496,7 @@ fn restore_package_entry<R: Read + Seek>(
|
|||||||
match restore_so_file_with_context(&entry_path, dest, context.verbose) {
|
match restore_so_file_with_context(&entry_path, dest, context.verbose) {
|
||||||
Ok(restored) => {
|
Ok(restored) => {
|
||||||
if let Some(module) = restored.method_index_module {
|
if let Some(module) = restored.method_index_module {
|
||||||
context.method_index_module = Some(module);
|
*context.method_index_module = Some(module);
|
||||||
}
|
}
|
||||||
let mut outcomes = vec![outcome(EntryKind::So, EntryStatus::Restored)];
|
let mut outcomes = vec![outcome(EntryKind::So, EntryStatus::Restored)];
|
||||||
if let Some(blob) = restored.embedded_metadata {
|
if let Some(blob) = restored.embedded_metadata {
|
||||||
|
|||||||
+27
-5
@@ -221,6 +221,7 @@ pub fn run_folder_opts(
|
|||||||
// files restore first so the cross-source dedup keeps them over a copy
|
// files restore first so the cross-source dedup keeps them over a copy
|
||||||
// inside a package (loose beats `.apk` beats `.apks`/`.xapk` bundle).
|
// inside a package (loose beats `.apk` beats `.apks`/`.xapk` bundle).
|
||||||
let mut android_seen = std::collections::HashSet::new();
|
let mut android_seen = std::collections::HashSet::new();
|
||||||
|
let mut android_method_index_module = None;
|
||||||
// Hashing a protected library costs a full read, so only pay it when a
|
// Hashing a protected library costs a full read, so only pay it when a
|
||||||
// duplicate source can actually exist in this run.
|
// duplicate source can actually exist in this run.
|
||||||
let android_dedup = scan.android_so.len() > 1 || !scan.android_packages.is_empty();
|
let android_dedup = scan.android_so.len() > 1 || !scan.android_packages.is_empty();
|
||||||
@@ -242,7 +243,12 @@ pub fn run_folder_opts(
|
|||||||
let input_owned = input.clone();
|
let input_owned = input.clone();
|
||||||
let dest_owned = dest.clone();
|
let dest_owned = dest.clone();
|
||||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||||
crate::android::restore_so_file(&input_owned, &dest_owned, verbose_steps)
|
crate::android::restore_so_file_with_method_index_module(
|
||||||
|
&input_owned,
|
||||||
|
&dest_owned,
|
||||||
|
verbose_steps,
|
||||||
|
&mut android_method_index_module,
|
||||||
|
)
|
||||||
}));
|
}));
|
||||||
match result {
|
match result {
|
||||||
Ok(Ok(embedded)) => {
|
Ok(Ok(embedded)) => {
|
||||||
@@ -310,12 +316,13 @@ pub fn run_folder_opts(
|
|||||||
let out_root_owned = out_root.clone();
|
let out_root_owned = out_root.clone();
|
||||||
let mut seen_taken = std::mem::take(&mut android_seen);
|
let mut seen_taken = std::mem::take(&mut android_seen);
|
||||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||||
let outcomes = crate::android::restore_package(
|
let outcomes = crate::android::restore_package_with_method_index_module(
|
||||||
&package_owned,
|
&package_owned,
|
||||||
&rel_owned,
|
&rel_owned,
|
||||||
&out_root_owned,
|
&out_root_owned,
|
||||||
&mut seen_taken,
|
&mut seen_taken,
|
||||||
verbose_steps,
|
verbose_steps,
|
||||||
|
&mut android_method_index_module,
|
||||||
);
|
);
|
||||||
(outcomes, seen_taken)
|
(outcomes, seen_taken)
|
||||||
}));
|
}));
|
||||||
@@ -361,7 +368,12 @@ pub fn run_folder_opts(
|
|||||||
let meta_owned = meta.clone();
|
let meta_owned = meta.clone();
|
||||||
let dest_owned = dest.clone();
|
let dest_owned = dest.clone();
|
||||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||||
deobfuscate_metadata_to(&meta_owned, &dest_owned, verbose_steps)
|
deobfuscate_metadata_to_with_module(
|
||||||
|
&meta_owned,
|
||||||
|
&dest_owned,
|
||||||
|
verbose_steps,
|
||||||
|
android_method_index_module.as_deref(),
|
||||||
|
)
|
||||||
}));
|
}));
|
||||||
match result {
|
match result {
|
||||||
Ok(Ok(report)) if report.remapped > 0 => {
|
Ok(Ok(report)) if report.remapped > 0 => {
|
||||||
@@ -735,6 +747,15 @@ pub fn deobfuscate_metadata_to(
|
|||||||
input: &Path,
|
input: &Path,
|
||||||
dest: &Path,
|
dest: &Path,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
) -> anyhow::Result<senbei_metadata::Report> {
|
||||||
|
deobfuscate_metadata_to_with_module(input, dest, verbose, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deobfuscate_metadata_to_with_module(
|
||||||
|
input: &Path,
|
||||||
|
dest: &Path,
|
||||||
|
verbose: bool,
|
||||||
|
method_index_module: Option<&[u8]>,
|
||||||
) -> anyhow::Result<senbei_metadata::Report> {
|
) -> anyhow::Result<senbei_metadata::Report> {
|
||||||
let data = std::fs::read(input)?;
|
let data = std::fs::read(input)?;
|
||||||
// The Android seeded-permutation variant is tried first (it validates
|
// The Android seeded-permutation variant is tried first (it validates
|
||||||
@@ -742,8 +763,9 @@ pub fn deobfuscate_metadata_to(
|
|||||||
// Windows path. The [`senbei_metadata::Error`] is preserved in the chain
|
// Windows path. The [`senbei_metadata::Error`] is preserved in the chain
|
||||||
// (rather than stringified) so the folder driver can apply its
|
// (rather than stringified) so the folder driver can apply its
|
||||||
// unsupported-version policy.
|
// unsupported-version policy.
|
||||||
let (out, report) = crate::android::restore_metadata_bytes(&data)
|
let (out, report) =
|
||||||
.map_err(|e| e.context(format!("{input:?}")))?;
|
crate::android::restore_metadata_bytes_with_module(&data, method_index_module)
|
||||||
|
.map_err(|e| e.context(format!("{input:?}")))?;
|
||||||
if report.remapped > 0 {
|
if report.remapped > 0 {
|
||||||
if let Some(parent) = dest.parent() {
|
if let Some(parent) = dest.parent() {
|
||||||
std::fs::create_dir_all(parent)?;
|
std::fs::create_dir_all(parent)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user