diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index f5c69520..485e9d35 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -139,6 +139,10 @@ int ksu_get_manager_signature_index(uid_t uid) int signature_index = -1; int i; + if (ksu_manager_uid != KSU_INVALID_UID && uid == ksu_manager_uid) { + return 1; + } + if (!is_dynamic_sign_enabled()) { return -1; } @@ -156,6 +160,7 @@ int ksu_get_manager_signature_index(uid_t uid) return signature_index; } + static void clear_all_managers(void) { unsigned long flags; @@ -442,6 +447,40 @@ void ksu_dynamic_sign_exit(void) pr_info("Dynamic sign exited with persistent storage\n"); } +// Get active managers for multi-manager APKs +int ksu_get_active_managers(struct manager_list_info *info) +{ + unsigned long flags; + int i, count = 0; + + if (!info) { + return -EINVAL; + } + + if (ksu_manager_uid != KSU_INVALID_UID && count < 2) { + info->managers[count].uid = ksu_manager_uid; + info->managers[count].signature_index = 1; + count++; + } + + if (is_dynamic_sign_enabled()) { + spin_lock_irqsave(&managers_lock, flags); + + for (i = 0; i < MAX_MANAGERS && count < 2; i++) { + if (active_managers[i].is_active) { + info->managers[count].uid = active_managers[i].uid; + info->managers[count].signature_index = active_managers[i].signature_index; + count++; + } + } + + spin_unlock_irqrestore(&managers_lock, flags); + } + + info->count = count; + return 0; +} + struct sdesc { struct shash_desc shash; char ctx[]; diff --git a/kernel/apk_sign.h b/kernel/apk_sign.h index 5c938638..44a4c343 100644 --- a/kernel/apk_sign.h +++ b/kernel/apk_sign.h @@ -3,6 +3,7 @@ #include #include "ksu.h" +#include "manager.h" bool is_manager_apk(char *path); @@ -23,6 +24,7 @@ void ksu_add_manager(uid_t uid, int signature_index); void ksu_remove_manager(uid_t uid); bool ksu_is_any_manager(uid_t uid); int ksu_get_manager_signature_index(uid_t uid); +int ksu_get_active_managers(struct manager_list_info *info); int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config); void ksu_dynamic_sign_init(void); diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 5be9f2d5..dad6cba0 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -364,6 +364,27 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, return 0; } + // Allow root manager to get active managers + if (arg2 == CMD_GET_MANAGERS) { + if (!from_root && !from_manager) { + return 0; + } + + struct manager_list_info manager_info; + int ret = ksu_get_active_managers(&manager_info); + + if (ret == 0) { + if (copy_to_user((void __user *)arg3, &manager_info, sizeof(manager_info))) { + pr_err("copy manager list failed\n"); + return 0; + } + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("get_managers: prctl reply error\n"); + } + } + return 0; + } + if (arg2 == CMD_REPORT_EVENT) { if (!from_root) { return 0; diff --git a/kernel/ksu.h b/kernel/ksu.h index 2f234c1d..156c740d 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -28,6 +28,7 @@ #define CMD_ENABLE_KPM 100 #define CMD_DYNAMIC_SIGN 103 +#define CMD_GET_MANAGERS 104 #define EVENT_POST_FS_DATA 1 #define EVENT_BOOT_COMPLETED 2 @@ -55,6 +56,14 @@ struct dynamic_sign_user_config { char hash[65]; }; +struct manager_list_info { + int count; + struct { + uid_t uid; + int signature_index; + } managers[2]; +}; + struct root_profile { int32_t uid; int32_t gid; diff --git a/manager/app/src/main/cpp/jni.c b/manager/app/src/main/cpp/jni.c index 2efdf314..964a7231 100644 --- a/manager/app/src/main/cpp/jni.c +++ b/manager/app/src/main/cpp/jni.c @@ -394,4 +394,43 @@ NativeBridgeNP(clearDynamicSign, jboolean) { bool result = clear_dynamic_sign(); LogDebug("clearDynamicSign: result=%d", result); return result; -} \ No newline at end of file +} + +NativeBridgeNP(getManagersList, jobject) { + struct manager_list_info info; + bool result = get_managers_list(&info); + + if (!result) { + LogDebug("getManagersList: failed to get managers list"); + return NULL; + } + + jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$ManagersList"); + jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "", "()V"); + jobject obj = GetEnvironment()->NewObject(env, cls, constructor); + + jfieldID countField = GetEnvironment()->GetFieldID(env, cls, "count", "I"); + jfieldID managersField = GetEnvironment()->GetFieldID(env, cls, "managers", "Ljava/util/List;"); + + GetEnvironment()->SetIntField(env, obj, countField, (jint)info.count); + + jclass arrayListCls = GetEnvironment()->FindClass(env, "java/util/ArrayList"); + jmethodID arrayListConstructor = GetEnvironment()->GetMethodID(env, arrayListCls, "", "()V"); + jobject managersList = GetEnvironment()->NewObject(env, arrayListCls, arrayListConstructor); + jmethodID addMethod = GetEnvironment()->GetMethodID(env, arrayListCls, "add", "(Ljava/lang/Object;)Z"); + + jclass managerInfoCls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$ManagerInfo"); + jmethodID managerInfoConstructor = GetEnvironment()->GetMethodID(env, managerInfoCls, "", "(II)V"); + + for (int i = 0; i < info.count; i++) { + jobject managerInfo = GetEnvironment()->NewObject(env, managerInfoCls, managerInfoConstructor, + (jint)info.managers[i].uid, + (jint)info.managers[i].signature_index); + GetEnvironment()->CallBooleanMethod(env, managersList, addMethod, managerInfo); + } + + GetEnvironment()->SetObjectField(env, obj, managersField, managersList); + + LogDebug("getManagersList: count=%d", info.count); + return obj; +} diff --git a/manager/app/src/main/cpp/ksu.c b/manager/app/src/main/cpp/ksu.c index ccd39319..7305c28d 100644 --- a/manager/app/src/main/cpp/ksu.c +++ b/manager/app/src/main/cpp/ksu.c @@ -37,6 +37,7 @@ #define CMD_HOOK_TYPE 101 #define CMD_GET_SUSFS_FEATURE_STATUS 102 #define CMD_DYNAMIC_SIGN 103 +#define CMD_GET_MANAGERS 104 #define DYNAMIC_SIGN_OP_SET 0 #define DYNAMIC_SIGN_OP_GET 1 @@ -173,4 +174,12 @@ bool clear_dynamic_sign() { struct dynamic_sign_user_config config; config.operation = DYNAMIC_SIGN_OP_CLEAR; return ksuctl(CMD_DYNAMIC_SIGN, &config, NULL); +} + +bool get_managers_list(struct manager_list_info* info) { + if (info == NULL) { + return false; + } + + return ksuctl(CMD_GET_MANAGERS, info, 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 7524c1ed..4bed5014 100644 --- a/manager/app/src/main/cpp/ksu.h +++ b/manager/app/src/main/cpp/ksu.h @@ -7,6 +7,7 @@ #include "prelude.h" #include +#include bool become_manager(const char *); @@ -105,6 +106,14 @@ struct app_profile { }; }; +struct manager_list_info { + int count; + struct { + uid_t uid; + int signature_index; + } managers[2]; +}; + bool set_app_profile(const struct app_profile* profile); bool get_app_profile(char* key, struct app_profile* profile); @@ -125,4 +134,6 @@ bool get_dynamic_sign(struct dynamic_sign_user_config* config); bool clear_dynamic_sign(); +bool get_managers_list(struct manager_list_info* info); + #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 a59d5486..1332c74e 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/Natives.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/Natives.kt @@ -118,6 +118,12 @@ object Natives { */ external fun clearDynamicSign(): Boolean + /** + * Get active managers list when dynamic sign is enabled + * @return ManagersList object containing active managers, or null if failed or not enabled + */ + external fun getManagersList(): ManagersList? + private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$" private const val NOBODY_UID = 9999 @@ -177,7 +183,6 @@ object Natives { val size: Int = 0, val hash: String = "" ) : Parcelable { - constructor() : this(0, "") fun isValid(): Boolean { return size > 0 && hash.length == 64 && hash.all { @@ -186,6 +191,22 @@ object Natives { } } + @Immutable + @Parcelize + @Keep + data class ManagersList( + val count: Int = 0, + val managers: List = emptyList() + ) : Parcelable + + @Immutable + @Parcelize + @Keep + data class ManagerInfo( + val uid: Int = 0, + val signatureIndex: Int = 0 + ) : Parcelable + @Immutable @Parcelize @Keep diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt index 2176aee0..b1d7c3ed 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt @@ -719,6 +719,32 @@ private fun InfoCard( icon = Icons.Default.SettingsSuggest, ) + // 活跃管理器 + if (systemInfo.isDynamicSignEnabled && systemInfo.managersList != null) { + val signatureMap = systemInfo.managersList.managers.groupBy { it.signatureIndex } + + val managersText = buildString { + signatureMap.toSortedMap().forEach { (signatureIndex, managers) -> + append(managers.joinToString(", ") { "UID:${it.uid}" }) + append(" ") + append( + when (signatureIndex) { + 1 -> "(${stringResource(R.string.default_signature)})" + 2 -> "(${stringResource(R.string.dynamic_signature)})" + else -> if (signatureIndex >= 0) "(${stringResource(R.string.signature_index, signatureIndex)})" else "(${stringResource(R.string.unknown_signature)})" + } + ) + append("; ") + } + }.trimEnd(' ', ';') + + InfoCardItem( + stringResource(R.string.multi_manager_list), + managersText.ifEmpty { stringResource(R.string.no_active_manager) }, + icon = Icons.Default.Group, + ) + } + InfoCardItem( stringResource(R.string.home_selinux_status), systemInfo.seLinuxStatus, diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt index 28ac3dcc..00e333eb 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt @@ -60,7 +60,9 @@ class HomeViewModel : ViewModel() { val susSUMode: String = "", val superuserCount: Int = 0, val moduleCount: Int = 0, - val kpmModuleCount: Int = 0 + val kpmModuleCount: Int = 0, + val managersList: Natives.ManagersList? = null, + val isDynamicSignEnabled: Boolean = false ) private val gson = Gson() @@ -242,6 +244,26 @@ class HomeViewModel : ViewModel() { } } + // 获取动态签名状态和管理器列表 + val dynamicSignConfig = try { + Natives.getDynamicSign() + } catch (e: Exception) { + Log.w(TAG, "Failed to get dynamic sign config", e) + null + } + + val isDynamicSignEnabled = dynamicSignConfig?.isValid() == true + val managersList = if (isDynamicSignEnabled) { + try { + Natives.getManagersList() + } catch (e: Exception) { + Log.w(TAG, "Failed to get managers list", e) + null + } + } else { + null + } + systemInfo = SystemInfo( kernelRelease = uname.release, androidVersion = Build.VERSION.RELEASE, @@ -256,7 +278,9 @@ class HomeViewModel : ViewModel() { susSUMode = susSUMode, superuserCount = getSuperuserCount(), moduleCount = getModuleCount(), - kpmModuleCount = getKpmModuleCount() + kpmModuleCount = getKpmModuleCount(), + managersList = managersList, + isDynamicSignEnabled = isDynamicSignEnabled ) } catch (e: Exception) { Log.e(TAG, "Error fetching system info", e) diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml index 326ba1f4..14fbb271 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -578,4 +578,9 @@ 无效的签名配置 动态签名已禁用 清除动态签名错误 + 动态 + 签名%1$d + 未知 + 活跃管理器 + 无活跃管理器 diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index bbfdff63..ef9ae13b 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -580,4 +580,10 @@ Invalid signature configuration Dynamic signature disabled Failed to clear dynamic signature + Dynamic + Signature %1$d + Unknown + Active Manager + No active manager + SukiSU