This commit is contained in:
Jean-Marie Mineau 2025-03-25 16:55:34 +01:00
parent ce261b75f9
commit a2c1f962db
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
2 changed files with 84 additions and 77 deletions

View file

@ -6,7 +6,7 @@ use std::path::PathBuf;
use androscalpel::{Apk, Class, IdType};
use patcher::{
code_loading_patcher::insert_code,
code_loading_patcher::{insert_code, CodePatchingStrategy},
labeling,
reflection_patcher::transform_method,
runtime_data::RuntimeData, // ReflectionInvokeData, ReflectionClassNewInstData, ReflectionCnstrNewInstData,
@ -29,6 +29,8 @@ struct Cli {
path: PathBuf,
#[arg(short, long)]
runtime_data: PathBuf,
#[arg(short, long, default_value_t, value_enum)]
code_loading_patch_strategy: CodePatchingStrategy,
}
fn main() {
@ -42,82 +44,9 @@ fn main() {
.read_to_string(&mut json)
.unwrap();
let rt_data: RuntimeData = serde_json::from_str(&json).unwrap();
/*
let rt_data = RuntimeData {
invoke_data: vec![
ReflectionInvokeData {
method: IdMethod::from_smali(
"Lcom/example/theseus/reflection/Reflectee;\
->transfer\
(Ljava/lang/String;)Ljava/lang/String;",
)
.unwrap(),
caller_method: IdMethod::from_smali(
"Lcom/example/theseus/reflection/MainActivity;\
->callVirtualMethodReflectCall()V",
)
.unwrap(),
addr: 0x2B,
},
ReflectionInvokeData {
method: IdMethod::from_smali(
"Lcom/example/theseus/reflection/Reflectee;\
->transfer(Ljava/lang/String;)Ljava/lang/String;",
)
.unwrap(),
caller_method: IdMethod::from_smali(
"Lcom/example/theseus/reflection/MainActivity;\
->callConstructorVirtualMethodReflectConstr()V",
)
.unwrap(),
addr: 0x38,
},
ReflectionInvokeData {
method: IdMethod::from_smali(
"Lcom/example/theseus/reflection/Reflectee;\
->transfer(Ljava/lang/String;)Ljava/lang/String;",
)
.unwrap(),
caller_method: IdMethod::from_smali(
"Lcom/example/theseus/reflection/MainActivity;\
->callVirtualMethodReflectOldConst()V",
)
.unwrap(),
addr: 0x28,
},
],
class_new_inst_data: vec![ReflectionClassNewInstData {
constructor: IdMethod::from_smali(
"Lcom/example/theseus/reflection/Reflectee;\
-><init>()V",
)
.unwrap(),
caller_method: IdMethod::from_smali(
"Lcom/example/theseus/reflection/MainActivity;\
->callVirtualMethodReflectOldConst()V",
)
.unwrap(),
addr: 0x12,
}],
cnstr_new_inst_data: vec![ReflectionCnstrNewInstData {
constructor: IdMethod::from_smali(
"Lcom/example/theseus/reflection/Reflectee;\
-><init>(Ljava/lang/String;)V",
)
.unwrap(),
caller_method: IdMethod::from_smali(
"Lcom/example/theseus/reflection/MainActivity;\
->callConstructorVirtualMethodReflectConstr()V",
)
.unwrap(),
addr: 0x22,
}],
};
println!("{}", serde_json::to_string(&rt_data).unwrap());
*/
// Dynamic Loading
insert_code(&mut apk, &rt_data).unwrap();
insert_code(cli.code_loading_patch_strategy, &mut apk, &rt_data).unwrap();
// Reflection
let mut test_methods = HashMap::new();

View file

@ -1,13 +1,33 @@
use std::collections::HashMap;
use std::fs::File;
use androscalpel::Apk;
use androscalpel::{Apk, IdType};
use anyhow::Result;
use clap::ValueEnum;
use crate::runtime_data::RuntimeData;
#[derive(ValueEnum, Debug, PartialEq, Clone, Copy, Default)]
pub enum CodePatchingStrategy {
#[default]
Naive,
ModelClassLoaders,
}
pub fn insert_code(
strategy: CodePatchingStrategy,
apk: &mut Apk,
data: &RuntimeData,
) -> Result<()> {
match strategy {
CodePatchingStrategy::Naive => insert_code_naive(apk, data),
CodePatchingStrategy::ModelClassLoaders => insert_code_model_class_loaders(apk, data),
}
}
/// Insert statically bytecode that was loaded from other source at runtime.
/// For now, we ignore class collision.
pub fn insert_code(apk: &mut Apk, data: &RuntimeData) -> Result<()> {
fn insert_code_naive(apk: &mut Apk, data: &RuntimeData) -> Result<()> {
for dyn_data in &data.dyn_code_load {
for file in &dyn_data.files {
let file = File::open(file)?;
@ -16,3 +36,61 @@ pub fn insert_code(apk: &mut Apk, data: &RuntimeData) -> Result<()> {
}
Ok(())
}
fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<()> {
let mut class_loaders = HashMap::new();
class_loaders.insert(
"MAIN".to_string(),
ClassLoader {
parent: None,
class: IdType::from_smali("Ljava/lang/Boolean;").unwrap(),
apk: ApkOrRef::Ref(apk),
},
);
for dyn_data in &data.dyn_code_load {
let mut apk = Apk::new();
let class = dyn_data.classloader_class.clone();
for file in &dyn_data.files {
let file = File::open(file)?;
apk.add_code(file, crate::labeling, false)?;
}
assert!(!class_loaders.contains_key(&dyn_data.classloader));
class_loaders.insert(
dyn_data.classloader.clone(),
ClassLoader {
parent: None,
class,
apk: ApkOrRef::Owned(apk),
},
);
}
// TODO: list colliding classes
// TODO: rename colliding classes according to class laoder
// TODO: get the ClassLoader::parent values...
// TODO: model the delegation behavior and rename ref to class accordingly
// TODO: update Runtime Data to reflect the name change
todo!()
}
/// Structure modelizing a class loader.
#[derive(Debug, PartialEq)]
struct ClassLoader<'a> {
pub parent: Option<String>,
pub class: IdType,
pub apk: ApkOrRef<'a>,
}
impl ClassLoader<'_> {
pub fn _apk(&mut self) -> &mut Apk {
match &mut self.apk {
ApkOrRef::Owned(ref mut apk) => apk,
ApkOrRef::Ref(ref mut apk) => apk,
}
}
}
#[derive(Debug, PartialEq)]
enum ApkOrRef<'a> {
Owned(Apk),
Ref(&'a mut Apk),
}