KernelSU App Profile 深度解析:基于内核的 Android 应用精细化权限配置机制
2026/9/13 23:04:30 网站建设 项目流程

KernelSU App Profile 深度解析:基于内核的 Android 应用精细化权限配置机制

【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU

App Profile(应用配置文件)是 KernelSU 提供的一套用于按应用粒度定制系统行为的机制:对已授予 root 权限的应用,它可以定制su命令执行后的uidgidgroupscapabilities(能力位)与SELinux域,实现最小权限原则下的精细化 root 授权;对普通应用,它可以控制内核与模块系统对该应用的行为(例如是否卸载模块挂载点)。本文以 App Profile 官方指南 为主线,结合仓库内核源码(kernel/policy/app_profile.c、kernel/policy/allowlist.c 等)逐层剖析其原理与实战配置,帮助读者理解并安全使用该机制。

App Profile 是什么

App Profile 是 KernelSU 面向"每一个应用"的配置载体,它分为两类:

  • Root Profile(root 配置文件):作用于已被授予 root 权限(即可以使用su)的应用,用于定制su命令的uidgidgroupscapabilitiesSELinux规则,从而限制 root 进程的实际权限。例如:只给防火墙应用网络权限而拒绝文件访问权限;给冻结类应用 shell 权限而不是完整 root 权限。其核心思想是最小权限原则(principle of least privilege)——把权力控制在必要范围内。
  • Non-root Profile(非 root 配置文件):作用于未授予 root 权限的普通应用,控制内核与模块系统对待这些应用的方式。例如决定是否对该应用生效模块造成的系统分区修改(即"隐藏"类操作)。

