* refact: use feature subsystem

* use 64bit feature

* fix

* add fixme

* add feature max to get_info

* use 32bit feature id

* allow root to get/set feature

* more clean perm_check functions

* fix

* add feature command to ksud

kernel: do not expose perm checker

* fix security_task_fix_setuid_handler_pre

* add android16-6.12 ci

* manager: add kernel_umount switch

Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>

* manager: Reinstate the LKM selection function

* kernel: add name and print command value

- Optimise sulog log display

Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>

* fix

* ksud: clippy

---------

Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
Co-authored-by: weishu <twsxtd@gmail.com>
This commit is contained in:
ShirkNeko
2025-11-02 20:01:24 +08:00
committed by GitHub
parent 00de4e1c64
commit 47bcc956a3
26 changed files with 963 additions and 852 deletions

View File

@@ -5,6 +5,7 @@
#include <sys/prctl.h>
#include <android/log.h>
#include <string.h>
#include <linux/capability.h>
NativeBridgeNP(getVersion, jint) {
uint32_t version = get_version();
@@ -300,6 +301,14 @@ NativeBridge(setSuEnabled, jboolean, jboolean enabled) {
return set_su_enabled(enabled);
}
NativeBridgeNP(isKernelUmountEnabled, jboolean) {
return is_kernel_umount_enabled();
}
NativeBridge(setKernelUmountEnabled, jboolean, jboolean enabled) {
return set_kernel_umount_enabled(enabled);
}
// Check if KPM is enabled
NativeBridgeNP(isKPMEnabled, jboolean) {
return is_KPM_enable();

View File

@@ -10,6 +10,7 @@
#include <android/log.h>
#include <dirent.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/syscall.h>
@@ -133,13 +134,56 @@ int get_app_profile(struct app_profile *profile) {
}
bool set_su_enabled(bool enabled) {
struct ksu_enable_su_cmd cmd = {.enable = enabled};
return ksuctl(KSU_IOCTL_ENABLE_SU, &cmd) == 0;
struct ksu_set_feature_cmd cmd = {};
cmd.feature_id = KSU_FEATURE_SU_COMPAT;
cmd.value = enabled ? 1 : 0;
return ksuctl(KSU_IOCTL_SET_FEATURE, &cmd) == 0;
}
bool is_su_enabled() {
struct ksu_is_su_enabled_cmd cmd = {};
return ksuctl(KSU_IOCTL_IS_SU_ENABLED, &cmd) == 0 && cmd.enabled;
struct ksu_get_feature_cmd cmd = {};
cmd.feature_id = KSU_FEATURE_SU_COMPAT;
if (ksuctl(KSU_IOCTL_GET_FEATURE, &cmd) != 0) {
return false;
}
if (!cmd.supported) {
return false;
}
return cmd.value != 0;
}
static inline bool get_feature(uint32_t feature_id, uint64_t *out_value, bool *out_supported) {
struct ksu_get_feature_cmd cmd = {};
cmd.feature_id = feature_id;
if (ksuctl(KSU_IOCTL_GET_FEATURE, &cmd) != 0) {
return false;
}
if (out_value) *out_value = cmd.value;
if (out_supported) *out_supported = cmd.supported;
return true;
}
static inline bool set_feature(uint32_t feature_id, uint64_t value) {
struct ksu_set_feature_cmd cmd = {};
cmd.feature_id = feature_id;
cmd.value = value;
return ksuctl(KSU_IOCTL_SET_FEATURE, &cmd) == 0;
}
bool set_kernel_umount_enabled(bool enabled) {
return set_feature(KSU_FEATURE_KERNEL_UMOUNT, enabled ? 1 : 0);
}
bool is_kernel_umount_enabled() {
uint64_t value = 0;
bool supported = false;
if (!get_feature(KSU_FEATURE_KERNEL_UMOUNT, &value, &supported)) {
return false;
}
if (!supported) {
return false;
}
return value != 0;
}
void get_full_version(char* buff) {

View File

@@ -6,7 +6,7 @@
#define KERNELSU_KSU_H
#include "prelude.h"
#include <linux/capability.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/ioctl.h>
@@ -105,10 +105,6 @@ bool set_app_profile(const struct app_profile* profile);
int get_app_profile(struct app_profile* profile);
bool set_su_enabled(bool enabled);
bool is_su_enabled();
bool is_KPM_enable();
void get_hook_type(char* hook_type);
@@ -129,50 +125,69 @@ bool set_uid_scanner_enabled(bool enabled);
bool clear_uid_scanner_environment();
// Feature IDs
enum ksu_feature_id {
KSU_FEATURE_SU_COMPAT = 0,
KSU_FEATURE_KERNEL_UMOUNT = 1,
};
// Generic feature API
struct ksu_get_feature_cmd {
uint32_t feature_id; // Input: feature ID
uint64_t value; // Output: feature value/state
uint8_t supported; // Output: whether the feature is supported
};
struct ksu_set_feature_cmd {
uint32_t feature_id; // Input: feature ID
uint64_t value; // Input: feature value/state to set
};
struct ksu_become_daemon_cmd {
__u8 token[65]; // Input: daemon token (null-terminated)
uint8_t token[65]; // Input: daemon token (null-terminated)
};
struct ksu_get_info_cmd {
__u32 version; // Output: KERNEL_SU_VERSION
__u32 flags; // Output: flags (bit 0: MODULE mode)
uint32_t version; // Output: KERNEL_SU_VERSION
uint32_t flags; // Output: flags (bit 0: MODULE mode)
uint32_t features; // Output: max feature ID supported (KSU_FEATURE_MAX)
};
struct ksu_report_event_cmd {
__u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc.
uint32_t event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc.
};
struct ksu_set_sepolicy_cmd {
__u64 cmd; // Input: sepolicy command
__aligned_u64 arg; // Input: sepolicy argument pointer
uint64_t cmd; // Input: sepolicy command
uint64_t arg; // Input: sepolicy argument pointer
};
struct ksu_check_safemode_cmd {
__u8 in_safe_mode; // Output: true if in safe mode, false otherwise
uint8_t in_safe_mode; // Output: true if in safe mode, false otherwise
};
struct ksu_get_allow_list_cmd {
__u32 uids[128]; // Output: array of allowed/denied UIDs
__u32 count; // Output: number of UIDs in array
__u8 allow; // Input: true for allow list, false for deny list
uint32_t uids[128]; // Output: array of allowed/denied UIDs
uint32_t count; // Output: number of UIDs in array
uint8_t allow; // Input: true for allow list, false for deny list
};
struct ksu_uid_granted_root_cmd {
__u32 uid; // Input: target UID to check
__u8 granted; // Output: true if granted, false otherwise
uint32_t uid; // Input: target UID to check
uint8_t granted; // Output: true if granted, false otherwise
};
struct ksu_uid_should_umount_cmd {
__u32 uid; // Input: target UID to check
__u8 should_umount; // Output: true if should umount, false otherwise
uint32_t uid; // Input: target UID to check
uint8_t should_umount; // Output: true if should umount, false otherwise
};
struct ksu_get_manager_uid_cmd {
__u32 uid; // Output: manager UID
uint32_t uid; // Output: manager UID
};
struct ksu_set_manager_uid_cmd {
__u32 uid; // Input: new manager UID
uint32_t uid; // Input: new manager UID
};
struct ksu_get_app_profile_cmd {
@@ -183,13 +198,13 @@ struct ksu_set_app_profile_cmd {
struct app_profile profile; // Input: app profile structure
};
struct ksu_is_su_enabled_cmd {
__u8 enabled; // Output: true if su compat enabled
};
// Su compat
bool set_su_enabled(bool enabled);
bool is_su_enabled();
struct ksu_enable_su_cmd {
__u8 enable; // Input: true to enable, false to disable
};
// Kernel umount
bool set_kernel_umount_enabled(bool enabled);
bool is_kernel_umount_enabled();
// Other command structures
struct ksu_get_full_version_cmd {
@@ -201,7 +216,7 @@ struct ksu_hook_type_cmd {
};
struct ksu_enable_kpm_cmd {
__u8 enabled; // Output: true if KPM is enabled
uint8_t enabled; // Output: true if KPM is enabled
};
struct ksu_dynamic_manager_cmd {
@@ -213,9 +228,9 @@ struct ksu_get_managers_cmd {
};
struct ksu_enable_uid_scanner_cmd {
__u32 operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV)
__u32 enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE)
__u64 status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
uint32_t operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV)
uint32_t enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE)
uint64_t status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
};
// IOCTL command definitions
@@ -231,8 +246,8 @@ struct ksu_enable_uid_scanner_cmd {
#define KSU_IOCTL_GET_MANAGER_UID _IOR('K', 10, struct ksu_get_manager_uid_cmd)
#define KSU_IOCTL_GET_APP_PROFILE _IOWR('K', 11, struct ksu_get_app_profile_cmd)
#define KSU_IOCTL_SET_APP_PROFILE _IOW('K', 12, struct ksu_set_app_profile_cmd)
#define KSU_IOCTL_IS_SU_ENABLED _IOR('K', 13, struct ksu_is_su_enabled_cmd)
#define KSU_IOCTL_ENABLE_SU _IOW('K', 14, struct ksu_enable_su_cmd)
#define KSU_IOCTL_GET_FEATURE _IOWR('K', 13, struct ksu_get_feature_cmd)
#define KSU_IOCTL_SET_FEATURE _IOW('K', 14, struct ksu_set_feature_cmd)
// Other IOCTL command definitions
#define KSU_IOCTL_GET_FULL_VERSION _IOR('K', 100, struct ksu_get_full_version_cmd)

View File

@@ -98,7 +98,16 @@ object Natives {
*/
external fun isSuEnabled(): Boolean
external fun setSuEnabled(enabled: Boolean): Boolean
external fun grantRoot(): Boolean
/**
* Kernel module umount can be disabled temporarily.
* 0: disabled
* 1: enabled
* negative : error
*/
external fun isKernelUmountEnabled(): Boolean
external fun setKernelUmountEnabled(enabled: Boolean): Boolean
external fun isKPMEnabled(): Boolean
external fun getHookType(): String

View File

@@ -17,6 +17,7 @@ import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Input
import androidx.compose.material.icons.filled.AutoFixHigh
import androidx.compose.material.icons.filled.FileUpload
import androidx.compose.material.icons.filled.Security
@@ -205,12 +206,29 @@ fun InstallScreen(
}
}
val selectLkmLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
it.data?.data?.let { uri ->
lkmSelection = LkmSelection.LkmUri(uri)
}
}
}
val onLkmUpload = {
selectLkmLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply {
type = "application/octet-stream"
})
}
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
Scaffold(
topBar = {
TopBar(
onBack = { navigator.popBackStack() },
onLkmUpload = onLkmUpload,
scrollBehavior = scrollBehavior
)
},
@@ -934,6 +952,7 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
@Composable
private fun TopBar(
onBack: () -> Unit = {},
onLkmUpload: () -> Unit = {},
scrollBehavior: TopAppBarScrollBehavior? = null
) {
val colorScheme = MaterialTheme.colorScheme
@@ -966,6 +985,17 @@ private fun TopBar(
windowInsets = WindowInsets.safeDrawing.only(
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
),
actions = {
IconButton(
modifier = Modifier.padding(end = 16.dp),
onClick = onLkmUpload
) {
Icon(
Icons.AutoMirrored.Filled.Input,
contentDescription = null
)
}
},
scrollBehavior = scrollBehavior
)
}

View File

@@ -17,6 +17,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Undo
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.rounded.FolderDelete
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@@ -165,6 +166,23 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
)
}
var isKernelUmountDisabled by rememberSaveable {
mutableStateOf(!Natives.isKernelUmountEnabled())
}
SwitchItem(
icon = Icons.Rounded.FolderDelete,
title = stringResource(id = R.string.settings_disable_kernel_umount),
summary = stringResource(id = R.string.settings_disable_kernel_umount_summary),
checked = isKernelUmountDisabled,
onCheckedChange = { checked: Boolean ->
val shouldEnable = !checked
if (Natives.setKernelUmountEnabled(shouldEnable)) {
isKernelUmountDisabled = !shouldEnable
}
}
)
// 强制签名验证开关
var forceSignatureVerification by rememberSaveable {
mutableStateOf(prefs.getBoolean("force_signature_verification", false))

View File

@@ -164,6 +164,8 @@
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
<string name="settings_disable_su">禁用 su 兼容性</string>
<string name="settings_disable_su_summary">临时禁止任何应用程序通过 su 命令获取 Root 权限(现有的 Root 进程不受影响)</string>
<string name="settings_disable_kernel_umount">关闭内核 umount</string>
<string name="settings_disable_kernel_umount_summary">临时关闭 KernelSU 控制的内核级 umount 行为。</string>
<string name="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string>
<string name="more_settings">更多设置</string>
<string name="selinux">SELinux</string>

View File

@@ -166,6 +166,8 @@
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
<string name="settings_disable_su">Disable su compatibility</string>
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (Existing root processes will not be affected)</string>
<string name="settings_disable_kernel_umount">Disable kernel umount</string>
<string name="settings_disable_kernel_umount_summary">Temporarily disable kernel-level umount behavior controlled by KernelSU.</string>
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
<string name="more_settings">More settings</string>
<string name="selinux">SELinux</string>