Skip to content

Commit 6e26fd7

Browse files
committed
Rework hook to only print log messages when a class was found
Also move the initZygote stuff to handleLoadPackage. This is early enough, we get the package name, and we can avoid duplicate logs
1 parent 591ef08 commit 6e26fd7

File tree

1 file changed

+89
-81
lines changed
  • ClassHunter/src/main/java/de/binarynoise/classHunter

1 file changed

+89
-81
lines changed

ClassHunter/src/main/java/de/binarynoise/classHunter/Hook.kt

Lines changed: 89 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -11,74 +11,41 @@ import dalvik.system.InMemoryDexClassLoader
1111
import dalvik.system.PathClassLoader
1212
import de.binarynoise.ClassHunter.BuildConfig
1313
import de.robv.android.xposed.IXposedHookLoadPackage
14-
import de.robv.android.xposed.IXposedHookZygoteInit
1514
import de.robv.android.xposed.XposedBridge
15+
import de.robv.android.xposed.XposedHelpers
1616
import de.robv.android.xposed.callbacks.XC_LoadPackage
1717
import de.robv.android.xposed.XC_MethodHook as MethodHook
1818

1919
const 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

Comments
 (0)