From 6b1033491916f014fe57ff4342f79cba17bd9073 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 8 Aug 2020 07:18:28 +0800 Subject: [PATCH] Remove dependency on librootjava --- mobile/build.gradle.kts | 1 - .../be/mygod/librootkotlinx/AppProcess.kt | 72 +++++++++++++++++++ .../be/mygod/librootkotlinx/RootServer.kt | 42 ++--------- .../java/be/mygod/librootkotlinx/Utils.kt | 24 ------- 4 files changed, 76 insertions(+), 63 deletions(-) create mode 100644 mobile/src/main/java/be/mygod/librootkotlinx/AppProcess.kt diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 0bd3b83c..d8eb8b49 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -93,7 +93,6 @@ dependencies { implementation("com.jakewharton.timber:timber:4.7.1") implementation("com.linkedin.dexmaker:dexmaker:2.28.0") implementation("com.takisoft.preferencex:preferencex-simplemenu:1.1.0") - implementation("eu.chainfire:librootjava:1.3.0") implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.2") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8") testImplementation("junit:junit:4.13") diff --git a/mobile/src/main/java/be/mygod/librootkotlinx/AppProcess.kt b/mobile/src/main/java/be/mygod/librootkotlinx/AppProcess.kt new file mode 100644 index 00000000..f58de671 --- /dev/null +++ b/mobile/src/main/java/be/mygod/librootkotlinx/AppProcess.kt @@ -0,0 +1,72 @@ +package be.mygod.librootkotlinx + +import android.os.Build +import android.os.Debug +import android.os.Process +import androidx.annotation.RequiresApi +import java.io.File + +object AppProcess { + /** + * Based on: https://android.googlesource.com/platform/bionic/+/aff9a34/linker/linker.cpp#3397 + */ + @get:RequiresApi(29) + val genericLdConfigFilePath: String get() { + "/system/etc/ld.config.${Build.VERSION.SDK_INT}.txt".let { if (File(it).isFile) return it } + if (Build.VERSION.SDK_INT >= 30) "/linkerconfig/ld.config.txt".let { + check(File(it).isFile) { "failed to find generated linker configuration from \"$it\"" } + return it + } + val prop = Class.forName("android.os.SystemProperties") + if (prop.getDeclaredMethod("getBoolean", String::class.java, Boolean::class.java).invoke(null, + "ro.vndk.lite", false) as Boolean) return "/system/etc/ld.config.vndk_lite.txt" + when (val version = prop.getDeclaredMethod("get", String::class.java, String::class.java).invoke(null, + "ro.vndk.version", "") as String) { + "", "current" -> { } + else -> "/system/etc/ld.config.$version.txt".let { if (File(it).isFile) return it } + } + return "/system/etc/ld.config.txt" + } + + val myExe get() = "/proc/${Process.myPid()}/exe" + + /** + * To workaround Samsung's stupid kernel patch that prevents exec, we need to relocate exe outside of /data. + * See also: https://github.com/Chainfire/librootjava/issues/19 + * + * @return The script to be executed to perform relocation and the relocated binary path. + */ + fun relocateScript(token: String): Pair { + val script = StringBuilder() + val (baseDir, relocated) = if (Build.VERSION.SDK_INT < 29) "/dev" to "/dev/app_process_$token" else { + val apexPath = "/apex/$token" + script.appendln("[ -d $apexPath ] || " + + "mkdir $apexPath && " + + // we need to mount a new tmpfs to override noexec flag + "mount -t tmpfs -o size=1M tmpfs $apexPath || exit 1") + // unfortunately native ld.config.txt only recognizes /data,/system,/system_ext as system directories; + // to link correctly, we need to add our path to the linker config too + val ldConfig = "$apexPath/etc/ld.config.txt" + script.appendln("[ -f $ldConfig ] || " + + "mkdir -p $apexPath/etc && " + + "echo dir.system = $apexPath >$ldConfig && " + + "cat $genericLdConfigFilePath >>$ldConfig || exit 1") + "$apexPath/bin" to "$apexPath/bin/app_process" + } + script.appendln("[ -f $relocated ] || " + + "mkdir -p $baseDir && " + + "cp $myExe $relocated && " + + "chmod 700 $relocated || exit 1") + return script to relocated + } + + fun launchString(packageCodePath: String, clazz: String, appProcess: String, niceName: String?): String { + val debugParams = if (Debug.isDebuggerConnected()) when (Build.VERSION.SDK_INT) { + in 29..Int.MAX_VALUE -> "-XjdwpProvider:adbconnection" + 28 -> "-XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable" + else -> "-Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable" + } else "" + val extraParams = if (niceName != null) " --nice-name=$niceName" else "" + return "CLASSPATH=$packageCodePath exec $appProcess $debugParams /system/bin$extraParams $clazz" + } +} diff --git a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt index ffc13464..4d27ccb7 100644 --- a/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt +++ b/mobile/src/main/java/be/mygod/librootkotlinx/RootServer.kt @@ -1,7 +1,6 @@ package be.mygod.librootkotlinx import android.content.Context -import android.os.Build import android.os.Looper import android.os.Parcelable import android.os.RemoteException @@ -11,9 +10,6 @@ import android.util.Log import androidx.collection.LongSparseArray import androidx.collection.set import androidx.collection.valueIterator -import eu.chainfire.librootjava.AppProcess -import eu.chainfire.librootjava.Logger -import eu.chainfire.librootjava.RootJava import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.SendChannel @@ -161,35 +157,10 @@ class RootServer @JvmOverloads constructor(private val warnLogger: (String) -> U val uuid = context.packageName + '@' + if (persistence.canRead()) persistence.readText() else { UUID.randomUUID().toString().also { persistence.writeText(it) } } - // to workaround Samsung's stupid kernel patch, we need to relocate outside of /data: https://github.com/Chainfire/librootjava/issues/19 - val (baseDir, relocated) = if (Build.VERSION.SDK_INT < 29) "/dev" to "/dev/app_process_$uuid" else { - val apexPath = "/apex/$uuid" - writer.writeBytes("[ -d $apexPath ] || " + - "mkdir $apexPath && " + - // we need to mount a new tmpfs to override noexec flag - "mount -t tmpfs -o size=1M tmpfs $apexPath || exit 1\n") - // unfortunately native ld.config.txt only recognizes /data,/system,/system_ext as system directories; - // to link correctly, we need to add our path to the linker config too - val ldConfig = "$apexPath/etc/ld.config.txt" - writer.writeBytes("[ -f $ldConfig ] || " + - "mkdir -p $apexPath/etc && " + - "echo dir.system = $apexPath >$ldConfig && " + - "cat $genericLdConfigFilePath >>$ldConfig || exit 1\n") - "$apexPath/bin" to "$apexPath/bin/app_process" - } - writer.writeBytes("[ -f $relocated ] || " + - "mkdir -p $baseDir && " + - "cp /proc/${android.os.Process.myPid()}/exe $relocated && " + - "chmod 700 $relocated || exit 1\n") - writer.writeBytes(RootJava.getLaunchString( - context.packageCodePath + " exec", // hack: plugging in exec - RootServer::class.java.name, relocated, - AppProcess.guessIfAppProcessIs64Bits(File("/proc/self/exe").canonicalPath), - arrayOf("$token2\n"), niceName).let { result -> - if (Build.VERSION.SDK_INT < 24) result - // undo the patch on newer APIs to let linker do the work - else result.replaceFirst(" LD_LIBRARY_PATH=", " __SUPPRESSED_LD_LIBRARY_PATH=") - }) + val (script, relocated) = AppProcess.relocateScript(uuid) + script.appendln(AppProcess.launchString(context.packageCodePath, RootServer::class.java.name, relocated, + niceName) + " $token2") + writer.writeBytes(script.toString()) writer.flush() reader.lookForToken(token2) // wait for ready signal output = writer @@ -365,10 +336,6 @@ class RootServer @JvmOverloads constructor(private val warnLogger: (String) -> U */ @JvmStatic var isDebugEnabled = false - set(value) { - field = value - Logger.setDebugLogging(value) - } private const val TAG = "RootServer" private const val SUCCESS = 0 @@ -435,7 +402,6 @@ class RootServer @JvmOverloads constructor(private val warnLogger: (String) -> U private fun rootMain(args: Array) { require(args.isNotEmpty()) - if (Build.VERSION.SDK_INT < 24) RootJava.restoreOriginalLdLibraryPath() val mainInitialized = CountDownLatch(1) val main = Thread({ @Suppress("DEPRECATION") diff --git a/mobile/src/main/java/be/mygod/librootkotlinx/Utils.kt b/mobile/src/main/java/be/mygod/librootkotlinx/Utils.kt index 11b36f51..f2f21c54 100644 --- a/mobile/src/main/java/be/mygod/librootkotlinx/Utils.kt +++ b/mobile/src/main/java/be/mygod/librootkotlinx/Utils.kt @@ -4,37 +4,13 @@ package be.mygod.librootkotlinx import android.annotation.SuppressLint import android.content.Context -import android.os.Build import android.os.Parcel import android.os.Parcelable import android.util.* -import androidx.annotation.RequiresApi import kotlinx.android.parcel.Parcelize -import java.io.File class NoShellException(cause: Throwable) : Exception("Root missing", cause) -/** - * Based on: https://android.googlesource.com/platform/bionic/+/aff9a34/linker/linker.cpp#3397 - */ -@get:RequiresApi(29) -val genericLdConfigFilePath: String get() { - "/system/etc/ld.config.${Build.VERSION.SDK_INT}.txt".let { if (File(it).isFile) return it } - if (Build.VERSION.SDK_INT >= 30) "/linkerconfig/ld.config.txt".let { - check(File(it).isFile) { "failed to find generated linker configuration from \"$it\"" } - return it - } - val prop = Class.forName("android.os.SystemProperties") - if (prop.getDeclaredMethod("getBoolean", String::class.java, Boolean::class.java).invoke(null, - "ro.vndk.lite", false) as Boolean) return "/system/etc/ld.config.vndk_lite.txt" - when (val version = prop.getDeclaredMethod("get", String::class.java, String::class.java).invoke(null, - "ro.vndk.version", "") as String) { - "", "current" -> { } - else -> "/system/etc/ld.config.$version.txt".let { if (File(it).isFile) return it } - } - return "/system/etc/ld.config.txt" -} - val systemContext by lazy { val classActivityThread = Class.forName("android.app.ActivityThread") val activityThread = classActivityThread.getMethod("systemMain").invoke(null)