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
}