ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

KernelSU App Profile 深度解析:基于内核的 Android 应用精细化权限配置机制 KernelSU App Profile 深度解析基于内核的 Android 应用精细化权限配置机制【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSUApp Profile应用配置文件是 KernelSU 提供的一套用于按应用粒度定制系统行为的机制对已授予 root 权限的应用它可以定制su命令执行后的uid、gid、groups、capabilities能力位与SELinux域实现最小权限原则下的精细化 root 授权对普通应用它可以控制内核与模块系统对该应用的行为例如是否卸载模块挂载点。本文以 App Profile 官方指南 为主线结合仓库内核源码kernel/policy/app_profile.c、kernel/policy/allowlist.c 等逐层剖析其原理与实战配置帮助读者理解并安全使用该机制。App Profile 是什么App Profile 是 KernelSU 面向每一个应用的配置载体它分为两类Root Profileroot 配置文件作用于已被授予 root 权限即可以使用su的应用用于定制su命令的uid、gid、groups、capabilities与SELinux规则从而限制 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_configroot 配置与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 Profilecurr_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_profilekernel/include/uapi/app_profile.hstruct root_profile { __s32 uid; // su 后的用户 ID __s32 gid; // su 后的主组 ID __u32 groups_count; // 附属组数量KernelSU 最多支持 32 个 __s32 groups[KSU_MAX_GROUPS]; struct { // capabilitiesLinux 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 与 GroupsLinux 系统中有用户与组两个概念每个用户拥有用户 IDUID一个用户可以属于多个组每个组有组 IDGID。UID 为0的用户称为 root 用户GID 为0的组称为 root 组root 组通常拥有最高的系统权限。在 Android 中每个应用都是一个独立用户共享 UID 的场景除外拥有唯一 UID。常见约定0为 root1000为 system2000为 ADB shell10000–19999为普通应用。需要说明的是这里的 UID 与 Android 的多用户 / 工作资料概念并不相同——工作资料实际是通过切分 UID 区间实现的例如10000–19999表示主用户110000–119999表示工作资料其中的每个普通应用仍有自己唯一的 UID。每个应用可以有多个组GID 代表主组通常与 UID 相同其余为附属组。某些权限通过组来控制例如网络访问权限、蓝牙访问权限。在 ADB shell 中执行id输出大致如下oriole:/ $ id uid2000(shell) gid2000(shell) groups2000(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) contextu: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它依然可以联网移除su的inet组仅仅阻止su联网。从源码实现看Root Profile 是在内核中强制生效的不依赖 root 应用的自觉行为。在 kernel/policy/app_profile.c 的escape_with_root_profile()中内核会依据发起su的 UID 查询对应的 root profileksu_get_root_profile(cred-uid.val)然后将 UID/GID 写入进程凭据的uid/suid/euid/fsuid与gid/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_effective、cap_permitted与cap_bset见 kernel/policy/app_profile.c 第 194-196 行BUILD_BUG_ON断言保证了 profile 中的 capability 结构与内核kernel_cap_t大小一致。强烈建议Linux capability 官方手册man7.org 的 capabilities(7)对每个能力位代表的权限有详细说明。如果打算定制 capabilities务必先通读该文档再动手。SELinuxSELinux 是强大的强制访问控制MAC机制遵循默认拒绝原则任何未显式允许的动作都会被拒绝。它有两种全局运行模式Permissive宽容模式记录被拒绝的事件但不强制执行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_sid、keycreate_sid、sockcreate_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 权限第一次执行su应用 Profile 生效切换到 UID2000ADB shell而非0root第二次执行su由于当前 UID 是2000而配置中你恰好给 UID2000ADB 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。从版本迁移逻辑看内核加载旧版v3allowlist 时会将FLAG_KSU_NO_NEW_PRIVS自动补充到已授予 root 的配置中。Root Profile 的默认值与生效链路当应用没有自定义 Root Profile 时内核使用缓存的默认 profilekernel/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()的查找顺序为管理器managerUID 直接使用默认 profile → 启用了allow_shell的 shell UID 使用默认 profile → 哈希表中匹配curr_uid且allow_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 ModulesKernelSU 通过 overlayfs 挂载实现无系统修改systemless的分区修改机制。但部分应用对这类挂载行为敏感例如检测到系统分区被修改因此可以通过设置卸载模块Umount modules选项为这些应用卸载已挂载的模块。管理器设置界面中还有一个默认卸载模块Umount modules by default开关默认开启意味着在没有额外设置的情况下KernelSU 或部分模块会为该应用卸载模块。如果你不喜欢该设置或它影响了某些应用有两种策略保留默认卸载模块开启然后在 App Profile 中为需要加载模块的应用单独关闭卸载模块选项相当于白名单关闭默认卸载模块然后在 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:s0v3 为 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后的uid、gid、groups、capabilities与SELinux域在保留 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),仅供参考
返回列表