diff --git a/patcher/src/bin/patcher.rs b/patcher/src/bin/patcher.rs index b4cb8e1..9aa974b 100644 --- a/patcher/src/bin/patcher.rs +++ b/patcher/src/bin/patcher.rs @@ -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;\ - ->()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;\ - ->(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(); diff --git a/patcher/src/code_loading_patcher.rs b/patcher/src/code_loading_patcher.rs index e03f449..2e1d321 100644 --- a/patcher/src/code_loading_patcher.rs +++ b/patcher/src/code_loading_patcher.rs @@ -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, + 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), +}