惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MongoDB | Blog
MongoDB | Blog
AI
AI
B
Blog RSS Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Threatpost
I
Intezer
P
Proofpoint News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
S
Schneier on Security
Webroot Blog
Webroot Blog
Recorded Future
Recorded Future
aimingoo的专栏
aimingoo的专栏
L
Lohrmann on Cybersecurity
Simon Willison's Weblog
Simon Willison's Weblog
MyScale Blog
MyScale Blog
Project Zero
Project Zero
L
LangChain Blog
B
Blog
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
F
Fortinet All Blogs
美团技术团队
Engineering at Meta
Engineering at Meta
Cisco Talos Blog
Cisco Talos Blog
D
Docker
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
S
Security Affairs
Attack and Defense Labs
Attack and Defense Labs
N
News | PayPal Newsroom
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
量子位
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
W
WeLiveSecurity
V2EX - 技术
V2EX - 技术
TaoSecurity Blog
TaoSecurity Blog
博客园 - Franky
P
Proofpoint News Feed
Jina AI
Jina AI
Google DeepMind News
Google DeepMind News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
雷峰网
雷峰网
The Hacker News
The Hacker News
G
GRAHAM CLULEY

qlAD 的技术笔记

2026 年 6 月 7 日 -- 我有社交恐惧症,恐惧外面到处雷电风雨声(大学生活记录) 手把手带你实现哈希表(C/C++ 数据结构) 手把手带你实现图结构(C/C++ 数据结构) 手把手带你实现二叉树(C/C++ 数据结构) 手把手带你实现栈和队列(C/C++ 数据结构) 手把手带你实现单/双链表(C/C++ 数据结构) 手把手带你实现动态数组(C/C++ 数据结构) 2026 年 1 月 2 日 -- 我不怕冷,因为我内心炽热 【2025 年度总结】(大学生活记录) 2025 年 12 月 2 日 -- 省电模式:大一 100 天耗电日志(大学生活记录) 蓝桥杯备赛第一期 —— 枚举与模拟 2025 年 11 月 3 日 -- 用爱发电、大爱无私(大学生活记录) 使用循环嵌套输出规则图形找规律讲解(嵌套循环-图形输出) 2025 年 10 月 1 日 -- 二字开头第一年的生日记录及感悟随笔(大学生活记录) Python 初级 04 -- 目前为止 Python 中你们做题可能会遇到的问题以及七七八八 2025 年 9 月 24 日 -- 开学后的第一课及军训汇演(大学生活记录) Python 初级 03 -- 程序设计的三种结构之选择结构和循环结构 Python 初级 02 -- 基础的数据类型和输入输出 2025 年 9 月 17 日 -- 大连 2025 国际大体联足球世界杯(大学生活记录) Python 初级 01 -- 初识编程、理解计算机语言及程序运行方式 适合大一新生学习编程的前一课 -- 编程先导(编程的本质) 2025 年 9 月 12 日 -- 三天军训感受以及一些七七八八(大学生活记录) 2025 年 9 月 9 日 -- 体检、军训前的准备(大学生活记录) 适合大一新生的计算机基础知识 -- 个人认为足够版 2025 年 9 月 7 日 -- 既来之、则安之(大学生活记录) C++ 程序的内存布局 —— 代码区、全局/静态区、栈区和堆区 联想小新 pro 16 2025 开箱记录:跳过联网、office 激活、更换 win 11 专业版 R 语言中的数学函数 计算机软件著作权申请过程记录 从聚类到回归:用 Python 解析鸢尾花数据集的完整数据科学流程 条件概率、全概率与贝叶斯公式 —— 一篇文章带你死磕概率公式 高中化学物质反应规则表 —— 方程式之间也有规律可循 集合元素性质、关系、子集公式、基本运算 —— 一篇文章带你全方面死磕集合 如何为你的项目选择合适的许可证 -- 全方位指南 解释 3:1 和 9:3:3:1 的详细来源 —— 建立减数分裂与孟德尔遗传学定律之间的联系 重新设置磁盘分区以及 Btrfs 子卷(subvolume)布局的无损方案 零基础实战:用 Docker 启动 dockermailserver 绕过 25 端口封锁搭建个人邮箱服务器 基于 GitHub Actions + OSS 的自托管 APT 仓库全面指南 如何在安装 Debian 时配置 Btrfs 子卷 | 详细分步指南 一个合格的个人网站站长应该知道哪些事? CSS 基础入门教程 —— 选择器、样式声明 HTML 基础入门 —— 基本结构和语法 给博客添加一个文章数据统计的页面 Git 学习笔记(命令备忘表) Anki 牌组选项详细解释与设置 安卓类原生系统补全计划 —— PixelExperience(一加 OnePlus) 如何使用 use-sound 为 React 应用添加声音效果 标准化项目的 GitHub Flow 工作流 如何搭建设计自己的博客网站? 使用安知鱼主题同款的 Twikoo 评论组件 配置 GitHub Actions 实现自动化提交百度收录 第00期 | 环境搭建 & 递归 (一) | 基本数列递归 逐字符讲解 C 语言 HelloWorld 程序 2023 年回顾 🤟 Anki:记忆神器,助你高效学习 Gentoo 学习笔记 美化你的 PixelExperience OS Markdown 语法指南 搭建 OneDrive 目录索引 为什么用 65 表示大写字母 A
手动编译带有 KernelSU 的 OnePlus 6 内核
qlAD · 2025-01-11 · via qlAD 的技术笔记

