Skip to content

集成指导 ​

SukiSU-Ultra 可以集成到 GKI 和非 GKI 内核中。前提条件:开源的、可启动的内核。

只想在 GKI 设备上使用?

无需自行构建内核,可通过管理器以可加载内核模块 (LKM) 方式安装:见 LKM 安装。

有些 OEM 定制会使大量内核代码游离于上游 Linux 或 ACK 之外。因此非 GKI 内核高度碎片化,缺乏通用的构建方法,我们无法提供非 GKI 内核的启动映像。

选择分支 ​

分支内核 Hook 方式典型用途
main运行时注册 Kprobes / kretprobes 与 sys_enter 系统调用 tracepoint,无需在内核源码中添加 hook 调用GKI 内核;可编译为模块 (LKM) 或内置
builtin不使用 kprobes,由内核源码调用 SukiSU-Ultra 的 hook 入口(手动 hook)需要手动修改的内核

已过时的选项

CONFIG_KSU_MANUAL_HOOK 和 CONFIG_KSU_TRACEPOINT_HOOK 在两个分支中都已不存在,旧版 Tracepoint Hook 教程使用的 ksu_trace.h 也已移除。仍要求启用它们的教程已过时。

main 分支 ​

要求 ​

  • CONFIG_KPROBES=y 和 CONFIG_EXT4_FS=y,CONFIG_KSU 依赖这两项。
  • 当内核提供时,hook 管理器会使用 kretprobes(CONFIG_KRETPROBES)和 sys_enter 系统调用 tracepoint(CONFIG_HAVE_SYSCALL_TRACEPOINTS)。
  • CONFIG_KSU 是 tristate:选 y 为内置,选 m 则编译为 kernelsu 模块。

添加到内核源码 ​

在内核源码根目录运行:

bash
bash
sh
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main

脚本会克隆仓库、链接到 drivers/kernelsu,并在 drivers/Makefile 与 drivers/Kconfig 中添加条目。使用 --cleanup 可撤销。

可选配置 ​

选项含义
CONFIG_KSU_MANUAL_SU(默认 y)使用手动 su:通过 prctl 授权对应的命令行和应用。
CONFIG_KPM启用 SukiSU KPM。需要 64 位内核,并会选中 CONFIG_KALLSYMS 与 CONFIG_KALLSYMS_ALL。可能影响系统稳定性。
CONFIG_KSU_DISABLE_MANAGER禁用管理器 APK 检测及管理器相关处理。
CONFIG_KSU_DISABLE_POLICY禁用按应用的配置。提权始终使用默认的完整 root 配置。
CONFIG_KSU_X86_PATCH_SYSCALL_DISPATCHER仅 x86_64。动态修补加固后的系统调用分发器以支持 syscall hook,可替代内核源码补丁,适用于 x86_64 的 LKM 模式。
CONFIG_KSU_DEBUG调试模式。

builtin 分支 ​

来源说明

本节手动 hook 集成教程来自 backslashxx 的 GitHub issue:scope-minimized manual hooks v2.3。

该分支不注册 kprobes。你需要在内核源码中为它的 hook 入口添加调用(手动 hook)。这些入口声明在该分支的头文件中(kernel/feature/sucompat.h、kernel/feature/kernel_umount.h、kernel/runtime/ksud.h 等),请对照你所构建分支的实际函数签名,下文补丁仅供参考。

先将该分支添加到你的源码树:

bash
bash
sh
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s builtin

该分支还包含 SUSFS 选项(CONFIG_KSU_SUSFS 及其子选项)和需要 64 位内核的 CONFIG_KPM。

开始之前 ​

  • 如果你在为 6.8+ 的非 ARM64 设备构建,请确保同时应用:https://github.com/backslashxx/KernelSU/issues/7
  • 在 ARM / ARM64 上,启用 CONFIG_KSU_TAMPER_SYSCALL_TABLE=y 可以跳过以下 hook:execve、sys_faccessat、sys_newfstatat、sys_newfstat ret_hook、sys_reboot、slow_avc_audit。启用后务必移除对应的 hook。
  • 在 4.19+ ARM64 上,可以尝试 CONFIG_KSU_HACK_ARM64_BRANCH_LINK=y 替代,可跳过全部下面的手动 hook。
  • 内核回移植情况各异,请选用与你的内核版本相近的补丁。

