Add UID scanner functionality and related infrastructure

- Introduced a new module `uid_scanner` in userspace for managing UID scanning.
- Created a new GitHub Actions workflow for building the `user_scanner`.
- Implemented kernel communication in `throne_comm.c` and `throne_comm.h` to handle user space updates and rescan requests.
- Developed the `uid_scanner` daemon in C to scan user directories and manage UID whitelists.
- Added configuration management for the UID scanner with support for multiple users and auto-scanning.
- Implemented logging and error handling throughout the UID scanning process.
- Created necessary build files for the `user_scanner` JNI integration.
- Added a `.gitignore` file to exclude build artifacts.
This commit is contained in:
ShirkNeko
2025-09-19 21:01:01 +08:00
parent 695e749e3e
commit cc1c66bb6f
21 changed files with 1565 additions and 179 deletions

View File

@@ -31,6 +31,8 @@ object Natives {
const val MINIMAL_SUPPORTED_DYNAMIC_MANAGER = 13215
const val MINIMAL_SUPPORTED_UID_SCANNER = 13347
const val ROOT_UID = 0
const val ROOT_GID = 0

View File

@@ -48,6 +48,8 @@ import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation
import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.getBugreportFile
import com.sukisu.ultra.ui.util.setUidAutoScan
import com.sukisu.ultra.ui.util.setUidMultiUserScan
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -127,7 +129,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
navigator.navigate(AppProfileTemplateScreenDestination)
}
)
// 卸载模块开关
var umountChecked by rememberSaveable {
mutableStateOf(Natives.isDefaultUmountModules())
@@ -177,6 +179,80 @@ fun SettingScreen(navigator: DestinationsNavigator) {
forceSignatureVerification = enabled
}
)
if (Natives.version >= Natives.MINIMAL_SUPPORTED_UID_SCANNER) {
var uidAutoScanEnabled by rememberSaveable {
mutableStateOf(prefs.getBoolean("uid_auto_scan", false))
}
var uidMultiUserScanEnabled by rememberSaveable {
mutableStateOf(prefs.getBoolean("uid_multi_user_scan", false))
}
// 用户态扫描应用列表开关
SwitchItem(
icon = Icons.Filled.Scanner,
title = stringResource(R.string.uid_auto_scan_title),
summary = stringResource(R.string.uid_auto_scan_summary),
checked = uidAutoScanEnabled,
onCheckedChange = { enabled ->
scope.launch {
try {
if (setUidAutoScan(enabled)) {
uidAutoScanEnabled = enabled
prefs.edit { putBoolean("uid_auto_scan", enabled) }
// 如果关闭了用户态扫描,则同时关闭多用户扫描
if (!enabled) {
uidMultiUserScanEnabled = false
prefs.edit { putBoolean("uid_multi_user_scan", false) }
}
} else {
snackBarHost.showSnackbar(context.getString(R.string.uid_scanner_setting_failed))
}
} catch (e: Exception) {
snackBarHost.showSnackbar(
context.getString(
R.string.uid_scanner_setting_error,
e.message ?: ""
)
)
}
}
}
)
// 多用户应用扫描开关 - 仅在启用用户态扫描时显示
AnimatedVisibility(
visible = uidAutoScanEnabled,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
SwitchItem(
icon = Icons.Filled.Groups,
title = stringResource(R.string.uid_multi_user_scan_title),
summary = stringResource(R.string.uid_multi_user_scan_summary),
checked = uidMultiUserScanEnabled,
onCheckedChange = { enabled ->
scope.launch {
try {
if (setUidMultiUserScan(enabled)) {
uidMultiUserScanEnabled = enabled
prefs.edit { putBoolean("uid_multi_user_scan", enabled) }
} else {
snackBarHost.showSnackbar(context.getString(R.string.uid_scanner_setting_failed))
}
} catch (e: Exception) {
snackBarHost.showSnackbar(
context.getString(
R.string.uid_scanner_setting_error,
e.message ?: ""
)
)
}
}
}
)
}
}
}
)
}
@@ -807,4 +883,4 @@ private fun TopBar(
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
scrollBehavior = scrollBehavior
)
}
}

View File

