尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
KernelSU 非 GKI 内核集成完全指南:kprobe 自动集成与手动源码补丁双方案解析
KernelSU 非 GKI 内核集成完全指南kprobe 自动集成与手动源码补丁双方案解析【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSUKernelSU 是一款基于内核的 Android Root 方案除了面向 GKIGeneric Kernel Image设备的主流路线外社区长期以来也支持将其集成进非 GKI 内核。本文以 KernelSU 官方非 GKI 集成文档为核心系统讲解两种集成路径借助kprobe的自动集成以及逐个函数打补丁的手动集成覆盖从setup.sh接入、Kconfig 配置、四处关键 hook 补丁到安全模式、pm命令修复与path_umount回移的全部细节。读完本文你将具备从设备内核源码出发自行编译出带 KernelSU 功能内核的完整实操能力。::: warning 重要声明 本文所依据的官方文档中文原档 / 英文原档仅供存档参考不再维护更新。自 KernelSU v1.0 版本之后官方已放弃对非 GKI 设备的官方支持最后的支持版本为v0.9.5。因此本文所有命令均以v0.9.5为准。 :::集成前提与可行性评估KernelSU 可以被集成到非 GKI 内核中官方文档说明其最低支持到内核4.14版本理论上也可以支持更低的版本。由于非 GKI 内核碎片化极其严重通常没有统一的方法来编译它因此 KernelSU 官方无法为非 GKI 设备提供现成的 boot 镜像你需要自己集成 KernelSU 然后编译内核使用。在开始之前必须满足一个硬性前提你必须有能力从你设备的内核源码编译出一个可以开机并且能正常使用的内核。如果你的设备内核不开源这通常难以做到——因为没有源码补丁与编译都无从谈起。如果上述准备已经就绪有两种方法可以将 KernelSU 集成到内核之中借助kprobe自动集成手动修改内核源码。下文将分别展开。方法一使用 kprobe 自动集成KernelSU 使用 kprobe 机制来做内核相关的 hook当前仓库中可看到对register_kprobe、unregister_kprobe的直接使用例如 kernel/supercall/supercall.c 中的reboot_kp、kernel/runtime/ksud_integration.c 中的input_event_kp。如果 kprobe 可以在你编译的内核中正常运行那么推荐用这个方法来集成因为它无需改动内核源码成本最低。第一步把 KernelSU 添加到内核源码树在内核的根目录执行以下命令curl -LSs https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh | bash -s v0.9.5::: info 版本说明 KernelSU 1.0 及更高版本已经不再支持非 GKI 内核官方 issue #1705 中有详细说明最后的支持版本为v0.9.5请务必使用正确的版本不要使用main分支的最新代码。 :::这条命令实际执行的是仓库根目录下的 kernel/setup.sh 脚本。从脚本源码看它完成的工作包括自动探测内核源码树中drivers/目录的位置$GKI_ROOT/common/drivers或$GKI_ROOT/drivers见 kernel/setup.sh克隆 KernelSU 仓库并git checkout到你指定的 tag 或 commit如v0.9.5在drivers/下创建指向KernelSU/kernel的符号链接kernelsu在drivers/Makefile追加obj-$(CONFIG_KSU) kernelsu/在drivers/Kconfig的endmenu前插入source drivers/kernelsu/Kconfig。该脚本同样支持清理与更新# 撤销脚本之前做的所有修改删除符号链接、还原 Makefile/Kconfig、删除 KernelSU 目录 bash setup.sh --cleanup # 不带参数时切换到最新的 tag 版本 bash setup.sh # 指定某个 commit 或 tag bash setup.sh commit-or-tag第二步检查并开启 kprobe 相关内核配置然后你需要检查你的内核是否开启了 kprobe 相关的配置如果没有开启需要在内核 defconfig或.config中添加以下配置CONFIG_KPROBESy CONFIG_HAVE_KPROBESy CONFIG_KPROBE_EVENTSyKernelSU 的 Kconfig 中明确写着KSU依赖KPROBES EXT4_FS见 kernel/Kconfig即 kprobe 是 KernelSU 内核 hook 能力的基石同时CONFIG_EXT4_FS也是被需要的用于ext4_unregister_sysfs。如果发现 KPROBES 仍未生效很可能是因为它的依赖项CONFIG_MODULES没有被启用如果还是没有生效请执行make menuconfig搜索 KPROBES 的其它依赖并逐一启用。第三步重新编译内核配置就绪后重新编译你的内核即可。编译出的 boot 镜像即可刷入设备使用。开机失败如何验证是不是 kprobe 的锅如果你在集成 KernelSU 之后手机无法启动bootloop那么很可能你的内核中kprobe 工作不正常。此时你需要修复这个 bug或者改用下面介绍的第二种方法手动修改内核源码。官方文档给出了一个非常实用的验证手段注释掉KernelSU/kernel/ksu.c中ksu_sucompat_init()和ksu_ksud_init()的调用如果正常开机那么就是 kprobe 的问题或者你可以手动尝试使用 kprobe 功能如果不正常手机会直接重启。在 v0.9.5 中这两个函数即位于KernelSU/kernel/ksu.c。作为对照在当前仓库的重构版本中ksu_sucompat_init()定义于 kernel/feature/sucompat.cksu_ksud_init()定义于 kernel/runtime/ksud_integration.c二者的调用点集中在 kernel/core/init.c 的初始化流程中——理解这条调用链有助于你在旧版本上快速定位需要注释的位置。方法二手动修改内核源码如果 kprobe 工作不正常通常是上游的 bug 或者内核版本过低那你可以尝试手动修改内核源码的方式集成。这种方式虽然繁琐但绕开了对 kprobe 的依赖兼容性更强。第一步添加 KernelSU 到内核源码树与 kprobe 方式相同在内核根目录执行curl -LSs https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh | bash -s v0.9.5第二步在 defconfig 中启用 CONFIG_KSU请注意某些设备的 defconfig 文件可能位于arch/arm64/configs/设备代号_defconfig或位于arch/arm64/configs/vendor/设备代号_defconfig。在你的 defconfig 文件中将CONFIG_KSU设置为y以启用 KernelSU或设置为n以禁用。例如在某个 defconfig 中arch/arm64/configs/...# KernelSU CONFIG_KSUy关于CONFIG_KSU的取值与衍生选项可以参考当前仓库的 kernel/Kconfig它定义了一个完整的 KernelSU 配置菜单配置项含义CONFIG_KSU三态选项tristate可编译进内核y或编译为内核模块kernelsuM依赖KPROBES EXT4_FS默认 yCONFIG_KSU_DEBUG开启 KernelSU 调试模式默认 nCONFIG_KSU_DISABLE_MANAGER禁用 KernelSU 管理器集成root 将替代仅管理器可用功能默认 nCONFIG_KSU_DISABLE_POLICY禁用按应用的 root/非 root 配置提权始终使用默认完整 root profile默认 n对于非 GKI 手动集成场景核心就是保证CONFIG_KSUy与其依赖的CONFIG_KPROBES、CONFIG_EXT4_FS处于开启状态。第三步将 KernelSU 调用添加到内核源代码接下来是手动集成的核心步骤——在内核源码的若干 VFS 关键路径上插入 KernelSU 的 hook 调用。官方提供了以下补丁作为参考主要是要改四个地方do_faccessat通常位于fs/open.cdo_execveat_common通常位于fs/exec.cvfs_read通常位于fs/read_write.cvfs_statx通常位于fs/stat.c::: code-groupdiff --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); }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;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))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;:::内核没有 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); #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系统调用的定义然后修改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;启用 KernelSU 内置安全模式强烈建议要使用 KernelSU 内置的安全模式你还需要修改drivers/input/input.c中的input_handle_event方法::: tip 强烈建议开启此功能对用户救砖会非常有帮助 :::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 得到印证内核侧对KEY_VOLUMEDOWN音量下键的按键事件计数当连续按下次数达到阈值volumedown_pressed_count 3时判定进入安全模式ksu_is_safe_mode()返回 true从而在启动早期禁用可能导致 bootloop 的模块/功能。::: info 莫名其妙进入安全模式 如果你采用手动集成的方式并且没有禁用CONFIG_KPROBES那么用户在开机之后按音量下也可能触发安全模式因为 kprobe 路线同样会注册 input 相关的 kprobe参见 kernel/runtime/ksud_integration.c 中通过register_kprobe注册的input_event_kp。因此如果使用手动集成你需要关闭CONFIG_KPROBES :::修复终端中pm命令执行失败如果集成后终端里执行pm命令失败你需要同时修改fs/devpts/inode.c补丁如下diff --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; } extern int ksu_handle_devpts(struct inode*); /** * 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) { ksu_handle_devpts(dentry-d_inode); if (dentry-d_sb-s_magic ! DEVPTS_SUPER_MAGIC) return NULL; return dentry-d_fsdata;为 pre-GKI 内核回移 path_umount模块卸载功能你可以通过从内核 5.9 向旧版本移植path_umount在 GKI 之前的内核上获得卸载模块的功能。如果内核版本低于 5.9 且不执行此回移Umount module 功能将无法正常工作。可以通过以下补丁作为参考将它应用到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.该补丁实现了两个函数can_umount()负责权限与合法性校验校验 flags 组合、may_mount()挂载权限、挂载点根目录、check_mnt()、MNT_LOCKED标志以及MNT_FORCE所需的CAP_SYS_ADMIN能力path_umount()则在校验通过后调用do_umount()完成真正的卸载并用dput()/mntput_no_expire()手动释放引用刻意不调用path_put()以避免清除mnt_expiry_mark。重新编译与验证所有补丁应用完毕后重新编译你的内核即可# 以常见流程为例具体命令取决于你的内核构建系统与工具链 make ARCHarm64 your_defconfig make ARCHarm64 -j$(nproc) # 打包出 boot.img 后刷入设备编译通过并刷机成功后安装 KernelSU 管理器即可正常使用 root 能力。常见问题速查现象原因与对策刷入后无法开机bootloop很可能是内核 kprobe 工作不正常先注释ksu_sucompat_init()与ksu_ksud_init()验证定位再决定修复 kprobe 或改用手动集成CONFIG_KPROBES始终无法启用检查其依赖CONFIG_MODULES是否开启或用make menuconfig搜索 KPROBES 的其它依赖手动集成后按音量下误入安全模式手动集成时未关闭CONFIG_KPROBES需要在 defconfig 中显式禁用终端执行pm命令失败缺少fs/devpts/inode.c中ksu_handle_devpts的调用补丁旧内核上卸载模块功能无效内核版本低于 5.9需要按上文回移path_umount到fs/namespace.cdefconfig 位置找不到部分设备 defconfig 在arch/arm64/configs/下的vendor/子目录中最后再次提醒以上流程针对的是v0.9.5及更早的非 GKI 内核支持路线KernelSU v1.0 起官方已停止非 GKI 支持。如需了解当前版本的 GKI 集成方式请参考仓库 website/docs 目录下的最新文档如 installation。【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