🟢 execve hook ​

  • 用于 sucompat。
  • 二选一,按你的内核版本选用;如果你的内核回移植了相关改动,按内核中实际存在的函数选用。
  • 如果你在自定义 ROM 上且需要隐藏 selinuxfs 上的 adb root,可安装 此 ksu 模块。

3.19+(通过 do_execveat_common):

patch
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1728,6 +1728,11 @@
/*
 * sys_execve() executes a new program.
 */
static int do_execveat_common(int fd, struct filename *filename,
			struct user_arg_ptr argv,
			struct user_arg_ptr envp,
			int flags)
{
	char *pathbuf = NULL;
	struct linux_binprm *bprm;
	struct file *file;
	struct files_struct *displaced;
	int retval;

+#ifdef CONFIG_KSU
+	extern int ksu_handle_execveat(int *, struct filename **, void *, void *, int *);
+	ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
+#endif
+
	if (IS_ERR(filename))
		return PTR_ERR(filename);

3.19+(通过 __do_execve_file):

patch
--- fs/exec.c
+++ fs/exec.c
@@ -1921,11 +1921,20 @@

/*
 * sys_execve() executes a new program.
 */
static int __do_execve_file(int fd, struct filename *filename,
			struct user_arg_ptr argv,
			struct user_arg_ptr envp,
			int flags, struct file *file)
{
	char *pathbuf = NULL;
	struct linux_binprm *bprm;
	struct files_struct *displaced;
	int retval;

+#ifdef CONFIG_KSU
+	extern int ksu_handle_execveat(int *, struct filename **, void *, void *, int *);
+	ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
+#endif
+
	if (IS_ERR(filename))
		return PTR_ERR(filename);

3.18(通过 do_execve_common):

  • 对 3.18,可将上游 do_execveat_common 的 hook 复用到 do_execve_common。
  • ≤ 3.18 没有 sys_execveat,所以这样比 hook sys_execve + compat_sys_execve 更合理。
  • 注意形参是 struct filename *filename。
patch
--- a/fs/exec.c
+++ b/fs/exec.c
/*
 * sys_execve() executes a new program.
 */
static int do_execve_common(struct filename *filename,
				struct user_arg_ptr argv,
				struct user_arg_ptr envp)
{
	struct linux_binprm *bprm;
	struct file *file;
	struct files_struct *displaced;
	int retval;

+#ifdef CONFIG_KSU
+	extern int ksu_handle_execveat(int *, struct filename **, void *, void *, int *);
+	ksu_handle_execveat((int *)AT_FDCWD, &filename, &argv, &envp, 0);
+#endif
+
	if (IS_ERR(filename))
		return PTR_ERR(filename);

3.0 ~ 3.10(通过 do_execve_common):

  • 针对 ≤ 3.10,仓库提供了 do_execve_common 的 handler。
  • ≤ 3.18 没有 sys_execveat,所以这样比 hook sys_execve + compat_sys_execve 更合理。
  • 注意形参是 const char *filename。
patch
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1615,6 +1615,12 @@
/*
 * sys_execve() executes a new program.
 */
static int do_execve_common(const char *filename,
				struct user_arg_ptr argv,
				struct user_arg_ptr envp)
{
	struct linux_binprm *bprm;
	struct file *file;
	struct files_struct *displaced;
	bool clear_in_exec;
	int retval;
	const struct cred *cred = current_cred();

+#ifdef CONFIG_KSU
+	extern int ksu_legacy_execve_sucompat(const char **, void *, void *);
+	ksu_legacy_execve_sucompat(&filename, (void *)&argv, (void *)&envp);
+#endif
+
	/*
	 * We move the actual failure in case of RLIMIT_NPROC excess from
	 * set*uid() to execve() because too many poorly written programs

🟢 sys_faccessat hook ​

  • 用于 sucompat。
  • 即使你的内核里有 do_faccessat,也请 hook sys_faccessat,以缩小拦截范围。
  • 如果你的内核回移植了相关改动,按内核中实际存在的函数选用。

5.10+:

patch
--- a/fs/open.c
+++ b/fs/open.c
@@ -483,6 +483,10 @@

SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
{
+#ifdef CONFIG_KSU
+	extern int ksu_handle_faccessat(int *, const char __user **, int *, int *);
+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+#endif
	return do_faccessat(dfd, filename, mode, 0);
}

4.19+:

patch
--- a/fs/open.c
+++ b/fs/open.c
@@ -483,6 +483,10 @@

SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
{
+#ifdef CONFIG_KSU
+	extern int ksu_handle_faccessat(int *, const char __user **, int *, int *);
+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+#endif
	return do_faccessat(dfd, filename, mode);
}

4.14 及更早:

patch
--- a/fs/open.c
+++ b/fs/open.c
/*
 * access() needs to use the real uid/gid, not the effective uid/gid.
 * We do this by temporarily clearing all FS-related capabilities and
 * switching the fsuid/fsgid around to the real ones.
 */
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
{
	const struct cred *old_cred;
	struct cred *override_cred;
	struct path path;
	struct inode *inode;
	int res;
	unsigned int lookup_flags = LOOKUP_FOLLOW;

+#ifdef CONFIG_KSU
+	extern int ksu_handle_faccessat(int *, const char __user **, int *, int *);
+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+#endif
+
	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
		return -EINVAL;

🟢 sys_newfstatat hook ​

  • 用于 sucompat。
  • 缩小拦截范围后,你需要 hook sys_newfstatat,而不是 vfs_statx()。
  • 如需 32 位 su 支持,可选 hook sys_fstatat64。
patch
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -371,6 +371,11 @@
#if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT)
SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
		struct stat __user *, statbuf, int, flag)
{
	struct kstat stat;
	int error;

+#ifdef CONFIG_KSU
+	extern int ksu_handle_stat(int *, const char __user **, int *);
+	ksu_handle_stat(&dfd, &filename, &flag);
+#endif
+
	error = vfs_fstatat(dfd, filename, &stat, flag);
	if (error)
		return error;
	return cp_new_stat(&stat, statbuf);
}
#endif

@@ -515,6 +520,11 @@
SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
		struct stat64 __user *, statbuf, int, flag)
{
	struct kstat stat;
	int error;

+#ifdef CONFIG_KSU // for 32-bit
+	extern int ksu_handle_stat(int *, const char __user **, int *);
+	ksu_handle_stat(&dfd, &filename, &flag);
+#endif
+
	error = vfs_fstatat(dfd, filename, &stat, flag);
	if (error)
		return error;
	return cp_new_stat64(&stat, statbuf);
}

🟢 sys_newfstat ret_hook ​

  • Android 16 及更新版本需要。
  • 在 fs/stat.c 中 hook newfstat 的返回前位置。
  • 如需 32 位 / 32-on-64 支持,可选 hook sys_fstat64(通常并不需要)。
  • 无需担心 #pragma GCC,在 Clang 下同样有效。
patch
diff --git a/fs/stat.c b/fs/stat.c
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -386,6 +392,14 @@
SYSCALL_DEFINE2(newfstat, unsigned int, fd, struct stat __user *, statbuf)
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

	if (!error)
		error = cp_new_stat(&stat, statbuf);

+#if defined(CONFIG_KSU) && !defined(CONFIG_KSU_KPROBES_KSUD)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
+	extern void ksu_handle_newfstat_ret(unsigned int *, struct stat __user **);
+	ksu_handle_newfstat_ret(&fd, &statbuf);
+#pragma GCC diagnostic pop
+#endif
+
	return error;
}

@@ -506,6 +520,14 @@
SYSCALL_DEFINE2(fstat64, unsigned long, fd, struct stat64 __user *, statbuf)
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

	if (!error)
		error = cp_new_stat64(&stat, statbuf);

+#if defined(CONFIG_KSU) && !defined(CONFIG_KSU_KPROBES_KSUD) // for 32-bit
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
+	extern void ksu_handle_fstat64_ret(unsigned long *, struct stat64 __user **);
+	ksu_handle_fstat64_ret(&fd, &statbuf);
+#pragma GCC diagnostic pop
+#endif
+
	return error;
}

🟢 sys_reboot hook ​

  • 新 KernelSU supercall(12143 引入)需要此 hook。
  • 在 sys_reboot 入口处添加调用即可。

3.18+(kernel/reboot.c):

patch
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -284,6 +284,11 @@
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
		void __user *, arg)
{
	struct pid_namespace *pid_ns = task_active_pid_ns(current);
	char buffer[256];
	int ret = 0;

+#if defined(CONFIG_KSU) && !defined(CONFIG_KSU_KPROBES_KSUD)
+	extern int ksu_handle_sys_reboot(int, int, unsigned int, void __user **);
+	ksu_handle_sys_reboot(magic1, magic2, cmd, &arg);
+#endif
+
	/* We only trust the superuser with rebooting the system. */
	if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
		return -EPERM;

3.0 ~ 3.10(kernel/sys.c):

patch
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -463,6 +463,11 @@
 *
 * reboot doesn't sync: do that yourself before calling this.
 */
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
		void __user *, arg)
{
	struct pid_namespace *pid_ns = task_active_pid_ns(current);
	char buffer[256];
	int ret = 0;

+#if defined(CONFIG_KSU) && !defined(CONFIG_KSU_KPROBES_KSUD)
+	extern int ksu_handle_sys_reboot(int, int, unsigned int, void __user **);
+	ksu_handle_sys_reboot(magic1, magic2, cmd, &arg);
+#endif
+
	/* We only trust the superuser with rebooting the system. */
	if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
		return -EPERM;

🟡 policy_rwlock(可选) ​

  • 可选,锦上添花但并非必需。
  • 主要用于 3.0 ~ 4.9,可能包括 4.14。
  • 多数 4.14 内核不需要此项(因为 ACK 回移植了 selinux_state)。
  • 找不到就不用管。
  • 基本上就是把 policy_rwlock 定义前的 static 去掉:
patch
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -74,7 +74,7 @@
 int selinux_android_netlink_route;
 int selinux_policycap_netpeer;
 int selinux_policycap_openperm;

-static DEFINE_RWLOCK(policy_rwlock);
+DEFINE_RWLOCK(policy_rwlock);

 static struct sidtab sidtab;
 struct policydb policydb;

🟡 slow_avc_audit(可选) ​

  • 可选。
  • ARM64 不需要。
  • 主要用于 ARM32,且可在用户空间处理。
  • 这不是标准的 KSU 功能,只是在此一并提供:
patch
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
/* This is the slow part of avc audit with big stack footprint */
noinline int slow_avc_audit(struct selinux_state *state,
			u32 ssid, u32 tsid, u16 tclass,
			u32 requested, u32 audited, u32 denied, int result,
			struct common_audit_data *a,
			unsigned int flags)
{
	struct common_audit_data stack_data;
	struct selinux_audit_data sad;

+#if defined(CONFIG_KSU) && !defined(CONFIG_KPROBES)
+	extern void ksu_slow_avc_audit(u32 *);
+	ksu_slow_avc_audit(&tsid);
+#endif
	if (!a) {
		a = &stack_data;
		a->type = LSM_AUDIT_DATA_NONE;

优化提示 ​

  • 如果你使用较新的 Clang 且启用了 FULL LTO,可以尝试提示优化器内联这些 hook。
  • 成功与否因编译器而异;如果生效,可以获得更好的优化:
patch
 #ifdef CONFIG_KSU
 	extern int ksu_handle_execveat(int *, struct filename **, void *, void *, int *);
-	ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
+	[[clang::always_inline]] ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
 #endif
patch
 #ifdef CONFIG_KSU
 	extern int ksu_handle_faccessat(int *, const char __user **, int *, int *);
-	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+	[[clang::always_inline]] ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
 #endif
patch
 #ifdef CONFIG_KSU
 	extern int ksu_handle_stat(int *, const char __user **, int *);
-	ksu_handle_stat(&dfd, &filename, &flag);
+	[[clang::always_inline]] ksu_handle_stat(&dfd, &filename, &flag);
 #endif

Kconfig 选项参考 ​

以下 Kconfig 来自 backslashxx 的 KernelSU 仓库(与该教程同源),供对照参考:

kconfig
menu "KernelSU"

config KSU
	tristate "KernelSU function support"
	depends on !CPU_BIG_ENDIAN
	depends on SECURITY_SELINUX
	select SECCOMP
	default y
	help
	  Enable kernel-level root privileges on Android System.

config KSU_KPROBES_KSUD
	bool "Enable dynamic kprobes for early boot hooks"
	depends on KSU
	depends on !KSU_TAMPER_SYSCALL_TABLE
	depends on !KSU_HACK_ARM64_BRANCH_LINK
	depends on KPROBES && KRETPROBES
	depends on DEPRECATED
	default n
	help
	  [deprecated] Use dynamic hooks via kprobes for functions only
	  on early boot. Hooks are unregistered at boot complete
	  to reduce overhead.

config KSU_HACK_ARM64_BRANCH_LINK
	bool "inline call-site hijacking (ARM64 bl insn)"
	depends on KSU
	depends on ARM64 && KALLSYMS
	depends on !KSU_TAMPER_SYSCALL_TABLE
	default y if KSU = m
	default n
	help
	  Attempts to provide sucompat by scanning kernel text and overwriting caller's
	  branch insn ('b' or 'bl') directly to our hooks, bypassing trampoline overhead
	  and CFI fuckups. Inspired by ARM64 optprobes.
	  Successful patching relies on symbol stability, so hooking success will
	  vary across different compliers, optimization, tweaks and backports.
	  When patching fails, it keeps syscall table hook for the target symbol.
	  Verified on 3.10 ~ 7.1. Recommended for 4.19+.

config KSU_TAMPER_SYSCALL_TABLE
	bool "tamper sys_call_table for sucompat + sys_reboot"
	depends on KSU
	depends on (ARM || ARM64)
	default n
	help
	  Use a high-performance syscall table hijacking method demonstrated on zx2c4's
	  kernel-assisted-superuser. Replaces sys_reboot, sys_execve, sys_newfstatat,
	  sys_faccessat, sys_newfstat_ret.
	  As syscalls are called directly, it avoids indirect branch (blr) overhead and
	  complies with Clang CFI.
	  Verified on 3.0 ~ 7.1. Recommended for 3.0 ~ 4.14.

config KSU_DISABLE_32BIT_HOOKS
	bool "disable 32-bit symbol hooking"
	depends on KSU
	depends on 64BIT && COMPAT
	depends on (KSU_TAMPER_SYSCALL_TABLE || KSU_HACK_ARM64_BRANCH_LINK)
	default n
	help
	  Skips hooking 32-bit syscalls (compat_sys_call_table). Mostly useful for zygote64-only devices.
	  Author assumes you know what you're doing, __builtin_assume(!!&user_brain).

config KSU_LSM_SECURITY_HOOKS
	bool "Use LSM security hooks"
	depends on KSU
	default y
	help
	  Disabling this is mostly useful for non-ARM64 with kernel > 6.8.
	  Make sure to implement manual hooks on security/security.c.

config KSU_FEATURE_SULOG
	bool "KernelSU SU Logging feature"
	depends on KSU
	default y
	help
	  Build KernelSU's SU Log.

config KSU_FEATURE_ADBROOT
	bool "KernelSU ADB Root feature"
	depends on KSU
	default y
	help
	  Build KernelSU's adb root feature.

config KSU_HOSTSREDIRECT
	bool "/system/etc/hosts redirection"
	depends on KSU
	depends on (ARM || ARM64)
	default n
	help
	  Kernel-level sys_openat fd redirect targeting /system/etc/hosts to /data/adb/hosts.
	  Allows hosts file modification without mounting hosts file directly, this is meant to
	  be used with bindhosts mode 3. If /data/adb/hosts does NOT exist at boot, the syscall
	  hook is automatically skipped. See like ZN-hostsredirect / APatch hosts file redirect.
	  If unsure, say n.

config KSU_ENABLE_FULL_UID_CHECKS
	bool "perform full uid checks (reduced performance)"
	depends on KSU
	default n
	help
	  Validate every non-root UID against the allowlist. When disabled, only shell (2000)
	  is checked. Recommended for permissive selinux / seccomp disabled scenarios.
	  Enable for extended security; keep disabled for performance.

config KSU_THRONE_TRACKER_ALWAYS_THREADED
	bool "Always run throne tracker in a kthread"
	depends on KSU
	default n
	help
	  Enable this option to run throne tracker in a kthread for the first
	  run, which happens at boot time / decryption stage. This can decrease
	  boot time, but can cause crowning failure on some FDE/FBEv1 setups.
	  If unsure, say n.

config KSU_NOPRINTK
	bool "disable ALL dmesg logging"
	depends on KSU
	depends on !KSU_DEBUG
	default n
	help
	  Disable KernelSU dmesg output.

config KSU_SHELL_HAS_SU_ALWAYS
	bool "give shell su"
	depends on KSU
	default n
	help
	  Always allow shell (2000) for su. Can speedup sucompat gate as a side-effect.

config KSU_DEBUG
	bool "KernelSU debug mode"
	depends on KSU
	default n
	help
	  Enable KernelSU debug mode.

config KSU_HEURISTIC_IN_TREE_BUILD
	bool "heuristic. do NOT touch"
	depends on KSU
	default y
	help
	  Note: if youre building out-of-tree, configure by editing ksu.c #defines instead.

config KPM
    bool "Enable SukiSU KPM"
    depends on KSU && 64BIT
    default n
    help
      Enabling this option will activate the KPM feature of SukiSU.
      This option is suitable for scenarios where you need to force KPM to be enabled.
      but it may affect system stability.
    select KALLSYMS
    select KALLSYMS_ALL

endmenu