diff --git a/frida/theseus_frida/__init__.py b/frida/theseus_frida/__init__.py index 4fa21ac..5272d20 100644 --- a/frida/theseus_frida/__init__.py +++ b/frida/theseus_frida/__init__.py @@ -59,7 +59,9 @@ def print_stack(stack, prefix: str): print(f" {prefix}{frame['method']}:{frame['bytecode_index']}{native}") -def cl_id_to_string(classloader: int) -> str: +def cl_id_to_string(classloader: int) -> str | None: + if classloader == 0: # 0 is the hash of java Null + return None if classloader < 0: classloader += 2 << (HASH_NB_BYTES * 8 - 1) return classloader.to_bytes(HASH_NB_BYTES).hex() @@ -67,7 +69,8 @@ def cl_id_to_string(classloader: int) -> str: def handle_classloader_data(data: dict, data_storage: dict): data["id"] = cl_id_to_string(data["id"]) - data_storage["initial_classloaders"].append(data) + data["parent_id"] = cl_id_to_string(data["parent_id"]) + data_storage["classloaders"].append(data) def handle_invoke_data(data, data_storage: dict): @@ -184,6 +187,7 @@ def handle_load_dex(data, data_storage: dict, file_storage: Path): dex = data["dex"] classloader_class = data["classloader_class"] classloader = cl_id_to_string(data["classloader"]) + classloader_parent = cl_id_to_string(data["classloader_parent"]) short_class = classloader_class.split("/")[-1].removesuffix(";") files = [] print("[+] DEX file loaded:") @@ -212,6 +216,7 @@ def handle_load_dex(data, data_storage: dict, file_storage: Path): "classloader_class": classloader_class, "classloader": classloader, "files": files, + "classloader_parent": classloader_parent, } ) @@ -344,7 +349,7 @@ def collect_runtime(apk: Path, device_name: str, file_storage: Path, output: Tex "class_new_inst_data": [], "cnstr_new_inst_data": [], "dyn_code_load": [], - "initial_classloaders": [], + "classloaders": [], } script.on( @@ -357,10 +362,13 @@ def collect_runtime(apk: Path, device_name: str, file_storage: Path, output: Tex # Resume the execution of the APK device.resume(pid) + script.post({"type": "dump-class-loaders"}) print("==> Press ENTER to finish the analysis <==") input() + + # Try to find the Main class loader main_class_loader: str | None = None - cls = {d["id"]: d for d in data_storage["initial_classloaders"]} + cls = {d["id"]: d for d in data_storage["classloaders"]} for load_data in data_storage["dyn_code_load"]: if load_data["classloader"] in cls: del cls[load_data["classloader"]] @@ -393,6 +401,10 @@ def collect_runtime(apk: Path, device_name: str, file_storage: Path, output: Tex main_class_loader = list(cls.keys())[0] data_storage["apk_cl_id"] = main_class_loader + # Dump all known classloaders + script.post({"type": "dump-class-loaders"}) + time.sleep(1) # TODO: wait for ack from frida + json.dump(data_storage, output, indent=" ") diff --git a/frida/theseus_frida/hook.js b/frida/theseus_frida/hook.js index b9ea4c9..5b21e50 100644 --- a/frida/theseus_frida/hook.js +++ b/frida/theseus_frida/hook.js @@ -1,3 +1,20 @@ +function dump_classloaders() { + Java.perform(() => { + const System = Java.use('java.lang.System'); + var class_loader = Java.enumerateClassLoadersSync(); + for (var cl of class_loader) { + send({"type": "classloader", "data": { + "id": System.identityHashCode(cl), + "parent_id": System.identityHashCode(cl.getParent()), + "str": cl.toString(), + "cname": cl.$className + }}); + } + }); +} + +recv('dump-class-loaders', function onMessage(msg) {dump_classloaders()}); + Java.perform(() => { /* @@ -15,6 +32,18 @@ Java.perform(() => { const Base64 = Java.use("android.util.Base64"); const InMemoryDexClassLoader = Java.use("dalvik.system.InMemoryDexClassLoader"); const ByteBuffer = Java.use("java.nio.ByteBuffer"); + const Method = Java.use("java.lang.reflect.Method"); + const Class = Java.use("java.lang.Class"); + const Constructor = Java.use("java.lang.reflect.Constructor"); + const Modifier = Java.use("java.lang.reflect.Modifier"); + const DexFile = Java.use("dalvik.system.DexFile"); + const File = Java.use('java.io.File'); + const Files = Java.use('java.nio.file.Files'); + const Path = Java.use('java.nio.file.Path'); + const System = Java.use('java.lang.System'); + const Arrays = Java.use('java.util.Arrays'); + + const myClassLoader = InMemoryDexClassLoader.$new( ByteBuffer.wrap(Base64.decode("", Base64.DEFAULT.value)), null @@ -80,17 +109,6 @@ Java.perform(() => { ")V"; }; - const Method = Java.use("java.lang.reflect.Method"); - const Class = Java.use("java.lang.Class"); - const Constructor = Java.use("java.lang.reflect.Constructor"); - const Modifier = Java.use("java.lang.reflect.Modifier"); - const DexFile = Java.use("dalvik.system.DexFile"); - - const File = Java.use('java.io.File'); - const Files = Java.use('java.nio.file.Files'); - const Path = Java.use('java.nio.file.Path'); - const System = Java.use('java.lang.System'); - const Arrays = Java.use('java.util.Arrays'); // ****** Reflexive Method Calls ****** @@ -198,6 +216,7 @@ Java.perform(() => { "dex": [b64], "classloader_class": classloader_class, "classloader": classloader_id, + "classloader_parent": System.identityHashCode(loader.getParent()), } }); @@ -268,6 +287,7 @@ Java.perform(() => { "dex": dex, "classloader_class": classloader_class, "classloader": classloader_id, + "classloader_parent": System.identityHashCode(loader.getParent()), } }); return this.openInMemoryDexFilesNative( @@ -279,22 +299,6 @@ Java.perform(() => { elements, ); }; - - // Find the main APK class loader: - // Not so easy, just send all class loader and sort this out later: - var class_loader = Java.enumerateClassLoadersSync(); - for (var cl of class_loader) { - //if (cl.toString().includes("dalvik.system.PathClassLoader[DexPathList[[directory \".\"],")) { - // continue; - //} - //if (cl.$className == "java.lang.BootClassLoader") { - // continue; - //} - send({"type": "classloader", "data": { - "id": System.identityHashCode(cl), - "str": cl.toString(), - "cname": cl.$className - }}); - } }); +recv('dump-class-loaders', function onMessage(msg) {dump_classloaders()}); diff --git a/patcher/src/code_loading_patcher.rs b/patcher/src/code_loading_patcher.rs index aacf350..1535325 100644 --- a/patcher/src/code_loading_patcher.rs +++ b/patcher/src/code_loading_patcher.rs @@ -75,7 +75,7 @@ fn insert_code_model_class_loaders(apk: &mut Apk, runtime_data: &mut RuntimeData let classes = apk.list_classes(); let mut class_loader = ClassLoader { id: dyn_data.classloader.clone(), - parent: None, + parent: dyn_data.classloader_parent.clone(), class, apk: ApkOrRef::Owned(apk), renamed_classes: HashMap::new(), @@ -283,7 +283,8 @@ impl ClassLoader<'_> { tys: &HashSet, class_loaders: &HashMap, ) -> HashMap { - tys.iter() + let r: HashMap = tys + .iter() .map(|ty| { ( ty.clone(), @@ -291,7 +292,12 @@ impl ClassLoader<'_> { .unwrap_or(ty.clone()), ) }) - .collect() + .collect(); + println!("rename for {}", self.id); + for (old, new) in &r { + println!(" {} -> {}", old.__str__(), new.__str__()); + } + r } pub fn get_ref_new_name( diff --git a/patcher/src/runtime_data.rs b/patcher/src/runtime_data.rs index db5a7dd..6362496 100644 --- a/patcher/src/runtime_data.rs +++ b/patcher/src/runtime_data.rs @@ -109,7 +109,6 @@ pub struct ReflectionInvokeData { pub addr: usize, /// If the method is static (static method don't take 'this' as argument) pub is_static: bool, - // TODO: type of invoke? } impl ReflectionInvokeData { @@ -183,6 +182,8 @@ pub struct DynamicCodeLoadingData { pub classloader_class: IdType, /// An identifier for the classloader, valid for one specific run of the application. pub classloader: String, + /// An identifier for the parent classloader, valid for one specific run of the applications. + pub classloader_parent: Option, /// The path to the files storing the .dex/.apk/other bytecode loaded. pub files: Vec, } diff --git a/patcher/tst/dex/PathClassLoader_0545b1a9_09d17bb5de42d5d9.bytecode b/patcher/tst/dex/PathClassLoader_00a972ce_09d17bb5de42d5d9.bytecode similarity index 100% rename from patcher/tst/dex/PathClassLoader_0545b1a9_09d17bb5de42d5d9.bytecode rename to patcher/tst/dex/PathClassLoader_00a972ce_09d17bb5de42d5d9.bytecode diff --git a/patcher/tst/dex/PathClassLoader_0b4ac535_09d17bb5de42d5d9.bytecode b/patcher/tst/dex/PathClassLoader_012faa7a_09d17bb5de42d5d9.bytecode similarity index 100% rename from patcher/tst/dex/PathClassLoader_0b4ac535_09d17bb5de42d5d9.bytecode rename to patcher/tst/dex/PathClassLoader_012faa7a_09d17bb5de42d5d9.bytecode diff --git a/patcher/tst/dex/PathClassLoader_0ceee91e_09d17bb5de42d5d9.bytecode b/patcher/tst/dex/PathClassLoader_0354b7e2_09d17bb5de42d5d9.bytecode similarity index 100% rename from patcher/tst/dex/PathClassLoader_0ceee91e_09d17bb5de42d5d9.bytecode rename to patcher/tst/dex/PathClassLoader_0354b7e2_09d17bb5de42d5d9.bytecode diff --git a/patcher/tst/dex/PathClassLoader_0dfe1ce0_09d17bb5de42d5d9.bytecode b/patcher/tst/dex/PathClassLoader_07069310_09d17bb5de42d5d9.bytecode similarity index 100% rename from patcher/tst/dex/PathClassLoader_0dfe1ce0_09d17bb5de42d5d9.bytecode rename to patcher/tst/dex/PathClassLoader_07069310_09d17bb5de42d5d9.bytecode diff --git a/patcher/tst/runtime.json b/patcher/tst/runtime.json index ae275fb..cd44ccf 100644 --- a/patcher/tst/runtime.json +++ b/patcher/tst/runtime.json @@ -1,39 +1,63 @@ { "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;", + "method_cl_id": "0620d2cb", + "renamed_method": null, "caller_method": "Lcom/example/theseus/dynloading/MainActivity;->directWithParent()V", + "caller_cl_id": "0620d2cb", + "renamed_caller_method": null, "addr": 39, "is_static": true + }, + { + "method": "Lcom/example/theseus/dynloading/AMain;->getColliderId()Ljava/lang/String;", + "method_cl_id": "012faa7a", + "renamed_method": null, + "caller_method": "Lcom/example/theseus/dynloading/MainActivity;->indirectWithoutParent()V", + "caller_cl_id": "0620d2cb", + "renamed_caller_method": null, + "addr": 33, + "is_static": true + }, + { + "method": "Lcom/example/theseus/dynloading/AMain;->getColliderId()Ljava/lang/String;", + "method_cl_id": "00a972ce", + "renamed_method": null, + "caller_method": "Lcom/example/theseus/dynloading/MainActivity;->indirectWithParent()V", + "caller_cl_id": "0620d2cb", + "renamed_caller_method": null, + "addr": 39, + "is_static": true + }, + { + "method": "Lcom/example/theseus/dynloading/Collider;->getColliderId()Ljava/lang/String;", + "method_cl_id": "0354b7e2", + "renamed_method": null, + "caller_method": "Lcom/example/theseus/dynloading/MainActivity;->directWithoutParent()V", + "caller_cl_id": "0620d2cb", + "renamed_caller_method": null, + "addr": 33, + "is_static": true } ], "class_new_inst_data": [ { "constructor": "Landroid/app/Application;->()V", + "constructor_cl_id": "00aaeddc", + "renamed_constructor": null, "caller_method": "Landroid/app/AppComponentFactory;->instantiateApplication(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/app/Application;", + "caller_cl_id": "00aaeddc", + "renamed_caller_method": null, "addr": 4 }, { "constructor": "Lcom/example/theseus/dynloading/MainActivity;->()V", + "constructor_cl_id": "0620d2cb", + "renamed_constructor": null, "caller_method": "Landroid/app/AppComponentFactory;->instantiateActivity(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/app/Activity;", + "caller_cl_id": "00aaeddc", + "renamed_caller_method": null, "addr": 4 } ], @@ -41,31 +65,104 @@ "dyn_code_load": [ { "classloader_class": "Ldalvik/system/PathClassLoader;", - "classloader": "0dfe1ce0", + "classloader": "07069310", "files": [ - "./tst/dex/PathClassLoader_0dfe1ce0_09d17bb5de42d5d9.bytecode" - ] + "./tst/dex/PathClassLoader_07069310_09d17bb5de42d5d9.bytecode" + ], + "classloader_parent": "0620d2cb" }, { "classloader_class": "Ldalvik/system/PathClassLoader;", - "classloader": "0b4ac535", + "classloader": "012faa7a", "files": [ - "./tst/dex/PathClassLoader_0b4ac535_09d17bb5de42d5d9.bytecode" - ] + "./tst/dex/PathClassLoader_012faa7a_09d17bb5de42d5d9.bytecode" + ], + "classloader_parent": null }, { "classloader_class": "Ldalvik/system/PathClassLoader;", - "classloader": "0ceee91e", + "classloader": "00a972ce", "files": [ - "./tst/dex/PathClassLoader_0ceee91e_09d17bb5de42d5d9.bytecode" - ] + "./tst/dex/PathClassLoader_00a972ce_09d17bb5de42d5d9.bytecode" + ], + "classloader_parent": "0620d2cb" }, { "classloader_class": "Ldalvik/system/PathClassLoader;", - "classloader": "0545b1a9", + "classloader": "0354b7e2", "files": [ - "./tst/dex/PathClassLoader_0545b1a9_09d17bb5de42d5d9.bytecode" - ] + "./tst/dex/PathClassLoader_0354b7e2_09d17bb5de42d5d9.bytecode" + ], + "classloader_parent": null } - ] + ], + "classloaders": [ + { + "id": "0096a9c1", + "parent_id": "00aaeddc", + "str": "dalvik.system.PathClassLoader[DexPathList[[directory \".\"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64, /system/lib64, /system_ext/lib64]]]", + "cname": "dalvik.system.PathClassLoader" + }, + { + "id": "00aaeddc", + "parent_id": null, + "str": "java.lang.BootClassLoader@aaeddc", + "cname": "java.lang.BootClassLoader" + }, + { + "id": "0035caa8", + "parent_id": null, + "str": "dalvik.system.InMemoryDexClassLoader[DexPathList[[dex file \"InMemoryDexFile[cookie=[0, 128037698188144]]\"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]]", + "cname": "dalvik.system.InMemoryDexClassLoader" + }, + { + "id": "0096a9c1", + "parent_id": "00aaeddc", + "str": "dalvik.system.PathClassLoader[DexPathList[[directory \".\"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64, /system/lib64, /system_ext/lib64]]]", + "cname": "dalvik.system.PathClassLoader" + }, + { + "id": "00aaeddc", + "parent_id": null, + "str": "java.lang.BootClassLoader@aaeddc", + "cname": "java.lang.BootClassLoader" + }, + { + "id": "0035caa8", + "parent_id": null, + "str": "dalvik.system.InMemoryDexClassLoader[DexPathList[[dex file \"InMemoryDexFile[cookie=[0, 128037698188144]]\"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]]", + "cname": "dalvik.system.InMemoryDexClassLoader" + }, + { + "id": "0620d2cb", + "parent_id": "00aaeddc", + "str": "dalvik.system.PathClassLoader[DexPathList[[zip file \"/data/app/~~G73yvcDB8EWWZQoHd7RyMQ==/com.example.theseus.dynloading-9TbLe3e6jrCqYe-MsGwAPA==/base.apk\"],nativeLibraryDirectories=[/data/app/~~G73yvcDB8EWWZQoHd7RyMQ==/com.example.theseus.dynloading-9TbLe3e6jrCqYe-MsGwAPA==/lib/x86_64, /system/lib64, /system_ext/lib64]]]", + "cname": "dalvik.system.PathClassLoader" + }, + { + "id": "07069310", + "parent_id": "0620d2cb", + "str": "dalvik.system.PathClassLoader[DexPathList[[dex file \"/data/user/0/com.example.theseus.dynloading/cache/a.dex\"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]]", + "cname": "dalvik.system.PathClassLoader" + }, + { + "id": "012faa7a", + "parent_id": null, + "str": "dalvik.system.PathClassLoader[DexPathList[[dex file \"/data/user/0/com.example.theseus.dynloading/cache/a.dex\"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]]", + "cname": "dalvik.system.PathClassLoader" + }, + { + "id": "00a972ce", + "parent_id": "0620d2cb", + "str": "dalvik.system.PathClassLoader[DexPathList[[dex file \"/data/user/0/com.example.theseus.dynloading/cache/a.dex\"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]]", + "cname": "dalvik.system.PathClassLoader" + }, + { + "id": "0354b7e2", + "parent_id": null, + "str": "dalvik.system.PathClassLoader[DexPathList[[dex file \"/data/user/0/com.example.theseus.dynloading/cache/a.dex\"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]]", + "cname": "dalvik.system.PathClassLoader" + } + ], + "apk_cl_id": "0035caa8" }