408数据结构复杂度分析:时间复杂度与空间复杂度全攻略

408数据结构复杂度分析:时间复杂度与空间复杂度全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/13 15:04:53
三步做出第一个换脸视频:roop 人脸替换完整新手指南

三步做出第一个换脸视频:roop 人脸替换完整新手指南

三步做出第一个换脸视频:roop 人脸替换完整新手指南 【免费下载链接】roop one-click face swap 项目地址: https://gitcode.com/GitHub_Trending/ro/roop roop 的核心是单图换脸:一张人脸照片加一个目标视频,跑一条命令就能产出替换好…

📅 2026/9/13 15:04:53
SpringBoot+Vue垃圾分类回收网站开发指南

SpringBoot+Vue垃圾分类回收网站开发指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/13 15:04:53
MORE NEWS

更多资讯

📰

PeakTech P1245台式示波器:面向产线、教学与维修的实战型中端选择

1. 这台“P1245”不是玩具,是能扛起产线调试、教学演示和维修诊断三块硬骨头的台式示波器PeakTech台式示波器P1245——光看型号名,很多人第一反应是“又一个德国牌子?”,但实际它背后是德国PeakTech公司深耕电子测试仪器领域三十多…

📰

LangChain Pregel引擎Agent状态写入机制详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Sa-Token 最新版本说明与 Maven 依赖引入实战指南

