@@ -11,74 +11,41 @@ import dalvik.system.InMemoryDexClassLoader
11
11
import dalvik.system.PathClassLoader
12
12
import de.binarynoise.ClassHunter.BuildConfig
13
13
import de.robv.android.xposed.IXposedHookLoadPackage
14
- import de.robv.android.xposed.IXposedHookZygoteInit
15
14
import de.robv.android.xposed.XposedBridge
15
+ import de.robv.android.xposed.XposedHelpers
16
16
import de.robv.android.xposed.callbacks.XC_LoadPackage
17
17
import de.robv.android.xposed.XC_MethodHook as MethodHook
18
18
19
19
const val TAG = " Hook"
20
20
21
- class Hook : IXposedHookLoadPackage , IXposedHookZygoteInit {
21
+ class Hook : IXposedHookLoadPackage {
22
+
22
23
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} " )
28
25
29
26
val packageClassLoader: ClassLoader = lpparam.classLoader
30
- collectParents (packageClassLoader, " packageClassLoader" , classLoaders )
27
+ tryFindClass (packageClassLoader, " packageClassLoader" , lpparam.packageName )
31
28
32
29
val moduleClassLoader: ClassLoader = this ::class .java.classLoader!!
33
- collectParents (moduleClassLoader, " moduleClassLoader" , classLoaders )
30
+ tryFindClass (moduleClassLoader, " moduleClassLoader" , lpparam.packageName )
34
31
35
32
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)
40
34
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} " )
69
41
}
70
42
}
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()} " )
77
43
})
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 >>(
82
49
ClassLoader ::class .java,
83
50
SecureClassLoader ::class .java,
84
51
URLClassLoader ::class .java,
@@ -94,49 +61,90 @@ class Hook : IXposedHookLoadPackage, IXposedHookZygoteInit {
94
61
}
95
62
96
63
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 >)
98
66
} catch (_: Throwable ) {
99
67
}
100
68
101
- val targetClassesShortName = BuildConfig .targetClass.map { it.substringAfterLast(" ." ) }.toSet()
69
+ // prevent nested classloader logs
70
+ val constructorLock: ThreadLocal <Int > = ThreadLocal .withInitial { 0 }
102
71
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 )
117
77
}
118
78
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
+ }
128
99
}
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)
130
106
}
131
-
107
+ }
108
+ }
109
+
110
+ fun tryFindClass (classLoader : ClassLoader , classLoaderName : String , packageName : String ) {
111
+ BuildConfig .targetClass.forEach { className ->
132
112
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 )
135
115
} catch (_: Throwable ) {
136
116
}
137
117
}
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
+ }
138
144
139
- Log .i(TAG , " Zygote looking for ${targetClassesShortName.joinToString()} in ${classLoaderClasses.size} ClassLoader classes" )
145
+ lines.forEach {
146
+ Log .i(TAG , it)
147
+ }
140
148
}
141
149
}
142
150
0 commit comments