在数据结构层面,两种配置统一封装在struct app_profile中(见 kernel/include/uapi/app_profile.h),其中以allow_su布尔值区分 root / 非 root 应用,再通过联合体rp_config(root 配置)与nrp_config(非 root 配置)承载各自的细分字段:

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 { // Root Profile bool use_default; // 是否使用默认 root profile char template_name[KSU_MAX_PACKAGE_NAME]; struct root_profile profile; } rp_config; struct { // Non-root Profile bool use_default; struct non_root_profile profile; } nrp_config; }; };

内核用一张哈希表(allow_list)维护所有 App Profile,curr_uid作为哈希键;配置既可以通过超级调用动态写入,也会在变更后持久化到/data/adb/ksu/.allowlist文件(含文件魔数FILE_MAGIC与格式版本FILE_FORMAT_VERSION),下次启动时由ksu_load_allow_list()重新加载(见 kernel/policy/allowlist.c)。

Root Profile 详解

Root Profile 可以定制su之后的进程凭据(credential),包括 UID、GID、附属组、capabilities 与 SELinux 域。这些字段在内核侧对应struct root_profile(kernel/include/uapi/app_profile.h):

struct root_profile { __s32 uid; // su 后的用户 ID __s32 gid; // su 后的主组 ID __u32 groups_count; // 附属组数量(KernelSU 最多支持 32 个) __s32 groups[KSU_MAX_GROUPS]; struct { // capabilities(Linux 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 };

UID、GID 与 Groups

Linux 系统中有用户与组两个概念:每个用户拥有用户 ID(UID),一个用户可以属于多个组,每个组有组 ID(GID)。UID 为0的用户称为 root 用户,GID 为0的组称为 root 组,root 组通常拥有最高的系统权限。

在 Android 中,每个应用都是一个独立用户(共享 UID 的场景除外),拥有唯一 UID。常见约定:0为 root,1000为 system,2000为 ADB shell,1000019999为普通应用。需要说明的是,这里的 UID 与 Android 的"多用户 / 工作资料"概念并不相同——工作资料实际是通过切分 UID 区间实现的,例如1000019999表示主用户,110000119999表示工作资料,其中的每个普通应用仍有自己唯一的 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 卡读写权限)等多个附属组。

KernelSU 的 Root Profile 允许定制su之后 root 进程的 UID、GID 与组。例如将某 root 应用的 Root Profile 的 UID 设为2000,则其执行su后的实际权限仅相当于 ADB shell 级别;再移除inet组,su命令将无法访问网络。

注意:App Profile 只控制su之后 root 进程的权限,并不控制应用自身的权限。如果应用自身已申请网络权限,即使不使用su它依然可以联网;移除suinet组仅仅阻止su联网。

从源码实现看,Root Profile 是在内核中强制生效的,不依赖 root 应用的自觉行为。在 kernel/policy/app_profile.c 的escape_with_root_profile()中,内核会依据发起su的 UID 查询对应的 root profile(ksu_get_root_profile(cred->uid.val)),然后将 UID/GID 写入进程凭据的uid/suid/euid/fsuidgid/sgid/egid/fsgid,并通过setup_groups()构建新的组信息。setup_groups()groups_count做了上限校验(超过KSU_MAX_GROUPS即 32 将拒绝),并对特殊情形做了优化:当组列表恰好为单个0时,直接复用预分配的root_groups

Capabilities(能力位)

Capabilities 是 Linux 的权限分离机制。传统 UNIX 将进程分为特权进程(有效 UID 为 0,即超级用户/root,绕过所有内核权限检查)与非特权进程(有效 UID 非零,接受完整的基于凭据的权限检查)。从 Linux 2.2 起,Linux 将原本与超级用户绑定的特权拆分为可独立开关的"能力单元"(capability)。

每个 capability 代表一项或多项特权。例如CAP_DAC_READ_SEARCH代表绕过文件读权限检查、目录读与执行权限检查的能力——如果一个有效 UID 为0的 root 用户没有该能力(或更高层能力),那么即使他是 root,也无法随心所欲地读取文件。

KernelSU 的 Root Profile 可以定制su之后 root 进程的 capabilities,从而实现"部分 root 权限"的授予。与 UID/GID 不同,部分 root 应用在使用su后必须保持 UID 为0,此时对 UID0的 root 用户裁剪 capabilities,就能限制其被允许执行的操作。

在内核实现中,escape_with_root_profile()会把 profile 中的capabilities.effective同时写入cap_effectivecap_permittedcap_bset(见 kernel/policy/app_profile.c 第 194-196 行);BUILD_BUG_ON断言保证了 profile 中的 capability 结构与内核kernel_cap_t大小一致。

强烈建议:Linux capability 官方手册(man7.org 的 capabilities(7))对每个能力位代表的权限有详细说明。如果打算定制 capabilities,务必先通读该文档再动手。

SELinux

SELinux 是强大的强制访问控制(MAC)机制,遵循默认拒绝原则:任何未显式允许的动作都会被拒绝。它有两种全局运行模式:

  1. Permissive(宽容)模式:记录被拒绝的事件但不强制执行;
  2. Enforcing(强制)模式:记录并强制执行拒绝。

警告:现代 Android 系统高度依赖 SELinux 保障整体安全。强烈不建议使用运行在"宽容模式"的自定义系统,这几乎与完全开放的系统没有实质区别。

SELinux 完整概念超出本文范围,可先参考 Wikipedia、Red Hat《What is SELinux?》与 ArchWiki 的 SELinux 条目建立基础认识。

KernelSU 的 Root Profile 可以定制su之后 root 进程的 SELinux 上下文(域),并为该上下文设定专门的控制规则,实现细粒度的 root 权限控制。典型场景:应用执行su时,进程通常会切换到无限制访问的域,如u:r:ksu:s0;通过 Root Profile 可以把该域切换为自定义域,例如u:r:app1:s0,并为该域定义一系列规则:

type app1 enforce app1 typeattribute app1 mlstrustedsubject allow app1 * * *

注意:allow app1 * * *仅用于演示,实际中不应广泛使用,因为它与宽容模式几乎没有区别。

从内核实现看,kernel/selinux/selinux.c 的setup_selinux()通过transive_to_domain()将目标域字符串转换为 SID 并写入进程凭据的tsec->sid(同时清空create_sidkeycreate_sidsockcreate_sid等派生 SID)。escape_with_root_profile()会在设置完 UID/GID 与 capabilities 之后调用setup_selinux(profile->selinux_domain, cred),确保切换后的进程进入自定义域。另外,默认 root profile 的 SELinux 域为u:r:ksu:s0KSU_DEFAULT_SELINUX_DOMAIN,见 kernel/policy/allowlist.c),内核在加载旧版本 allowlist 时还会把u:r:su:s0自动迁移为u:r:ksu:s0

