资讯动态

KernelSU 非 GKI 内核集成指南:从 kprobe 自动整合到手工 patch 源码

发布时间:2026/9/14 14:57:49 来源:尧图企业网站定制
KernelSU 非 GKI 内核集成指南从 kprobe 自动整合到手工 patch 源码【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU本篇指南讲解如何将 KernelSU 集成到非 GKIGeneric Kernel Image内核中覆盖两种主流整合方式——借助kprobe的自动集成以及不依赖 kprobe 的手工修改内核源码方案并包含安全模式、pm命令修复、path_umount向后移植等关键实战细节。读完本文你将掌握在 4.14 及以上理论上更低版本亦可的非 GKI 内核源码树中接入 KernelSU、正确配置 Kconfig、打全四类 hook 补丁并成功构建可开机内核的完整流程。⚠️ 归档警告本文内容基于 KernelSU 官方文档存档整理该文档不再维护更新。自 KernelSU v1.0 版本之后官方已放弃对非 GKI 设备的官方支持最后一个支持非 GKI 内核的版本为v0.9.5。集成时务必使用正确版本标签。一、集成前的硬性前提非 GKI 内核的碎片化极其严重通常没有统一的方法来构建它官方也无法为非 GKI 设备提供 Boot 镜像。因此在动手之前你必须满足以下条件有能力自行构建内核能够从设备的内核源码构建出一个可以开机、且能够正常使用的内核。如果设备内核并非开源这一步几乎无法完成。版本对齐KernelSU 最低支持到内核4.14理论上也可以支持更低的版本但需要额外处理例如下文提到的旧版faccessat与vfs_statx替换。选择集成路线根据目标内核中 kprobe 是否可用选择以下两种方式之一方式一借助kprobe自动整合推荐若 kprobe 正常运作方式二手动修改内核源码kprobe 无法正常运作时使用。从当前仓库的 Kconfig 可以看出KernelSU 的内核配置CONFIG_KSU声明为tristate且depends on KPROBES EXT4_FS——即依赖 kprobe 与 ext4 文件系统这也从源码层面印证了 kprobe 在 KernelSU 集成中的基础地位。二、方式一使用 kprobe 自动整合KernelSU 使用kprobe 机制来处理内核的相关 hook。如果你的内核中 kprobe 可以正常工作强烈建议使用此方法。2.1 将 KernelSU 加入内核源码树在内核源码的根目录执行以下命令注意必须指定v0.9.5版本curl -LSs https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh | bash -s v0.9.5该命令会拉取 KernelSU 官方仓库的 setup.sh 脚本并在当前内核树中执行。对照仓库中的脚本源码其核心工作包括自动探测common/drivers或drivers目录GKI 内核与普通内核的差异git cloneKernelSU 仓库并checkout到指定 tag此处即v0.9.5在内核drivers/下创建指向KernelSU/kernel的kernelsu软链接向drivers/Makefile追加obj-$(CONFIG_KSU) kernelsu/向drivers/Kconfig追加source drivers/kernelsu/Kconfig。此外setup.sh 还支持--cleanup参数用于撤销上述全部修改以及不带参数时自动 checkout 到最新 tag便于在 GKI 场景下复用。2.2 检查并启用 kprobe 相关配置集成后需要检查你的内核是否启用了 kprobe。若未启用需要在 defconfig 中追加CONFIG_KPROBESy CONFIG_HAVE_KPROBESy CONFIG_KPROBE_EVENTSy排障提示如果发现 KPROBES 仍未生效很可能是它依赖的CONFIG_MODULES未被启用若依然无效请执行make menuconfig搜索 KPROBES 的其他依赖项并逐一启用。2.3 重新构建内核完成配置后重新构建内核即可。若集成后手机无法启动则极大概率是内核中的 kprobe 无法正常运作——你需要修复该问题或改用下文的手工 patch 方案。2.4 如何诊断 kprobe 是否损坏两种快速验证手段注释初始化函数法将旧版源码KernelSU/kernel/ksu.c中的ksu_sucompat_init()和ksu_ksud_init()注释掉后重新构建。如果此时能正常开机即说明是 kprobe 损坏所致。在当前仓库中这两个初始化函数的对应实现分别位于 kernel/feature/sucompat.cksu_sucompat_init注册 su 兼容特性处理器与 kernel/runtime/ksud_integration.cksu_ksud_init负责注册__NR_read/__NR_fstat系统调用 hook 与input_eventkprobe。手动触发法手动尝试使用 kprobe 功能如果不正常手机会直接重启。2.5 为低版本内核启用模块卸载功能如果内核版本小于 5.10需要将path_umount向后移植到fs/namespace.c否则模块卸载功能将无法工作。具体移植方法见下文第六节。三、方式二手动修改内核源码如果 kprobe 无法正常运作在 4.8 之前可能是上游或内核自身的错误可以改用此方法。它不依赖 kprobe而是直接在内核源码的关键路径上插入 KernelSU 的 hook 调用。3.1 加入 KernelSU 源码树同样在内核根目录执行curl -LSs https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh | bash -s v0.9.53.2 通过 defconfig 启用 KernelSU注意在某些设备上defconfig 可能位于arch/arm64/configs或位于arch/arm64/configs/vendor/你的defconfig。无论使用哪个 defconfig请确保使用CONFIG_KSUy启用 KernelSU或使用n停用。启用时 defconfig 应包含# KernelSU CONFIG_KSUy3.3 四处核心源码修改必改KernelSU 需要 hook 四个系统调用/VFS 关键路径通常需要修改以下四个文件。以下是官方文档提供的完整 patch 参考①fs/exec.c—— 修改do_execveat_commondiff --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; } 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); static int do_execveat_common(int fd, struct filename *filename, struct user_arg_ptr argv, struct user_arg_ptr envp, int flags) { if (unlikely(ksu_execveat_hook)) ksu_handle_execveat(fd, filename, argv, envp, flags); else ksu_handle_execveat_sucompat(fd, filename, argv, envp, flags); return __do_execve_file(fd, filename, argv, envp, flags, NULL); }②fs/open.c—— 修改do_faccessatdiff --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); } extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *flags); /* * 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; ksu_handle_faccessat(dfd, filename, mode, NULL); if (mode ~S_IRWXO) /* wheres F_OK, X_OK, W_OK, R_OK? */ return -EINVAL;③fs/read_write.c—— 修改vfs_readdiff --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); 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); ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { ssize_t ret; if (unlikely(ksu_vfs_read_hook)) ksu_handle_vfs_read(file, buf, count, pos); if (!(file-f_mode FMODE_READ)) return -EBADF; if (!(file-f_mode FMODE_CAN_READ))④fs/stat.c—— 修改vfs_statxdiff --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); extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); /** * 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; ksu_handle_stat(dfd, filename, flags); if ((flags ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) ! 0) return -EINVAL;四处修改的定位汇总修改目标函数文件位置1do_faccessat通常位于fs/open.c2do_execveat_common通常位于fs/exec.c3vfs_read通常位于fs/read_write.c4vfs_statx通常位于fs/stat.c实现佐证这些 hook 入口在当前仓库的 kernel/feature/sucompat.c 中均有对应实现例如ksu_handle_execveat_sucompatsucompat.c会在su兼容路径下把目标进程的 execve 重定向为对/data/adb/ksud的execveat调用并通过escape_with_root_profile()应用 root 配置ksu_handle_faccessat_sucompat/ksu_handle_stat_sucompat则用于权限校验路径的兼容处理声明见 kernel/feature/sucompat.h。3.4 内核没有vfs_statx用vfs_fstatat替换如果你的内核没有vfs_statx可以使用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); extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); 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; ksu_handle_stat(dfd, filename, flag); if ((flag ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH)) ! 0) goto out;3.5 早于 4.17 的内核直接修改faccessat系统调用对于早于 4.17 的内核如果没有do_faccessat可以直接找到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; } extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *flags); /* * 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; ksu_handle_faccessat(dfd, filename, mode, NULL); if (mode ~S_IRWXO) /* wheres F_OK, X_OK, W_OK, R_OK? */ return -EINVAL;四、启用 KernelSU 内置安全模式安全模式用于应对开机循环等故障场景官方文档强烈建议启用。需要在drivers/input/input.c中修改input_handle_event方法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; } extern bool ksu_input_hook __read_mostly; extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value); 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); if (unlikely(ksu_input_hook)) ksu_handle_input_handle_event(type, code, value); if (disposition ! INPUT_IGNORE_EVENT type ! EV_SYN) add_input_randomness(type, code, value);安全模式的触发与判定逻辑对应源码见 kernel/runtime/ksud_integration.c内核通过输入事件 hook 统计KEY_VOLUMEDOWN音量减键的按下次数当连续按下音量减键达到 3 次volumedown_pressed_count 3时判定为进入安全模式判定后通过ksu_is_safe_mode()对外暴露状态并停止输入 hookksu_stop_input_hook_runtime()内部通过 workqueue 注销input_event的 kprobe。⚠️ 重要提醒如果你使用手动整合且没有停用CONFIG_KPROBES那么启动后按下音量减键就会触发安全模式因此使用手动集成时必须停用CONFIG_KPROBES。五、终端无法执行pm修改 devpts如果你在终端中无法执行pm命令需要修改fs/devpts/inode.cdiff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 32f6f1c68..d69d8eca2 100644 --- a/fs/devpts/inode.c b/fs/devpts/inode.c -602,6 602,8 struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv) return dentry; } #ifdef CONFIG_KSU extern int ksu_handle_devpts(struct inode*); #endif /** * devpts_get_priv -- get private data for a slave * pts_inode: inode of the slave -610,6 612,7 struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv) */ void *devpts_get_priv(struct dentry *dentry) { #ifdef CONFIG_KSU ksu_handle_devpts(dentry-d_inode); #endif if (dentry-d_sb-s_magic ! DEVPTS_SUPER_MAGIC) return NULL; return dentry-d_fsdata;注意此处的声明使用了#ifdef CONFIG_KSU进行条件编译保护与 defconfig 中的CONFIG_KSUy开关联动。六、向后移植path_umount内核 5.10模块卸载功能依赖path_umount函数。如果内核版本小于 5.10 且未包含该函数需要将其从较新内核向后移植到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.源码佐证当前仓库的模块卸载功能实现位于 kernel/feature/kernel_umount.c它通过extern int path_umount(struct path *path, int flags);声明直接调用该函数见 kernel_umount.c并在 zygote 派生出的应用进程中按模块挂载清单逐个执行try_umount。因此若目标内核缺少path_umount模块卸载功能将无法工作——这正是需要向后移植的根本原因。七、最终构建与验证完成上述所有修改后重新构建你的内核KernelSU 将会如期运作。最后的核对清单✅setup.sh已成功将 KernelSU 源码接入内核树drivers/kernelsu软链接、Makefile 与 Kconfig 条目✅ defconfig 中CONFIG_KSUy已启用手动集成时务必停用CONFIG_KPROBES以免音量键误触安全模式✅ 四个 hook 点do_execveat_common/do_faccessat/vfs_read/vfs_statx或其替代函数已按内核版本打上对应 patch✅ 已按需完成安全模式input_handle_event、pm命令修复devpts、path_umount向后移植等可选修改✅ 构建出的内核镜像可在目标设备正常开机且 kprobe 场景下无启动崩溃。附相关资源索引集成脚本 kernel/setup.sh支持--cleanup撤销与指定 tag/commit 切换内核配置项 kernel/KconfigCONFIG_KSU依赖KPROBES EXT4_FS可编译为模块或KSU_DEBUG等子项su 兼容 hook 实现 kernel/feature/sucompat.c 与 kernel/feature/sucompat.hksud 运行时集成与安全模式 kernel/runtime/ksud_integration.c模块卸载与path_umount调用 kernel/feature/kernel_umount.c相关官方文档 安装指南、GKI 集成相关说明【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价