@@ -11,74 +11,41 @@ import dalvik.system.InMemoryDexClassLoader
1111import dalvik.system.PathClassLoader
1212import de.binarynoise.ClassHunter.BuildConfig
1313import de.robv.android.xposed.IXposedHookLoadPackage
14- import de.robv.android.xposed.IXposedHookZygoteInit
1514import de.robv.android.xposed.XposedBridge
15+ import de.robv.android.xposed.XposedHelpers
1616import de.robv.android.xposed.callbacks.XC_LoadPackage
1717import de.robv.android.xposed.XC_MethodHook as MethodHook
1818
1919const val TAG = " Hook"
2020
21- class Hook : IXposedHookLoadPackage , IXposedHookZygoteInit {
21+ class Hook : IXposedHookLoadPackage {
22+
2223 override fun handleLoadPackage (lpparam : XC_LoadPackage .LoadPackageParam ) {
23- Log .i(TAG , " *" .repeat(50 ))
24- Log .i(TAG , " loading package: ${lpparam.packageName} " )
25- Log .i(TAG , " hunting for ${BuildConfig .targetClass.joinToString()} " )
26-
27- val classLoaders = mutableSetOf<ClassLoader >()
24+ Log .d(TAG , " handleLoadPackage: ${lpparam.packageName} " )
2825
2926 val packageClassLoader: ClassLoader = lpparam.classLoader
30- collectParents (packageClassLoader, " packageClassLoader" , classLoaders )
27+ tryFindClass (packageClassLoader, " packageClassLoader" , lpparam.packageName )
3128
3229 val moduleClassLoader: ClassLoader = this ::class .java.classLoader!!
33- collectParents (moduleClassLoader, " moduleClassLoader" , classLoaders )
30+ tryFindClass (moduleClassLoader, " moduleClassLoader" , lpparam.packageName )
3431
3532 val systemClassLoader: ClassLoader = ClassLoader .getSystemClassLoader()
36- collectParents(systemClassLoader, " systemClassLoader" , classLoaders)
37-
38- val bootClassLoader = classLoaders.find { it.javaClass.simpleName == " BootClassLoader" }!!
39- collectParents(bootClassLoader, " bootClassLoader" , classLoaders)
33+ tryFindClass(systemClassLoader, " systemClassLoader" , lpparam.packageName)
4034
41- Log .i(TAG , " currently known classloaders:" )
42- classLoaders.forEach { classLoader ->
43- Log .v(TAG , " ${classLoader.toObjectString()} - $classLoader " )
44-
45- BuildConfig .targetClass.forEach { className ->
46- try {
47- val cls = Class .forName(className, false , classLoader)
48- Log .i(TAG , " - found class: ${cls.name} in package ${lpparam.packageName} by ${cls.toObjectString()} " )
49-
50- Log .i(TAG , " - constructors:" )
51- cls.declaredConstructors.map { c -> " ${c.name} (${c.parameters.joinToString(" , " ) { p -> p.name + " " + p.type.name }} )" }
52- .sorted()
53- .forEach { c ->
54- Log .v(TAG , " - $c " )
55- }
56-
57- Log .i(TAG , " - methods:" )
58- cls.declaredMethods.map { m -> " ${m.returnType.name} ${m.name} (${m.parameters.joinToString(" , " ) { p -> p.name + " " + p.type.name }} )" }
59- .sorted()
60- .forEach { m ->
61- Log .v(TAG , " - $m " )
62- }
63-
64- Log .i(TAG , " - fields:" )
65- cls.declaredFields.map { f -> " ${f.type.name} ${f.name} " }.sorted().forEach { f ->
66- Log .v(TAG , " - $f " )
67- }
68- } catch (_: Throwable ) {
35+ XposedHelpers .findAndHookMethod(ClassLoader ::class .java, " loadClass" , String ::class .java, Boolean ::class .java, object : MethodHook () {
36+ override fun afterHookedMethod (param : MethodHookParam ) {
37+ with (param) {
38+ val cls = result as ? Class <* >? ? : return
39+ if (! cls.isAssignableFrom(ClassLoader ::class .java)) return
40+ Log .i(TAG , " loadClass afterHookedMethod: loaded new classLoader class: ${cls.name} " )
6941 }
7042 }
71- }
72- }
73-
74- private fun collectParents (classLoader : ClassLoader , name : String , classLoaders : MutableSet <ClassLoader >) {
75- classLoaders.addAll(generateSequence(classLoader) { it.parent }.onEach {
76- Log .v(TAG , name + " - ${it.toObjectString()} " )
7743 })
78- }
79-
80- override fun initZygote (startupParam : IXposedHookZygoteInit .StartupParam ) {
81- val classLoaderClasses = mutableSetOf<Class <* >>(
44+
45+ // //////////////////////////////////////////////////////////////////////////////////////////////////
46+
47+ @Suppress(" RemoveExplicitTypeArguments" ) //
48+ val classLoaderClasses = mutableSetOf<Class <out ClassLoader >>(
8249 ClassLoader ::class .java,
8350 SecureClassLoader ::class .java,
8451 URLClassLoader ::class .java,
@@ -94,49 +61,90 @@ class Hook : IXposedHookLoadPackage, IXposedHookZygoteInit {
9461 }
9562
9663 try {
97- classLoaderClasses.add(Class .forName(" android.app.LoadedApk\$ WarningContextClassLoader" , false , null ))
64+ @Suppress(" UNCHECKED_CAST" ) //
65+ classLoaderClasses.add(Class .forName(" android.app.LoadedApk\$ WarningContextClassLoader" , false , null ) as Class <out ClassLoader >)
9866 } catch (_: Throwable ) {
9967 }
10068
101- val targetClassesShortName = BuildConfig .targetClass.map { it.substringAfterLast(" ." ) }.toSet()
69+ // prevent nested classloader logs
70+ val constructorLock: ThreadLocal <Int > = ThreadLocal .withInitial { 0 }
10271
103- classLoaderClasses.forEach {
104- val loadClassHook = object : MethodHook () {
105- override fun afterHookedMethod (param : MethodHookParam ) {
106- val cls = param.result as ? Class <* > ? : return
107-
108- if (cls.name.substringAfterLast(" ." ) in targetClassesShortName) {
109- Log .i(TAG , " Maybe found class: ${cls.name} by ${param.thisObject.toObjectString()} " )
110- }
111- }
112- }
113- val loadNewClassloaderHook = object : MethodHook () {
114- override fun afterHookedMethod (param : MethodHookParam ) {
115- if (param.thisObject !is PathClassLoader ) {
116- Log .v(TAG , " Created a new ClassLoader: ${param.thisObject.toObjectString()} ${param.thisObject} " )
72+ classLoaderClasses.forEach { classLoaderClass ->
73+ try {
74+ XposedBridge .hookAllConstructors(classLoaderClass, object : MethodHook () {
75+ override fun beforeHookedMethod (param : MethodHookParam ) {
76+ constructorLock.set(constructorLock.get()!! + 1 )
11777 }
11878
119- val cls: Class <* > = param.thisObject::class .java
120- if (cls !in classLoaderClasses) {
121- Log .i(TAG , " Found a new ClassLoader class: ${cls} , created by ${cls.classLoader} " )
122- }
123- try {
124- // Will hang the device:
125- // XposedBridge.hookAllConstructors(it, loadNewClassloaderHook)
126- // XposedBridge.hookAllMethods(it, "loadClass", loadClassHook)
127- } catch (_: Throwable ) {
79+ override fun afterHookedMethod (param : MethodHookParam ) {
80+ constructorLock.set(constructorLock.get()!! - 1 )
81+ if (constructorLock.get()!! > 0 ) return
82+ constructorLock.remove()
83+ with (param) {
84+ val newClassLoader = thisObject as ? ClassLoader ? ? : return
85+ val string = newClassLoader.toString()
86+
87+ val ignored = arrayOf(
88+ " dalvik.system.PathClassLoader[null]" ,
89+ " dalvik.system.PathClassLoader[DexPathList[[],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]]" ,
90+ " LspModuleClassLoader[instantiating]" ,
91+ )
92+ if (string !in ignored) {
93+ Log .v(TAG , " ClassLoader constructor: created new classLoader: ${newClassLoader.toObjectString()} $string " )
94+ }
95+
96+ tryFindClass(newClassLoader, " constructor" , lpparam.packageName)
97+
98+ }
12899 }
129- }
100+ })
101+ // Log.v(TAG, "initZygote: hooked constructor of ${classLoaderClass.name}")
102+ } catch (_: NoSuchMethodError ) {
103+ } catch (_: NoSuchMethodException ) {
104+ } catch (t: Throwable ) {
105+ Log .w(TAG , " initZygote: failed to hook constructor of ${classLoaderClass.name} " , t)
130106 }
131-
107+ }
108+ }
109+
110+ fun tryFindClass (classLoader : ClassLoader , classLoaderName : String , packageName : String ) {
111+ BuildConfig .targetClass.forEach { className ->
132112 try {
133- XposedBridge .hookAllConstructors(it, loadNewClassloaderHook )
134- XposedBridge .hookAllMethods(it, " loadClass " , loadClassHook )
113+ val cls = Class .forName(className, false , classLoader )
114+ logClass(cls, classLoaderName, classLoader, packageName )
135115 } catch (_: Throwable ) {
136116 }
137117 }
118+ }
119+
120+ private fun logClass (cls : Class <* >, classLoaderName : String , classLoader : ClassLoader , packageName : String ) {
121+ val lines = mutableListOf<String >()
122+ lines.add(" *" .repeat(150 ))
123+
124+ lines.add(" found class: ${cls.name} in package $packageName by $classLoaderName : ${classLoader.toObjectString()} - $classLoader " )
125+
126+ lines.add(" - constructors:" )
127+ cls.declaredConstructors.map { c -> " ${c.name} (${c.parameters.joinToString(" , " ) { p -> p.name + " " + p.type.name }} )" }
128+ .sorted()
129+ .forEach { c ->
130+ lines.add(" - $c " )
131+ }
132+
133+ lines.add(" - methods:" )
134+ cls.declaredMethods.map { m -> " ${m.returnType.name} ${m.name} (${m.parameters.joinToString(" , " ) { p -> p.name + " " + p.type.name }} )" }
135+ .sorted()
136+ .forEach { m ->
137+ lines.add(" - $m " )
138+ }
139+
140+ lines.add(" - fields:" )
141+ cls.declaredFields.map { f -> " ${f.type.name} ${f.name} " }.sorted().forEach { f ->
142+ lines.add(" - $f " )
143+ }
138144
139- Log .i(TAG , " Zygote looking for ${targetClassesShortName.joinToString()} in ${classLoaderClasses.size} ClassLoader classes" )
145+ lines.forEach {
146+ Log .i(TAG , it)
147+ }
140148 }
141149}
142150
0 commit comments