溺的文档
KernelSU · 第 3 篇 / 共 8 篇

Android 安全机制与 KernelSU 绕过

2026-06-30 · 阅读 1

本章从安全角度分析 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=100000FIRST_APPLICATION_UID=10000LAST_APPLICATION_UID=19999FIRST_ISOLATED_UID=99000WEBVIEW_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_ADMINCAP_NET_RAWCAP_SYS_MODULECAP_DAC_OVERRIDE 等,共约 40+ 种)。每个进程维护多个能力集合:

集合 作用
Effective 实际生效(权限检查时使用)
Permitted 可置入 Effective 的上限
Inheritable execve 时可继承
Bounding (bset) 进程及子进程的能力上限
Ambient execve 无条件继承(4.3+)

3.2 KernelSU 的设置

escape_with_root_profileprofile->capabilities.effective 同时拷贝给 cap_effectivecap_permittedcap_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.capply_kernelsu_rules)。dup 当前 policydb → 在副本上加规则 → RCU 热替换:把 ksu domain 设为 permissiveallow 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"...)。当前实现完全不同:由 setresuid hook 触发,遍历由 IOCTL 维护的 mount_list,在目标进程自己的 mount namespace 中卸载。

ksu_handle_umount(old_uid, new_uid)(由 setuid_hook 在每次 zygote→app/isolated 的 setresuid 后调用):

  1. 若模块未挂载(!ksu_module_mounted)或 feature 关闭 → 返回;
  2. 只处理 is_appuid(new_uid) || is_isolated_process(new_uid)
  3. ksu_uid_should_umount(new_uid) 判定该 uid 是否需要卸载;
  4. 检查旧进程 SELinux 上下文必须是 zygoteis_zygote(current_cred()))——否则忽略(防止"全局 ns 里的 su app setuid 到 untrusted_app"被误卸载,那会破坏系统);
  5. 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 运行在内核层能绕过上层大部分检查,但也带来风险:

  1. 内核稳定性:内核模块 bug 可导致系统崩溃(故 KernelSU 用 check_symbol 构建期校验符号、用 fixmap 避开 hypervisor);
  2. 权限滥用:一旦被恶意利用即获完全控制,故有 Manager V2 签名校验、per-app profile、no-new-privs 等约束;
  3. 检测对抗:应用仍可能从其它侧信道检测,KernelSU 与检测方持续博弈;
  4. 版本兼容:需适配众多内核版本的 API 差异(代码中大量 LINUX_VERSION_CODE 分支)。

10. 总结

KernelSU 在内核层逐一应对 Android 的安全机制:

  1. UID/GIDescape_with_root_profilecred
  2. Capabilities:effective 拷入三集合,默认 CAP_FULL_SET
  3. SELinux:切 u:r:ksu:s0 domain,dup/RCU 热替换 policydb 注入规则;
  4. Seccomp:提权时禁用,或选择性放行 reboot;
  5. Namespace:mount ns 策略 + setresuid 触发的模块卸载(前置 zygote 校验)。

这些操作都建立在 内核层实现 描述的 hook 机制、提权流程与 supercall 接口之上。


下一章节用户空间实现 将解析 ksud 与 Manager 如何与内核通信。

评论

  • 还没有评论,来说点什么吧。

无需注册或登录,填个昵称即可评论。