Intergrate for non-GKI devices
KernelSU can be integrated into non-GKI kernels and was backported to 4.14 and earlier versions.
Due to the fragmentation of non-GKI kernels, we don't have a universal method to build it, so we cannot provide the non-GKI boot.img. However, you can build the kernel with KernelSU integrated on your own.
First, you should be able to build a bootable kernel from kernel source code. If the kernel isn't open source, then it is difficult to run KernelSU for your device.
If you're able to build a bootable kernel, there are two ways to integrate KernelSU into the kernel source code:
- Automatically with
kprobe
- Manually
Integrate with kprobe
KernelSU uses kprobe to do kernel hooks, if kprobe runs well in your kernel, it's recommended to use it this way.
First, add KernelSU to your kernel source tree:
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5
INFO
KernelSU 1.0 and later versions no longer support non-GKI kernels. The last supported version isv0.9.5
, so make sure to use the correct version.
Then, you should check if kprobe is enabled in your kernel config. If it isn't, add these configs to it:
CONFIG_KPROBES=yCONFIG_HAVE_KPROBES=yCONFIG_KPROBE_EVENTS=y
Now, when you re-build your kernel, KernelSU should work correctly.
If you find that KPROBES is still not enabled, you can try enablingCONFIG_MODULES
. If that doesn't solve the issue, usemake menuconfig
to search for other KPROBES dependencies.
However, if you encounter a bootloop after integrating KernelSU, this may indicate that thekprobe is broken in your kernel, which means that you should fix the kprobe bug or use another way.
HOW TO CHECK IF KPROBE IS BROKEN?
Comment outksu_enable_sucompat()
andksu_enable_ksud()
inKernelSU/kernel/ksu.c
, if the device boots normally, then kprobe may be broken.
HOW TO GET MODULE UMOUNT FEATURE WORKING ON PRE-GKI?
If your kernel is older than 5.9, you should backportpath_umount
tofs/namespace.c
. This is required to get "Umount module" feature work correctly. If you don't backportpath_umount
, "Umount module" feature won't work. You can get more info on how to achieve this at the end of this page.
Manually modify the kernel source
If kprobe doesn't work on your kernel this may be caused by an upstream bug or if the kernel is below 4.8), then you can try the following:
First, add KernelSU to your kernel source tree:
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5
Keep in mind that, on some devices, your defconfig may be located atarch/arm64/configs
or in other cases, it may be atarch/arm64/configs/vendor/your_defconfig
. Regardless of the defconfig you're using, make sure to enableCONFIG_KSU
withy
to enable orn
to disable it. For example, if you choose to enable it, your defconfig should contain the following string:
# KernelSUCONFIG_KSU=y
Next, add KernelSU calls to the kernel source. Below are some patches for reference:
diff --git a/fs/exec.c b/fs/exec.cindex 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.cindex 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)/* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL;
diff --git a/fs/read_write.c b/fs/read_write.cindex 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.cindex 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;
You should find the four functions in kernel source:
do_faccessat
, usually infs/open.c
do_execveat_common
, usually infs/exec.c
vfs_read
, usually infs/read_write.c
vfs_statx
, usually infs/stat.c
If your kernel doesn't have thevfs_statx
function, usevfs_fstatat
instead:
diff --git a/fs/stat.c b/fs/stat.cindex 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;
For kernels eariler than 4.17, if you cannot finddo_faccessat
, just go to the definition of thefaccessat
syscall and place the call there:
diff --git a/fs/open.c b/fs/open.cindex 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)/* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL;
Safe Mode
To enable KernelSU's built-in Safe Mode, you should modify theinput_handle_event
function indrivers/input/input.c
:
TIP
It's strongly recommended to enable this feature, it's very useful for preventing bootloops!
diff --git a/drivers/input/input.c b/drivers/input/input.cindex 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);
ENTERING SAFE MODE ACCIDENTALLY?
If you're using manual integration and don't disableCONFIG_KPROBES
, the user will be able to trigger Safe Mode by pressing the volume down button after booting! Therefore, if you're using manual integration, it's necessary to disableCONFIG_KPROBES
!
Failed to executepm
in terminal?
You should modifyfs/devpts/inode.c
. Reference:
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.cindex 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;
How to backport path_umount
You can make the "Umount modules" feature work on pre-GKI kernels by manually backportingpath_umount
from 5.9. You can use this patch as reference:
--- 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 mustn't 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.
Finally, build your kernel again, and KernelSU should work correctly.