Skip to content

Commit 2b4ca71

Browse files
committed
improve ClassHunter
1 parent 75b81e0 commit 2b4ca71

File tree

7 files changed

+187
-19
lines changed

7 files changed

+187
-19
lines changed

ClassHunter/build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@ android {
2424
}
2525
}
2626

27-
dependencies {}
27+
dependencies {
28+
implementation(libs.libsu.core)
29+
implementation(projects.logger)
30+
}

ClassHunter/src/main/AndroidManifest.xml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,23 @@
22
<manifest
33
xmlns:android="http://schemas.android.com/apk/res/android">
44

5-
<application android:label="ClassHunter">
5+
<application
6+
android:label="ClassHunter"
7+
android:theme="@android:style/Theme.DeviceDefault.Settings"
8+
>
9+
<activity
10+
android:name="de.binarynoise.classHunter.SetScopeActivity"
11+
android:exported="true"
12+
>
13+
<intent-filter>
14+
<action android:name="android.intent.action.MAIN" />
15+
<action android:name="android.intent.action.VIEW" />
16+
17+
<category android:name="android.intent.category.LAUNCHER" />
18+
<category android:name="android.intent.category.DEFAULT" />
19+
</intent-filter>
20+
</activity>
21+
622
<meta-data
723
android:name="xposedmodule"
824
android:value="true"

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

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package de.binarynoise.classHunter
22

