From a12b14ef46eb2556fab905823dbe1920480ba9ab Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Mon, 16 Jun 2025 20:16:25 +0800 Subject: [PATCH] manager: Refactoring get the status of SUSFS functions - More precise status cases - Requires a recompile of your kernel to use it. --- manager/app/src/main/cpp/jni.c | 48 +++++ manager/app/src/main/cpp/ksu.c | 9 + manager/app/src/main/cpp/ksu.h | 21 ++ .../src/main/java/com/sukisu/ultra/Natives.kt | 48 +++++ .../com/sukisu/ultra/ui/util/SuSFSManager.kt | 204 +++--------------- 5 files changed, 154 insertions(+), 176 deletions(-) diff --git a/manager/app/src/main/cpp/jni.c b/manager/app/src/main/cpp/jni.c index 7b10e069..5f4e2d47 100644 --- a/manager/app/src/main/cpp/jni.c +++ b/manager/app/src/main/cpp/jni.c @@ -299,4 +299,52 @@ NativeBridgeNP(getHookType, jstring) { char hook_type[16]; get_hook_type(hook_type, sizeof(hook_type)); return GetEnvironment()->NewStringUTF(env, hook_type); +} + +NativeBridgeNP(getSusfsFeatureStatus, jobject) { + struct susfs_feature_status status; + bool result = get_susfs_feature_status(&status); + + if (!result) { + return NULL; + } + + jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$SusfsFeatureStatus"); + jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "", "()V"); + jobject obj = GetEnvironment()->NewObject(env, cls, constructor); + + // 设置各个字段 + jfieldID statusSusPathField = GetEnvironment()->GetFieldID(env, cls, "statusSusPath", "Z"); + jfieldID statusSusMountField = GetEnvironment()->GetFieldID(env, cls, "statusSusMount", "Z"); + jfieldID statusAutoDefaultMountField = GetEnvironment()->GetFieldID(env, cls, "statusAutoDefaultMount", "Z"); + jfieldID statusAutoBindMountField = GetEnvironment()->GetFieldID(env, cls, "statusAutoBindMount", "Z"); + jfieldID statusSusKstatField = GetEnvironment()->GetFieldID(env, cls, "statusSusKstat", "Z"); + jfieldID statusTryUmountField = GetEnvironment()->GetFieldID(env, cls, "statusTryUmount", "Z"); + jfieldID statusAutoTryUmountBindField = GetEnvironment()->GetFieldID(env, cls, "statusAutoTryUmountBind", "Z"); + jfieldID statusSpoofUnameField = GetEnvironment()->GetFieldID(env, cls, "statusSpoofUname", "Z"); + jfieldID statusEnableLogField = GetEnvironment()->GetFieldID(env, cls, "statusEnableLog", "Z"); + jfieldID statusHideSymbolsField = GetEnvironment()->GetFieldID(env, cls, "statusHideSymbols", "Z"); + jfieldID statusSpoofCmdlineField = GetEnvironment()->GetFieldID(env, cls, "statusSpoofCmdline", "Z"); + jfieldID statusOpenRedirectField = GetEnvironment()->GetFieldID(env, cls, "statusOpenRedirect", "Z"); + jfieldID statusMagicMountField = GetEnvironment()->GetFieldID(env, cls, "statusMagicMount", "Z"); + jfieldID statusOverlayfsAutoKstatField = GetEnvironment()->GetFieldID(env, cls, "statusOverlayfsAutoKstat", "Z"); + jfieldID statusSusSuField = GetEnvironment()->GetFieldID(env, cls, "statusSusSu", "Z"); + + GetEnvironment()->SetBooleanField(env, obj, statusSusPathField, status.status_sus_path); + GetEnvironment()->SetBooleanField(env, obj, statusSusMountField, status.status_sus_mount); + GetEnvironment()->SetBooleanField(env, obj, statusAutoDefaultMountField, status.status_auto_default_mount); + GetEnvironment()->SetBooleanField(env, obj, statusAutoBindMountField, status.status_auto_bind_mount); + GetEnvironment()->SetBooleanField(env, obj, statusSusKstatField, status.status_sus_kstat); + GetEnvironment()->SetBooleanField(env, obj, statusTryUmountField, status.status_try_umount); + GetEnvironment()->SetBooleanField(env, obj, statusAutoTryUmountBindField, status.status_auto_try_umount_bind); + GetEnvironment()->SetBooleanField(env, obj, statusSpoofUnameField, status.status_spoof_uname); + GetEnvironment()->SetBooleanField(env, obj, statusEnableLogField, status.status_enable_log); + GetEnvironment()->SetBooleanField(env, obj, statusHideSymbolsField, status.status_hide_symbols); + GetEnvironment()->SetBooleanField(env, obj, statusSpoofCmdlineField, status.status_spoof_cmdline); + GetEnvironment()->SetBooleanField(env, obj, statusOpenRedirectField, status.status_open_redirect); + GetEnvironment()->SetBooleanField(env, obj, statusMagicMountField, status.status_magic_mount); + GetEnvironment()->SetBooleanField(env, obj, statusOverlayfsAutoKstatField, status.status_overlayfs_auto_kstat); + GetEnvironment()->SetBooleanField(env, obj, statusSusSuField, status.status_sus_su); + + return obj; } \ No newline at end of file diff --git a/manager/app/src/main/cpp/ksu.c b/manager/app/src/main/cpp/ksu.c index 710edd6c..0a7048fd 100644 --- a/manager/app/src/main/cpp/ksu.c +++ b/manager/app/src/main/cpp/ksu.c @@ -32,6 +32,7 @@ #define CMD_ENABLE_SU 15 #define CMD_ENABLE_KPM 100 #define CMD_HOOK_TYPE 101 +#define CMD_GET_SUSFS_FEATURE_STATUS 102 static bool ksuctl(int cmd, void* arg1, void* arg2) { int32_t result = 0; @@ -123,4 +124,12 @@ bool get_hook_type(char* hook_type, size_t size) { strncpy(hook_type, cached_hook_type, size); hook_type[size - 1] = '\0'; return true; +} + +bool get_susfs_feature_status(struct susfs_feature_status* status) { + if (status == NULL) { + return false; + } + + return ksuctl(CMD_GET_SUSFS_FEATURE_STATUS, status, NULL); } \ No newline at end of file diff --git a/manager/app/src/main/cpp/ksu.h b/manager/app/src/main/cpp/ksu.h index e894286b..9fdc8c0e 100644 --- a/manager/app/src/main/cpp/ksu.h +++ b/manager/app/src/main/cpp/ksu.h @@ -26,6 +26,25 @@ bool is_lkm_mode(); #define KSU_MAX_GROUPS 32 #define KSU_SELINUX_DOMAIN 64 +// SUSFS Functional State Structures +struct susfs_feature_status { + bool status_sus_path; + bool status_sus_mount; + bool status_auto_default_mount; + bool status_auto_bind_mount; + bool status_sus_kstat; + bool status_try_umount; + bool status_auto_try_umount_bind; + bool status_spoof_uname; + bool status_enable_log; + bool status_hide_symbols; + bool status_spoof_cmdline; + bool status_open_redirect; + bool status_magic_mount; + bool status_overlayfs_auto_kstat; + bool status_sus_su; +}; + struct root_profile { int32_t uid; int32_t gid; @@ -86,4 +105,6 @@ bool is_KPM_enable(); bool get_hook_type(char* hook_type, size_t size); +bool get_susfs_feature_status(struct susfs_feature_status* status); + #endif //KERNELSU_KSU_H \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/Natives.kt b/manager/app/src/main/java/com/sukisu/ultra/Natives.kt index 72302f2f..cd0375d7 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/Natives.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/Natives.kt @@ -71,6 +71,12 @@ object Natives { external fun isKPMEnabled(): Boolean external fun getHookType(): String + /** + * Get SUSFS feature status from kernel + * @return SusfsFeatureStatus object containing all feature states, or null if failed + */ + external fun getSusfsFeatureStatus(): SusfsFeatureStatus? + private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$" private const val NOBODY_UID = 9999 @@ -95,6 +101,48 @@ object Natives { return version < MINIMAL_SUPPORTED_KERNEL } + @Immutable + @Parcelize + @Keep + data class SusfsFeatureStatus( + val statusSusPath: Boolean = false, + val statusSusMount: Boolean = false, + val statusAutoDefaultMount: Boolean = false, + val statusAutoBindMount: Boolean = false, + val statusSusKstat: Boolean = false, + val statusTryUmount: Boolean = false, + val statusAutoTryUmountBind: Boolean = false, + val statusSpoofUname: Boolean = false, + val statusEnableLog: Boolean = false, + val statusHideSymbols: Boolean = false, + val statusSpoofCmdline: Boolean = false, + val statusOpenRedirect: Boolean = false, + val statusMagicMount: Boolean = false, + val statusOverlayfsAutoKstat: Boolean = false, + val statusSusSu: Boolean = false + ) : Parcelable { + fun toMap(): Map { + return mapOf( + "CONFIG_KSU_SUSFS_SUS_PATH" to statusSusPath, + "CONFIG_KSU_SUSFS_SUS_MOUNT" to statusSusMount, + "CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT" to statusAutoDefaultMount, + "CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT" to statusAutoBindMount, + "CONFIG_KSU_SUSFS_SUS_KSTAT" to statusSusKstat, + "CONFIG_KSU_SUSFS_TRY_UMOUNT" to statusTryUmount, + "CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT" to statusAutoTryUmountBind, + "CONFIG_KSU_SUSFS_SPOOF_UNAME" to statusSpoofUname, + "CONFIG_KSU_SUSFS_ENABLE_LOG" to statusEnableLog, + "CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS" to statusHideSymbols, + "CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG" to statusSpoofCmdline, + "CONFIG_KSU_SUSFS_OPEN_REDIRECT" to statusOpenRedirect, + "CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT" to statusMagicMount, + "CONFIG_KSU_SUSFS_SUS_OVERLAYFS" to statusOverlayfsAutoKstat, + "CONFIG_KSU_SUSFS_SUS_SU" to statusSusSu + ) + } + } + + @Immutable @Parcelize @Keep diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/util/SuSFSManager.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/util/SuSFSManager.kt index a5dd0f4a..3e31aa98 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/util/SuSFSManager.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/util/SuSFSManager.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.SharedPreferences import android.widget.Toast import com.dergoogler.mmrl.platform.Platform.Companion.context +import com.sukisu.ultra.Natives import com.sukisu.ultra.R import com.topjohnwu.superuser.Shell import kotlinx.coroutines.Dispatchers @@ -13,7 +14,6 @@ import kotlinx.coroutines.withContext import java.io.FileOutputStream import java.io.IOException import java.io.File -import java.util.zip.GZIPInputStream /** * SuSFS 配置管理器 @@ -71,25 +71,6 @@ object SuSFSManager { val canConfigure: Boolean = false // 是否可配置(通过弹窗) ) - /** - * 功能配置映射数据类 - */ - private data class FeatureMapping( - val id: String, - val config: String - ) - - /** - * 执行Shell命令并返回输出 - */ - private fun runCmd(shell: Shell, cmd: String): String { - return shell.newJob() - .add(cmd) - .to(mutableListOf(), null) - .exec().out - .joinToString("\n") - } - /** * 获取Root Shell实例 */ @@ -303,56 +284,6 @@ object SuSFSManager { return getPrefs(context).getString(KEY_SDCARD_PATH, "/sdcard") ?: "/sdcard" } - /** - * 读取并解析/proc/config.gz文件 - */ - private suspend fun readProcConfig(): Map = withContext(Dispatchers.IO) { - val configMap = mutableMapOf() - try { - val shell = getRootShell() - - // 首先检查/proc/config.gz是否存在 - val checkResult = shell.newJob().add("test -f /proc/config.gz").exec() - if (!checkResult.isSuccess) { - return@withContext configMap - } - - // 读取并解压/proc/config.gz - val result = shell.newJob().add("zcat /proc/config.gz").exec() - if (result.isSuccess) { - result.out.forEach { line -> - val trimmedLine = line.trim() - when { - // 处理启用的配置项 CONFIG_XXX=y - trimmedLine.contains("=y") -> { - val configName = trimmedLine.substringBefore("=y") - if (configName.startsWith("CONFIG_")) { - configMap[configName] = "y" - } - } - // 处理未启用的配置项 # CONFIG_XXX is not set - trimmedLine.startsWith("# ") && trimmedLine.endsWith(" is not set") -> { - val configName = trimmedLine.removePrefix("# ").removeSuffix(" is not set") - if (configName.startsWith("CONFIG_")) { - configMap[configName] = "not_set" - } - } - // 处理其他类型的配置项 CONFIG_XXX=value - trimmedLine.contains("=") && trimmedLine.startsWith("CONFIG_") -> { - val parts = trimmedLine.split("=", limit = 2) - if (parts.size == 2) { - configMap[parts[0]] = parts[1] - } - } - } - } - } - } catch (e: Exception) { - e.printStackTrace() - } - configMap - } - /** * 从assets复制ksu_susfs文件到/data/adb/ksu/bin/ */ @@ -785,63 +716,14 @@ object SuSFSManager { } /** - * 获取功能配置映射表 - */ - private fun getFeatureMappings(): List { - return listOf( - FeatureMapping("status_sus_path", "CONFIG_KSU_SUSFS_SUS_PATH"), - FeatureMapping("status_sus_mount", "CONFIG_KSU_SUSFS_SUS_MOUNT"), - FeatureMapping("status_auto_default_mount", "CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT"), - FeatureMapping("status_auto_bind_mount", "CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT"), - FeatureMapping("status_sus_kstat", "CONFIG_KSU_SUSFS_SUS_KSTAT"), - FeatureMapping("status_try_umount", "CONFIG_KSU_SUSFS_TRY_UMOUNT"), - FeatureMapping("status_auto_try_umount_bind", "CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT"), - FeatureMapping("status_spoof_uname", "CONFIG_KSU_SUSFS_SPOOF_UNAME"), - FeatureMapping("status_enable_log", "CONFIG_KSU_SUSFS_ENABLE_LOG"), - FeatureMapping("status_hide_symbols", "CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS"), - FeatureMapping("status_spoof_cmdline", "CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG"), - FeatureMapping("status_open_redirect", "CONFIG_KSU_SUSFS_OPEN_REDIRECT"), - FeatureMapping("status_magic_mount", "CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT"), - FeatureMapping("status_overlayfs_auto_kstat", "CONFIG_KSU_SUSFS_SUS_OVERLAYFS"), - FeatureMapping("status_sus_su", "CONFIG_KSU_SUSFS_SUS_SU") - ) - } - - /** - * 获取启用功能状态 + * 获取SuSFS启用功能状态 */ suspend fun getEnabledFeatures(context: Context): List = withContext(Dispatchers.IO) { try { - // 每次都重新执行命令获取最新状态 - val shell = getRootShell() - val targetPath = getSuSFSTargetPath() - - // 首先检查二进制文件是否存在于目标位置 - val checkResult = shell.newJob().add("test -f '$targetPath'").exec() - - val binaryPath = if (checkResult.isSuccess) { - // 如果目标位置存在,直接使用 - targetPath + val susfsStatus = Natives.getSusfsFeatureStatus() + if (susfsStatus != null) { + parseEnabledFeaturesFromStatus(context, susfsStatus) } else { - // 如果不存在,尝试从assets复制 - copyBinaryFromAssets(context) - } - - if (binaryPath == null) { - return@withContext emptyList() - } - - // 使用runCmd执行show enabled_features命令获取实时状态 - val command = "$binaryPath show enabled_features" - val output = runCmd(shell, command) - - // 读取/proc/config.gz进行二次检查 - val procConfig = readProcConfig() - - if (output.isNotEmpty()) { - parseEnabledFeatures(context, output, procConfig) - } else { - // 如果命令输出为空,返回空列表 emptyList() } } catch (e: Exception) { @@ -851,65 +733,35 @@ object SuSFSManager { } /** - * 解析启用功能状态输出 + * 解析SuSFS启用功能状态 */ - private fun parseEnabledFeatures(context: Context, output: String, procConfig: Map): List { + private fun parseEnabledFeaturesFromStatus(context: Context, status: Natives.SusfsFeatureStatus): List { val features = mutableListOf() - // 将输出按行分割并保存到集合中进行快速查找 - val outputLines = output.lines().map { it.trim() }.filter { it.isNotEmpty() }.toSet() - - // 获取功能配置映射表 - val featureMappings = getFeatureMappings() - - // 定义功能名称映射(id到显示名称) - val featureNameMap = mapOf( - "status_sus_path" to context.getString(R.string.sus_path_feature_label), - "status_sus_mount" to context.getString(R.string.sus_mount_feature_label), - "status_try_umount" to context.getString(R.string.try_umount_feature_label), - "status_spoof_uname" to context.getString(R.string.spoof_uname_feature_label), - "status_spoof_cmdline" to context.getString(R.string.spoof_cmdline_feature_label), - "status_open_redirect" to context.getString(R.string.open_redirect_feature_label), - "status_enable_log" to context.getString(R.string.enable_log_feature_label), - "status_auto_default_mount" to context.getString(R.string.auto_default_mount_feature_label), - "status_auto_bind_mount" to context.getString(R.string.auto_bind_mount_feature_label), - "status_auto_try_umount_bind" to context.getString(R.string.auto_try_umount_bind_feature_label), - "status_hide_symbols" to context.getString(R.string.hide_symbols_feature_label), - "status_sus_kstat" to context.getString(R.string.sus_kstat_feature_label), - "status_magic_mount" to context.getString(R.string.magic_mount_feature_label), - "status_overlayfs_auto_kstat" to context.getString(R.string.overlayfs_auto_kstat_feature_label), - "status_sus_su" to context.getString(R.string.sus_su_feature_label) + // 定义功能名称和状态的映射 + val featureList = listOf( + Triple("status_sus_path", context.getString(R.string.sus_path_feature_label), status.statusSusPath), + Triple("status_sus_mount", context.getString(R.string.sus_mount_feature_label), status.statusSusMount), + Triple("status_try_umount", context.getString(R.string.try_umount_feature_label), status.statusTryUmount), + Triple("status_spoof_uname", context.getString(R.string.spoof_uname_feature_label), status.statusSpoofUname), + Triple("status_spoof_cmdline", context.getString(R.string.spoof_cmdline_feature_label), status.statusSpoofCmdline), + Triple("status_open_redirect", context.getString(R.string.open_redirect_feature_label), status.statusOpenRedirect), + Triple("status_enable_log", context.getString(R.string.enable_log_feature_label), status.statusEnableLog), + Triple("status_auto_default_mount", context.getString(R.string.auto_default_mount_feature_label), status.statusAutoDefaultMount), + Triple("status_auto_bind_mount", context.getString(R.string.auto_bind_mount_feature_label), status.statusAutoBindMount), + Triple("status_auto_try_umount_bind", context.getString(R.string.auto_try_umount_bind_feature_label), status.statusAutoTryUmountBind), + Triple("status_hide_symbols", context.getString(R.string.hide_symbols_feature_label), status.statusHideSymbols), + Triple("status_sus_kstat", context.getString(R.string.sus_kstat_feature_label), status.statusSusKstat), + Triple("status_magic_mount", context.getString(R.string.magic_mount_feature_label), status.statusMagicMount), + Triple("status_overlayfs_auto_kstat", context.getString(R.string.overlayfs_auto_kstat_feature_label), status.statusOverlayfsAutoKstat), + Triple("status_sus_su", context.getString(R.string.sus_su_feature_label), status.statusSusSu) ) - // 根据映射表检查每个功能的启用状态 - featureMappings.forEach { mapping -> - val displayName = featureNameMap[mapping.id] ?: mapping.id - - // 检查show enabled_features命令输出中是否存在该配置 - val existsInOutput = outputLines.contains(mapping.config) - - // 检查/proc/config.gz中的配置状态 - val procConfigValue = procConfig[mapping.config] - - val isEnabled = when { - // 如果show enabled_features命令中存在该CONFIG,则认为启用 - existsInOutput -> true - - // 如果show enabled_features命令中不存在该CONFIG,则检查/proc/config.gz中是否存在y - !existsInOutput && procConfigValue == "y" -> true - - // 如果/proc/config.gz中对应的为is not set,则为未启用 - procConfigValue == "not_set" -> false - - // 如果/proc/config.gz中没有对应的CONFIG,则按照show enabled_features命令为主 - procConfigValue == null -> false - - else -> false - } - + // 根据功能列表创建EnabledFeature对象 + featureList.forEach { (id, displayName, isEnabled) -> val statusText = if (isEnabled) context.getString(R.string.susfs_feature_enabled) else context.getString(R.string.susfs_feature_disabled) - // 只有 enable_log 功能可以配置 - val canConfigure = mapping.id == "status_enable_log" + // 只有对应功能可以配置 + val canConfigure = id == "status_enable_log" features.add(EnabledFeature(displayName, isEnabled, statusText, canConfigure)) }