跳转至

内核编译与KernelSU集成

内核编译

确认手机信息

在Android终端执行getprop | grep device,并寻找形似 ro.xx.device 这一行,里面的内容即为你的手机代号,这里我的手机是Google Pixel 3 ,型号如下所示

image-20230604002803199

之后需要获取设备架构,执行uname -m,这里我的设备显示是aarch64

最后一步是获取当前设备的内核版本,执行uname -r,这里我的设备显示是 4.9.337-NekoMiya+

同步内核源码

直接同步内核源码

先找到Pixel3的内核源码,由于这里用的是PixelExperience,所以到Github上能找到源码的目录 PixelExperience-Devices/kernel_google_bluecross: Dependency of Google Pixel 3 (blueline), Google Pixel 3 XL (crosshatch) (github.com)

如果想编译AOSP的内核,那么可以查看Google的官方指导构建内核 | Android 开源项目 | Android Open Source Project

分支的话有4个,这里选最新的 thirteen-plus

通过repo获取内核源码

由于我这边也需要编译整机代码,加上工具链本身也比较复杂,所以这里就一并都使用repo同步下来了。

image-20230604040736034

编译

由于我这边直接使用了repo,所以执行make bootimage,最后可以在指定的目录中找到boot.img

image-20230604053658885

KernelSU

这里我们参考官方的教程,链接为如何为非 GKI 内核集成 KernelSU,并使用 kprobe 集成

Kprobe配置

先进入到内核源码目录下,在根目录新建分支,这里命名为kernelsu

1
git checkout -b kernelsu # 此处新建一个分支,方便之后进行同步和更改

建立完成后执行如下命令

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

image-20230604041106417

接着检查内核是否开启了kprobe,在arch/arm64/configs/b1c1_defconfig中添加如下配置

1
2
3
4
# KPROBE config
CONFIG_KPROBES=y
CONFIG_HAVE_KPROBES=y
CONFIG_KPROBE_EVENTS=y

image-20230604045632803

修改文件

  • 修改build.config.bluecross

build.config.bluecross中注释掉POST_DEFCONFIG_CMDS这一行,否则编译会报错

image-20230604041459869

  • 修改fs/exec.c中的do_execveat_common

image-20230604053323694

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// kernelsu add
extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, 
            void *argv,
            void *envp, 
            int *flags);
// kernelsu end

/*
 * 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;

    // kernelsu add
    ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
    // kernelsu end

    if (IS_ERR(filename))
        return PTR_ERR(filename);
...
  • 修改fs/open.c,找到faccessat的系统调用定义并修改

image-20230604050614525

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// kernelsu add
extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, 
            int *mode,
            int *flags);
// kernelsu end

/*
 * 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;

    // kernelsu add
    ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
    // kernelsu end

    if (mode & ~S_IRWXO)
...
  • 修改fs/read_write.c中的vfs_read

image-20230604043617929

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// kernelsu add
extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
            size_t *count_ptr, loff_t **pos);
// kernelsu end

ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
    ssize_t ret;

    // kernelsu add
    ksu_handle_vfs_read(&file, &buf, &count, &pos);
    // kernelsu end
    if (!(file->f_mode & FMODE_READ))
...
  • 修改fs/stat.c中的vfs_fstatat(Pixel 3中适配的旧版本内核没有vfs_statx函数)

image-20230604044525839

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// kernelsu add
extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
// kernelsu end

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;

    // kernelsu add
    ksu_handle_stat(&dfd, &filename, &flag);
    // kernelsu end
...
  • 打开 KernelSU内置的安全模式,修改drivers/input/input.c中的input_handle_event 方法

image-20230604044930297

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// kernelsu add
extern int ksu_handle_input_handle_event(unsigned int *type, 
                    unsigned int *code, 
                    int *value);
// kernelsu end

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);

    // kernelsu add
    ksu_handle_input_handle_event(&type, &code, &value);
    // kernelsu end
    if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
        add_input_randomness(type, code, value);
...

编译的过程就和上面的步骤是一样的

结果验证

使用adb reboot bootloader重启到bootloader,将编译好的boot.img使用fastboot flash boot boot.img刷到手机里,启动手机,安装KernelSU管理器,即可看到KernelSU正常工作

image-20230604053409933

接下来就可以快乐玩耍了!

内核同步

在上述内核修改结束后,可以将定制的内核文件同步到自己的github上,方便以后的更新,修改和编译

1
2
3
4
$ git remote add kernelsu git@github.com:jygzyc/kernel_google_bluecross_KernelSU.git # 添加远程分支
$ git add .
$ git commit -m "KernelSU Adaptation"
$ git push -u kernelsu kernelsu:thirteen-plus # 首次将kernelsu分支推到远程kernelsu仓库的thirteen-plus分支

之后如果内核代码更新,那么就需要合并处理

1
2
3
4
5
6
$ git fetch pixel-devices thirteen-plus # 从远程仓库pixel-devices拉取thirteen-plus的代码

$ git checkout kernelsu  # 切换到本地分支
$ git merge # 将远程分支合并到本地分支

$ git push kernelsu kernelsu:thirteen-plus # 将kernelsu分支推到远程kernelsu仓库的thirteen-plus分支

参考文献

Android从零开始的内核编译 - 掘金 (juejin.cn)

如何为非 GKI 内核集成 KernelSU | KernelSU

AOSP编译保姆级教程(三)-KernelSU (qq.com)

评论