diff --git a/patcher/src/code_loading_patcher.rs b/patcher/src/code_loading_patcher.rs index 2e1d321..f29f77d 100644 --- a/patcher/src/code_loading_patcher.rs +++ b/patcher/src/code_loading_patcher.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fs::File; use androscalpel::{Apk, IdType}; @@ -38,13 +38,16 @@ fn insert_code_naive(apk: &mut Apk, data: &RuntimeData) -> Result<()> { } fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<()> { + let mut class_defined = apk.list_classes(); let mut class_loaders = HashMap::new(); class_loaders.insert( "MAIN".to_string(), ClassLoader { + id: "MAIN".to_string(), parent: None, class: IdType::from_smali("Ljava/lang/Boolean;").unwrap(), apk: ApkOrRef::Ref(apk), + renamed_classes: HashSet::new(), }, ); for dyn_data in &data.dyn_code_load { @@ -54,17 +57,25 @@ fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result< 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), - }, - ); + + let classes = apk.list_classes(); + let mut class_loader = ClassLoader { + id: dyn_data.classloader.clone(), + parent: None, + class, + apk: ApkOrRef::Owned(apk), + renamed_classes: HashSet::new(), + }; + let collisions = class_defined.intersection(&classes); + for cls in collisions { + class_loader.rename_classdef(cls); + } + class_defined.extend(classes); + + class_loaders.insert(dyn_data.classloader.clone(), class_loader); } - // 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 @@ -75,9 +86,11 @@ fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result< /// Structure modelizing a class loader. #[derive(Debug, PartialEq)] struct ClassLoader<'a> { + pub id: String, pub parent: Option, pub class: IdType, pub apk: ApkOrRef<'a>, + pub renamed_classes: HashSet, } impl ClassLoader<'_> { @@ -87,6 +100,16 @@ impl ClassLoader<'_> { ApkOrRef::Ref(ref mut apk) => apk, } } + + pub fn rename_classdef(&mut self, cls: &IdType) { + use androscalpel::SmaliName; + println!( + "TODO: rename {} -> {}_{}", + cls.try_to_smali().unwrap(), + cls.try_to_smali().unwrap(), + self.id + ); + } } #[derive(Debug, PartialEq)] diff --git a/patcher/tst.sh b/patcher/tst.sh new file mode 100644 index 0000000..dcc9b15 --- /dev/null +++ b/patcher/tst.sh @@ -0,0 +1 @@ +cargo run --bin patcher -- --code-loading-patch-strategy model-class-loaders --runtime-data ./tst/runtime.json --path ../test_apks/dynloading/build/test_dynloading.apk --out /tmp/patched_dynloading.apk -k ../test_apks/dynloading/ToyKey.keystore -z $(which zipalign) -a $(which apksigner) diff --git a/patcher/tst/dex/PathClassLoader_0545b1a9_09d17bb5de42d5d9.bytecode b/patcher/tst/dex/PathClassLoader_0545b1a9_09d17bb5de42d5d9.bytecode new file mode 100644 index 0000000..58770fc Binary files /dev/null and b/patcher/tst/dex/PathClassLoader_0545b1a9_09d17bb5de42d5d9.bytecode differ diff --git a/patcher/tst/dex/PathClassLoader_0b4ac535_09d17bb5de42d5d9.bytecode b/patcher/tst/dex/PathClassLoader_0b4ac535_09d17bb5de42d5d9.bytecode new file mode 100644 index 0000000..58770fc Binary files /dev/null and b/patcher/tst/dex/PathClassLoader_0b4ac535_09d17bb5de42d5d9.bytecode differ diff --git a/patcher/tst/dex/PathClassLoader_0ceee91e_09d17bb5de42d5d9.bytecode b/patcher/tst/dex/PathClassLoader_0ceee91e_09d17bb5de42d5d9.bytecode new file mode 100644 index 0000000..58770fc Binary files /dev/null and b/patcher/tst/dex/PathClassLoader_0ceee91e_09d17bb5de42d5d9.bytecode differ diff --git a/patcher/tst/dex/PathClassLoader_0dfe1ce0_09d17bb5de42d5d9.bytecode b/patcher/tst/dex/PathClassLoader_0dfe1ce0_09d17bb5de42d5d9.bytecode new file mode 100644 index 0000000..58770fc Binary files /dev/null and b/patcher/tst/dex/PathClassLoader_0dfe1ce0_09d17bb5de42d5d9.bytecode differ diff --git a/patcher/tst/runtime.json b/patcher/tst/runtime.json new file mode 100644 index 0000000..ae275fb --- /dev/null +++ b/patcher/tst/runtime.json @@ -0,0 +1,71 @@ +{ + "invoke_data": [ + { + "method": "Lcom/example/theseus/dynloading/AMain;->getColliderId()Ljava/lang/String;", + "caller_method": "Lcom/example/theseus/dynloading/MainActivity;->indirectWithoutParent()V", + "addr": 33, + "is_static": true + }, + { + "method": "Lcom/example/theseus/dynloading/AMain;->getColliderId()Ljava/lang/String;", + "caller_method": "Lcom/example/theseus/dynloading/MainActivity;->indirectWithParent()V", + "addr": 39, + "is_static": true + }, + { + "method": "Lcom/example/theseus/dynloading/Collider;->getColliderId()Ljava/lang/String;", + "caller_method": "Lcom/example/theseus/dynloading/MainActivity;->directWithoutParent()V", + "addr": 33, + "is_static": true + }, + { + "method": "Lcom/example/theseus/dynloading/Collider;->getColliderId()Ljava/lang/String;", + "caller_method": "Lcom/example/theseus/dynloading/MainActivity;->directWithParent()V", + "addr": 39, + "is_static": true + } + ], + "class_new_inst_data": [ + { + "constructor": "Landroid/app/Application;->()V", + "caller_method": "Landroid/app/AppComponentFactory;->instantiateApplication(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/app/Application;", + "addr": 4 + }, + { + "constructor": "Lcom/example/theseus/dynloading/MainActivity;->()V", + "caller_method": "Landroid/app/AppComponentFactory;->instantiateActivity(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/app/Activity;", + "addr": 4 + } + ], + "cnstr_new_inst_data": [], + "dyn_code_load": [ + { + "classloader_class": "Ldalvik/system/PathClassLoader;", + "classloader": "0dfe1ce0", + "files": [ + "./tst/dex/PathClassLoader_0dfe1ce0_09d17bb5de42d5d9.bytecode" + ] + }, + { + "classloader_class": "Ldalvik/system/PathClassLoader;", + "classloader": "0b4ac535", + "files": [ + "./tst/dex/PathClassLoader_0b4ac535_09d17bb5de42d5d9.bytecode" + ] + }, + { + "classloader_class": "Ldalvik/system/PathClassLoader;", + "classloader": "0ceee91e", + "files": [ + "./tst/dex/PathClassLoader_0ceee91e_09d17bb5de42d5d9.bytecode" + ] + }, + { + "classloader_class": "Ldalvik/system/PathClassLoader;", + "classloader": "0545b1a9", + "files": [ + "./tst/dex/PathClassLoader_0545b1a9_09d17bb5de42d5d9.bytecode" + ] + } + ] +} diff --git a/theseus_autopatcher/.gitignore b/theseus_autopatcher/.gitignore index d959d6c..f514b74 100644 --- a/theseus_autopatcher/.gitignore +++ b/theseus_autopatcher/.gitignore @@ -1,3 +1,2 @@ -dist -__pycache__ -src/theseus_autopatcher/patcher_86_64_musl +# Created by venv; see https://docs.python.org/3/library/venv.html +* diff --git a/theseus_autopatcher/src/theseus_autopatcher/__init__.py b/theseus_autopatcher/src/theseus_autopatcher/__init__.py index 37e8ef5..dd9b443 100644 --- a/theseus_autopatcher/src/theseus_autopatcher/__init__.py +++ b/theseus_autopatcher/src/theseus_autopatcher/__init__.py @@ -10,7 +10,7 @@ from theseus_frida import collect_runtime def get_android_sdk_path() -> Path | None: if "ANDROID_HOME" in os.environ: - return os.environ["ANDROID_HOME"] + return Path(os.environ["ANDROID_HOME"]) default = Path.home() / "Android" / "Sdk" if default.exists(): return default @@ -29,10 +29,10 @@ def get_build_tools_path(toolname: str) -> Path | None: path = which(toolname) if path is not None: - return path + return Path(path) path = which(toolname + ".exe") if path is not None: - return path + return Path(path) sdk = get_android_sdk_path() if sdk is None: @@ -54,8 +54,12 @@ def get_build_tools_path(toolname: str) -> Path | None: def get_keytool_path() -> Path | None: path = which("keytool") if path is not None: - return path - return which("keytool.exe") + return Path(path) + path = which("keytool.exe") + if path is not None: + return Path(path) + else: + return None def gen_keystore(keytool: Path, storepath: Path):