add main branch files

This commit is contained in:
樱檩殇雪
2025-03-17 02:38:37 +08:00
commit a6e3221bdc
360 changed files with 44453 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
# App Profile
App Profile 是 KernelSU 提供的一种针对各种应用自定义其使用配置的机制。
对授予了 root 权限(也即可以使用 `su`的应用来说App Profile 也可以称之为 Root Profile它可以自定义 `su``uid`, `gid`, `groups`, `capabilities` 以及 `SELinux` 规则,从而限制 root 用户的权限;比如可以针对防火墙应用仅授予网络权限,而不授予文件访问权限,针对冻结类应用仅授予 shell 权限而不是直接给 root通过最小化权限原则**把权力关进笼子里**。
对于没有被授予 root 权限的普通应用App Profile 可以控制内核以及模块系统对此应用的行为;比如是否需要针对此应用卸载模块造成的修改等。内核和模块系统可以通过此配置决定是否要做一些类似“隐藏痕迹”类的操作。
## Root Profile
### UID、GID 和 groups
Linux 系统中有用户和组两个概念。每个用户都有一个用户 ID(UID),一个用户可以属于多个组,每个组也有组 ID(GID)。该 ID 用于识别系统的用户并确定用户可以访问哪些系统资源。
UID 为 0 的用户被称之为 root 用户GID 为 0 的组被称之为 root 组root 用户组通常拥有系统的最高权限。
对于 Android 系统来说,每一个 App 都是一个单独的用户(不考虑 share uid 的情况),拥有一个唯一的 UID。比如 `0` 是 root 用户,`1000``system``2000` 是 ADB shell10000-19999 的是普通用户。
:::info
此处的 UID 跟 Android 系统的多用户或者说工作资料Work Profile不是一个概念。工作资料实际上是对 UID 进行分片实现的,比如 10000-19999 是主用户110000-119999 是工作资料;他们中的任何一个普通应用都拥有自己独有的 UID。
:::
每一个 App 可以有若干个组GID 是其主要的组,通常与 UID 一致;其他的组被称之为补充组 (groups)。某些权限是通过组控制的,比如网络访问,蓝牙等。
例如,如果我们在 ADB shell 中执行 `id` 命令,会得到如下输出:
```sh
oriole:/ $ id
uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
```
其中UID 为 `2000`GID 也即主要组 ID 也为 `2000`;除此之外它还在很多补充组里面,例如 `inet` 组代表可以创建 `AF_INET``AF_INET6` 的 socket访问网络`sdcard_rw` 代表可以读写 sdcard 等。
KernelSU 的 Root Profile 可以自定义执行 `su` 后 root 进程的 UID, GID 和 groups。例如你可以设置某个 root 应用的 Root Profile 其 UID 为 `2000`,这意味着此应用在使用 `su` 的时候,它的实际权限是 ADB Shell 级别;你可以去掉 groups 中的 `inet`,这样这个 `su` 就无法访问网络。
:::tip 注意
App Profile 仅仅是控制 root 应用使用 `su` 后的权限,它并非控制 App 本身的权限!如果 App 本身申请了网络访问权限,那么它即使不使用 `su` 也可以访问网络;为 `su` 去掉 `inet` 组仅仅是让 `su` 无法访问网络。
:::
与应用通过 `su` 主动切换用户或者组不同Root Profile 是在内核中强制实施的,不依赖 root 应用的自觉行为,`su` 权限的授予完全取决于用户而非开发者。
### Capabilities
Capabilities 是 Linux 的一种分权机制。
传统的 UNIX 系统为了执行权限检查,将进程分为两类:特权进程(其有效用户 ID 为 0称为超级用户或 root和非特权进程其有效 UID 为非零)。特权进程会绕过所有内核权限检查,而非特权进程则根据其凭据(通常是有效 UID、有效 GID 和补充组列表)进行完整的权限检查。
从 Linux 2.2 开始Linux 将传统上与超级用户关联的特权分解为独立的单元,称为 Capabilities有的也翻译为“权能”它们可以独立启用和禁用。
每一个 Capability 代表一个或者一类权限。比如 `CAP_DAC_READ_SEARCH` 就代表是否有能力绕过文件读取权限检查和目录读取和执行权限检查。如果一个有效 UID 为 `0` 的用户root 用户)没有 `CAP_DAC_READ_SEARCH` 或者更高 Capalities这意味着即使它是 root 也不能随意读取文件。
KernelSU 的 Root Profile 可以自定义执行 `su` 后 root 进程的 Capabilities从而实现只授予“部分 root 权限”。与上面介绍的 UID, GID 不同,某些 root 应用就是需要 `su` 后 UID 是 `0`,此时我们可以通过限制这个 UID 为 `0` 的 root 用户的 Capabilities就可以限制它能够执行的操作。
:::tip 强烈建议
Linux 系统关于 Capability 的 [官方文档](https://man7.org/linux/man-pages/man7/capabilities.7.html),解释了每一项 Capability 所代表的能力,写的非常详细,如果你想要自定义 Capabilities请务必先阅读此文档。
:::
### SELinux
SELinux 是一种强大的强制性权限访问控制MAC机制。它按照**默认拒绝**的原则运行:任何未经明确允许的行为都会被拒绝。
SELinux 可按两种全局模式运行:
1. 宽容模式:权限拒绝事件会被记录下来,但不会被强制执行。
2. 强制模式:权限拒绝事件会被记录下来**并**强制执行。
:::warning 警告
现代的 Android 系统极度依赖 SELinux 来保障整个系统的安全性,我们强烈建议您不要使用任何以“宽容模式”运行的自定义系统,因为那样与裸奔没什么区别。
:::
SELinux 的完整概念比较复杂,我们这里不打算讲解它的具体工作方式,建议你先通过以下资料来了解其工作原理:
1. [wikipedia](https://en.wikipedia.org/wiki/Security-Enhanced_Linux)
2. [Redhat: what-is-selinux](https://www.redhat.com/en/topics/linux/what-is-selinux)
3. [ArchLinux: SELinux](https://wiki.archlinux.org/title/SELinux)
KernelSU 的 Root Profile 可以自定义执行 `su` 后 root 进程的 SELinux context并且可以针对这个 context 设置特定的访问控制规则,从而更加精细化地控制 root 权限。
通常情况下,应用执行 `su` 后,会将进程切换到一个 **不受任何限制** 的 SELinux 域,比如 `u:r:su:s0`,通过 Root Profile我们可以将它切换到一个自定义的域比如 `u:r:app1:s0`,然后为这个域制定一系列规则:
```sh
type app1
enforce app1
typeattribute app1 mlstrustedsubject
allow app1 * * *
```
注意:此处的 `allow app1 * * *` 仅仅作为演示方便而使用,实际过程中不应使用这个规则,因为它跟 permissive 区别不大。
### 逃逸
如果 Root Profile 的配置不合理那么可能会发生逃逸的情况Root Profile 的限制会意外失效。
比如,如果你为 ADB shell 用户设置允许 root 权限(这是相当常见的情况);然后你给某个普通应用允许 root 权限,但是配置它的 root profile 中的 UID 为 2000ADB shell 用户的 UID那么此时这个 App 可以通过执行两次 `su` 来获得完整的 root 权限:
1. 第一次执行 `su`,由于 App Profile 强制生效,会正常切换到 UID 为 `2000(adb shell)` 而非 `0(root)`
2. 第二次执行 `su`,由于此时它 UID 是 `2000`,而你给 `2000(adb shell)` 配置了允许 root它会获得完整的 root 权限!
:::warning 注意
这是完全符合预期的行为,并非 BUG因此我们建议
如果你的确需要给 adb 授予 root 权限(比如你是开发者),那么不建议你在配置 Root Profile 的时候将 UID 改成 `2000`,用 `1000(system)` 会更好。
:::
## Non Root Profile
### 卸载模块
KernelSU 提供了一种 systemless 的方式来修改系统分区,这是通过挂载 overlayfs 来实现的。但有些情况下App 可能会对这种行为比较敏感;因此,我们可以通过设置“卸载模块”来卸载挂载在这些 App 上的模块。
另外KernelSU 管理器的设置界面还提供了一个“默认卸载模块”的开关,这个开关默认情况下是**开启**的,这意味着**如果不对 App 做额外的设置**,默认情况下 KernelSU 或者某些模块会对此 App 执行卸载操作。当然,如果你不喜欢这个设置或者这个设置会影响某些 App可以有如下选择
1. 保持“默认卸载模块”的开关,然后针对不需要“卸载模块”的 App 进行单独的设置,在 App Profile 中关闭“卸载模块”;(相当于“白名单”)。
2. 关闭“默认卸载模块”的开关,然后针对需要“卸载模块”的 App 进行单独的设置,在 App Profile 中开启“卸载模块”;(相当于“黑名单”)。
:::info
KernelSU 在 5.10 及以上内核上,内核会执行“卸载模块”的操作;但在 5.10 以下的设备上这个开关仅仅是一个“配置项”KernelSU 本身不会做任何动作,一些模块(如 Zygisksu 会通过这个模块决定是否需要卸载)
:::

View File

@@ -0,0 +1,28 @@
# KernelSU 模块与 Magisk 的差异 {#title}
虽然 KernelSU 模块与 Magisk 模块有很多相似之处,但由于它们的实现机制完全不同,因此不可避免地会有一些差异;如果你希望你的模块能同时在 Magisk 与 KernelSU 中运行,那么你必须了解这些差异。
## 相同之处 {#similarities}
- 模块文件格式: 都以 zip 的方式组织模块,并且模块的格式几乎相同
- 模块安装目录: 都在 `/data/adb/modules`
- systemless: 都支持通过模块的形式以 systemless 修改 /system
- `post-fs-data.sh`: 执行时机完全一致,语义也完全一致
- `service.sh`: 执行时机完全一致,语义也完全一致
- `system.prop`: 完全相同
- `sepolicy.rule`: 完全相同
- BusyBox脚本都在 BusyBox 中以“独立模式”运行
## 不同之处 {#differences}
在了解不同之处之前,你需要知道如何区分你的模块是运行在 KernelSU 还是运行在 Magisk 之中;在所有你可以运行模块脚本的地方(`customize.sh`, `post-fs-data.sh`, `service.sh`),你都可以通过环境变量`KSU` 来区分,在 KernelSU 中,这个环境变量将被设置为 `true`
以下是一些不同之处:
1. KernelSU 的模块不支持在 Recovery 中安装。
2. KernelSU 的模块没有内置的 Zygisk 支持(但你可以通过 [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) 来使用 Zygisk 模块)。
3. KernelSU 模块替换或者删除文件与 Magisk 完全不同。KernelSU 不支持 `.replace` 方式,相反,你需要通过 `mknod filename c 0 0` 创建同名文件夹来删除对应文件。
4. BusyBox 的目录不同KernelSU 内置的 BusyBox 在 `/data/adb/ksu/bin/busybox` 而 Magisk 在 `/data/adb/magisk/busybox`**注意此为 KernelSU 内部行为,未来可能会更改!**
5. KernelSU 不支持 `.replace` 文件;但 KernelSU 支持 `REPLACE``REMOVE` 变量。
6. KernelSU 新增了一种脚本 `boot-completed.sh`,以便在 Android 系统启动后运行某些任务。
7. KernelSU 新增了一种脚本 `post-mount.sh`,以便在 Overlayfs 挂载后运行某些任务。

View File

@@ -0,0 +1,74 @@
# 常见问题
## KernelSU 是否支持我的设备?
首先,您的设备应该能够解锁 bootloader。 如果不能,则不支持。
然后在你的设备上安装 KernelSU 管理器并打开它,如果它显示 `不支持` ,那么你的设备没有官方支持的开箱即用的 boot image但你可以自己编译内核集成 KernelSU 进而使用它。
## KernelSU 是否需要解锁 Bootloader
当然需要。
## KernelSU 是否支持模块?
支持。请查阅 [模块](module.md)。
## KernelSU 是否支持 Xposed
支持。LSPosed 可以在 [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) 的支持下正常运行。
## KernelSU 支持 Zygisk 吗?
KernelSU 本体不支持 Zygisk但是你可以用 [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) 来使用 Zygisk 模块。
## KernelSU 与 Magisk 兼容吗?
KernelSU 的模块系统与 Magisk 的 magic mount 有冲突,如果 KernelSU 中启用了任何模块,那么整个 Magisk 将无法工作。
但是如果你只使用 KernelSU 的 `su`,那么它会和 Magisk 一起工作KernelSU 修改 `kernel` 、 Magisk 修改 `ramdisk`,它们可以一起工作。
## KernelSU 会替代 Magisk 吗?
我们不这么认为这也不是我们的目标。Magisk 对于用户空间 root 解决方案来说已经足够好了它会存活很久。KernelSU 的目标是为用户提供内核接口,而不是替代 Magisk。
## KernelSU 可以支持非 GKI 设备吗?
可以。但是你应该下载内核源代码并将 KernelSU 集成到源代码树中并自己编译内核。
## KernelSU 支持 Android 12 以下的设备吗?
影响 KernelSU 兼容性的是设备内核的版本,它与设备的 Android 版本没有直接的关系。唯一有关联的是:**出厂** Android 12 的设备,一定是 5.10 或更高的内核GKI 设备);因此结论如下:
1. 出厂 Android 12 的设备必定是支持的GKI 设备)
2. 旧版本内核的设备(即使是 Android 12也可能是旧内核是兼容的你需要自己编译内核
## KernelSU 可以支持旧内核吗?
可以,目前最低支持到 4.14;更低的版本你需要手动移植它,欢迎 PR
## 如何为旧内核集成 KernelSU
参考[教程](how-to-integrate-for-non-gki)
## 为什么我手机系统是 Android 13但内核版本却是 "android12-5.10"
内核版本与 Android 版本无关,如果你需要刷入 KernelSU请永远使用**内核版本**而非 Android 版本,如果你为 "android12-5.10" 的设备刷入 Android 13 的内核,等待你的将是 bootloop.
## 我是 GKI1.0, 能用 KernelSU 吗?
GKI1 跟 GKI2 完全是两个东西,所以你需要自行编译内核。
## 如何把 `/system` 变成挂载为可读写?
我们不建议你直接修改系统分区,你应该使用[模块功能](module.md) 来做修改;如果你执意要这么做,可以看看 [magisk_overlayfs](https://github.com/HuskyDG/magic_overlayfs)
## KernelSU 能修改 hosts 吗,我如何使用 AdAway
当然可以。但这个功能 KernelSU 没有内置,你可以安装这个 [systemless-hosts](https://github.com/symbuzzer/systemless-hosts-KernelSU-module)
## 为什么有个 1 TB 的超大文件?
1 TB 大小的 `modules.img` 是一个磁盘镜像文件,**不要担心它的大小**,它是一种被称之为[稀疏文件](https://en.wikipedia.org/wiki/Sparse_file)的文件格式,它的实际大小只有你使用的模块的大小,并且你在删除模块后它会动态缩小;它并不实际占用 1 TB 大小的磁盘空间(实际上你手机可能并没有这么多空间)。
如果你真的对这个文件的大小感到不爽,你可以使用 `resize2fs -M` 命令让它变成实际大小;但此时模块可能无法正常工作,我们也不会为此提供任何支持。

View File

@@ -0,0 +1,7 @@
# 隐藏功能
## ksurc
默认情况下,`/system/bin/sh` 会加载 `/system/etc/mkshrc`
可以通过创建 `/data/adb/ksu/.ksurc` 文件来让 su 加载该文件而不是 `/system/etc/mkshrc`

View File

@@ -0,0 +1,63 @@
# 如何构建 KernelSU
首先,您应该阅读内核构建的 Android 官方文档:
1. [构建内核](https://source.android.com/docs/setup/build/building-kernels)
2. [通用内核映像 (GKI) 发布构建](https://source.android.com/docs/core/architecture/kernel/gki-release-builds)
::: warning
本文档适用于 GKI 设备,如果你是旧内核,请参考[如何为非GKI设备集成 KernelSU](how-to-integrate-for-non-gki)
:::
## 构建内核
### 同步内核源码
```sh
repo init -u https://android.googlesource.com/kernel/manifest
mv <kernel_manifest.xml> .repo/manifests
repo init -m manifest.xml
repo sync
```
`<kernel_manifest.xml>` 是一个可以唯一确定构建的清单文件,您可以使用该清单进行可重新预测的构建。 您应该从 [通用内核映像 (GKI) 发布构建](https://source.android.com/docs/core/architecture/kernel/gki-release-builds) 下载清单文件
### 构建
请先查看 [官方文档](https://source.android.com/docs/setup/build/building-kernels)。
例如,我们需要构建 aarch64 内核镜像:
```sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
```
不要忘记添加 `LTO=thin`, 否则,如果您的计算机内存小于 24GB构建可能会失败.
从 Android 13 开始,内核由 `bazel` 构建:
```sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
```
## 使用 KernelSU 构建内核
如果您可以成功构建内核,那么构建 KernelSU 就很容易,根据自己的需求在内核源代码根目录中运行以下任一命令:
::: code-group
```sh[最新 tag(稳定版本)]
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
```
```sh[main 分支(开发版本)]
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
```
```sh[指定 tag(比如 v0.5.2)]
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
```
:::
然后重建内核,您将获得带有 KernelSU 的内核映像!

View File

@@ -0,0 +1,369 @@
# 如何为非 GKI 内核集成 KernelSU {#introduction}
KernelSU 可以被集成到非 GKI 内核中,现在它最低支持到内核 4.14 版本;理论上也可以支持更低的版本。
由于非 GKI 内核的碎片化极其严重,因此通常没有统一的方法来编译它,所以我们也无法为非 GKI 设备提供 boot 镜像。但你完全可以自己集成 KernelSU 然后编译内核使用。
首先,你必须有能力从你设备的内核源码编译出一个可以开机并且能正常使用的内核,如果内核不开源,这通常难以做到。
如果你已经做好了上述准备,那有两个方法来集成 KernelSU 到你的内核之中。
1. 借助 `kprobe` 自动集成
2. 手动修改内核源码
## 使用 kprobe 集成 {#using-kprobes}
KernelSU 使用 kprobe 机制来做内核的相关 hook如果 *kprobe* 可以在你编译的内核中正常运行,那么推荐用这个方法来集成。
首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:
```sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5
```
:::info
[KernelSU 1.0 及更高版本已经不再支持非 GKI 内核](https://github.com/tiann/KernelSU/issues/1705),最后的支持版本为 `v0.9.5`,请注意使用正确的版本。
:::
然后,你需要检查你的内核是否开启了 *kprobe* 相关的配置,如果没有开启,需要添加以下配置:
```
CONFIG_KPROBES=y
CONFIG_HAVE_KPROBES=y
CONFIG_KPROBE_EVENTS=y
```
最后,重新编译你的内核即可。
如果你发现 KPROBES 仍未生效,很有可能是因为它的依赖项`CONFIG_MODULES`没有被启用(如果还是未生效请键入`make menuconfig`搜索 KPROBES 的其它依赖并启用)
如果你在集成 KernelSU 之后手机无法启动,那么很可能你的内核中 **kprobe 工作不正常**,你需要修复这个 bug 或者用第二种方法。
:::tip 如何验证是否是 kprobe 的问题?
注释掉 `KernelSU/kernel/ksu.c``ksu_enable_sucompat()``ksu_enable_ksud()`,如果正常开机,那么就是 kprobe 的问题;或者你可以手动尝试使用 kprobe 功能,如果不正常,手机会直接重启。
:::
## 手动修改内核源码 {#modify-kernel-source-code}
如果 kprobe 工作不正常(通常是上游的 bug 或者内核版本过低),那你可以尝试这种方法:
首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:
```sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5
```
请注意,某些设备的 defconfig 文件可能在`arch/arm64/configs/设备代号_defconfig`或位于`arch/arm64/configs/vendor/设备代号_defconfig`。在您的 defconfig 文件中,将`CONFIG_KSU`设置为`y`以启用 KernelSU或设置为`n`以禁用。比如在某个 defconfig 中:
`arch/arm64/configs/...`
```sh
+# KernelSU
+CONFIG_KSU=y
```
然后,将 KernelSU 调用添加到内核源代码中,这里有几个补丁可以参考:
::: code-group
```diff[exec.c]
diff --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[open.c]
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) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
```
```diff[read_write.c]
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[stat.c]
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;
```
:::
主要是要改四个地方:
1. do_faccessat通常位于 `fs/open.c`
2. do_execveat_common通常位于 `fs/exec.c`
3. vfs_read通常位于 `fs/read_write.c`
4. vfs_statx通常位于 `fs/stat.c`
如果你的内核没有 `vfs_statx`, 使用 `vfs_fstatat` 来代替它:
```diff
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 的内核,如果没有 `do_faccessat`,可以直接找到 `faccessat` 系统调用的定义然后修改:
```diff
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) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
```
### 安全模式
要使用 KernelSU 内置的安全模式,你还需要修改 `drivers/input/input.c` 中的 `input_handle_event` 方法:
:::tip
强烈建议开启此功能,对用户救砖会非常有帮助!
:::
:::info 莫名其妙进入安全模式?
如果你采用手动集成的方式,并且没有禁用`CONFIG_KPROBES`,那么用户在开机之后按音量下,也可能触发安全模式!因此如果使用手动集成,你需要关闭 `CONFIG_KPROBES`
:::
```diff
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);
```
### pm 命令执行失败?
你需要同时修改 `fs/devpts/inode.c`,补丁如下:
```diff
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;
```
### path_umount {#how-to-backport-path-umount}
你可以通过从 K5.9 向旧版本移植 `path_umount`,在 GKI 之前的内核上获得卸载模块的功能。你可以通过以下补丁作为参考:
```diff
--- 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.
```
改完之后重新编译内核即可。

View File

@@ -0,0 +1,279 @@
# 安装 {#title}
## 检查您的设备是否被支持 {#check-if-supported}
从 [GitHub Releases](https://github.com/tiann/KernelSU/releases) 下载 KernelSU 管理器应用,然后将应用程序安装到设备并打开:
- 如果应用程序显示 “不支持”,则表示您的设备不支持 KernelSU你需要自己编译设备的内核才能使用KernelSU 官方不会也永远不会为你提供一个可以刷写的 boot 镜像。
- 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备;可以进行下一步操作。
:::info
对于显示“不支持”的设备,这里有一个[非官方支持设备列表](unofficially-support-devices.md),你可以用这个列表里面的内核自行编译。
:::
## 备份你的 boot.img {#backup-boot-image}
在进行刷机操作之前,你必须先备份好自己的原厂 boot.img。如果你后续刷机出现了任何问题你都可以通过使用 fastboot 刷回原厂 boot 来恢复系统。
::: warning
任何刷机操作都是有风险的,请务必做好这一步再进行下一步操作!!必要时你还可以备份你手机的所有数据。
:::
## 必备知识 {#acknowage}
### ADB 和 fastboot {#adb-and-fastboot}
此教程默认你会使用 ADB 和 fastboot 工具,如果你没有了解过,建议使用搜索引擎先学习相关知识。
### KMI
KMI 全称 Kernel Module Interface相同 KMI 的内核版本是**兼容的** 这也是 GKI 中“通用”的含义所在;反之,如果 KMI 不同,那么这些内核之间无法互相兼容,刷入与你设备 KMI 不同的内核镜像可能会导致死机。
具体来说,对 GKI 的设备,其内核版本格式应该如下:
```txt
KernelRelease :=
Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
w .x .y -zzz -k -something
```
其中,`w.x-zzz-k` 为 KMI 版本。例如,一个设备内核版本为`5.10.101-android12-9-g30979850fc20`,那么它的 KMI 为 `5.10-android12-9`;理论上刷入其他这个 KMI 的内核也能正常开机。
::: tip
请注意,内核版本中的 SubLevel 不属于 KMI 的范畴!也就是说 `5.10.101-android12-9-g30979850fc20``5.10.137-android12-9-g30979850fc20` 的 KMI 相同!
:::
### 安全补丁级别 {#security-patch-level}
新的 Android 设备上可能采取了防回滚机制,它不允许刷入一个安全补丁更旧的内核。比如,如果你的设备内核是 `5.10.101-android12-9-g30979850fc20`,它的安全补丁为 `2023-11`;即使你刷入与内核 KMI 一致的内核,如果安全补丁级别比 `2023-11` 要老(例如`2023-06`),那么很可能会无法开机。
因此,在保持 KMI 一致的情况下,优先采用安全补丁级别更新的内核。
### 内核版本与 Android 版本 {#kernel-version-vs-android-version}
请注意:**内核版本与 Android 版本并不一定相同!**
如果您发现您的内核版本是 `android12-5.10.101`,然而你 Android 系统的版本为 Android 13 或者其他;请不要觉得奇怪,因为 Android 系统的版本与 Linux 内核的版本号不一定是一致的Linux 内核的版本号一般与**设备出厂的时候自带的 Android 系统的版本一致**,如果后续 Android 系统升级,内核版本一般不会发生变化。如果你需要刷机,**请以内核版本为准!!**
## 安装介绍 {#installationintroduction}
`0.9.0` 版本以后,在 GKI 设备中KernelSU 支持两种运行模式:
1. `GKI`:使用**通用内核镜像**GKI替换掉设备原有的内核。
2. `LKM`:使用**可加载内核模块**LKM的方式加载到设备内核中不会替换掉设备原有的内核。
这两种方式适用于不同的场景,你可以根据自己的需求选择。
### GKI 模式 {#gki-mode}
GKI 模式会替换掉设备原有的内核,使用 KernelSU 提供的通用内核镜像。GKI 模式的优点是:
1. 通用型强,适用于大多数设备;比如三星开启了 KNOX 的设备LKM 模式无法运作。还有一些冷门的魔改设备,也只能使用 GKI 模式;
2. 不依赖官方固件即可使用;不需要等待官方固件更新,只要 KMI 一致,就可以使用;
### LKM 模式 {#lkm-mode}
LKM 模式不会替换掉设备原有的内核而是使用可加载内核模块的方式加载到设备内核中。LKM 模式的优点是:
1. 不会替换掉设备原有的内核;如果你对设备原有的内核有特殊需求,或者你希望在使用第三方内核的同时使用 KernelSU可以使用 LKM 模式;
2. 升级和 OTA 较为方便;升级 KernelSU 时,可以直接在管理器里面安装,无需再手动刷写;系统 OTA 后,可以直接安装到第二个槽位,也无需再手动刷写;
3. 适用于一些特殊场景;比如使用临时 ROOT 权限也可以加载 LKM由于不需要替换 boot 分区,因此不会触发 avb不会使设备意外变砖
4. LKM 可以被临时卸载;如果你临时想取消 root可以卸载 LKM这个过程不需要刷写分区甚至也不用重启设备如果你想再次 root只需要重启设备即可
:::tip 两种模式共存
打开管理器后,你可以在首页看到设备当前运行的模式;注意 GKI 模式的优先级高于 LKM如你既使用 GKI 内核替换掉了原有的内核,又使用 LKM 的方式修补了 GKI 内核,那么 LKM 会被忽略,设备将永远以 GKI 的模式运行。
:::
### 选哪个? {#which-one}
如果你的设备是手机,我们建议您优先考虑 LKM 模式如果你的设备是模拟器、WSA 或者 Waydroid 等,我们建议您优先考虑 GKI 模式。
## LKM 安装
### 获取官方固件
使用 LKM 的模式,需要获取官方固件,然后在官方固件的基础上修补;如果你使用的是第三方内核,可以把第三方内核的 boot.img 作为官方固件。
获取官方固件的方法有很多,如果你的设备支持 `fastboot boot`,那么我们最推荐以及最简单的方法是使用 `fastboot boot` 临时启动 KernelSU 提供的 GKI 内核,然后安装管理器,最后在管理器中直接安装;这种方法不需要你手动下载官方固件,也不需要你手动提取 boot。
如果你的设备不支持 `fastboot boot`,那么你可能需要手动去下载官方固件包,然后从中提取 boot。
与 GKI 模式不同LKM 模式会修改 `ramdisk`,因此在出厂 Android 13 的设备上,它需要修补的是 `init_boot` 分区而非 `boot` 分区;而 GKI 模式则永远是操作 `boot` 分区。
### 使用管理器
打开管理器,点击右上角的安装图标,会出现若干个选项:
1. 选择并修补一个文件;如果你手机目前没有 root 权限,你可以选择这个选项,然后选择你的官方固件,管理器会自动修补它;你只需要刷入这个修补后的文件,即可永久获取 root 权限;
2. 直接安装;如果你手机已经 root你可以选择这个选项管理器会自动获取你的设备信息然后自动修补官方固件然后刷入你可以考虑使用 `fastboot boot` KernelSU 的 GKI 内核来获取临时 root 安装管理器,然后再使用这个选项;这种方式也是 KernelSU 升级最主要的方式;
3. 安装到另一个分区;如果你的设备支持 A/B 分区,你可以选择这个选项,管理器会自动修补官方固件,然后安装到另一个分区;这种方式适用于 OTA 后的设备,你可以在 OTA 后直接安装到另一个分区,然后重启设备即可;
### 使用命令行
如果你不想使用管理器,你也可以使用命令行来安装 LKMKernelSU 提供的 `ksud` 工具可以帮助你快速修补官方固件,然后刷入。
这个工具支持 macOS、Linux 和 Windows你可以在 [GitHub Release](https://github.com/tiann/KernelSU/releases) 下载对应的版本。
使用方法:`ksud boot-patch` 具体的使用方法你可以查看命令行帮助。
```sh
oriole:/ # ksud boot-patch -h
Patch boot or init_boot images to apply KernelSU
Usage: ksud boot-patch [OPTIONS]
Options:
-b, --boot <BOOT> boot image path, if not specified, will try to find the boot image automatically
-k, --kernel <KERNEL> kernel image path to replace
-m, --module <MODULE> LKM module path to replace, if not specified, will use the builtin one
-i, --init <INIT> init to be replaced
-u, --ota will use another slot when boot image is not specified
-f, --flash Flash it to boot partition after patch
-o, --out <OUT> output path, if not specified, will use current directory
--magiskboot <MAGISKBOOT> magiskboot path, if not specified, will use builtin one
--kmi <KMI> KMI version, if specified, will use the specified KMI
-h, --help Print help
```
需要说明的几个选项:
1. `--magiskboot` 选项可以指定 magiskboot 的路径如果不指定ksud 会在环境变量中查找;如果你不知道如何获取 magiskboot可以查阅[这里](#patch-boot-image)
2. `--kmi` 选项可以指定 `KMI` 版本,如果你的设备内核名字没有遵循 KMI 规范,你可以通过这个选项来指定;
最常见的使用方法为:
```sh
ksud boot-patch -b <boot.img> --kmi android13-5.10
```
## GKI 安装
GKI 的安装方法有如下几种,各自适用于不同的场景,请按需选择:
1. 使用 KernelSU 提供的**通用内核镜像**使用 fastboot 安装
2. 使用内核刷写 App如 KernelFlasher安装
3. 手动修补 boot.img 然后安装
4. 使用自定义 Recovery如 TWRP安装
## 使用 KernelSU 提供的 boot.img 安装 {#install-by-kernelsu-boot-image}
如果你设备的 `boot.img` 采用常用的压缩格式,那么可以采用 KernelSU 提供的的通用内核镜像直接刷入,它不需要 TWRP 或者自行修补镜像。
### 找到合适的 boot.img {#found-propery-image}
KernelSU 为 GKI 设备提供了通用的 boot.img您应该将 boot.img 刷写到设备的 boot 分区。
您可以从 [GitHub Release](https://github.com/tiann/KernelSU/releases) 下载 boot.img, 请注意您应该使用正确版本的 boot.img。如果您不知道应该下载哪一个文件请仔细阅读本文档中关于 [KMI](#kmi) 和[安全补丁级别](#security-patch-level)的描述。
通常情况下,同一个 KMI 和 安全补丁级别下会有三个不同格式的 boot 文件,它们除了内核压缩格式不同之外都一样。请检查您原有 boot.img 的内核压缩格式,您应该使用正确的格式,例如 `lz4``gz`;如果是用不正确的压缩格式,刷入 boot 后可能无法开机。
::: info
1. 您可以通过 magiskboot 来获取你原来 boot 的压缩格式;当然您也可以询问与您机型相同的其他更有经验的童鞋。另外,内核的压缩格式通常不会发生变化,如果您使用某个压缩格式成功开机,后续可优先尝试这个格式。
2. 小米设备通常使用 `gz` 或者 **不压缩**
3. Pixel 设备有些特殊,请查看下面的教程。
:::
### 将 boot.img 刷入设备 {#flash-boot-image}
使用 `adb` 连接您的设备,然后执行 `adb reboot bootloader` 进入 fastboot 模式,然后使用此命令刷入 KernelSU
```sh
fastboot flash boot boot.img
```
::: info
如果你的设备支持 `fastboot boot`,可以先使用 `fastboot boot boot.img` 来先尝试使用 boot.img 引导系统,如果出现意外,再重启一次即可开机。
:::
### 重启 {#reboot}
刷入完成后,您应该重新启动您的设备:
```sh
fastboot reboot
```
## 使用内核刷写 App 安装 {#install-by-kernel-flasher}
步骤:
1. 下载 AnyKernel3 的刷机包,如果你不知道下载哪一个,请仔细查阅上述文档中关于 [KMI](#kmi) 和[安全补丁级别](#security-patch-level)的描述;下载错误的刷机包很可能导致无法开机,请注意备份。
2. 打开内核刷写 App授予必要的 root 权限),使用提供的 AnyKernel3 刷机包刷入。
这种方法需要内核刷写 App 拥有 root 权限,你可以用如下几种方法实现:
1. 你的设备已经获取了 root 权限,比如你已经安装好了 KernelSU 想升级到最新的版本,又或者你通过其他方法(如 Magisk获取了 root。
2. 如果你的手机没有 root但手机支持 `fastboot boot boot.img` 这种临时启动的方法,你可以用 KernelSU 提供的 GKI 镜像临时启动你的设备,获取临时的 root 权限,然后使用内核刷写器刷入获取永久 root 权限。
如果您以前没有使用过内核刷写 App建议使用以下应用
1. [Kernel Flasher](https://github.com/capntrips/KernelFlasher/releases)
2. [Franco Kernel Manager](https://play.google.com/store/apps/details?id=com.franco.kernel)
3. [Ex Kernel Manager](https://play.google.com/store/apps/details?id=flar2.exkernelmanager)
## 手动修补 boot.img {#patch-boot-image}
对于某些设备来说,其 boot.img 格式不那么常见,比如不是 `lz4`, `gz` 和未压缩;最典型的就是 Pixel它 boot.img 的格式是 `lz4_legacy` 压缩ramdisk 可能是 `gz` 也可能是 `lz4_legacy` 压缩;此时如果你直接刷入 KernelSU 提供的 boot.img手机可能无法开机这时候你可以通过手动修补 boot.img 来实现。
任何情况下都推荐使用 `magiskboot` 来修补 boot 镜像,有两个方法:
1. [magiskboot](https://github.com/topjohnwu/Magisk/releases)
2. [magiskboot_build](https://github.com/ookiineko/magiskboot_build/releases/tag/last-ci)
Magisk 官方提供的 `magiskboot` 只能运行在 Android/Linux 设备上,如果你想在 macOS/Windows 上使用 `magiskboot` 可以使用第二个方法。
::: tip
不再推荐使用 Android-Image-Kitchen因为它可能没有合理地处理 boot 元数据(比如安全补丁级别),从而导致某些设备上会无法启动。
:::
### 准备 {#patch-preparation}
1. 获取你手机的原厂 boot.img你可以通过你手机的线刷包解压后之间获取如果你是卡刷包那你也许需要 [payload-dumper-go](https://github.com/ssut/payload-dumper-go)
2. 下载 KernelSU 提供的与你设备 KMI 版本一致的 AnyKernel3 刷机包;如果您不知道应该下载哪一个文件,请仔细阅读本文档中关于 [KMI](#kmi) 和[安全补丁级别](#security-patch-level)的描述。
3. 解压缩 AnyKernel3 刷机包,获取其中的 `Image` 文件,此文件为 KernelSU 的内核文件。
### 在 Android 设备上使用 magiskboot {#using-magiskboot-on-Android-devices}
1. 在 Magisk 的 [Release 页面](https://github.com/topjohnwu/Magisk/releases) 下载最新的 Magisk 安装包。
2.`Magisk-*(version).apk` 重命名为 `Magisk-*.zip` 然后解压缩。
3. 将解压后的 `Magisk-*/lib/arm64-v8a/libmagiskboot.so` 文件,使用 adb push 到手机:`adb push Magisk-*/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot`
4. 使用 adb 将原厂 boot.img 和 AnyKernel3 中的 Image 推送到手机
5. adb shell 进入 /data/local/tmp/ 目录,然后赋予刚 push 文件的可执行权限 `chmod +x magiskboot`
6. adb shell 进入 /data/local/tmp/ 目录,执行 `./magiskboot unpack boot.img` 此时会解包 `boot.img` 得到一个叫做 `kernel` 的文件,这个文件为你原厂的 kernel
7. 使用 `Image` 替换 `kernel`: `mv -f Image kernel`
8. 执行 `./magiskboot repack boot.img` 打包 img此时你会得到一个 `new-boot.img` 的文件,使用这个文件 fastboot 刷入设备即可。
### 在 macOS/Windows/Linux 上使用 magiskboot {#using-magiskboot-on-PC}
1. 在 [magiskboot_build](https://github.com/ookiineko/magiskboot_build/releases/tag/last-ci) 下载适合你操作系统的 `magiskboot` 二进制文件。
2. 在你的 PC 上准备好设备原厂的 boot.img 和 KernelSU 的 Image。
3. `chmod +x magiskboot`
4. 在你 PC 上合适的目录执行 `./magiskboot unpack boot.img` 来解包 `boot.img`, 你会得到一个 `kernel` 文件,这个文件是你设备原厂的 kernel。
5. 使用 `Image` 替换 `kernel`: `mv -f Image kernel`
6. 执行 `./magiskboot repack boot.img` 打包 img此时你会得到一个 `new-boot.img` 的文件,使用这个文件 fastboot 刷入设备即可。
:::info
Magisk 官方的 `magiskboot` 可以在 Linux 设备上执行,如果你是 Linux 用户,可以直接用官方版本。
:::
## 使用自定义 Recovery 安装 {#install-by-recovery}
前提:你的设备必须有自定义的 Recovery如 TWRP如果没有或者只有官方 Recovery请使用其他方法。
步骤:
1. 在 KernelSU 的 [Release 页面](https://github.com/tiann/KernelSU/releases) 下载与你手机版本匹配的以 AnyKernel3 开头的 zip 刷机包;如果你不知道下载哪一个,请仔细查阅上述文档中关于 **KMI** 和**安全补丁级别**的描述;下载错误的刷机包很可能导致无法开机,请注意备份。
2. 重启手机进入 TWRP。
3. 使用 adb 将 AnyKernel3-*.zip 放到手机 /sdcard 然后在 TWRP 图形界面选择安装;或者你也可以直接 `adb sideload AnyKernel-*.zip` 安装。
PS. 这种方法适用于任何情况下的安装(不限于初次安装或者后续升级),只要你用 TWRP 就可以操作。
## 其他变通方法 {#other-methods}
其实所有这些安装方法的主旨只有一个,那就是**替换原厂的内核为 KernelSU 提供的内核**;只要能实现这个目的,就可以安装;比如以下是其他可行的方法:
1. 首先安装 Magisk通过 Magisk 获取 root 权限后使用内核刷写器刷入 KernelSU 的 AnyKernel 包。
2. 使用某些 PC 上的刷机工具箱刷入 KernelSU 提供的内核。
如果这些方法导致无法开机,请优先尝试用 `magiskboot` 的方法。

View File

@@ -0,0 +1,48 @@
# 模块 WebUI
KernelSU 的模块除了执行启动脚本和修改系统文件之外,还支持显示 UI 界面和与用户交互。
你可以通过任何 Web 技术编写 HTML + CSS + JavaScript 页面KernelSU 的管理器将通过 WebView 显示这些页面。此外KernelSU 还提供了一些用于与系统交互的 JavaScript API例如执行 shell 命令。
## WebUI 根目录
Web 资源文件应放置在模块根目录的 `webroot` 子目录中,并且其中**必须**有一个名为`index.html`的文件,该文件是模块页面入口。包含 Web 界面的最简单的模块结构如下:
````txt
tree .
.
|-- module.prop
`-- webroot
`--index.html
````
:::warning
安装模块时KernelSU 会自动设置 `webroot` 目录的权限和 SELinux context如果您不知道自己在做什么请不要自行设置该目录的权限
:::
如果您的页面包含 CSS 和 JavaScript您也需要将其放入此目录中。
## JavaScript API
如果只是一个显示页面那它和普通网页没有什么区别。更重要的是KernelSU 提供了一系列的系统 API可以让您实现模块特有的功能。
KernelSU 提供了一个 JavaScript 库并[在 npm 上发布](https://www.npmjs.com/package/kernelsu),您可以在网页的 JavaScript 代码中使用它。
例如,您可以执行 shell 命令来获取特定配置或修改属性:
```JavaScript
import { exec } from 'kernelsu';
const { errno, stdout } = await exec("getprop ro.product.model");
````
再比如,你可以让网页全屏显示,或者显示一个 Toast。
[API 文档](https://www.npmjs.com/package/kernelsu)
如果您发现现有的 API 不能满足您的需求或者使用不方便,欢迎[在这里](https://github.com/tiann/KernelSU/issues)给我们提出建议!
## 一些技巧
1. 您可以正常使用`localStorage`存储一些数据,但卸载管理器后,这些数据将会丢失。 如果需要持久保存,可以自己将数据写入某个目录。
2. 对于简单的页面,我建议您使用 [parceljs](https://parceljs.org/) 进行打包。它零配置,使用非常方便。不过,如果你是前端高手或者有自己的喜好,那就选择你喜欢的吧!

View File

@@ -0,0 +1,333 @@
# 模块开发指南 {#introduction}
KernelSU 提供了一个模块机制,它可以在保持系统分区完整性的同时达到修改系统分区的效果;这种机制通常被称之为 systemless。
KernelSU 的模块运作机制与 Magisk 几乎是一样的,如果你熟悉 Magisk 模块的开发,那么开发 KernelSU 的模块大同小异,你可以跳过下面有关模块的介绍,只需要了解 [KernelSU 模块与 Magisk 模块的异同](difference-with-magisk.md)。
## 模块界面
KernelSU 的模块支持显示界面并与用户交互,请参阅 [WebUI 文档](module-webui.md)。
## Busybox
KernelSU 提供了一个功能完备的 BusyBox 二进制文件(包括完整的 SELinux 支持)。可执行文件位于 `/data/adb/ksu/bin/busybox`
KernelSU 的 BusyBox 支持运行时可切换的 "ASH Standalone Shell Mode"。
这种独立模式意味着在运行 BusyBox 的 ash shell 时,每个命令都会直接使用 BusyBox 中内置的应用程序,而不管 PATH 设置为什么。
例如,`ls``rm``chmod` 等命令将不会使用 PATH 中设置的命令(在 Android 的情况下,默认情况下分别为 `/system/bin/ls``/system/bin/rm``/system/bin/chmod`),而是直接调用 BusyBox 内置的应用程序。
这确保了脚本始终在可预测的环境中运行,并始终具有完整的命令套件,无论它运行在哪个 Android 版本上。
要强制一个命令不使用 BusyBox你必须使用完整路径调用可执行文件。
在 KernelSU 上下文中运行的每个 shell 脚本都将在 BusyBox 的 ash shell 中以独立模式运行。对于第三方开发者相关的内容,包括所有启动脚本和模块安装脚本。
对于想要在 KernelSU 之外使用这个“独立模式”功能的用户,有两种启用方法:
1. 设置环境变量 `ASH_STANDALONE``1`。例如:`ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>`
2. 使用命令行选项切换:`/data/adb/ksu/bin/busybox sh -o standalone <script>`
为了确保所有后续的 `sh` shell 都在独立模式下执行,第一种是首选方法(这也是 KernelSU 和 KernelSU 管理器内部使用的方法),因为环境变量会被继承到子进程中。
::: tip 与 Magisk 的差异
KernelSU 的 BusyBox 现在是直接使用 Magisk 项目编译的二进制文件,**感谢 Magisk**
因此,你完全不用担心 BusyBox 脚本与在 Magisk 和 KernelSU 之间的兼容问题,因为他们是完全一样的!
:::
## KernelSU 模块 {#kernelsu-modules}
KernelSU 模块就是一个放置在 `/data/adb/modules` 内且满足如下结构的文件夹:
```txt
/data/adb/modules
├── .
├── .
|
├── $MODID <--- 模块的文件夹名称与模块 ID 相同
│ │
│ │ *** 模块配置文件 ***
│ │
│ ├── module.prop <--- 此文件保存模块相关的一些配置,如模块 ID、版本等
│ │
│ │ *** 模块内容 ***
│ │
│ ├── system <--- 这个文件夹通常会被挂载到系统
│ │ ├── ...
│ │ ├── ...
│ │ └── ...
│ │
│ │ *** 标记文件 ***
│ │
│ ├── skip_mount <--- 如果这个文件存在,那么模块的 `/system` 将不会被挂载
│ ├── disable <--- 如果这个文件存在,那么模块会被禁用
│ ├── remove <--- 如果这个文件存在,下次重启的时候模块会被移除
│ │
│ │ *** 可选文件 ***
│ │
│ ├── post-fs-data.sh <--- 这个脚本将会在 post-fs-data 模式下运行
│ ├── post-mount.sh <--- 这个脚本将会在 post-mount 模式下运行
│ ├── service.sh <--- 这个脚本将会在 late_start 服务模式下运行
│ ├── boot-completed.sh <--- 这个脚本将会在 Android 系统启动完毕后以服务模式运行
| ├── uninstall.sh <--- 这个脚本将会在模块被卸载时运行
│ ├── system.prop <--- 这个文件中指定的属性将会在系统启动时通过 resetprop 更改
│ ├── sepolicy.rule <--- 这个文件中的 SELinux 策略将会在系统启动时加载
│ │
│ │ *** 自动生成的目录,不要手动创建或者修改! ***
│ │
│ ├── vendor <--- 如果 /system/vendor 是符号链接且存在,从 $MODID/system/vendor 移动到模块根目录
│ ├── product <--- 如果 /system/product 是符号链接且存在,从 $MODID/system/product 移动到模块根目录
│ ├── system_ext <--- 如果 /system/system_ext 是符号链接且存在,从 $MODID/system/system_ext 移动到模块根目录
│ │
│ │ *** Any additional files / folders are allowed ***
│ │
│ ├── ...
│ └── ...
|
├── another_module
│ ├── .
│ └── .
├── .
├── .
```
::: tip 与 Magisk 的差异
KernelSU 没有内置的针对 Zygisk 的支持,因此模块中没有 Zygisk 相关的内容,但你可以通过 [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) 来支持 Zygisk 模块,此时 Zygisk 模块的内容与 Magisk 所支持的 Zygisk 是完全相同的。
:::
### module.prop
module.prop 是一个模块的配置文件,在 KernelSU 中如果模块中不包含此文件,那么它将不被认为是一个模块;此文件的格式如下:
```txt
id=<string>
name=<string>
version=<string>
versionCode=<int>
author=<string>
description=<string>
```
- id 必须与这个正则表达式匹配:`^[a-zA-Z][a-zA-Z0-9._-]+$` 例如:✓ `a_module`,✓ `a.module`,✓ `module-101`,✗ `a module`,✗ `1_module`,✗ `-a-module`。这是您的模块的唯一标识符,发布后不应更改。
- versionCode 必须是一个整数,用于比较版本。
- 其他未在上面提到的内容可以是任何单行字符串。
- 请确保使用 UNIXLF换行类型而不是 WindowsCR + LF或 MacintoshCR
### Shell 脚本 {#shell-scripts}
请阅读 [启动脚本](#boot-scripts) 一节,以了解 `post-fs-data.sh`, `post-mount.sh`, `service.sh``boot-completed.sh` 之间的区别。对于大多数模块开发者来说,如果您只需要运行一个启动脚本,`service.sh` 应该已经足够了。
在您的模块的所有脚本中,请使用`MODDIR=${0%/*}`来获取您的模块的基本目录路径;请勿在脚本中硬编码您的模块路径。
:::tip 与 Magisk 的差异
你可以通过环境变量 `KSU` 来判断脚本是运行在 KernelSU 还是 Magisk 中,如果运行在 KernelSU这个值会被设置为 `true`
:::
### `system` 目录 {#system-directories}
这个目录的内容会在系统启动后,以 `overlayfs` 的方式叠加在系统的 `/system` 分区之上,这意味着:
1. 系统中对应目录的同名文件会被此目录的文件覆盖。
2. 系统中对应目录的同名文件夹会与此目录的文件夹合并。
如果你想删掉系统原来目录某个文件或者文件夹,你需要在模块目录通过 `mknod filename c 0 0` 来创建一个 `filename` 的同名文件;这样 overlayfs 系统会自动 whiteout 等效删除此文件(`/system` 分区并没有被更改)。
你也可以在 `customize.sh` 中声明一个名为 `REMOVE` 并且包含一系列目录的变量来执行删除操作KernelSU 会自动为你在模块对应目录执行 `mknod <TARGET> c 0 0`。例如:
```sh
REMOVE="
/system/app/YouTube
/system/app/Bloatware
"
```
上面的这个列表将会执行: `mknod $MODPATH/system/app/YouTuBe c 0 0``mknod $MODPATH/system/app/Bloatware c 0 0`;并且 `/system/app/YouTube``/system/app/Bloatware` 将会在模块生效后被删除。
如果你想替换掉系统的某个目录,你需要在模块目录创建一个相同路径的目录,然后为此目录设置此属性:`setfattr -n trusted.overlay.opaque -v y <TARGET>`;这样 overlayfs 系统会自动将系统内相应目录替换(`/system` 分区并没有被更改)。
你可以在 `customize.sh` 中声明一个名为 `REPLACE` 并且包含一系列目录的变量来执行替换操作KernelSU 会自动为你在模块对应目录执行相关操作。例如:
```sh
REPLACE="
/system/app/YouTube
/system/app/Bloatware
"
```
上面这个列表将会:自动创建目录 `$MODPATH/system/app/YouTube``$MODPATH//system/app/Bloatware`,然后执行 `setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTube``setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware`;并且 `/system/app/YouTube``/system/app/Bloatware` 将会在模块生效后替换为空目录。
::: tip 与 Magisk 的差异
KernelSU 的 systemless 机制是通过内核的 overlayfs 实现的,而 Magisk 当前则是通过 magic mount (bind mount),二者实现方式有着巨大的差异,但最终的目标实际上是一致的:不修改物理的 `/system` 分区但实现修改 `/system` 文件。
:::
如果你对 overlayfs 感兴趣,建议阅读 Linux Kernel 关于 [overlayfs 的文档](https://docs.kernel.org/filesystems/overlayfs.html)
### system.prop
这个文件的格式与 `build.prop` 完全相同:每一行都是 `[key]=[value]` 的形式。
### sepolicy.rule
如果您的模块需要一些额外的 SELinux 策略补丁,请将这些规则添加到此文件中。这个文件中的每一行都将被视为一个策略语句。
## 模块安装包 {#module-installer}
KernelSU 的模块安装包就是一个可以通过 KernelSU 管理器 APP 刷入的 zip 文件,此 zip 文件的格式如下:
```txt
module.zip
├── customize.sh <--- (Optional, more details later)
│ This script will be sourced by update-binary
├── ...
├── ... /* 其他模块文件 */
```
:::warning
KernelSU 模块不支持在 Recovery 中安装!!
:::
### 定制安装过程 {#customizing-installation}
如果你想控制模块的安装过程,可以在模块的目录下创建一个名为 `customize.sh` 的文件,这个脚本将会在模块被解压后**导入**到当前 shell 中,如果你的模块需要根据设备的 API 版本或者设备构架做一些额外的操作,那这个脚本将非常有用。
如果你想完全控制脚本的安装过程,你可以在 `customize.sh` 中声明 `SKIPUNZIP=1` 来跳过所有的默认安装步骤;此时,你需要自行处理所有安装过程(如解压模块,设置权限等)
`customize.sh` 脚本以“独立模式”运行在 KernelSU 的 BusyBox `ash` shell 中。你可以使用如下变量和函数:
#### 变量 {#variables}
- `KSU` (bool): 标记此脚本运行在 KernelSU 环境下,此变量的值将永远为 `true`,你可以通过它区分 Magisk。
- `KSU_VER` (string): KernelSU 当前的版本名字 (如: `v0.4.0`)
- `KSU_VER_CODE` (int): KernelSU 用户空间当前的版本号 (如. `10672`)
- `KSU_KERNEL_VER_CODE` (int): KernelSU 内核空间当前的版本号 (如. `10672`)
- `BOOTMODE` (bool): 此变量在 KernelSU 中永远为 `true`
- `MODPATH` (path): 当前模块的安装目录
- `TMPDIR` (path): 可以存放临时文件的目录
- `ZIPFILE` (path): 当前模块的安装包文件
- `ARCH` (string): 设备的 CPU 构架,有如下几种: `arm`, `arm64`, `x86`, or `x64`
- `IS64BIT` (bool): 是否是 64 位设备
- `API` (int): 当前设备的 Android API 版本 (如Android 6.0 上为 `23`)
::: warning
`MAGISK_VER_CODE` 在 KernelSU 中永远为 `25200``MAGISK_VER` 则为 `v25.2`,请不要通过这两个变量来判断是否是 KernelSU
:::
#### 函数 {#functions}
```txt
ui_print <msg>
print <msg> to console
Avoid using 'echo' as it will not display in custom recovery's console
abort <msg>
print error message <msg> to console and terminate the installation
Avoid using 'exit' as it will skip the termination cleanup steps
set_perm <target> <owner> <group> <permission> [context]
if [context] is not set, the default is "u:object_r:system_file:s0"
this function is a shorthand for the following commands:
chown owner.group target
chmod permission target
chcon context target
set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
if [context] is not set, the default is "u:object_r:system_file:s0"
for all files in <directory>, it will call:
set_perm file owner group filepermission context
for all directories in <directory> (including itself), it will call:
set_perm dir owner group dirpermission context
```
## 启动脚本 {#boot-scripts}
在 KernelSU 中根据脚本运行模式的不同分为两种post-fs-data 模式和 late_start 服务模式。
- post-fs-data 模式
- 这个阶段是阻塞的。在执行完成之前或者 10 秒钟之后,启动过程会暂停。
- 脚本在任何模块被挂载之前运行。这使得模块开发者可以在模块被挂载之前动态地调整它们的模块。
- 这个阶段发生在 Zygote 启动之前。
- 使用 setprop 会导致启动过程死锁!请使用 `resetprop -n <prop_name> <prop_value>` 代替。
- **只有在必要时才在此模式下运行脚本**。
- late_start 服务模式
- 这个阶段是非阻塞的。你的脚本会与其余的启动过程**并行**运行。
- **大多数脚本都建议在这种模式下运行**。
在 KernelSU 中,启动脚本根据存放位置的不同还分为两种:通用脚本和模块脚本。
- 通用脚本
- 放置在 `/data/adb/post-fs-data.d`, `/data/adb/post-mount.d`, `/data/adb/service.d``/data/adb/boot-completed.d` 中。
- 只有在脚本被设置为可执行(`chmod +x script.sh`)时才会被执行。
-`post-fs-data.d` 中的脚本以 post-fs-data 模式运行,在 `service.d` 中的脚本以 late_start 服务模式运行。
- 模块**不应**在安装过程中添加通用脚本。
- 模块脚本
- 放置在模块自己的文件夹中。
- 只有当模块被启用时才会执行。
- `post-fs-data.sh` 以 post-fs-data 模式运行,`post-mount.sh` 以 post-mount 模式运行,而 `service.sh` 则以 late_start 服务模式运行,`boot-completed` 在 Android 系统启动完毕后以服务模式运行。
所有启动脚本都将在 KernelSU 的 BusyBox ash shell 中运行,并启用“独立模式”。
### 启动脚本的流程解疑 {#Boot-scripts-process-explanation}
以下是 Android 的相关启动流程(部分省略),其中包括了 KernelSU 的操作(带前导星号),应该能帮助你更好地理解这些启动脚本的用途:
```txt
0. Bootloader (nothing on screen)
load patched boot.img
load kernel:
- GKI mode: GKI kernel with KernelSU integrated
- LKM mode: stock kernel
...
1. kernel exec init (oem logo on screen):
- GKI mode: stock init
- LKM mode: exec ksuinit, insmod kernelsu.ko, exec stock init
mount /dev, /dev/pts, /proc, /sys, etc.
property-init -> read default props
read init.rc
...
early-init -> init -> late_init
early-fs
start vold
fs
mount /vendor, /system, /persist, etc.
post-fs-data
*safe mode check
*execute general scripts in post-fs-data.d/
*load sepolicy.rule
*mount tmpfs
*execute module scripts post-fs-data.sh
**(Zygisk)./bin/zygisk-ptrace64 monitor
*(pre)load system.prop (same as resetprop -n)
*remount modules /system
*execute general scripts in post-mount.d/
*execute module scripts post-mount.sh
zygote-start
load_all_props_action
*execute resetprop (actual set props for resetprop with -n option)
... -> boot
class_start core
start-service logd, console, vold, etc.
class_start main
start-service adb, netd (iptables), zygote, etc.
2. kernel2user init (rom animation on screen, start by service bootanim)
*execute general scripts in service.d/
*execute module scripts service.sh
*set props for resetprop without -p option
**(Zygisk) hook zygote (start zygiskd)
**(Zygisk) mount zygisksu/module.prop
start system apps (autostart)
...
boot complete (broadcast ACTION_BOOT_COMPLETED event)
*execute general scripts in boot-completed.d/
*execute module scripts boot-completed.sh
3. User operable (lock screen)
input password to decrypt /data/data
*actual set props for resetprop with -p option
start user apps (autostart)
```
如果你对 Android 的 init 语言感兴趣,推荐阅读[文档](https://android.googlesource.com/platform/system/core/+/master/init/README.md)。

View File

@@ -0,0 +1,50 @@
# 救砖 {#intruduction}
在刷机的时候我们可能会遇到设备“变砖”的情况,理论上讲,如果你只是使用 fastboot 刷入 boot 分区或者安装不合适的模块导致设备无法启动,那么这都可以通过合适的操作恢复手机;本文档旨在提供一些急救方法让你可以在“变砖”中恢复。
## 刷入 boot 变砖
在 KernelSU 中,刷入 boot 变砖有如下可能:
1. 你刷入了错误格式的 boot 镜像。比如你的手机 boot 格式是 `gz` 的,但你刷入了 `lz4` 格式的镜像,那么此时手机无法启动。
2. 你的手机需要关闭 avb 验证才能正常启动(注意这通常意味着需要清除手机所有数据)。
3. 你的 kernel 有某些 bug 或者你的 kernel 不适合你这个手机刷入。
无论哪种情况,你都可以通过**刷入原厂 boot**恢复;因此,在安装教程最开始,我们已经强烈建议大家,在刷机之前备份自己的原厂 boot如果你没有备份那么你可以通过其他跟你相同设备的童鞋或者官方固件包获取原厂 boot。
## 刷入模块变砖
刷入模块变砖可能是大家遇到更常见的情况,但是这里必须郑重告诉大家:**请勿刷入来路不明的模块!!**。因为模块其实是有 root 权限的,它完全可能导致你的设备发生不可逆的损坏!
### 普通模块变砖
如果大家刷入某些开源的或者被证明是安全的模块使得手机无法启动,那么这种情况在 KernelSU 中非常容易恢复完全无需担心。KernelSU 内置了如下两种机制来救砖:
1. AB 更新
2. 音量键救砖
#### AB 更新 {#ab-update}
KernelSU 的模块更新借鉴了 Android 系统 OTA 更新时的 AB 更新机制,如果你安装了新模块或者对已有模块有更新操作,不会直接操作当前使用的模块文件,而是会把所有模块构建成另外一个 update 镜像;系统重启之后,会使用这个 update 镜像尝试启动一次,如果 Android 系统成功启动,才会真正更新模块。
因此,最简单最常用的救砖方法就是:**强制重启一次**。如果你在刷某个模块之后系统无法启动,你可以长按电源键超过 10 秒,系统会自动重启;重启之后会回滚到更新模块之前的状态,之前更新的模块会被自动禁用。
#### 音量键救砖 {#volume-down}
如果 AB 更新依然无法解决,你可以尝试使用**安全模式**。进入安全模式之后,所有的模块都会被禁用。
进入安全模式的方法有两种:
1. 某些系统自带的安全模式有些系统是长按音量下有些系统比如MIUI可以在 Recovery 中开启安全模式。进入系统的安全模式后KernelSU 也会进入安全模式,自动禁用模块。
2. KernelSU 内置的安全模式;操作方法:开机第一屏后,**连续按音量下键超过三次**。注意是按下-松开、按下-松开、按下-松开,不是按着不动。
进入安全模式以后KernelSU 管理器的模块页面所有模块都被禁用,但你可以执行“卸载”操作,卸载可能会有问题的模块。
内置的安全模式是在内核里面实现的,因此不会出现按键事件被拦截导致捕获不到的情况。不过对于非 GKI 内核,可能需要手动集成代码,可以参考官网教程。
### 格机或其他病毒模块变砖
如果以上方法无法拯救你的设备,那么很有可能你装的模块有恶意操作或者通过其他方式损坏了你的设备,这种情况下,只有两个建议:
1. 清除数据后刷入完整刷入官方系统。
2. 咨询售后服务。

View File

@@ -0,0 +1,30 @@
# 非官方支持设备
::: warning
本文档列出由其他开发者维护的支持 KernelSU 的非 GKI 设备内核
:::
::: warning
本文档仅方便查找设备对应源码,这并不意味该源码**被** KernelSU 开发者**审查**,你应自行承担使用风险。
:::
<script setup>
import data from '../../repos.json'
</script>
<table>
<thead>
<tr>
<th>维护者</th>
<th>仓库地址</th>
<th>支持设备</th>
</tr>
</thead>
<tbody>
<tr v-for="repo in data" :key="repo.devices">
<td><a :href="repo.maintainer_link" target="_blank" rel="noreferrer">{{ repo.maintainer }}</a></td>
<td><a :href="repo.kernel_link" target="_blank" rel="noreferrer">{{ repo.kernel_name }}</a></td>
<td>{{ repo.devices }}</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,21 @@
# 什么是 KernelSU {#introduction}
KernelSU 是 Android GKI 设备的 root 解决方案,它工作在内核模式,并直接在内核空间中为用户空间应用程序授予 root 权限。
## 功能 {#features}
KernelSU 的主要特点是它是**基于内核的**。KernelSU 运行在内核空间,所以它可以提供我们以前从未有过的内核接口。例如,我们可以在内核模式下为任何进程添加硬件断点;我们可以在任何进程的物理内存中访问,而无人知晓;我们可以在内核空间拦截任何系统调用; 等等。
KernelSU 还提供了一个基于 overlayfs 的模块系统,允许您加载自定义插件到系统中。它还提供了一种修改 /system 分区中文件的机制。
## 如何使用 {#how-to-use}
请参考: [安装](installation)
## 如何构建 {#how-to-build}
请参考: [如何构建](how-to-build)
## 讨论 {#discussion}
- Telegram: [@KernelSU](https://t.me/KernelSU)

View File

@@ -0,0 +1,29 @@
---
layout: home
title: Android 上的内核级的 root 方案
hero:
name: KernelSU
text: Android 上的内核级的 root 方案
tagline: ""
image:
src: /logo.png
alt: KernelSU
actions:
- theme: brand
text: 开始了解
link: /zh_CN/guide/what-is-kernelsu
- theme: alt
text: 在 GitHub 中查看
link: https://github.com/tiann/KernelSU
features:
- title: 基于内核
details: KernelSU 运行在内核空间,对用户空间应用有更强的掌控。
- title: 白名单访问控制
details: 只有被授权的 App 才可以访问 `su`,而其他 App 无法感知其存在。
- title: 受限制的 root 权限
details: KernelSU 可以自定义 `su` 的 uid, gid, groups, capabilities 和 SELinux 规则:把 root 权限关进笼子里。
- title: 模块系统 & 开源
details: KernelSU 支持通过 overlayfs 修改 /system并且是 GPL-3 许可下的开源项目。