cover

由于我 前几天写的文章前几天写的文章 中提到了 一加 6 如何启用 KernelSU。后经过本人测试,如果使用 kprobe 集成的方法编译出的内核会无法启动,而 kernelSU 官网中提到的手动修改源代码的方法编译出的内核会无法使用模块。所以干脆记录一下我的修改和编译过程。

理论上开源了的内核都可以用这个方法,尽量不要使用和系统不匹配的内核。

本文的方法不用编译整个安卓系统,仅编译内核。使用 GitHub Action 的方式编译,整个过程在 20 分钟之内。

一、拉取 PixelExperience 的内核源码

1、Fork 内核仓库

由于 PixelExperience 开源了内核,所以直接 fork 到自己的仓库即可。

https://github.com/PixelExperience-Devices/kernel_oneplus_sdm845

kernel_oneplus_sdm845

当然你也尝试选择 LineageOS 的内核:https://github.com/LineageOS/android_kernel_oneplus_sdm845

2、分支相关设置和说明(可选)

设置默认分支

由于 PixelExperience 官网中最后支持 OnePlus 6 的系统版本是 Android 13 ,并且手机也用的是 Android 13,所以可以设置默认分支为 thirteen。

其中 thirteen-wip 分支中的 wip 一般代表 work in progress

kernel_branches

删除不需要的分支

kernel_branches

理由同上,只用保留 thirteen 和 thirteen-wip 这两个分支。

3、clone 到本地

在你想要的目录下运行:

git clone -b thirteen --depth 1 https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

使用 -depth 1 选项仅拉取最近一次提交,不然下载的会很大。

二、手动修改源码

你可以使用喜欢的编译器来修改代码,比如 VSCode 或者 Vim。

1、将 KernelSU 添加到源码树

进入你的源码目录

使用以下命令下载相关文件和修改对应设置(官网提供)

curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5

非 GKI 设备官网支持到 v0.9.5,所以这里会下载 KernelSU(v0.9.5)

如果没有出错,会自动下载 KernelSU 到源码目录,并且修改了如下文件:

drivers/Kconfig:

source "drivers/oneplus/Kconfig"

+source "drivers/kernelsu/Kconfig"

endmenu

drivers/Makefile:

obj-$(CONFIG_SENSORS_SSC) += sensors/

obj-$(CONFIG_TEE) += tee/

obj-y += oneplus/

+

+obj-$(CONFIG_KSU) += kernelsu/

drivers/kernelsu:

2、修改对应的内核配置文件