3+
import java.net.URLClassLoader
34
import java.security.SecureClassLoader
45
import android.os.Build
56
import android.util.Log
67
import dalvik.system.BaseDexClassLoader
78
import dalvik.system.DelegateLastClassLoader
9+
import dalvik.system.DexClassLoader
10+
import dalvik.system.InMemoryDexClassLoader
811
import dalvik.system.PathClassLoader
912
import de.binarynoise.ClassHunter.BuildConfig
1013
import de.robv.android.xposed.IXposedHookLoadPackage
@@ -36,31 +39,31 @@ class Hook : IXposedHookLoadPackage, IXposedHookZygoteInit {
3639
collectParents(bootClassLoader, "bootClassLoader", classLoaders)
3740

3841
Log.i(TAG, "currently known classloaders:")
39-
classLoaders.forEach {
40-
Log.v(TAG, "${it.toObjectString()} - $it")
42+
classLoaders.forEach { classLoader ->
43+
Log.v(TAG, "${classLoader.toObjectString()} - $classLoader")
4144

4245
BuildConfig.targetClass.forEach { className ->
4346
try {
44-
val cls = Class.forName(className, false, it)
45-
Log.i(TAG, " - found class: ${cls.name} in package ${lpparam.packageName} by ${it.toObjectString()}")
47+
val cls = Class.forName(className, false, classLoader)
48+
Log.i(TAG, " - found class: ${cls.name} in package ${lpparam.packageName} by ${cls.toObjectString()}")
4649

4750
Log.i(TAG, " - constructors:")
4851
cls.declaredConstructors.map { c -> "${c.name}(${c.parameters.joinToString(", ") { p -> p.name + " " + p.type.name }})" }
4952
.sorted()
5053
.forEach { c ->
51-
Log.v(TAG, " - $it")
54+
Log.v(TAG, " - $c")
5255
}
5356

5457
Log.i(TAG, " - methods:")
5558
cls.declaredMethods.map { m -> "${m.returnType.name} ${m.name}(${m.parameters.joinToString(", ") { p -> p.name + " " + p.type.name }})" }
5659
.sorted()
5760
.forEach { m ->
58-
Log.v(TAG, " - $it")
61+
Log.v(TAG, " - $m")
5962
}
6063

6164
Log.i(TAG, " - fields:")
6265
cls.declaredFields.map { f -> "${f.type.name} ${f.name}" }.sorted().forEach { f ->
63-
Log.v(TAG, " - $it")
66+
Log.v(TAG, " - $f")
6467
}
6568
} catch (_: Throwable) {
6669
}
@@ -78,11 +81,11 @@ class Hook : IXposedHookLoadPackage, IXposedHookZygoteInit {
7881
val classLoaderClasses = mutableSetOf<Class<*>>(
7982
ClassLoader::class.java,
8083
SecureClassLoader::class.java,
81-
// URLClassLoader::class.java,
84+
URLClassLoader::class.java,
8285
BaseDexClassLoader::class.java,
83-
// PathClassLoader::class.java,
84-
// InMemoryDexClassLoader::class.java,
85-
// DexClassLoader::class.java,
86+
PathClassLoader::class.java,
87+
InMemoryDexClassLoader::class.java,
88+
DexClassLoader::class.java,
8689
ClassLoader.getSystemClassLoader()::class.java, // BootClassLoader
8790
)
8891

@@ -107,8 +110,7 @@ class Hook : IXposedHookLoadPackage, IXposedHookZygoteInit {
107110
}
108111
}
109112
}
110-
var loadNewClassloaderHook: MethodHook? = null
111-
loadNewClassloaderHook = object : MethodHook() {
113+
val loadNewClassloaderHook = object : MethodHook() {
112114
override fun afterHookedMethod(param: MethodHookParam) {
113115
if (param.thisObject !is PathClassLoader) {
114116
Log.v(TAG, "Created a new ClassLoader: ${param.thisObject.toObjectString()} ${param.thisObject}")
@@ -119,9 +121,9 @@ class Hook : IXposedHookLoadPackage, IXposedHookZygoteInit {
119121
Log.i(TAG, "Found a new ClassLoader class: ${cls}, created by ${cls.classLoader}")
120122
}
121123
try {
122-
123-
XposedBridge.hookAllConstructors(it, loadNewClassloaderHook)
124-
XposedBridge.hookAllMethods(it, "loadClass", loadClassHook)
124+
// Will hang the device:
125+
// XposedBridge.hookAllConstructors(it, loadNewClassloaderHook)
126+
// XposedBridge.hookAllMethods(it, "loadClass", loadClassHook)
125127
} catch (_: Throwable) {
126128
}
127129
}
@@ -133,6 +135,8 @@ class Hook : IXposedHookLoadPackage, IXposedHookZygoteInit {
133135
} catch (_: Throwable) {
134136
}
135137
}
138+
139+
Log.i(TAG, "Zygote looking for ${targetClassesShortName.joinToString()} in ${classLoaderClasses.size} ClassLoader classes")
136140
}
137141
}
138142

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package de.binarynoise.classHunter
2+
3+
import kotlin.concurrent.thread
4+
import android.annotation.SuppressLint
5+
import android.app.Activity
6+
import android.os.Bundle
7+
import android.widget.Button
8+
import android.widget.LinearLayout
9+
import android.widget.TextView
10+
import com.topjohnwu.superuser.Shell
11+
import de.binarynoise.ClassHunter.BuildConfig
12+
import de.binarynoise.ClassHunter.R
13+
import de.binarynoise.logger.Logger
14+
15+
class SetScopeActivity : Activity() {
16+
17+
override fun onCreate(savedInstanceState: Bundle?) {
18+
super.onCreate(savedInstanceState)
19+
setContentView(R.layout.set_scope_activity)
20+
21+
val setScopeButton = findViewById<Button>(R.id.set_scope_button)
22+
val rebootButton = findViewById<Button>(R.id.reboot_button)
23+
val list = findViewById<LinearLayout>(R.id.log)
24+
25+
setScopeButton.setOnClickListener {
26+
thread {
27+
try {
28+
log(list, "requesting root...")
29+
Shell.enableVerboseLogging = BuildConfig.DEBUG
30+
Shell.getShell()
31+
log(list, "got root")
32+
33+
val lsposedDB = """/data/adb/lspd/config/modules_config.db"""
34+
35+
log(list, Shell.cmd("/system/bin/id").exec().out.joinToString())
36+
37+
val mid: Int =
38+
Shell.cmd("""sqlite3 $lsposedDB "SELECT mid FROM modules WHERE module_pkg_name = '${BuildConfig.APPLICATION_ID}';"""")
39+
.exec()
40+
.let { result ->
41+
check(result.isSuccess) { "Error:\n${result.err.joinToString("\n")}" }
42+
result.out.single().toInt()
43+
}
44+
log(list, "my mid: $mid")
45+
46+
log(list, "querying packages...")
47+
val packages: MutableList<String> = Shell.cmd("pm list packages").exec().let { result ->
48+
check(result.isSuccess) { "Error:\n${result.err.joinToString("\n")}" }
49+
result.out.asSequence().map { it.substringAfter(":") }.toMutableList()
50+
}
51+
packages += "system"
52+
53+
log(list, "packages: ${packages.size}")
54+
55+
log(list, "clearing scope...")
56+
Shell.cmd("""sqlite3 $lsposedDB "delete from scope where mid=$mid";""").exec().let { result ->
57+
check(result.isSuccess) { "Error:\n${result.err.joinToString("\n")}" }
58+
}
59+
60+
log(list, "setting scope...")
61+
Shell.cmd("""sqlite3 $lsposedDB "INSERT INTO scope (mid, app_pkg_name, user_id) VALUES ${packages.joinToString { "($mid, '$it', 0)" }};"""")
62+
.exec()
63+
.let { result ->
64+
check(result.isSuccess) { "Error:\n${result.err.joinToString("\n")}" }
65+
}
66+
log(list, "done")
67+
} catch (t: Throwable) {
68+
log(list, "Failed", t)
69+
}
70+
}
71+
}
72+
73+
rebootButton.setOnClickListener {
74+
thread {
75+
try {
76+
log(list, "rebooting...")
77+
Shell.cmd("svc power reboot").exec()
78+
} catch (t: Throwable) {
79+
log(list, "Failed", t)
80+
}
81+
}
82+
}
83+
}
84+
85+
@Suppress("NOTHING_TO_INLINE")
86+
@SuppressLint("SetTextI18n")
87+
private inline fun log(list: LinearLayout, message: String, throwable: Throwable? = null) {
88+
if (throwable == null) Logger.log(message) else Logger.log(message, throwable)
89+
runOnUiThread {
90+
list.addView(TextView(this).apply {
91+
if (throwable == null) {
92+
text = message
93+
} else {
94+
text = message + "\n" + throwable.message
95+
}
96+
})
97+
}
98+
}
99+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:orientation="vertical"
6+
android:layout_width="match_parent"
7+
android:layout_height="match_parent"
8+
android:padding="12dp"
9+
tools:context="de.binarynoise.classHunter.SetScopeActivity"
10+
>
11+
12+
<Button
13+
android:text="Set Scope"
14+
android:layout_width="match_parent"
15+
android:layout_height="wrap_content"
16+
android:id="@+id/set_scope_button"
17+
style="@android:style/Widget.DeviceDefault.Button"
18+
android:textAllCaps="false"
19+
/>
20+
21+
<Button
22+
android:text="Reboot"
23+
android:layout_width="match_parent"
24+
android:layout_height="wrap_content"
25+
android:id="@+id/reboot_button"
26+
style="@android:style/Widget.DeviceDefault.Button"
27+
android:textAllCaps="false"
28+
/>
29+
30+
<ScrollView
31+
android:layout_width="match_parent"
32+
android:layout_height="match_parent"
33+
>
34+
35+
<LinearLayout
36+
android:layout_width="match_parent"
37+
android:layout_height="wrap_content"
38+
android:orientation="vertical"
39+
android:id="@+id/log"
40+
/>
41+
</ScrollView>
42+
43+
44+
</LinearLayout>

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ androidTools = "31.9.0"
55
androidxAnnotation = "1.9.1"
66
collection = "1.5.0"
77
collectionKtx = "1.5.0"
8+
libsu = "6.0.0"
89
githubApi = "1.321"
910
hiddenapibypass = "5.0"
1011
jebrainsAnnotations = "26.0.2"
@@ -24,6 +25,7 @@ jebtrains-annotations = { module = "org.jetbrains:annotations", version.ref = "j
2425
kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
2526
kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin" }
2627
kotlin-stdlib-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8" }
28+
libsu-core = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu" }
2729
xposed-api = { module = "de.robv.android.xposed:api", version.ref = "xposed" }
2830

2931
[plugins]

settings.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ dependencyResolutionManagement {
3030
}
3131
}
3232
mavenCentral()
33-
// maven("https://jitpack.io")
33+
maven("https://jitpack.io")
3434
// gradlePluginPortal()
3535
}
3636
}

0 commit comments

Comments
 (0)