Android 安全机制与 KernelSU 绕过
本章从安全角度分析 Android 的多层防护机制,以及 KernelSU 如何通过内核层操作实现绕过。安全机制的原理部分是通用知识;KernelSU 的实现部分已对应当前重构后的代码(详见 内核层实现)。
1. Android 安全架构概览
Android 安全模型建立在 Linux 内核安全机制之上,并构建了多层防护:
┌─────────────────────────────────────────┐
│ 应用层:APK 签名验证、运行时权限 │
├─────────────────────────────────────────┤
│ 框架层:Permission、Binder IPC 检查 │
├─────────────────────────────────────────┤
│ 原生层:SELinux (MAC)、Seccomp 过滤 │
├─────────────────────────────────────────┤
│ 内核层:UID/GID (DAC)、Capabilities、 │
│ Namespace 隔离 │
└─────────────────────────────────────────┘
KernelSU 运行在内核层,位于上述大部分机制之下,因此能够直接操作内核数据结构来绕过它们——但仍受内核自身保护机制(KASLR/PAN/PXN、以及厂商 hypervisor 如 MTK MKP)的约束,这也是 内核层实现 中"用 fixmap 而非改 PTE"等设计的由来。
2. Linux UID/GID 权限模型
2.1 进程凭证(cred)
每个进程的身份由 struct cred 描述:
struct cred {
kuid_t uid, euid, suid, fsuid; // 真实/有效/保存/文件系统 UID
kgid_t gid, egid, sgid, fsgid; // 对应 GID
struct group_info *group_info; // 附加组
kernel_cap_t cap_effective, cap_permitted, cap_inheritable, cap_bset, cap_ambient;
void *security; // SELinux 等 LSM 私有数据
...
};
2.2 Android 的 UID 分配
Android 用 UID 实现应用沙箱:
| UID 范围 | 用途 |
|---|---|
| 0–999 | 系统保留(root、system=1000、shell=2000 等) |
| 10000–19999 | 第一用户的应用(app_id) |
| 99000–99999 | isolated 进程 |
u*100000 + app_id |
多用户/工作资料下的应用 |
KernelSU 中相关常量(policy/allowlist.h):PER_USER_RANGE=100000、FIRST_APPLICATION_UID=10000、LAST_APPLICATION_UID=19999、FIRST_ISOLATED_UID=99000、WEBVIEW_ZYGOTE_UID=1053。
2.3 GID 与硬件访问
Android 用 GID 控制硬件/资源访问,如 inet(3003) 网络、audio(1005)、sdcard_rw(1015) 等。KernelSU 的 root profile 可自定义附加组。
2.4 KernelSU 如何绕过
KernelSU 直接修改进程 cred。提权由 escape_with_root_profile()(kernel/policy/app_profile.c)完成,其中设置 UID/GID 四元组并配置附加组:
// setup_groups(policy/app_profile.c)——按 profile 设置附加组
// 特例:仅 root 组(groups_count==1 && groups[0]==0) 时直接用静态 root_groups
// 否则 groups_alloc + make_kgid + groups_sort + set_groups
完整提权流程见 内核层实现 §3.3。
3. Capabilities 机制
3.1 概念
Linux Capabilities 把 root 的"全能"细分为独立能力(如 CAP_SYS_ADMIN、CAP_NET_RAW、CAP_SYS_MODULE、CAP_DAC_OVERRIDE 等,共约 40+ 种)。每个进程维护多个能力集合:
| 集合 | 作用 |
|---|---|
| Effective | 实际生效(权限检查时使用) |
| Permitted | 可置入 Effective 的上限 |
| Inheritable | execve 时可继承 |
| Bounding (bset) | 进程及子进程的能力上限 |
| Ambient | execve 无条件继承(4.3+) |
3.2 KernelSU 的设置
escape_with_root_profile 把 profile->capabilities.effective 同时拷贝给 cap_effective、cap_permitted、cap_bset 三个集合:
// policy/app_profile.c(escape_with_root_profile 内)
BUILD_BUG_ON(sizeof(profile->capabilities.effective) != sizeof(kernel_cap_t));
// effective → cap_effective / cap_permitted / cap_bset 三者一致
默认 root profile 的 capabilities.effective = CAP_FULL_SET(全部能力,init_default_profiles)。App Profile 可为每个应用精确裁剪能力集(见 高级特性与安全)。
与早期实现不同:当前
escape_with_root_profile不再临时往 effective 里塞CAP_DAC_READ_SEARCH。sucompat 读取/data/adb/ksud改用override_creds(ksu_cred)(以 KSU 凭据访问),更干净。
4. SELinux 强制访问控制
4.1 基础概念
SELinux 是强制访问控制(MAC),在 DAC(UID/GID)之上增加安全层。安全上下文形如 u:r:untrusted_app:s0:c512,c768,其中 Type/Domain(第三段)最关键。Android 用它实现严格沙箱:untrusted_app 只能访问自己的数据/网络,不能碰其它应用数据或 /system。
KernelSU 自身的 domain 是 ksu(context u:r:ksu:s0)——注意不再是早期的 u:r:su:s0。
4.2 KernelSU 的 SELinux 操作
KernelSU 在 kernel/selinux/ 做三件事:
① domain 切换(selinux.c)。提权时把进程切到 u:r:ksu:s0:
// transive_to_domain(domain, cred, clear_exec_sid)
// 取 selinux_cred(cred),security_secctx_to_secid(domain) 得 sid,
// 写 tsec->sid,清 create_sid/keycreate_sid/sockcreate_sid
// setup_selinux(domain, cred) 在其上封装,被 escape_* 调用
为加速判定,cache_sid() 在 policy 加载后缓存 ksu/zygote/init/ksu_file 四个 context 的 SID;is_ksu_domain()、is_zygote(cred)、is_init(cred) 优先比缓存 SID,未缓存才回退字符串比较。
② 注入内置规则(rules.c 的 apply_kernelsu_rules)。dup 当前 policydb → 在副本上加规则 → RCU 热替换:把 ksu domain 设为 permissive 并 allow ksu *:*:*,创建无约束 ksu_file 类型,注入一系列 Magisk 风格规则(fd/binder/memfd/unix_socket 等)。同时保存 backup_sepolicy 供 selinux_hide 使用。
③ 动态 sepolicy 批处理(handle_sepolicy,经 IOCTL SET_SEPOLICY)。userspace 下发序列化的批量命令(allow/deny/xperm/type/typeattribute/permissive 等 9 类,上限 8 MiB),在 policydb 副本上增删后 RCU 切换。
4.3 域检测的用途
is_ksu_domain():验证进程是否已在 ksu domain。这也是 uid==0 提权校验的关键:__ksu_is_allow_uid_for_current在 uid==0 时返回is_ksu_domain(),防止任意 root 进程冒用 KernelSU 接口。is_zygote(cred):验证父进程是否 zygote,是模块卸载(umount)的前置安全检查(见 §6)。
5. Seccomp 系统调用过滤
5.1 原理
Seccomp(Secure Computing)用 BPF 程序限制进程可调用的 syscall。Android 应用在 zygote fork 后会装上 seccomp 过滤器,禁止 mount/umount2/reboot/ptrace/kexec_load 等敏感调用。过滤动作有 ALLOW/ERRNO/KILL 等。
5.2 KernelSU 的两种处理
① 完全禁用(提权时)。escape_with_root_profile 调用 disable_seccomp()(policy/app_profile.c):
// disable_seccomp(要点)
// 持 current->sighand->siglock:
// 5.11+/GENERIC_ENTRY: clear_syscall_work(SECCOMP),否则 clear_thread_flag(TIF_SECCOMP)
// current->seccomp.mode = 0; filter = NULL; filter_count = 0
// 解锁后用一个 fake task_struct 接管原 filter 并 seccomp_filter_release,正确释放引用
相比早期实现,当前版本用"fake task +
seccomp_filter_release"来正确释放原 filter 的引用计数(按内核版本设PF_EXITING/清sighand),避免内存泄漏。
② 选择性放行 reboot(装 fd 用)。授权进程/Manager 需要调用 reboot(magic 协议装 fd),但其 seccomp 可能拦截 reboot。setuid_hook 调用 ksu_seccomp_allow_cache(filter, __NR_reboot)(infra/seccomp_cache.c)直接置位 filter 的 allow 决策缓存,让 reboot 跳过 BPF 评估被放行:
// infra/seccomp_cache.c
// ksu_seccomp_allow_cache(filter, nr): set_bit(nr, filter->cache.allow_native/allow_compat)
6. 命名空间(Namespace)隔离与模块可见性
6.1 Mount Namespace
Android 为应用创建独立的 mount namespace。KernelSU 利用它实现模块的选择性可见性:模块通过 OverlayFS 叠加到系统分区,授权应用能看到,未授权应用则被卸载(看不到)。
6.2 su 进程的 namespace 策略(infra/su_mount_ns.c)
提权时按 root profile 的 namespaces 字段(setup_mount_ns)选择:
| 模式 | 值 | 行为 |
|---|---|---|
KSU_NS_INHERITED |
0 | 继承调用者 ns(默认) |
KSU_NS_GLOBAL |
1 | ksu_mnt_ns_global():setns 到 init(pid1) 的全局 ns(能看到模块挂载) |
KSU_NS_INDIVIDUAL |
2 | ksu_mnt_ns_individual():unshare(CLONE_NEWNS) + 把 root 设为 private |
6.3 模块卸载(feature/kernel_umount.c)
重要更正:早期实现用 workqueue 里的
do_umount_work切换 mnt_ns 后try_umount("/system"...)。当前实现完全不同:由setresuidhook 触发,遍历由 IOCTL 维护的mount_list,在目标进程自己的 mount namespace 中卸载。
ksu_handle_umount(old_uid, new_uid)(由 setuid_hook 在每次 zygote→app/isolated 的 setresuid 后调用):
- 若模块未挂载(
!ksu_module_mounted)或 feature 关闭 → 返回; - 只处理
is_appuid(new_uid) || is_isolated_process(new_uid); - 按
ksu_uid_should_umount(new_uid)判定该 uid 是否需要卸载; - 检查旧进程 SELinux 上下文必须是 zygote(
is_zygote(current_cred()))——否则忽略(防止"全局 ns 里的 su app setuid 到 untrusted_app"被误卸载,那会破坏系统); override_creds(ksu_cred)后遍历mount_list,对每个挂载点调try_umount(mnt, flags)(kern_path找到挂载根后path_umount)。
待卸载的挂载点列表 mount_list 由 IOCTL ADD_TRY_UMOUNT(WIPE/ADD/DEL)维护——即"卸载哪些路径"由 userspace(ksud/metamodule)告知内核,内核负责在合适时机执行。
7. 安全机制绕过对比
| 安全机制 | 目的 | KernelSU 方案 | 实现位置 |
|---|---|---|---|
| UID/GID | 进程隔离 | 直接改 cred 四元组 + 附加组 | escape_with_root_profile |
| Capabilities | 细粒度权限 | effective→permitted/bset,可 per-app 裁剪 | escape_with_root_profile |
| SELinux | MAC | 切 u:r:ksu:s0,注入 ksu permissive 规则 + 动态 sepolicy |
selinux/ |
| Seccomp | syscall 过滤 | 提权时完全禁用;或选择性放行 reboot | disable_seccomp / seccomp_cache |
| Namespace | 资源隔离 | mount ns 选择性隔离 + 模块卸载 | su_mount_ns / kernel_umount |
| APK 签名 | 应用认证 | 只认纯 V2,证书 SHA-256 校验 | manager/apk_sign.c |
8. KernelSU 引入的反检测能力
除绕过上述机制外,当前版本还内建若干"对抗 root 检测"的能力(详见 高级特性与安全):
- selinux_hide(feature):对应用进程伪造 SELinux 状态页与上下文/访问校验结果,让"检测 SELinux 是否被改"的应用看到"未被修改"的假象;
- 模块卸载:未授权应用看不到模块对系统分区的修改;
- ext4 sysfs 清理(IOCTL
NUKE_EXT4_SYSFS):移除模块 ext4 镜像在 sysfs 的痕迹; - 匿名通信:
[ksu_driver]匿名 inode + IOCTL,无/dev节点; - 模块自隐藏:LKM 从
/sys/module摘除自身。
9. 深度防御视角与风险
Android 采用深度防御:即使某层被绕过,其它层仍提供保护。KernelSU 运行在内核层能绕过上层大部分检查,但也带来风险:
- 内核稳定性:内核模块 bug 可导致系统崩溃(故 KernelSU 用
check_symbol构建期校验符号、用 fixmap 避开 hypervisor); - 权限滥用:一旦被恶意利用即获完全控制,故有 Manager V2 签名校验、per-app profile、no-new-privs 等约束;
- 检测对抗:应用仍可能从其它侧信道检测,KernelSU 与检测方持续博弈;
- 版本兼容:需适配众多内核版本的 API 差异(代码中大量
LINUX_VERSION_CODE分支)。
10. 总结
KernelSU 在内核层逐一应对 Android 的安全机制:
- UID/GID:
escape_with_root_profile改cred; - Capabilities:effective 拷入三集合,默认
CAP_FULL_SET; - SELinux:切
u:r:ksu:s0domain,dup/RCU 热替换 policydb 注入规则; - Seccomp:提权时禁用,或选择性放行 reboot;
- Namespace:mount ns 策略 + setresuid 触发的模块卸载(前置 zygote 校验)。
这些操作都建立在 内核层实现 描述的 hook 机制、提权流程与 supercall 接口之上。
下一章节:用户空间实现 将解析 ksud 与 Manager 如何与内核通信。
评论
- 还没有评论,来说点什么吧。