Skip to content

Commit 462ce94

Browse files
committed
improve Class detection
find classes with prefixes and from preloaded ClassLoaders
1 parent a1d5de3 commit 462ce94

File tree

2 files changed

+73
-27
lines changed

2 files changed

+73
-27
lines changed

ClassHunter/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ android {
2727
dependencies {
2828
implementation(libs.libsu.core)
2929
implementation(projects.logger)
30+
implementation(projects.reflection)
3031
}

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

Lines changed: 72 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.binarynoise.classHunter
22

3+
import java.io.File
34
import java.net.URLClassLoader
45
import java.security.SecureClassLoader
56
import android.os.Build
@@ -10,15 +11,18 @@ import dalvik.system.DexClassLoader
1011
import dalvik.system.InMemoryDexClassLoader
1112
import dalvik.system.PathClassLoader
1213
import de.binarynoise.ClassHunter.BuildConfig
14+
import de.binarynoise.logger.Logger.log
15+
import de.binarynoise.reflection.cast
1316
import de.robv.android.xposed.IXposedHookLoadPackage
17+
import de.robv.android.xposed.IXposedHookZygoteInit
1418
import de.robv.android.xposed.XposedBridge
1519
import de.robv.android.xposed.XposedHelpers
1620
import de.robv.android.xposed.callbacks.XC_LoadPackage
1721
import de.robv.android.xposed.XC_MethodHook as MethodHook
1822

1923
const val TAG = "Hook"
2024

21-
class Hook : IXposedHookLoadPackage {
25+
class Hook : IXposedHookLoadPackage, IXposedHookZygoteInit {
2226

2327
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
2428
Log.d(TAG, "handleLoadPackage: ${lpparam.packageName}")
@@ -36,14 +40,35 @@ class Hook : IXposedHookLoadPackage {
3640
override fun afterHookedMethod(param: MethodHookParam) {
3741
with(param) {
3842
val cls = result as? Class<*>? ?: return
39-
if (!cls.isAssignableFrom(ClassLoader::class.java)) return
43+
if (!ClassLoader::class.java.isAssignableFrom(cls)) return
4044
Log.i(TAG, "loadClass afterHookedMethod: loaded new classLoader class: ${cls.name}")
4145
}
4246
}
4347
})
4448

4549
////////////////////////////////////////////////////////////////////////////////////////////////////
4650

51+
try {
52+
val SystemServerClassLoaderFactoryClass = XposedHelpers.findClass("com.android.internal.os.SystemServerClassLoaderFactory", null)
53+
val sLoadedPaths: Map<String, PathClassLoader> =
54+
XposedHelpers.getStaticObjectField(SystemServerClassLoaderFactoryClass, "sLoadedPaths").cast()
55+
Log.v(TAG, "sLoadedPaths:")
56+
sLoadedPaths.forEach { (path, loader) ->
57+
Log.v(TAG, "sLoadedPaths: $path -> ${loader.toObjectString()}")
58+
tryFindClass(loader, "SystemServerClassLoaderFactory", lpparam.packageName)
59+
}
60+
} catch (_: NoSuchMethodError) {
61+
} catch (_: NoSuchMethodException) {
62+
} catch (_: XposedHelpers.ClassNotFoundError) {
63+
} catch (_: ClassNotFoundException) {
64+
} catch (e: Exception) {
65+
log("failed to hook", e)
66+
}
67+
}
68+
69+
override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
70+
val packageName = File("/proc/self/cmdline").readText().replace(0.toChar(), ' ')
71+
4772
@Suppress("RemoveExplicitTypeArguments") //
4873
val classLoaderClasses = mutableSetOf<Class<out ClassLoader>>(
4974
ClassLoader::class.java,
@@ -61,8 +86,7 @@ class Hook : IXposedHookLoadPackage {
6186
}
6287

6388
try {
64-
@Suppress("UNCHECKED_CAST") //
65-
classLoaderClasses.add(Class.forName("android.app.LoadedApk\$WarningContextClassLoader", false, null) as Class<out ClassLoader>)
89+
classLoaderClasses.add(Class.forName("android.app.LoadedApk\$WarningContextClassLoader", false, null).cast())
6690
} catch (_: Throwable) {
6791
}
6892

@@ -88,12 +112,13 @@ class Hook : IXposedHookLoadPackage {
88112
"dalvik.system.PathClassLoader[null]",
89113
"dalvik.system.PathClassLoader[DexPathList[[],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]]",
90114
"LspModuleClassLoader[instantiating]",
115+
"""dalvik.system.PathClassLoader[DexPathList[[directory "/proc/self/task"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]]""",
91116
)
92117
if (string !in ignored) {
93118
Log.v(TAG, "ClassLoader constructor: created new classLoader: ${newClassLoader.toObjectString()} $string")
94119
}
95120

96-
tryFindClass(newClassLoader, "constructor", lpparam.packageName)
121+
tryFindClass(newClassLoader, "constructor", packageName)
97122

98123
}
99124
}
@@ -107,6 +132,23 @@ class Hook : IXposedHookLoadPackage {
107132
}
108133
}
109134