权限提升(Escalation)风险

如果 Root Profile 配置不当,可能产生权限提升(escalation)场景:Root Profile 施加的限制可能被无意绕过。

例如:你给 ADB shell 用户授予了 root 权限(常见做法),随后又给某个普通应用授予 root 权限,但将其 Root Profile 的 UID 配置为2000(即 ADB shell 的 UID)。此时该应用只需执行两次su即可获得完整 root 权限:

  1. 第一次执行su,应用 Profile 生效,切换到 UID2000(ADB shell)而非0(root);
  2. 第二次执行su,由于当前 UID 是2000,而配置中你恰好给 UID2000(ADB shell)授予了 root 访问权,应用便获得了完整 root 权限。

建议:可以在自定义 App Profile 中启用NO_NEW_PRIVS标志。这能防止进程通过su逃逸并再次提权。但注意,该标志阻止 KernelSU 为该进程提权,进程仍可能借助其他 Linux 机制逃逸。因此请务必谨慎配置权限。

NO_NEW_PRIVS在内核中对应FLAG_KSU_NO_NEW_PRIVS1ULL << 0,见 kernel/include/uapi/app_profile.h)。escape_with_root_profile()在成功提交凭据后检查该标志:若设置,则调用set_thread_flag(TIF_KSU_DISABLE_ESCAPE_WITH_ROOT)打上线程标记;此后该进程再次发起su时,escape_with_root_profile()会因检测到TIF_KSU_DISABLE_ESCAPE_WITH_ROOT而直接中止(见 kernel/policy/app_profile.c 第 145-148 行与 kernel/policy/app_profile.h 中的宏定义TIF_KSU_DISABLE_ESCAPE_WITH_ROOT 63)。从版本迁移逻辑看,内核加载旧版(v3)allowlist 时会将FLAG_KSU_NO_NEW_PRIVS自动补充到已授予 root 的配置中。

Root Profile 的默认值与生效链路

当应用没有自定义 Root Profile 时,内核使用缓存的默认 profile(kernel/policy/allowlist.c 的init_default_profiles()):

default_root_profile.uid = 0; default_root_profile.gid = 0; default_root_profile.groups_count = 1; default_root_profile.groups[0] = 0; // root 组 default_root_profile.capabilities.effective = CAP_FULL_SET; // 全量能力 default_root_profile.namespaces = KSU_NS_INHERITED; default_root_profile.selinux_domain = "u:r:ksu:s0";

ksu_get_root_profile()的查找顺序为:管理器(manager)UID 直接使用默认 profile → 启用了allow_shell的 shell UID 使用默认 profile → 哈希表中匹配curr_uidallow_su为 true 的条目;若命中use_default或未命中,则回落到默认 profile。

整个提权过程由su兼容层的 execve 钩子触发:在 kernel/feature/sucompat.c 中,拦截到su调用后会先重写系统调用参数(通过execveat携带空路径的方式执行 ksud 守护进程),随后调用escape_with_root_profile()应用 Root Profile,再继续执行execveat;只有 profile 成功应用后,才会安装 su 会话文件描述符(ksu_install_su_fd()),把 scoped driver capability 授予选定的 root 进程。可见从 UID/GID、组、capabilities 到 SELinux 域、命名空间,均在内核态一次性完成切换,不依赖用户态应用配合。

非 Root Profile:模块卸载(Umount Modules)

KernelSU 通过 overlayfs 挂载实现无系统修改(systemless)的分区修改机制。但部分应用对这类挂载行为敏感(例如检测到系统分区被修改),因此可以通过设置"卸载模块"(Umount modules)选项,为这些应用卸载已挂载的模块。

管理器设置界面中还有一个"默认卸载模块"(Umount modules by default)开关,默认开启,意味着在没有额外设置的情况下,KernelSU 或部分模块会为该应用卸载模块。如果你不喜欢该设置或它影响了某些应用,有两种策略:

  1. 保留"默认卸载模块"开启,然后在 App Profile 中为需要加载模块的应用单独关闭"卸载模块"选项(相当于"白名单");
  2. 关闭"默认卸载模块",然后在 App Profile 中为需要卸载模块的应用单独开启"卸载模块"选项(相当于"黑名单")。