Sa-Token 最新版本说明与 Maven 依赖引入实战指南 【免费下载链接】Sa-Token ✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集…

📰

Matlab Robotics Toolbox机械臂GUI仿真原理与实战指南

简介:本资源是一套基于MATLAB Robotics Toolbox开发的机械臂仿真GUI工具箱,面向计算机、电子信息工程及自动化等专业的高年级本科生与研究生,适用于机器人学课程设计、毕业设计及科研原型验证等场景。压缩包共989个文件,涵盖657个…

📰

使用 Leptos 与 Rust/Wasm 进行浏览器与 VSCode 断点调试:counter_dwarf_debug 实战指南

使用 Leptos 与 Rust/Wasm 进行浏览器与 VSCode 断点调试:counter_dwarf_debug 实战指南 【免费下载链接】leptos Build fast web applications with Rust. 项目地址: https://gitcode.com/GitHub_Trending/le/leptos 本文基于 Leptos 仓库中的 counter_dwar…

📰

Iosevka 25.0.0 版本全解析:变体命名重构、长 s 与西里尔字母变体扩充、新字符集与字形修复

Iosevka 25.0.0 版本全解析:变体命名重构、长 s 与西里尔字母变体扩充、新字符集与字形修复 【免费下载链接】Iosevka Versatile typeface for code, from code. 项目地址: https://gitcode.com/GitHub_Trending/io/Iosevka Iosevka 25.0.0 是该项目 25.x 系…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