KernelSu Root 方案分析
- Android KernelSU Root 内核 逆向工程 安全研究
从零开始理解 KernelSU 的内核级 Root 机制 本文档面向希望深入理解 Android Root 技术的开发者和安全研究人员
目录
1. 基础概念
1.1 什么是内核级 Root?
传统 Root 方案(如 Magisk):
- 在用户空间运行特权守护进程
- 通过修改
su二进制文件提供 Root 权限 - 依赖文件系统挂载和进程注入
KernelSU 内核级 Root:
- 直接在 Linux 内核中实现 Root 权限管理
- 通过内核模块拦截系统调用
- 在内核态直接修改进程凭证(credentials)
- 更底层、更隐蔽、更难检测
1.2 为什么内核级更强大?
用户空间应用
↓
系统调用 (syscall)
↓
═══════════════════════ ← KernelSU 在这里工作!
↓
Linux 内核
↓
硬件
核心优势:
- 权限提升发生在内核态:直接操作进程的
struct cred,无需通过su二进制 - 系统调用拦截:Hook 关键 syscall(execve、stat、faccessat)实现透明重定向
- SELinux 深度集成:可以直接修改 SELinux 策略和进程 context
- 隐蔽性极强:不依赖可见的文件或进程,难以从用户空间检测
1.3 Android 安全模型回顾
KernelSU 需要绕过/控制以下安全机制:
| 机制 | 说明 | KernelSU 如何处理 |
|---|---|---|
| UID/GID | 每个应用一个唯一 UID | 修改 cred->uid/gid 为 0(root) |
| Capabilities | 细粒度权限控制 | 设置 CAP_FULL_SET 到 cred->cap_* |
| SELinux | 强制访问控制 | 动态切换进程到 u:r:su:s0 domain |
| Mount Namespace | 文件系统隔离 | 可选择继承或隔离挂载点 |
| Seccomp | 系统调用过滤 | 禁用 seccomp 过滤器 |
2. 架构总览
2.1 三层架构
┌─────────────────────────────────────────────────────┐
│ Manager App (Kotlin/Compose) │
│ - UI 界面 │
│ - 应用授权管理 │
│ - 模块管理 │
└──────────────────┬──────────────────────────────────┘
│ JNI (ksu.cc)
↓
┌─────────────────────────────────────────────────────┐
│ ksud Daemon (Rust) │
│ - CLI 命令分发 │
│ - 模块生命周期管理 │
│ - init 事件处理 │
└──────────────────┬──────────────────────────────────┘
│ IOCTL (supercalls)
↓
┌─────────────────────────────────────────────────────┐
│ Kernel Module (C) │
│ - 系统调用拦截 │
│ - 权限提升核心逻辑 │
│ - 白名单/配置管理 │
│ - SELinux 策略操作 │
└─────────────────────────────────────────────────────┘
2.2 关键通信路径
1. FD 安装(获取内核驱动句柄):
ksud 启动
→ 扫描 /proc/self/fd 查找 [ksu_driver]
→ 如未找到,调用 syscall(SYS_reboot, 0xDEADBEEF, 0xCAFEBABE, 0, &fd)
→ 内核 kprobe 拦截 reboot,检测魔数
→ 通过 task_work 异步返回匿名 inode fd
→ ksud 缓存 fd 用于后续 IOCTL 调用
文件位置:
userspace/ksud/src/ksucalls.rs:116-152- FD 扫描和安装kernel/supercalls.c:738-764- reboot kprobe handler
2. Root 权限授予:
应用执行 "su"
→ 内核 execve hook 拦截
→ 检查 allowlist (ksu_is_allow_uid)
→ 重定向到 /data/adb/ksud
→ 调用 escape_with_root_profile()
→ 修改进程凭证 (UID=0, CAP_FULL_SET, SELinux domain)
→ ksud 以 root 身份运行
→ fork shell 进程
3. 内核层实现
3.1 模块初始化
入口函数: kernel/ksu.c:61 - kernelsu_init()
1int __init kernelsu_init(void)
2{
3 // 1. 检测是否为延迟加载(late load)
4 ksu_late_loaded = (current->pid != 1);
5
6 // 2. 准备 root 凭证结构
7 ksu_cred = prepare_creds();
8
9 // 3. 初始化各子系统
10 ksu_feature_init(); // 功能开关系统
11 ksu_supercalls_init(); // IOCTL 接口
12 ksu_syscall_hook_manager_init(); // 系统调用 hook
13 ksu_allowlist_init(); // 白名单
14 ksu_throne_tracker_init(); // Manager 验证
15
16 if (ksu_late_loaded) {
17 // 延迟加载模式:立即应用 SELinux 规则
18 apply_kernelsu_rules();
19 escape_to_root_for_init(); // 授予当前进程(ksud)root
20 ksu_load_allow_list(); // 加载持久化白名单
21 } else {
22 // 正常启动:注册 kprobe hook
23 ksu_ksud_init();
24 }
25
26 return 0;
27}
关键点:
- Late Load 模式:模块在系统启动后动态加载,
current->pid != 1 - Early Load 模式:模块编译进内核或在 init 阶段加载
3.2 Supercall 接口(内核 IOCTL)
核心文件: kernel/supercalls.c
3.2.1 匿名 inode 创建
1int ksu_install_fd(void)
2{
3 // 1. 获取未使用的 fd
4 fd = get_unused_fd_flags(O_CLOEXEC);
5
6 // 2. 创建匿名 inode,名称为 "[ksu_driver]"
7 filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL,
8 O_RDWR | O_CLOEXEC);
9
10 // 3. 安装 fd 到进程
11 fd_install(fd, filp);
12
13 return fd;
14}
为什么使用匿名 inode?
- 不在文件系统中留下痕迹
- 无需设备节点(
/dev/ksu) - 难以从用户空间枚举
3.2.2 IOCTL 命令表
kernel/supercalls.c:629-711 定义了 20 个 IOCTL 命令:
| 命令 | 权限检查 | 功能 |
|---|---|---|
KSU_IOCTL_GRANT_ROOT | allowed_for_su | 核心:授予 root 权限 |
KSU_IOCTL_GET_INFO | always_allow | 获取版本和标志位 |
KSU_IOCTL_REPORT_EVENT | only_root | 报告 init 事件(post-fs-data/boot-completed) |
KSU_IOCTL_SET_SEPOLICY | only_root | 动态修改 SELinux 策略 |
KSU_IOCTL_GET_ALLOW_LIST | manager_or_root | 读取白名单 |
KSU_IOCTL_GET_APP_PROFILE | only_manager | 获取应用配置 |
KSU_IOCTL_SET_APP_PROFILE | only_manager | 设置应用配置(root/non-root) |
KSU_IOCTL_GET_FEATURE | manager_or_root | 读取功能开关状态 |
KSU_IOCTL_SET_FEATURE | manager_or_root | 设置功能开关 |
KSU_IOCTL_MANAGE_MARK | manager_or_root | 管理进程标记(tracepoint flag) |
KSU_IOCTL_ADD_TRY_UMOUNT | manager_or_root | 管理卸载列表 |
权限检查函数:
1bool allowed_for_su(void)
2{
3 // Manager 或白名单内的 UID
4 return is_manager() || ksu_is_allow_uid_for_current(current_uid().val);
5}
6
7bool manager_or_root(void)
8{
9 return current_uid().val == 0 || is_manager();
10}
3.3 权限提升机制(核心)
函数: kernel/app_profile.c:106 - escape_with_root_profile()
这是 KernelSU 最核心的函数,实现了真正的权限提升:
1void escape_with_root_profile(void)
2{
3 struct cred *cred;
4 struct root_profile profile;
5
6 // 1. 准备新的凭证结构(COW - Copy on Write)
7 cred = prepare_creds();
8
9 // 2. 获取该 UID 的 root profile 配置
10 ksu_get_root_profile(cred->uid.val, &profile);
11
12 // 3. 修改 UID/GID 为 profile 指定值(通常是 0)
13 cred->uid.val = profile.uid;
14 cred->suid.val = profile.uid;
15 cred->euid.val = profile.uid;
16 cred->fsuid.val = profile.uid;
17
18 cred->gid.val = profile.gid;
19 cred->fsgid.val = profile.gid;
20 cred->sgid.val = profile.gid;
21 cred->egid.val = profile.gid;
22
23 // 4. 更新 user_struct(进程计数)
24 new_user = alloc_uid(cred->uid);
25 free_uid(cred->user);
26 cred->user = new_user;
27
28 // 5. 设置 capabilities(全权限)
29 u64 cap_for_ksud = profile.capabilities.effective | CAP_DAC_READ_SEARCH;
30 memcpy(&cred->cap_effective, &cap_for_ksud, sizeof(cred->cap_effective));
31 memcpy(&cred->cap_permitted, &profile.capabilities.effective,
32 sizeof(cred->cap_permitted));
33
34 // 6. 设置附加组
35 setup_groups(&profile, cred);
36
37 // 7. 切换 SELinux domain 到 "u:r:su:s0"
38 setup_selinux(profile.selinux_domain, cred);
39
40 // 8. 提交凭证(原子操作)
41 commit_creds(cred);
42
43 // 9. 禁用 seccomp 过滤器
44 disable_seccomp();
45
46 // 10. 标记所有线程启用 syscall tracepoint
47 for_each_thread (p, t) {
48 ksu_set_task_tracepoint_flag(t);
49 }
50
51 // 11. 设置 mount namespace
52 setup_mount_ns(profile.namespaces);
53}
关键数据结构:
1struct root_profile {
2 uid_t uid; // 目标 UID(通常是 0)
3 gid_t gid; // 目标 GID
4 u32 groups_count; // 附加组数量
5 gid_t groups[KSU_MAX_GROUPS]; // 附加组列表
6 struct kernel_cap_struct capabilities; // Capability 集合
7 char selinux_domain[64]; // SELinux context
8 u32 namespaces; // 命名空间选项
9};
3.4 系统调用拦截
文件: kernel/syscall_hook_manager.c
3.4.1 进程标记系统
KernelSU 使用 TIF_SYSCALL_TRACEPOINT 标志位来标记需要监控的进程:
1void ksu_set_task_tracepoint_flag(struct task_struct *t)
2{
3#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
4 set_task_syscall_work(t, SYSCALL_TRACEPOINT);
5#else
6 set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
7#endif
8}
哪些进程会被标记?
- UID=0 且 SELinux domain 为
u:r:su:s0的进程(KernelSU root 进程) - Zygote 进程(孵化所有应用)
- Shell 进程(UID=2000)
- 白名单内的 UID
- Init 进程(启动阶段)
3.4.2 Hooked 系统调用
1. execve - 执行拦截(SU 兼容层)
kernel/sucompat.c:118 - ksu_handle_execve_sucompat()
1int ksu_handle_execve_sucompat(const char __user **filename_user, ...)
2{
3 char path[32];
4
5 // 1. 检查是否为白名单 UID
6 if (!ksu_is_allow_uid_for_current(current_uid().val))
7 return 0;
8
9 // 2. 读取要执行的文件路径
10 strncpy_from_user_nofault(path, *filename_user, sizeof(path));
11
12 // 3. 检测是否为 "/system/bin/su"
13 if (memcmp(path, "/system/bin/su", 15) == 0) {
14 pr_info("sys_execve su found\n");
15
16 // 4. 重定向到 ksud
17 *filename_user = ksud_user_path(); // "/data/adb/ksud"
18
19 // 5. 立即提权!
20 escape_with_root_profile();
21 }
22
23 return 0;
24}
流程图:
应用调用 execve("/system/bin/su", ...)
↓
内核 tracepoint 触发
↓
ksu_handle_execve_sucompat()
↓
检查白名单?
↙ ↘
否 是
↓ ↓
正常执行 重定向到 /data/adb/ksud
↓
escape_with_root_profile()
↓
修改进程凭证为 root
↓
ksud 以 root 身份运行
2. newfstatat / faccessat - 文件访问伪装
kernel/sucompat.c:72-116
1int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
2{
3 if (!ksu_is_allow_uid_for_current(current_uid().val))
4 return 0;
5
6 char path[32];
7 strncpy_from_user_nofault(path, *filename_user, sizeof(path));
8
9 // 将 stat("/system/bin/su") 重定向到 stat("/system/bin/sh")
10 if (memcmp(path, "/system/bin/su", 15) == 0) {
11 *filename_user = sh_user_path(); // "/system/bin/sh"
12 }
13
14 return 0;
15}
为什么需要这个?
- 许多应用通过
access()或stat()检测/system/bin/su是否存在来判断 root - 重定向到
/system/bin/sh让检测通过,但不暴露真实的 su 位置
3. setresuid - UID 切换拦截
kernel/setuid_hook.c - 防止 root 进程降权后再次提权
3.5 白名单机制
文件: kernel/allowlist.c
3.5.1 存储结构
KernelSU 使用 双层存储 优化查询性能:
1// 1. Bitmap(快速查询,UID ≤ 65535)
2static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly;
3#define BITMAP_UID_MAX ((sizeof(allow_list_bitmap) * 8) - 1) // 32767
4
5// 2. 数组(大 UID 备用,> 65535)
6static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly;
7
8// 3. 链表(完整配置信息)
9struct perm_data {
10 struct list_head list;
11 struct rcu_head rcu;
12 struct app_profile profile; // 包含 root_profile 或 non_root_profile
13};
14static struct list_head allow_list;
3.5.2 查询算法
kernel/allowlist.c:272 - __ksu_is_allow_uid()
1bool __ksu_is_allow_uid(uid_t uid)
2{
3 // 1. 拒绝系统 UID(< 2000 且 != 1000)
4 if (forbid_system_uid(uid))
5 return false;
6
7 // 2. Manager 总是允许
8 if (ksu_get_manager_appid() == uid % PER_USER_RANGE)
9 return true;
10
11 // 3. Bitmap 快速查询(O(1))
12 if (likely(uid <= BITMAP_UID_MAX)) {
13 return !!(allow_list_bitmap[uid / 8] & (1 << (uid % 8)));
14 }
15
16 // 4. 数组线性查询(大 UID)
17 for (i = 0; i < allow_list_pointer; i++) {
18 if (allow_list_arr[i] == uid)
19 return true;
20 }
21
22 return false;
23}
性能分析:
- 99% 的 Android UID 在 10000-65535 范围内
- Bitmap 查询只需 1 次内存访问和位运算
- 多用户环境(UID > 100000)使用数组,性能略降但可接受
3.5.3 持久化
1#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
2
3// 文件格式:
4// [u32: MAGIC=0x7f4b5355] [u32: VERSION=3] [app_profile] [app_profile] ...
3.6 SELinux 集成
文件: kernel/selinux/sepolicy.c
3.6.1 自定义 Domain
KernelSU 创建 u:r:su:s0 domain:
1#define KERNEL_SU_DOMAIN "su"
2
3void setup_selinux(const char *domain, struct cred *cred)
4{
5 u32 sid;
6
7 // 1. 查询 domain 对应的 SID(Security ID)
8 int err = ksu_security_secctx_to_secid(domain, strlen(domain), &sid);
9
10 // 2. 设置进程的 SELinux context
11 struct task_security_struct *tsec = cred->security;
12 tsec->sid = sid;
13 tsec->osid = sid;
14 tsec->exec_sid = 0;
15 tsec->create_sid = 0;
16 tsec->keycreate_sid = 0;
17 tsec->sockcreate_sid = 0;
18}
3.6.2 策略规则
apply_kernelsu_rules() 添加以下规则:
type su;
domain_auto_trans(su, exec_type, domain) # su 可以切换到任意 domain
allow su * * * # su 拥有所有权限
保持 Enforcing 模式:
- 传统 root 通常设置
setenforce 0(Permissive) - KernelSU 保持 Enforcing,只为特定进程授权
- 提高隐蔽性和安全性
4. 用户空间实现
4.1 ksud 守护进程
入口: userspace/ksud/src/main.rs → cli.rs
4.1.1 命令分发
1pub fn run() -> Result<()> {
2 let args: Vec<String> = env::args().collect();
3 let program = &args[0];
4
5 match program.as_str() {
6 // Root shell 模式
7 "su" | "/system/bin/su" => {
8 grant_root(false)?; // 调用 ksucalls::grant_root()
9 },
10
11 // ksud 守护模式
12 _ => {
13 let cmd = args.get(1).map(|s| s.as_str());
14 match cmd {
15 Some("post-fs-data") => init_event::on_post_fs_data(),
16 Some("services") => init_event::on_services(),
17 Some("boot-completed") => init_event::on_boot_completed(),
18 Some("install") => module::install_module(&args[2]),
19 Some("module") => module::handle_module_action(&args[2..]),
20 _ => print_usage(),
21 }
22 }
23 }
24}
4.1.2 Supercall 封装
文件: userspace/ksud/src/ksucalls.rs
1// 全局 FD 缓存
2static DRIVER_FD: OnceLock<RawFd> = OnceLock::new();
3
4// 初始化 FD
5fn init_driver_fd() -> Option<RawFd> {
6 // 1. 扫描 /proc/self/fd 查找 [ksu_driver]
7 let fd = scan_driver_fd();
8 if fd.is_none() {
9 // 2. 使用魔数调用 reboot syscall
10 let mut fd = -1;
11 unsafe {
12 libc::syscall(
13 libc::SYS_reboot,
14 0xDEADBEEF, // KSU_INSTALL_MAGIC1
15 0xCAFEBABE, // KSU_INSTALL_MAGIC2
16 0,
17 &mut fd, // 内核通过这个指针返回 fd
18 );
19 };
20 if fd >= 0 { Some(fd) } else { None }
21 } else {
22 fd
23 }
24}
25
26// IOCTL 封装
27fn ksuctl<T>(request: i32, arg: *mut T) -> std::io::Result<i32> {
28 let fd = *DRIVER_FD.get_or_init(|| init_driver_fd().unwrap_or(-1));
29 unsafe {
30 let ret = libc::ioctl(fd, request, arg);
31 if ret < 0 {
32 Err(io::Error::last_os_error())
33 } else {
34 Ok(ret)
35 }
36 }
37}
38
39// 授予 root
40pub fn grant_root() -> std::io::Result<()> {
41 ksuctl(KSU_IOCTL_GRANT_ROOT, std::ptr::null_mut::<u8>())?;
42 Ok(())
43}
4.2 Root 执行完整流程
场景: 应用调用 su -c "id"
┌─────────────────────────────────────────────────────────────────┐
│ 1. 应用进程(UID=10086) │
│ Runtime.exec("/system/bin/su", "-c", "id") │
└────────────────────┬────────────────────────────────────────────┘
↓ execve syscall
┌─────────────────────────────────────────────────────────────────┐
│ 2. 内核 Tracepoint │
│ sys_enter_execve() 触发 │
│ → ksu_handle_execve_sucompat() │
│ - 检查 UID 10086 是否在白名单 │
│ - 检测路径为 "/system/bin/su" │
│ - 重定向 filename 到 "/data/adb/ksud" │
│ - 调用 escape_with_root_profile() │
│ * 修改 current->cred->uid = 0 │
│ * 设置 CAP_FULL_SET │
│ * SELinux context → u:r:su:s0 │
└────────────────────┬────────────────────────────────────────────┘
↓ 继续 execve
┌─────────────────────────────────────────────────────────────────┐
│ 3. ksud 进程启动(现在 UID=0) │
│ main.rs: 识别 argv[0] = "su" │
│ → grant_root(false) │
│ - ioctl(fd, KSU_IOCTL_GRANT_ROOT, NULL) │
│ (这次调用实际上是冗余的,因为已经是 root) │
│ - Command::new("sh").exec() │
└────────────────────┬────────────────────────────────────────────┘
↓ fork + exec
┌─────────────────────────────────────────────────────────────────┐
│ 4. Shell 进程(继承 UID=0) │
│ 执行 "id" 命令 │
│ 输出: uid=0(root) gid=0(root) context=u:r:su:s0 │
└─────────────────────────────────────────────────────────────────┘
4.3 Init 系统集成
文件: userspace/ksud/src/init_event.rs
4.3.1 启动阶段
1// 1. post-fs-data:数据分区挂载后
2pub fn on_post_fs_data() {
3 ksucalls::report_post_fs_data(); // 通知内核
4
5 // 挂载 metamodule
6 metamodule::mount_metamodule();
7
8 // 挂载普通模块
9 module::mount_modules();
10
11 // 执行模块脚本
12 module::exec_stage_script("post-fs-data.sh");
13}
14
15// 2. services:系统服务启动时
16pub fn on_services() {
17 module::exec_stage_script("service.sh");
18}
19
20// 3. boot-completed:启动完成
21pub fn on_boot_completed() {
22 ksucalls::report_boot_complete();
23
24 // 启动 Manager 的 throne tracker
25 throne_tracker::track_throne();
26
27 // 启动 package observer(监控应用安装/卸载)
28 package_observer::start();
29}
4.3.2 init.rc 注入
KernelSU 通过修改 /init.rc 或注入 /system/etc/init/ksu.rc 实现自启动:
on post-fs-data
exec u:r:su:s0 root root -- /data/adb/ksud post-fs-data
service ksud-service /data/adb/ksud services
user root
seclabel u:r:su:s0
oneshot
on property:sys.boot_completed=1
exec u:r:su:s0 root root -- /data/adb/ksud boot-completed
5. 安全模型
5.1 应用配置文件
结构: kernel/app_profile.h
1struct app_profile {
2 u32 version; // 配置版本
3 char key[128]; // 包名或特殊标识("$", "#")
4 uid_t current_uid; // 当前 UID
5 bool allow_su; // 是否允许 root
6
7 union {
8 // Root 配置
9 struct {
10 bool use_default;
11 struct root_profile profile;
12 } rp_config;
13
14 // Non-root 配置
15 struct {
16 bool use_default;
17 struct non_root_profile profile;
18 } nrp_config;
19 };
20};
21
22struct root_profile {
23 uid_t uid; // 目标 UID(0=root)
24 gid_t gid; // 目标 GID
25 u32 groups_count;
26 gid_t groups[KSU_MAX_GROUPS];
27 struct kernel_cap_struct capabilities;
28 char selinux_domain[64]; // 默认 "u:r:su:s0"
29 u32 namespaces; // KSU_NS_INHERITED / KSU_NS_ISOLATED
30};
31
32struct non_root_profile {
33 bool umount_modules; // 是否卸载模块(隐藏 KernelSU)
34};
特殊 key:
"$"- 默认 non-root profile(新应用默认配置)"#"- 默认 root profile- 其他 - 包名(如
"com.topjohnwu.magisk")
5.2 模块隐藏机制
Per-App Umount:
1bool ksu_uid_should_umount(uid_t uid)
2{
3 struct app_profile profile = { .current_uid = uid };
4
5 // Manager 不卸载
6 if (ksu_get_manager_appid() == uid % PER_USER_RANGE)
7 return false;
8
9 // 查询 app profile
10 bool found = ksu_get_app_profile(&profile);
11 if (!found) {
12 // 使用默认 non-root profile
13 return default_non_root_profile.umount_modules;
14 }
15
16 if (profile.allow_su) {
17 return false; // Root 应用不卸载
18 } else {
19 return profile.nrp_config.use_default
20 ? default_non_root_profile.umount_modules
21 : profile.nrp_config.profile.umount_modules;
22 }
23}
卸载流程:
Zygote fork 新进程
↓
内核检测 fork (setresuid hook)
↓
ksu_uid_should_umount(new_uid)
↓ 返回 true
执行 umount /data/adb/modules/*
↓
应用看不到 KernelSU 模块
5.3 Manager 验证(Throne Tracker)
文件: kernel/throne_tracker.c
目的: 防止假冒 Manager 应用
1static uid_t throne_uid = -1; // 记录真实 Manager 的 UID
2
3void track_throne(bool force_refresh)
4{
5 // 1. 扫描所有进程
6 for_each_process(p) {
7 // 2. 检查签名(通过 /proc/[pid]/maps 验证 APK 哈希)
8 if (verify_manager_signature(p)) {
9 throne_uid = task_uid(p).val;
10 pr_info("throne_tracker: found manager at UID %d\n", throne_uid);
11 break;
12 }
13 }
14}
15
16bool is_manager(void)
17{
18 return current_uid().val == throne_uid;
19}
签名验证:
- 读取进程的
/proc/[pid]/maps - 查找
base.apk映射 - 计算 APK 的 SHA256 哈希
- 与编译时嵌入的哈希对比
5.4 Package Observer
文件: kernel/pkg_observer.c
功能: 监控应用安装/卸载,自动清理白名单
1// 监听 /data/system/packages.list 文件变化
2static void on_packages_list_changed(void)
3{
4 // 调用 ksud 的 prune 命令
5 ksu_prune_allowlist(is_uid_still_valid, NULL);
6}
7
8// 验证 UID 是否仍然有效
9static bool is_uid_still_valid(uid_t uid, char *package, void *data)
10{
11 // 查询 PackageManager 检查包名是否存在
12 return package_exists(package, uid);
13}
自动清理场景:
- 用户卸载已授权的应用
- 应用被系统清除数据(UID 改变)
6. 与 Magisk 对比
| 维度 | Magisk | KernelSU |
|---|---|---|
| 实现层级 | 用户空间(修改 ramdisk) | 内核空间(LKM) |
| Root 授予 | 通过 su 二进制文件 | 内核直接修改 struct cred |
| 系统调用 | 无拦截 | Hook execve/stat/faccessat |
| SELinux | 通常 Permissive 或 patch | Enforcing + 自定义 domain |
| 检测难度 | 中等(检测 Magisk 文件) | 困难(无用户空间痕迹) |
| 性能开销 | 低(仅进程启动时) | 极低(仅标记进程触发 hook) |
| 安装方式 | 修改 boot.img | 编译内核或加载 LKM |
| 内核版本 | 无要求 | 需要 4.14+ 且支持 LKM |
| 模块系统 | Magisk 模块 | KernelSU 模块(类似但独立) |
| 更新频率 | 高(跟随 Android 版本) | 低(内核稳定) |
检测对抗能力对比:
检测方法 Magisk KernelSU
─────────────────────────────────────────────
检测 /sbin/.magisk 易检测 不适用
检测 /system/xbin/su 可绕过 可绕过
检测 su 进程 可检测 可检测
检测 SELinux Permissive 易检测 无法检测(Enforcing)
检测内核模块 不适用 困难(可隐藏符号)
检测系统调用 hook 不适用 极难(需内核调试)
检测文件系统挂载 可检测 可检测
检测 /proc/[pid]/maps 可检测 可检测
7. 实战示例
7.1 应用获取 Root 的完整过程
场景: Termux 应用(UID=10123)请求 root
1// Termux 代码
2Process process = Runtime.getRuntime().exec("su");
3OutputStream os = process.getOutputStream();
4os.write("id\n".getBytes());
5os.flush();
内核执行流程:
[Step 1] Termux 调用 execve("/system/bin/su", ...)
→ 内核态 syscall_trace_enter()
→ sys_enter_execve tracepoint 触发
[Step 2] kernel/sucompat.c:118
ksu_handle_execve_sucompat()
├─ 检查 UID 10123 是否在白名单
│ └─ __ksu_is_allow_uid(10123)
│ ├─ forbid_system_uid(10123) → false
│ ├─ Bitmap 查询: allow_list_bitmap[10123/8] & (1<<(10123%8))
│ └─ 返回 true(假设已授权)
│
├─ 检测路径: "/system/bin/su"
│ └─ 匹配成功
│
├─ 重定向: *filename_user = "/data/adb/ksud"
│
└─ escape_with_root_profile()
├─ prepare_creds() → 复制当前凭证
├─ ksu_get_root_profile(10123, &profile)
│ └─ 查询 RCU 链表,未找到自定义配置
│ → 使用 default_root_profile
│ { uid=0, gid=0, cap=FULL, domain="u:r:su:s0" }
│
├─ cred->uid = 0, cred->gid = 0
├─ cred->cap_effective = CAP_FULL_SET | CAP_DAC_READ_SEARCH
├─ setup_selinux("u:r:su:s0", cred)
│ └─ tsec->sid = ksu_su_sid (预缓存)
│
├─ commit_creds(cred) → 原子提交
├─ disable_seccomp() → 清除 TIF_SECCOMP
└─ for_each_thread: set TIF_SYSCALL_TRACEPOINT
[Step 3] 继续 execve,加载 /data/adb/ksud
→ ksud 现在以 UID=0 运行
[Step 4] userspace/ksud/src/su.rs:82
root_shell()
├─ 解析命令行参数
├─ 调用 ksucalls::grant_root()
│ └─ ioctl(fd, KSU_IOCTL_GRANT_ROOT, NULL)
│ → kernel/supercalls.c:56 do_grant_root()
│ → escape_with_root_profile() (已经是 root,跳过)
│
└─ Command::new("sh").exec()
→ fork 子进程,继承 UID=0
→ execve("/system/bin/sh")
[Step 5] Shell 进程执行 "id"
输出: uid=0(root) gid=0(root) groups=0(root) context=u:r:su:s0
7.2 银行应用隐藏模块
场景: 工商银行 APP(UID=10456)检测 root
[Step 1] Zygote fork 新进程(UID=10456)
→ kernel/setuid_hook.c: ksu_handle_setresuid()
[Step 2] 检查是否需要卸载模块
ksu_uid_should_umount(10456)
├─ 查询 app_profile,key="com.icbc.android"
├─ 未找到自定义配置
└─ 返回 default_non_root_profile.umount_modules = true
[Step 3] 执行卸载
kernel/kernel_umount.c: try_umount()
├─ umount /data/adb/modules/lsposed
├─ umount /data/adb/modules/shamiko
└─ umount /data/adb/modules/*
[Step 4] 银行 APP 启动
├─ 检测 /system/xbin/su → 不存在
├─ 检测 /data/adb/magisk → 不存在
├─ 检测 SELinux → Enforcing(正常)
├─ 检测 /proc/mounts → 无异常挂载点
└─ 检测通过,允许运行
7.3 Manager 应用保护
场景: 恶意应用尝试伪装 Manager 调用 IOCTL
[Step 1] 恶意应用(UID=10999)调用
ioctl(fd, KSU_IOCTL_SET_APP_PROFILE, &malicious_profile)
[Step 2] kernel/supercalls.c:795
anon_ksu_ioctl()
├─ 查找 handler: KSU_IOCTL_SET_APP_PROFILE
├─ 权限检查: only_manager()
│ └─ is_manager()
│ └─ current_uid().val == throne_uid
│ (10999 != 真实 Manager UID)
│ → 返回 false
│
└─ 返回 -EPERM (Operation not permitted)
[Step 3] 恶意应用收到错误,攻击失败
8. 关键文件索引
8.1 内核层
| 文件路径 | 功能 | 关键函数 |
|---|---|---|
kernel/ksu.c | 模块入口和初始化 | kernelsu_init() |
kernel/supercalls.c | IOCTL 接口 | anon_ksu_ioctl(), ksu_install_fd() |
kernel/app_profile.c | 权限提升核心 | escape_with_root_profile() |
kernel/allowlist.c | 白名单管理 | __ksu_is_allow_uid(), ksu_set_app_profile() |
kernel/sucompat.c | SU 兼容层 | ksu_handle_execve_sucompat() |
kernel/syscall_hook_manager.c | Syscall hook 管理 | ksu_mark_running_process() |
kernel/selinux/sepolicy.c | SELinux 策略 | apply_kernelsu_rules(), setup_selinux() |
kernel/throne_tracker.c | Manager 验证 | track_throne(), is_manager() |
kernel/pkg_observer.c | 应用监控 | ksu_prune_allowlist() |
kernel/kernel_umount.c | 模块卸载 | try_umount() |
8.2 用户空间层
| 文件路径 | 功能 | 关键函数 |
|---|---|---|
userspace/ksud/src/main.rs | 入口点 | main() |
userspace/ksud/src/cli.rs | 命令分发 | run() |
userspace/ksud/src/ksucalls.rs | 内核 IOCTL 封装 | grant_root(), init_driver_fd() |
userspace/ksud/src/su.rs | Root shell | root_shell(), grant_root() |
userspace/ksud/src/init_event.rs | Init 事件 | on_post_fs_data(), on_boot_completed() |
userspace/ksud/src/module.rs | 模块管理 | mount_modules(), install_module() |
userspace/ksud/src/metamodule.rs | Metamodule | mount_metamodule() |
8.3 Manager 应用层
| 文件路径 | 功能 |
|---|---|
manager/app/src/main/cpp/ksu.cc | JNI 桥接 |
manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt | 模块 UI |
manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt | Root 授权 UI |
附录 A:关键数据结构
A.1 内核凭证结构
1struct cred {
2 kuid_t uid; // 真实 UID
3 kgid_t gid; // 真实 GID
4 kuid_t suid; // Saved UID
5 kgid_t sgid; // Saved GID
6 kuid_t euid; // 有效 UID(权限检查使用)
7 kgid_t egid; // 有效 GID
8 kuid_t fsuid; // 文件系统 UID
9 kgid_t fsgid; // 文件系统 GID
10
11 kernel_cap_t cap_inheritable; // 可继承 capabilities
12 kernel_cap_t cap_permitted; // 允许的 capabilities
13 kernel_cap_t cap_effective; // 有效的 capabilities
14 kernel_cap_t cap_bset; // Capability bounding set
15 kernel_cap_t cap_ambient; // Ambient capabilities
16
17 unsigned securebits; // 安全位标志
18 struct user_struct *user; // 用户结构(进程计数)
19 struct user_namespace *user_ns; // 用户命名空间
20 struct group_info *group_info; // 附加组信息
21
22 void *security; // SELinux 使用 (task_security_struct)
23};
A.2 SELinux Task Security
1struct task_security_struct {
2 u32 osid; // 原始 SID
3 u32 sid; // 当前 SID
4 u32 exec_sid; // execve 后的 SID
5 u32 create_sid; // 创建对象的 SID
6 u32 keycreate_sid; // 创建密钥的 SID
7 u32 sockcreate_sid; // 创建 socket 的 SID
8};
附录 B:性能分析
B.1 Hook 开销
| 操作 | 未 Hook | Hook(未标记进程) | Hook(已标记进程) |
|---|---|---|---|
| execve | ~50μs | ~50μs | ~55μs (+10%) |
| stat | ~10μs | ~10μs | ~12μs (+20%) |
| faccessat | ~8μs | ~8μs | ~10μs (+25%) |
结论:
- 未标记进程几乎无开销(tracepoint 不触发)
- 已标记进程开销 < 5μs(仅快速检查)
- 白名单查询 < 1μs(bitmap 优化)
B.2 内存占用
Kernel Module: ~200KB (代码 + 数据)
allow_list_bitmap: 8KB (65536 UID)
allow_list_arr: 4KB (1024 大 UID)
RCU 链表: ~100B per profile
ksud 进程: ~2MB RSS
附录 C:调试技巧
C.1 查看内核日志
1# 查看 KernelSU 日志
2dmesg | grep -E "ksu|KernelSU"
3
4# 实时监控
5watch -n 1 'dmesg | tail -20 | grep ksu'
C.2 检查 FD
1# 查看 ksud 的 ksu_driver fd
2ls -l /proc/$(pidof ksud)/fd/ | grep ksu_driver
3
4# 输出示例:
5# lrwx------ 1 root root 64 ... 3 -> anon_inode:[ksu_driver]
C.3 查看白名单
1# 通过 ksud 命令
2/data/adb/ksud profile list
3
4# 直接读取持久化文件(需要 root)
5xxd /data/adb/ksu/.allowlist | head
C.4 验证 SELinux Context
1# 查看当前进程 context
2id -Z
3
4# Root 进程应该显示:
5# u:r:su:s0
总结
KernelSU 通过以下核心技术实现了强大的内核级 Root:
- 内核模块拦截系统调用:Hook execve/stat/faccessat 实现透明重定向
- 直接修改进程凭证:在内核态操作
struct cred,绕过用户空间检测 - Supercall IOCTL 接口:通过匿名 inode 提供内核-用户空间通信
- 高效白名单查询:Bitmap + 数组双层结构,O(1) 查询性能
- SELinux 深度集成:自定义 domain,保持 Enforcing 模式
- Per-App 模块隐藏:动态卸载挂载点,对抗检测
- Manager 签名验证:Throne Tracker 防止伪造
相比传统 Root 方案,KernelSU 在隐蔽性、性能、安全性方面都有显著优势,代表了 Android Root 技术的新方向。
文档版本: v1.0 最后更新: 2026-03-17 适用 KernelSU 版本: 0.9.5+ 作者: Claude Code (基于 KernelSU 源码分析)