提示:在 5.10 及以上版本内核的设备上,模块卸载由内核自身执行;而在低于 5.10 的内核上,该开关只是一项配置选项,KernelSU 本身不会采取任何行动(若要在 5.10 之前的内核上使用,需要回移植path_umount函数,参见非 GKI 设备集成文档末尾的说明)。部分模块(如 Zygisksu)也会使用该开关来决定是否需要卸载模块。

在源码中,"默认卸载模块"的默认值在init_default_profiles()中被设为default_non_root_profile.umount_modules = true,与文档描述一致。内核侧的判定函数是ksu_uid_should_umount()(kernel/policy/allowlist.c),其决策逻辑为:

  • 管理器应用自身永不卸载(防止卸载掉管理器的挂载点);
  • 未找到 App Profile 的应用:回落到默认非 root profile 的umount_modules
  • 命中且allow_su的应用:不卸载
  • 命中且为非 root 应用:若use_default则跟随全局默认值,否则使用该应用 profile 中显式配置的umount_modules

实际的卸载动作由 kernel/feature/kernel_umount.c 的ksu_handle_umount()执行:它首先检查是否有模块挂载(ksu_module_mounted)以及内核级卸载功能是否启用(ksu_kernel_umount_enabled,对应KSU_FEATURE_KERNEL_UMOUNT特性,可动态开关),随后通过 UID 判断目标是否为普通应用 / webview zygote / 隔离进程,再结合ksu_uid_should_umount()的结果决定是否遍历挂载列表(mount_list)逐个调用path_umount()卸载umountable挂载点。同时它要求发起卸载的进程必须是 zygote 子进程(检查旧进程的 SELinux 上下文),以避免误伤通过setuid切换身份、但仍位于全局挂载命名空间中的 su 应用。

配置持久化与版本迁移

App Profile 由管理器(KernelSU Manager)通过超级调用下发到内核,内核将其存入allow_list哈希表,并异步持久化到/data/adb/ksu/.allowlist

  • 文件头包含 4 字节魔数0x7f4b5355(即' KSU')与 4 字节格式版本FILE_FORMAT_VERSION
  • 每条记录为序列化的struct app_profile
  • 写入在 init 进程的 task_work 中执行(ksu_persistent_allow_list()),并借助ksu_cred提升权限后再filp_open创建文件。

启动加载时,ksu_load_allow_list()校验魔数与版本,逐条调用ksu_set_app_profile()恢复配置,并依据版本号执行迁移(migrate_profile()):v2 将u:r:su:s0迁移为u:r:ksu:s0,v3 为 root 配置补上FLAG_KSU_NO_NEW_PRIVS,最终统一升到当前版本KSU_APP_PROFILE_VER = 4。加载完成后若发现版本过旧,还会触发一次重新持久化以升级存储格式。此外,ksu_prune_allowlist()会在系统启动完成后根据包名/UID 的有效性清理已卸载应用的残留配置(管理器 UID9999与 webview zygote UID 会被保留)。

小结与安全实践

App Profile 是 KernelSU 在内核态实现"按应用精细化授权"的核心机制:

  • Root Profile通过定制su后的uidgidgroupscapabilitiesSELinux域,在保留 root 能力的同时落实最小权限原则——限制网络、文件访问,或降级为 shell 权限;
  • 非 Root Profile通过"卸载模块"选项控制普通应用对模块挂载的可见性,配合"默认卸载模块"开关可实现白名单或黑名单两种策略;
  • 所有配置由内核强制生效,持久化于/data/adb/ksu/.allowlist,并支持版本迁移与失效条目清理。

实践建议:定制 capabilities 前先研读 capabilities(7) 手册;自定义 SELinux 域时避免使用allow app1 * * *这类等价于宽容模式的宽泛规则;对"给了 shell 提权、又给普通应用降权"的交叉场景务必启用FLAG_KSU_NO_NEW_PRIVSNO_NEW_PRIVS),防止通过二次su形成权限提升。本文涉及的实现证据可进一步查阅 kernel/policy/app_profile.c、kernel/policy/allowlist.c、kernel/include/uapi/app_profile.h、kernel/feature/sucompat.c、kernel/feature/kernel_umount.c 与 kernel/selinux/selinux.c。

【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询