KernelSU App Profile 完全指南:基于最小权限原则的细粒度 Root 权限管理
【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU
App Profile 是 KernelSU 提供的应用级配置机制,让用户针对单个应用自定义su授予后的 root 进程权限(UID/GID/组、Capabilities、SELinux 域),也能控制普通应用对模块挂载的可见性。本文以官方文档为骨架,结合仓库内核源码与 ksud 用户态实现,系统讲解 Root Profile 与非 root 配置的完整玩法、底层强制逻辑与权限升级风险防范,读完即可上手配置安全、精细的 root 权限策略。
什么是 App Profile
App Profile 是 KernelSU 提供的一套机制,用于灵活定制不同应用的行为。它分为两类场景:
- Root Profile(root 配置文件):针对已授予 root 权限(即可以使用
su)的应用。它可以调整su命令执行后的uid、gid、groups、capabilities以及SELinux规则,从而精细地限制 root 用户的特权。例如,只给防火墙应用网络权限而禁止其文件访问,或只给"冻结"类应用 shell 权限而非完整 root——将力量关进笼子里,践行最小权限原则。 - 非 root 配置文件(Non-root profile):针对没有 root 权限的普通应用,控制内核与模块系统如何对待这些应用,例如决定是否向该应用"隐藏"模块系统造成的改动。
从源码层面看,这两种配置统一由内核中的struct app_profile承载,定义在 kernel/include/uapi/app_profile.h:
struct app_profile { __u32 version; // 配置文件版本,当前为 KSU_APP_PROFILE_VER 4 char key[KSU_MAX_PACKAGE_NAME]; // 通常是应用包名,特殊应用可为其他值 __s32 curr_uid; // 应用当前 UID bool allow_su; // 是否允许 su union { struct { bool use_default; char template_name[KSU_MAX_PACKAGE_NAME]; struct root_profile profile; } rp_config; // 授予 su 时使用 Root Profile struct { bool use_default; struct non_root_profile profile; } nrp_config; // 未授予 su 时使用非 root 配置 }; };其中struct root_profile定义了 su 之后进程的完整身份与权限画像:
struct root_profile { __s32 uid; // 目标 UID __s32 gid; // 目标主组 GID __u32 groups_count; // 补充组数量(最多 KSU_MAX_GROUPS,即 32 个) __s32 groups[KSU_MAX_GROUPS]; struct { // capability 三件套(对应 capabilities v3) __u64 effective; __u64 permitted; __u64 inheritable; } capabilities; char selinux_domain[KSU_SELINUX_DOMAIN]; // 目标 SELinux 域,最长 64 字节 __s32 namespaces; // 挂载命名空间策略 __u64 flags; // 标志位,如 FLAG_KSU_NO_NEW_PRIVS };该头文件同时声明了FLAG_KSU_NO_NEW_PRIVS (1ULL << 0),即后文将提到的防权限升级标志。
Root Profile:UID、GID 与组
Linux/Android 身份模型
Linux 系统存在"用户"与"组"两个概念:每个用户拥有用户 ID(UID),一个用户可属于多个组,每个组拥有组 ID(GID)。系统通过这两类 ID 识别用户,并判断其可访问的系统资源。
- UID 为
0的用户是 root 用户;GID 为0的组是 root 组,一般拥有系统最高权限。 - 在 Android 中,每个应用(除 shared UID 情况外)都作为一个独立用户运行,拥有唯一 UID。例如
0是 root、1000是system、2000是 ADB shell、10000~19999分配给普通应用。
注意:这里的 UID 与 Android 的多用户/工作资料(Work Profile)概念不同。工作资料本质上是切分 UID 区间实现的:例如
10000-19999属于主用户,110000-119999属于工作资料,其中的每个应用各自拥有独立 UID。
每个应用可属于多个组,GID 表示主组(通常与 UID 相同),其余组为补充组。部分权限通过组来管理,例如网络访问、蓝牙访问等。在 ADB shell 中执行id命令可以看到真实示例:
oriole:/ $ id uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0这里 UID 为2000,GID(主组)也是2000,同时属于多个补充组,例如inet(可创建AF_INET/AF_INET6套接字,即具备网络访问能力)和sdcard_rw(可读写 SD 卡)。
通过 Root Profile 自定义 su 进程身份
KernelSU 的 Root Profile 允许自定义su执行后 root 进程的 UID、GID 与所属组。例如,将某 root 应用的 UID 设为2000,则该应用即使使用su,实际权限也仅相当于 ADB shell;将inet组从组列表中移除,则su命令将无法访问网络。
提示:App Profile 只控制
su之后的 root 进程权限,并不控制应用自身的权限。若应用本身已申请并获得了网络权限,那么不使用su也能访问网络;从su中移除inet组,只是不让su访问网络。
Root Profile 由内核强制执行,不依赖 root 应用"自觉"。是否授予su权限完全由用户掌控,而非开发者。
内核如何落地这些身份设置
su请求到达内核后,最终由 kernel/policy/app_profile.c 中的escape_with_root_profile()完成身份切换(第 125-222 行)。其核心逻辑依次为:
prepare_creds()准备新凭据,若当前euid已为 0 或已设置TIF_KSU_DISABLE_ESCAPE_WITH_ROOT(对应 NO_NEW_PRIVS 标志),则中止逃逸;- 通过
ksu_get_root_profile(uid)取到该应用配置的root_profile,将uid/suid/euid/fsuid与gid/sgid/egid/fsgid全部替换为目标值; - 通过
alloc_uid/set_cred_ucounts(v5.14+)同步用户计数,避免 RLIMIT_NPROC 记账错乱; - 将
cap_effective、cap_permitted、cap_bset写为目标 capabilities; - 调用
setup_groups()设置补充组(源码中对组数超过KSU_MAX_GROUPS、GID 非法等情况均有校验与告警日志); - 调用
setup_selinux()切换到目标 SELinux 域; commit_creds()提交凭据,并关闭 seccomp 过滤器(disable_seccomp());- 若配置了
FLAG_KSU_NO_NEW_PRIVS,则设置线程标志,最后按namespaces字段切换挂载命名空间并设置 tracepoint 标志。
若应用没有自定义 Root Profile(use_default为真),内核使用缓存的默认配置。默认值在 kernel/policy/allowlist.c 的init_default_profiles()中初始化(第 40-57 行):uid=0、gid=0、groups 仅[0]、capabilities 为CAP_FULL_SET(完整能力集)、命名空间为KSU_NS_INHERITED、SELinux 域为u:r:ksu:s0。
Capabilities:拆开 root 的"能力包"
Capability 是 Linux 的权限分离机制。传统 UNIX 将进程分为两类:effective UID 为0的特权进程(superuser/root,绕过所有内核权限检查)与 UID 非零的非特权进程(基于凭据做完整检查)。自 Linux 2.2 起,Linux 把传统上归属于 root 的特权拆分为独立单元——capability,每个 capability 可单独启用/禁用。
每个 capability 代表一项或多项特权。例如CAP_DAC_READ_SEARCH可绕过文件读取、目录读取与执行所需的权限检查;即使 effective UID 为0,若缺少CAP_DAC_READ_SEARCH等能力,root 也无法随意读取文件。
KernelSU 的 Root Profile 可自定义su之后进程的 capability,从而实现"部分 root"。与 UID/GID 不同,部分 root 应用确实需要su后保持 UID0;此时保持 UID0但限制 capability,即可限定其可执行的操作范围。
强烈建议:Linux 的 capability 语义繁多(各版本还有细微差异),官方 man 手册
capabilities(7)对每个能力有详尽解释。若打算自定义 capabilities,请务必先通读该文档,再结合上文中struct root_profile的capabilities.effective/permitted/inheritable三段式结构来填写。
从源码看,KernelSU 对 capabilities 的处理比较"直接":escape_with_root_profile()中通过memcpy将配置的effective同时写入cap_effective、cap_permitted与cap_bset(kernel/policy/app_profile.c 第 194-196 行),并用编译期断言BUILD_BUG_ON保证kernel_cap_t与配置结构大小一致。因此配置时应以effective为主填写实际需要的能力集合。
SELinux:default deny 下的精细域控制
SELinux 是一种强制的访问控制(MAC)机制,遵循default deny原则:凡未显式允许的操作一律拒绝。它有两种全局模式:
- Permissive 模式:记录拒绝事件但不强制执行;
- Enforcing 模式:记录拒绝事件并强制执行。
警告:现代 Android 高度依赖 SELinux 保障整体系统安全。运行于 Permissive 模式的自定义系统相比完全开放的系统没有任何实质优势,强烈不建议使用。
SELinux 全貌非常复杂,超出本文范围。建议先通过 Wikipedia 的 "Security-Enhanced Linux" 词条、Red Hat 官方 "What Is SELinux?" 以及 Arch Linux Wiki 的 SELinux 条目建立基本认知,再回到 KernelSU 动手配置。
su 后切换到自定义域
典型场景下,应用执行su后会切换到不受限的 SELinux 域,例如u:r:ksu:s0。该域正是内核默认配置并注入的:在 kernel/selinux/selinux.h 中定义KERNEL_SU_DOMAIN "ksu"与KERNEL_SU_CONTEXT "u:r:ksu:s0",而 kernel/selinux/rules.c 则为其声明了type ksu、permissive ksu、mlstrustedsubject、netdomain、bluetoothdomain等属性,并给出allow ksu * * *级别的宽泛规则(第 86-98 行)——这就是"不受限"的来源。
通过 Root Profile,可以把 su 进程切换到自定义域,例如u:r:app1:s0,并为该域定义规则:
type app1 enforce app1 typeattribute app1 mlstrustedsubject allow app1 * * *注意:示例中的
allow app1 * * *仅用于演示,实际不应这样写——它几乎等同于 Permissive 模式。
用户态如何下发自定义 sepolicy
自定义域的规则由 ksud 负责落盘与加载。userspace/ksud/src/profile.rs 中的set_sepolicy(pkg, policy)会把策略写入defs::PROFILE_SELINUX_DIR(即 profile 目录下的selinux/子目录,定义于 userspace/ksud/src/defs.rs),随后调用sepolicy::apply_file实时应用;apply_sepolies()则在启动阶段遍历该目录,将全部策略文件依次应用,任一文件失败只会记录日志、不影响其余策略。此外 ksud 还提供模板(template)机制(set_template/get_template/delete_template/list_templates),Root Profile 可通过template_name引用预设模板,模板文件存放在PROFILE_TEMPLATE_DIR(profile 目录下templates/),便于批量复用到多个应用。
权限升级(Escalation)风险与 NO_NEW_PRIVS
如果 Root Profile 配置不当,可能会出现"限制被无意绕过"的权限升级场景。典型示例:
假设你授予了 ADB shell 用户 root 权限(常见配置),随后又授予某个普通应用 root 权限,但将其 Root Profile 的 UID 配置为2000(ADB shell 的 UID)。此时该应用只需连续执行两次su即可获得完整 root:
- 第一次
su受 App Profile 限制,将 UID 切换到2000(ADB shell)而非0(root); - 第二次
su时,由于当前 UID 已是2000,而配置中该 UID 已被授予 root 权限,应用因此获得完整 root。
用 NO_NEW_PRIVS 阻断二次提权
为避免这类问题,可以在自定义 App Profile 中启用NO_NEW_PRIVS标志。该标志的底层实现是线程标志TIF_KSU_DISABLE_ESCAPE_WITH_ROOT(定义于 kernel/policy/app_profile.h),当配置携带FLAG_KSU_NO_NEW_PRIVS时,escape_with_root_profile()会在提权完成后设置该标志(kernel/policy/app_profile.c 第 205-207 行);后续若该进程再次调用su,内核在escape_with_root_profile()入口检测到该标志便直接中止(第 145-148 行),从而阻止进程再次通过su逃逸升级。
提示:该标志只阻止 KernelSU 对进程再次提权,进程仍可能通过其他 Linux 机制(如 setuid 程序、命名空间操作等)尝试提权。因此权限配置务必谨慎,NO_NEW_PRIVS 并非万能保险。
非 root 配置文件:模块卸载(Umount modules)
KernelSU 通过挂载 OverlayFS 以 systemless 方式修改系统分区。部分应用对这类改动较为敏感(例如检测到挂载痕迹的应用),此时可以为该应用开启"Umount modules"选项,让内核在该应用中卸载已挂载的模块。
KernelSU 管理器设置界面还提供全局的"Umount modules by default"选项,默认开启。这意味着在未做额外设置时,KernelSU 及部分模块会默认对应用卸载模块。若希望调整,可二选一:
- 保持"Umount modules by default"开启,对需要加载模块的应用,在其 App Profile 中单独关闭"Umount modules"(白名单式);
- 关闭"Umount modules by default",仅对需要卸载模块的应用,在其 App Profile 中单独开启"Umount modules"(黑名单式)。
内核中的裁决逻辑
该选项的裁决实现在 kernel/policy/allowlist.c 的ksu_uid_should_umount(uid)(第 293-326 行):
- 若 UID 是 KernelSU 管理器自身,绝不卸载(返回 false);
- 若该 UID 没有 App Profile,则返回默认的非 root 配置值
default_non_root_profile.umount_modules; - 若该 UID 已被授予 su(
allow_su为真),则不卸载(root 应用通常需要完整模块环境); - 其余情况按该 UID 的
nrp_config判断:use_default为真时沿用全局默认值,否则使用其自带的umount_modules字段。
注意,默认非 root 配置在init_default_profiles()中被初始化为umount_modules = true,正是"默认卸载模块"这一行为的来源。此外,内核把全部 App Profile 持久化在/data/adb/ksu/.allowlist(见KERNEL_SU_ALLOWLIST宏),带魔数与版本号序列化存储,开机时由ksu_load_allow_list()加载并做版本迁移(migrate_profile会为旧版本自动补全 flags 等字段)。
内核版本前提
- 运行内核 5.10 及以上的设备:内核无需额外处理即可完成模块卸载;
- 运行5.10 以下内核的设备:该选项仅是一个配置标记,KernelSU 本身不会执行任何卸载动作。若要在旧内核上使用"Umount modules",需要自行把
fs/namespace.c中的path_umount函数移植(backport)过来,具体步骤可参考 为非 GKI 设备集成 KernelSU 文档末尾的说明。
此外,部分模块(例如 Zygisksu)也会读取该选项来决定自身是否需要执行模块卸载,因此它对模块生态存在跨组件影响。
总结
App Profile 是 KernelSU"最小权限原则"的核心落地载体:通过 Root Profile 精细裁剪 su 进程的 UID/GID/组、capabilities 与 SELinux 域,配合NO_NEW_PRIVS阻断二次提权;通过非 root 配置控制模块卸载行为,兼顾 root 能力与系统完整性检测的平衡。理解这些配置项在内核中的真实执行路径(kernel/policy/app_profile.c、kernel/policy/allowlist.c)与用户态下发链路(userspace/ksud/src/profile.rs),能帮助你在配置时做出更安全、更精准的决策。
【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考