135+
// https://cs.android.com/search?q=%22--prefix%20%22%20file:Android.bp
136+
// /(?<=--prefix )([\w\.]+)/
137+
val prefixes = arrayOf(
138+
"android.net.connectivity",
139+
"android.net.http.internal",
140+
"com.android.captiveportallogin",
141+
"com.android.networkstack",
142+
"com.android.server.nearby",
143+
"com.android.server.remoteauth",
144+
"gfxstream_vk",
145+
"lvp",
146+
"vk_cmd_enqueue",
147+
"vk_cmd_enqueue_unless_primary",
148+
"vk_common",
149+
"wsi",
150+
)
151+
110152
fun tryFindClass(classLoader: ClassLoader, classLoaderName: String, packageName: String) {
111153
BuildConfig.targetClass.forEach { className ->
112154
var cls: Class<*>? = null
@@ -115,12 +157,20 @@ class Hook : IXposedHookLoadPackage {
115157
var successClassLoader: ClassLoader? = null
116158
var i = 0
117159

118-
while (classLoader != null && i++ < 10) {
160+
loop@ while (classLoader != null && i++ < 10) {
119161
try {
120162
cls = Class.forName(className, false, classLoader)
121163
successClassLoader = classLoader
164+
break@loop
122165
} catch (_: Throwable) {
123-
break
166+
}
167+
prefixes.forEach { prefix ->
168+
try {
169+
cls = Class.forName("$prefix.$className", false, classLoader)
170+
successClassLoader = classLoader
171+
break@loop
172+
} catch (_: Throwable) {
173+
}
124174
}
125175
classLoader = classLoader.parent
126176
}
@@ -131,29 +181,24 @@ class Hook : IXposedHookLoadPackage {
131181
}
132182
}
133183

134-
private fun logClass(cls: Class<*>, classLoaderName: String, classLoader: ClassLoader, packageName: String) {
184+
private fun logClass(cls: Class<*>, classLoaderName: String, classLoader: ClassLoader, packageName: String, dump: Boolean = true) {
135185
val lines = mutableListOf<String>()
136186
lines.add("*".repeat(150))
137187

138188
lines.add("found class: ${cls.name} in package $packageName by $classLoaderName: ${classLoader.toObjectString()} - $classLoader")
139-
140-
lines.add(" - constructors:")
141-
cls.declaredConstructors.map { c -> "${c.name}(${c.parameters.joinToString(", ") { p -> p.name + " " + p.type.name }})" }
142-
.sorted()
143-
.forEach { c ->
144-
lines.add(" - $c")
145-
}
146-
147-
lines.add(" - methods:")
148-
cls.declaredMethods.map { m -> "${m.returnType.name} ${m.name}(${m.parameters.joinToString(", ") { p -> p.name + " " + p.type.name }})" }
149-
.sorted()
150-
.forEach { m ->
151-
lines.add(" - $m")
152-
}
153-
154-
lines.add(" - fields:")
155-
cls.declaredFields.map { f -> "${f.type.name} ${f.name}" }.sorted().forEach { f ->
156-
lines.add(" - $f")
189+
if (dump) {
190+
lines.add(" - constructors:")
191+
cls.declaredConstructors.map { c -> "${c.name}(${c.parameters.joinToString(", ") { p -> p.name + " " + p.type.name }})" }
192+
.sorted()
193+
.forEach { c -> lines.add(" - $c") }
194+
195+
lines.add(" - methods:")
196+
cls.declaredMethods.map { m -> "${m.returnType.name} ${m.name}(${m.parameters.joinToString(", ") { p -> p.name + " " + p.type.name }})" }
197+
.sorted()
198+
.forEach { m -> lines.add(" - $m") }
199+
200+
lines.add(" - fields:")
201+
cls.declaredFields.map { f -> "${f.type.name} ${f.name}" }.sorted().forEach { f -> lines.add(" - $f") }
157202
}
158203

159204
lines.forEach {
@@ -164,5 +209,5 @@ class Hook : IXposedHookLoadPackage {
164209

165210
fun Any?.toObjectString(): String {
166211
if (this == null) return "null"
167-
return this::class.simpleName + "@" + Integer.toHexString(hashCode())
212+
return this::class.simpleName + "@" + Integer.toHexString(hashCode()).padStart(8, '0')
168213
}

0 commit comments

Comments
 (0)