ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

KernelSU 非 GKI 内核集成指南:kprobe 自动集成与手工源码打补丁全流程

2026/9/14 0:30:58 拓冰建站 浏览量
KernelSU 非 GKI 内核集成指南:kprobe 自动集成与手工源码打补丁全流程 KernelSU 非 GKI 内核集成指南kprobe 自动集成与手工源码打补丁全流程【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU本文基于 KernelSU 官方归档文档website/docs/ja_JP/guide/how-to-integrate-for-non-gki.md整理并结合仓库内 kernel/setup.sh、kernel/Kconfig、kernel/runtime/ksud_integration.c 等源码进行深度印证。读者将掌握在非 GKI非通用内核镜像设备上自编译集成 KernelSU 的两种完整路线基于kprobe的自动挂接以及不依赖kprobe的手工内核源码打补丁方法并学会 Safe Mode、path_umount回移等配套实操。::: warning 归档声明 本文档对应的原始文档已标记为仅供归档参考不再维护。自KernelSU v1.0起官方已终止对非 GKI 设备的支持本文涉及的内核源码补丁与集成流程适用于 v1.0 之前的版本官方文档明确给出的最后受支持版本为v0.9.5。在当前仓库源码中相关内核模块实现如 kernel/core/init.c、kernel/runtime/ksud_integration.c仍保留着这些历史集成接口的完整实现可作为逆向理解 KernelSU 内核挂接原理的绝佳素材。 :::为什么非 GKI 设备需要手动集成GKIGeneric Kernel Image是由 Google 提供的通用内核镜像所有 GKI 设备共享同一份内核因此 KernelSU 可以发布统一的 boot.img。而非 GKI 内核由各厂商各自维护碎片化极其严重内核版本、驱动补丁、defconfig 布局各不相同不存在统一的构建方式官方也无法为每个设备提供预编译的 boot.img。因此非 GKI 设备使用 KernelSU 的前提是你能够从该设备的内核源码构建出可引导的内核内核源码是开放的——如果厂商未开源内核则基本无法在设备上运行 KernelSUKernelSU 官方将支持范围回移到了 4.14 及更早的内核版本老旧内核同样在覆盖范围内。满足上述条件后将 KernelSU 集成进内核源码有两种路径kprobe 自动集成——依赖内核的kprobe机制动态挂接改动最小手工修改内核源码——在指定内核函数中插入 KernelSU 调用点不依赖kprobe。下面分别展开。方式一基于 kprobe 的自动集成kprobe是 Linux 内核提供的动态插桩机制KernelSU 在内核挂接hook层的实现正是建立在它之上见 kernel/hook/ 目录下的 lsm_hook.c、setuid_hook.c 等实现。如果kprobe在你的内核上运行稳定官方推荐优先使用这种方式因为它几乎不需要改动内核源码。第一步将 KernelSU 加入内核源码树在内核源码根目录执行官方提供的setup.shcurl -LSs https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh | bash -::: tip 版本选择重要 由于 v1.0 起不再支持非 GKI请务必使用v0.9.5或更早的兼容版本例如# 指定兼容版本 curl -LSs https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh | bash -s v0.9.5setup.sh的最后一个参数即 git tag 或 commit不传参数时默认切换到最新 tag当前仓库的 kernel/setup.sh 中可以看到该逻辑git checkout $(git describe --abbrev0 --tags)。 :::从仓库内的 kernel/setup.sh 源码可以看到这个脚本实际做了四件事自动探测drivers/目录优先common/drivers兼容非 GKI 内核的目录布局将 KernelSU 内核代码 clone 到KernelSU/kernel并在drivers/下创建名为kernelsu的符号链接在drivers/Makefile追加obj-$(CONFIG_KSU) kernelsu/在drivers/Kconfig的endmenu之前插入source drivers/kernelsu/Kconfig。此外脚本还提供--cleanup参数可一键回滚上述全部改动删除符号链接、还原 Makefile 与 Kconfig、删除 KernelSU 目录非常适合在尝试不同 tag 时反复清理环境。第二步确认并开启 kprobe 相关内核配置检查内核 defconfig 中是否已启用 kprobe若未启用追加以下配置CONFIG_KPROBESy CONFIG_HAVE_KPROBESy CONFIG_KPROBE_EVENTSy然后重新编译内核KernelSU 应当能正常工作。如果发现KPROBES仍然没有被启用可以尝试开启CONFIG_MODULES若依然无效则使用make menuconfig搜索 kprobe 的其它依赖项并逐一满足。这一点与当前仓库的 kernel/Kconfig 相互印证——其中KSU配置项明确声明config KSU tristate KernelSU function support depends on KPROBES EXT4_FS即KernelSU 的编译前提是CONFIG_KPROBES与CONFIG_EXT4_FS同时开启这也解释了为何官方文档要求先确保 kprobe 可用。第三步集成后遇到开机循环bootloop怎么办如果在集成 KernelSU 后出现 bootloop大概率是kprobe 在你的内核上存在缺陷broken。此时有两种选择修复 kprobe 的 bug或改用下文的手工集成方式。如何确认 kprobe 是否损坏官方给出的诊断方法注释掉KernelSU/kernel/ksu.c中的ksu_sucompat_init()与ksu_ksud_init()两个初始化调用重新编译后尝试引导如果设备能正常启动 → 高度怀疑 kprobe 存在缺陷如果仍然无法启动 → 问题大概率出在别处如 SELinux、ramdisk 等。在当前仓库中这两个函数依然存在并被实际调用ksu_sucompat_init()定义于 kernel/feature/sucompat.c末尾的void __init ksu_sucompat_init()由 kernel/hook/syscall_hook_manager.c 调用ksu_ksud_init()定义于 kernel/runtime/ksud_integration.c在 kernel/core/init.c 的非 late-load 初始化分支中被调用。注释掉它们即可临时禁用 su 兼容层与 ksud 相关的 kprobe 挂接用于隔离故障。方式二手工修改内核源码不依赖 kprobe如果 kprobe 在你的内核上无法工作——无论是上游内核的 bug还是内核版本低于 4.8——则需要采用手工方式直接在内核源码的关键函数中插入 KernelSU 的调用点。第一步拉取 KernelSU 源码与方式一相同先执行setup.sh。官方文档给出了三种拉取方式当前仓库 kernel/setup.sh 均支持# 方式 1main 分支开发版 curl -LSs https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh | bash -s main # 方式 2指定 tag如 v0.5.2非 GKI 兼容版本请参考 v0.9.5 curl -LSs https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh | bash -s v0.5.2第二步在 defconfig 中启用 CONFIG_KSU注意 defconfig 的位置因设备而异可能位于arch/arm64/configs也可能位于arch/arm64/configs/vendor/your_defconfig。无论使用哪一份 defconfig都需要显式声明 KernelSU 的开关# KernelSU CONFIG_KSUyCONFIG_KSUy启用 KernelSUCONFIG_KSUn禁用 KernelSU。第三步在四个内核函数中插入调用点KernelSU 手工集成需要在内核源码中定位四个函数并插入调用官方给出的清单如下函数所在文件KernelSU 钩子do_execveat_commonfs/exec.cksu_handle_execveat/ksu_handle_execveat_sucompatdo_faccessatfs/open.cksu_handle_faccessatvfs_readfs/read_write.cksu_handle_vfs_readvfs_statxfs/stat.cksu_handle_stat以下是官方提供的参考补丁可直接作为 diff 应用1)fs/exec.c—do_execveat_commonexecve 系列系统调用入口diff --git a/fs/exec.c b/fs/exec.c index ac59664eaecf..bdd585e1d2cc 100644 --- a/fs/exec.c b/fs/exec.c -1890,11 1890,14 static int __do_execve_file(int fd, struct filename *filename, return retval; } #ifdef CONFIG_KSU extern bool ksu_execveat_hook __read_mostly; extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags); extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags); #endif static int do_execveat_common(int fd, struct filename *filename, struct user_arg_ptr argv, struct user_arg_ptr envp, int flags) { #ifdef CONFIG_KSU if (unlikely(ksu_execveat_hook)) ksu_handle_execveat(fd, filename, argv, envp, flags); else ksu_handle_execveat_sucompat(fd, filename, argv, envp, flags); #endif return __do_execve_file(fd, filename, argv, envp, flags, NULL); }2)fs/open.c—do_faccessataccess/faccessat 权限检查diff --git a/fs/open.c b/fs/open.c index 05036d819197..965b84d486b8 100644 --- a/fs/open.c b/fs/open.c -348,6 348,8 SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len) return ksys_fallocate(fd, mode, offset, len); } #ifdef CONFIG_KSU extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *flags); #endif /* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and -355,6 357,7 SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len) */ long do_faccessat(int dfd, const char __user *filename, int mode) { const struct cred *old_cred; struct cred *override_cred; struct path path; struct inode *inode; struct vfsmount *mnt; int res; unsigned int lookup_flags LOOKUP_FOLLOW; #ifdef CONFIG_KSU ksu_handle_faccessat(dfd, filename, mode, NULL); #endif if (mode ~S_IRWXO) /* wheres F_OK, X_OK, W_OK, R_OK? */ return -EINVAL;3)fs/read_write.c—vfs_read文件读取路径diff --git a/fs/read_write.c b/fs/read_write.c index 650fc7e0f3a6..55be193913b6 100644 --- a/fs/read_write.c b/fs/read_write.c -434,10 434,14 ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos) } EXPORT_SYMBOL(kernel_read); #ifdef CONFIG_KSU extern bool ksu_vfs_read_hook __read_mostly; extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, size_t *count_ptr, loff_t **pos); #endif ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { ssize_t ret; #ifdef CONFIG_KSU if (unlikely(ksu_vfs_read_hook)) ksu_handle_vfs_read(file, buf, count, pos); #endif if (!(file-f_mode FMODE_READ)) return -EBADF; if (!(file-f_mode FMODE_CAN_READ))4)fs/stat.c—vfs_statx文件状态查询diff --git a/fs/stat.c b/fs/stat.c index 376543199b5a..82adcef03ecc 100644 --- a/fs/stat.c b/fs/stat.c -148,6 148,8 int vfs_statx_fd(unsigned int fd, struct kstat *stat, } EXPORT_SYMBOL(vfs_statx_fd); #ifdef CONFIG_KSU extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); #endif /** * vfs_statx - Get basic and extra attributes by filename * dfd: A file descriptor representing the base dir for a relative filename -170,6 172,7 int vfs_statx(int dfd, const char __user *filename, int flags, int error -EINVAL; unsigned int lookup_flags LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT; #ifdef CONFIG_KSU ksu_handle_stat(dfd, filename, flags); #endif if ((flags ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) ! 0) return -EINVAL;这些调用点与当前仓库源码中的实现一一对应例如ksu_handle_faccessat_sucompat的实现位于 kernel/feature/sucompat.c其核心逻辑是当进程 uid 命中白名单kernel/policy/allowlist.c且访问路径为/system/bin/su时将路径重定向为 ksud 的路径KSUD_PATH并以内核凭证执行从而实现su → ksud的兼容层转发ksu_handle_execveat_sucompat等钩子的系统调用桥接层则位于 kernel/hook/syscall_event_bridge.c。内核没有 vfs_statx改用 vfs_fstatat如果目标内核没有vfs_statx常见于较老的内核则在fs/stat.c中改用vfs_fstatat插入调用diff --git a/fs/stat.c b/fs/stat.c index 068fdbcc9e26..5348b7bb9db2 100644 --- a/fs/stat.c b/fs/stat.c -87,6 87,8 int vfs_fstat(unsigned int fd, struct kstat *stat) } EXPORT_SYMBOL(vfs_fstat); #ifdef CONFIG_KSU extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); #endif int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, int flag) { -94,6 96,8 int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, int error -EINVAL; unsigned int lookup_flags 0; #ifdef CONFIG_KSU ksu_handle_stat(dfd, filename, flag); #endif if ((flag ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH)) ! 0) goto out;内核低于 4.17在 faccessat 系统调用定义处插入对于 4.17 之前的内核若找不到do_faccessat则直接定位faccessat系统调用SYSCALL_DEFINE3(faccessat, ...)的定义将调用点放到函数体内diff --git a/fs/open.c b/fs/open.c index 2ff887661237..e758d7db7663 100644 --- a/fs/open.c b/fs/open.c -355,6 355,9 SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len) return error; } #ifdef CONFIG_KSU extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *flags); #endif /* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and -370,6 373,8 SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode) int res; unsigned int lookup_flags LOOKUP_FOLLOW; #ifdef CONFIG_KSU ksu_handle_faccessat(dfd, filename, mode, NULL); #endif if (mode ~S_IRWXO) /* wheres F_OK, X_OK, W_OK, R_OK? */ return -EINVAL;Safe Mode安全模式修改 input_handle_eventKernelSU 内置了 Safe Mode用于在模块或配置导致 bootloop 时一键进入安全模式排查问题。启用它需要修改drivers/input/input.c中的input_handle_event函数::: tip 强烈建议 官方文档明确建议务必启用该功能——它在防止 bootloop 方面非常有用 :::diff --git a/drivers/input/input.c b/drivers/input/input.c index 45306f9ef247..815091ebfca4 100755 --- a/drivers/input/input.c b/drivers/input/input.c -367,10 367,13 static int input_get_disposition(struct input_dev *dev, return disposition; } #ifdef CONFIG_KSU extern bool ksu_input_hook __read_mostly; extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value); #endif static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { int disposition input_get_disposition(dev, type, code, value); #ifdef CONFIG_KSU if (unlikely(ksu_input_hook)) ksu_handle_input_handle_event(type, code, value); #endif if (disposition ! INPUT_IGNORE_EVENT type ! EV_SYN) add_input_randomness(type, code, value);当前仓库中 kernel/runtime/ksud_integration.c 给出了该钩子的实现细节ksu_handle_input_handle_event()监听EV_KEY类型的KEY_VOLUMEDOWN事件连续按下音量减键 3 次后即判定进入安全模式is_volumedown_enough()要求count 3随后由ksu_is_safe_mode()对外暴露判定结果。误入 Safe Mode手动集成时务必禁用 KPROBES::: danger 重要警告 如果使用手工集成且没有禁用CONFIG_KPROBES那么任何用户都可能在开机后通过连按音量减键触发安全模式——这是不可接受的。因此只要采用手工集成方式就必须禁用CONFIG_KPROBES:::让卸载模块功能在旧内核上工作回移 path_umountKernelSU 的 Umount modules卸载模块挂载功能依赖内核 5.9 引入的path_umount接口。对于5.9 之前的内核需要手动将该函数从 5.9 回移backport到fs/namespace.c。官方给出的参考补丁如下--- a/fs/namespace.c b/fs/namespace.c -1739,6 1739,39 static inline bool may_mandlock(void) } #endif static int can_umount(const struct path *path, int flags) { struct mount *mnt real_mount(path-mnt); if (flags ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) return -EINVAL; if (!may_mount()) return -EPERM; if (path-dentry ! path-mnt-mnt_root) return -EINVAL; if (!check_mnt(mnt)) return -EINVAL; if (mnt-mnt.mnt_flags MNT_LOCKED) /* Check optimistically */ return -EINVAL; if (flags MNT_FORCE !capable(CAP_SYS_ADMIN)) return -EPERM; return 0; } int path_umount(struct path *path, int flags) { struct mount *mnt real_mount(path-mnt); int ret; ret can_umount(path, flags); if (!ret) ret do_umount(mnt, flags); /* we mustnt call path_put() as that would clear mnt_expiry_mark */ dput(path-dentry); mntput_no_expire(mnt); return ret; } /* * Now umount can handle mount points as well as block devices. * This is important for filesystems which use unnamed block devices.不完成此回移的后果Umount modules 功能将无法工作。该补丁与仓库源码的依赖关系可以在 kernel/feature/kernel_umount.c 中得到印证该文件通过extern int path_umount(struct path *path, int flags);声明直接调用path_umount()见ksu_umount_mnt()并配合kern_path()校验挂载点path.dentry ! path.mnt-mnt_root则跳过。也就是说内核是否提供path_umount直接决定了 KernelSU 能否在内核态完成模块挂载的卸载。完整收尾重新编译内核无论采用哪种集成方式最后一步都是重新编译内核并刷入设备。编译通过后 KernelSU 即应正常工作。集成要点速查表项目关键配置 / 修改说明版本约束使用v0.9.5及更早版本v1.0 起官方停止非 GKI 支持kprobe 方式CONFIG_KPROBESy、CONFIG_HAVE_KPROBESy、CONFIG_KPROBE_EVENTSy必要时加CONFIG_MODULES编译前提CONFIG_KSUyKPROBESEXT4_FS见 kernel/Kconfig手工打点4 处fs/exec.c、fs/open.c、fs/read_write.c、fs/stat.c低版本内核按文档替换vfs_fstatat/faccessatSafe Mode修改drivers/input/input.c的input_handle_event连按 3 次音量减触发手工集成必须禁用CONFIG_KPROBES卸载模块回移 5.9 的path_umount到fs/namespace.c5.9 以下内核必须回移否则功能失效延伸阅读官方英文版归档文档website/docs/guide/how-to-integrate-for-non-gki.md集成脚本实现kernel/setup.sh内核配置项定义kernel/Kconfigksud 运行时钩子实现kernel/runtime/ksud_integration.csu 兼容层实现kernel/feature/sucompat.c卸载模块功能实现kernel/feature/kernel_umount.c系统调用桥接层kernel/hook/syscall_event_bridge.c更多官方指南website/docs/guide/【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考