内核配置文件 .config 中存放着一些参数的配置。选择对应手机的型号添加 kernleSU 的参数。

一般配置文件在 arch/arm64/configs 或者 arch/arm64/configs/vendor 中。

Oneplus 6 的代号是 enchilada 所以选择 arch/arm64/configs/enchilada_defconfig

CONFIG_LZ4_COMPRESS=y

CONFIG_LZ4_DECOMPRESS=y

CONFIG_DECOMPRESS_LZ4=y

+

+# KernelSU

+CONFIG_KSU=y

3、添加 KernelSU 的调用

主要是要改四个地方,更详细的可以看我的这次提交 Commit 465796c

  • faccessat,通常位于 fs/open.c
@@ -354,22 +354,31 @@
	}
	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
 * 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;
	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;
  • do_execveat_common,通常位于 fs/exec.c
@@ -1675,18 +1675,34 @@

/*
 * sys_execve() executes a new program.
 */
#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)
{
	char *pathbuf = NULL;
	struct linux_binprm *bprm;
	struct file *file;
	struct files_struct *displaced;
	int retval;

	#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
	if (IS_ERR(filename))
		return PTR_ERR(filename);
  • vfs_read,通常位于 fs/read_write.c
@@ -456,10 +456,21 @@ ssize_t __vfs_read(struct file *file, char __user *buf, size_t count,
}
EXPORT_SYMBOL(__vfs_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))
  • vfs_fstatat,通常位于 fs/stat.c
@@ -87,13 +87,21 @@ 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)
{
	struct path path;
	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、其他建议的修改

开启安全模式 drivers/input/input.c:

@@ -377,11 +377,21 @@ 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);

pm fs/devpts/inode.c:

@@ -548,25 +548,28 @@

	dentry = d_alloc_name(root, s);
	if (dentry) {
		dentry->d_fsdata = priv;
		d_add(dentry, inode);
		fsnotify_create(d_inode(root), dentry);
	} else {
		iput(inode);
		dentry = ERR_PTR(-ENOMEM);
	}

	return dentry;
}

extern int ksu_handle_devpts(struct inode*);
/**
 * devpts_get_priv -- get private data for a slave
 * @pts_inode: inode of the slave
 *
 * Returns whatever was passed as priv in devpts_pty_new for a given inode.
 */
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;

path_umount fs/namespace.c:

@@ -1711,6 +1711,40 @@ 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.

修改完所有代码后建议提交到远程。

三、编译并刷入内核

一共有两种方法,建议尝试第二种

1、本地编译(不推荐)

修改好代码后就可以编译内核了

make ARCH=arm64 O=out enchilada_defconfig
make ARCH=arm64 -j$(nproc) O=out

然后将编译出来的 Image.gz-dtb 通过 Anykernel3 制作成刷机包,通过 adb sideload 的方式刷入手机重启即可。

2、利用 GitHub Action

使用安卓内核编译模板,我选择的是 kernelSU_Action

如果全部是按照本文修改的话,配置文件只用改这几行

KERNEL_SOURCE=https://github.com/用户名/kernel_oneplus_sdm845
KERNEL_SOURCE_BRANCH=thirteen
KERNEL_CONFIG=enchilada_defconfig

SOURCE_BOOT_IMAGE=你提取的 boot.img 直链

然后开一个 Action 等待构建完成,我的构建时间如下:

action time

构建完成只会会自动打包 Anykernel3.zip

Anykernel3.zip

下载到电脑然后通过 adb sideload Anykernel3-xxxxxxxxxxxxxxx.zip 刷入重启即可

四、一些问题

通过上面的内容,你编译的内核就可以直接用了,KernelSU 也显示工作中。那么以下就是途中可能会遇到的一些问题。

1、当前 KernelSU 版本 16 过低

KernelSU 版本 16 过低

来源:#issues/1210

解决方案:将你的 KernelSU 文件夹作为 git 子模块使用,然后再重新编译

2、模块按钮无法使用

模块激活失败

来源:#issues/824

解决方案:见这次提交 Commit d959a55 修改 selinux 相关代码