@@ -569,3 +569,47 @@ fun getZygiskImplement(): String {
Log.i(TAG, "Zygisk implement: $result")
return result
}
fun getUidScannerDaemonPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libuid_scanner.so"
}
fun ensureUidScannerExecutable(): Boolean {
val shell = getRootShell()
val uidScannerPath = getUidScannerDaemonPath()
val targetPath = "/data/adb/uid_scanner"
if (!ShellUtils.fastCmdResult(shell, "test -f $targetPath")) {
val copyResult = ShellUtils.fastCmdResult(shell, "cp $uidScannerPath $targetPath")
if (!copyResult) {
return false
}
}
val result = ShellUtils.fastCmdResult(shell, "chmod 755 $targetPath")
return result
}
fun setUidAutoScan(enabled: Boolean): Boolean {
val shell = getRootShell()
if (!ensureUidScannerExecutable()) {
return false
}
val enableValue = if (enabled) 1 else 0
val cmd = "${getUidScannerDaemonPath()} --auto-scan $enableValue && ${getUidScannerDaemonPath()} reload"
val result = ShellUtils.fastCmdResult(shell, cmd)
return result
}
fun setUidMultiUserScan(enabled: Boolean): Boolean {
val shell = getRootShell()
if (!ensureUidScannerExecutable()) {
return false
}
val enableValue = if (enabled) 1 else 0
val cmd = "${getUidScannerDaemonPath()} --multi-user $enableValue && ${getUidScannerDaemonPath()} reload"
val result = ShellUtils.fastCmdResult(shell, cmd)
return result
}

View File

@@ -1,15 +1,13 @@
package io.sukisu.ultra;
import static com.sukisu.ultra.ui.util.KsuCliKt.getKpmmgrPath;
import static com.sukisu.ultra.ui.util.KsuCliKt.getSuSFSDaemonPath;
import static com.sukisu.ultra.ui.util.KsuCliKt.*;
import android.annotation.SuppressLint;
public class UltraToolInstall {
private static final String OUTSIDE_KPMMGR_PATH = "/data/adb/ksu/bin/kpmmgr";
private static final String OUTSIDE_SUSFSD_PATH = "/data/adb/ksu/bin/susfsd";
@SuppressLint("SetWorldReadable")
@SuppressWarnings("ResultOfMethodCallIgnored")
public static void tryToInstall() {
String kpmmgrPath = getKpmmgrPath();
if (UltraShellHelper.isPathExists(OUTSIDE_KPMMGR_PATH)) {

View File

@@ -653,4 +653,11 @@
<!-- KPM选项单选按钮组字符串 -->
<string name="kpm_follow_kernel_file">跟随内核</string>
<string name="kpm_follow_kernel_description">原样使用内核,不进行任何 KPM 修改</string>
<!-- UID Scanner Settings -->
<string name="uid_auto_scan_title">用户态扫描应用列表</string>
<string name="uid_auto_scan_summary">开启后将使用用户态扫描应用列表,提高稳定性 (因内核扫描应用列表出现卡死等问题可以尝试打开此选项) </string>
<string name="uid_multi_user_scan_title">多用户应用扫描</string>
<string name="uid_multi_user_scan_summary">开启后将扫描所有用户的应用,包括工作资料等</string>
<string name="uid_scanner_setting_failed">设置失败,请检查权限</string>
<string name="uid_scanner_setting_error">设置失败: %s</string>
</resources>

View File

@@ -661,4 +661,11 @@ Important Note:\n
<!-- KPM option radio group strings -->
<string name="kpm_follow_kernel_file">Follow Kernel</string>
<string name="kpm_follow_kernel_description">Use kernel as-is without any KPM modifications</string>
<!-- UID Scanner Settings -->
<string name="uid_auto_scan_title">User-mode scanning application list</string>
<string name="uid_auto_scan_summary">Enabling this option will use user-mode scanning for the application list, improving stability. (If you encounter issues such as freezing during kernel scanning of the application list, you may try enabling this option.)</string>
<string name="uid_multi_user_scan_title">Multi-User Application Scanning</string>
<string name="uid_multi_user_scan_summary">When enabled, scans applications for all users, including work profiles</string>
<string name="uid_scanner_setting_failed">Setting failed, please check permissions</string>
<string name="uid_scanner_setting_error">Setting failed: %s</string>
</resources>