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,119 @@
# App Profile {#app-profile}
App Profile 是 KernelSU 提供的一種針對各種應用程式自訂其使用配置的機制。
對於授予了 root 權限(即可以使用 `su`的應用程式來說App Profile 也可以稱為 Root Profile它可以自訂 `su``uid``gid``groups`` capabilities` 以及 `SELinux context` 規則,從而限制 root 使用者的權限。
例如可以針對防火牆應用程式僅授予網路權限,而不授予檔案存取權限,針對凍結類別應用程式僅授予 shell 權限而不是直接給 root ;透過最小化權限原則**把權力關進籠子裡**。
對於沒有被授予 root 權限的普通應用App Profile 可以控制核心以及模組系統對此應用的行為;例如是否需要針對此應用程式卸載模組造成的修改等。核心和模組系統可以透過此配置決定是否要做一些類似「隱藏痕跡」類別的操作。
## Root Profile {#root-profile}
### UID、GID 和 groups {#uid-gid-and-groups}
Linux 系統中有使用者和群組兩個概念。每個使用者都有一個使用者 ID(UID),一個使用者可以屬於多個群組,每個群組也有群組 ID(GID)。此 ID 用於識別系統的使用者並確定使用者可以存取哪些系統資源。
UID 為 0 的使用者稱為 root 使用者GID 為 0 的群組稱為 root 群組root 使用者群組通常擁有系統的最高權限。
對於 Android 系統來說,每個應用程式都是一個單獨的使用者(不考慮 share uid 的情況),擁有一個唯一的 UID。例如 `0` 是 root 使用者,`1000``system``2000` 是 ADB shell10000-19999 的是一般使用者。
:::info 補充
此處的 UID 跟 Android 系統的多使用者或者說工作資料Work Profile是不同概念。工作資料實際上是對 UID 進行分片實現的,例如 10000-19999 是主使用者110000-119999 是工作資料;他們中的任何一個普通應用都擁有自己獨有的 UID。
:::
每一個應用程式可以有若干個群組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_ww) (ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readreadtracefs:s05:
```
其中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` 後的權限,它並非控制應用程式本身的權限!如果應用程式本身申請了網路存取權限,那麼它即使不使用 `su` 也可以存取網路;為 `su` 去掉 `inet` 群組只是讓 `su` 無法存取網路。
:::
與應用程式透過 `su` 主動切換使用者或群組不同Root Profile 是在核心中強制實施的,不依賴 root 應用程式的自覺行為,`su` 權限的授予完全取決於使用者而非開發者。
### Capabilities {#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}
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 * * *` 僅僅作為示範方便而使用,實際過程中不應使用這個規則,因為它跟寬容模式區別不大。
### 逃逸 {#escalation}
如果 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 {#non-root-profile}
### 卸載模組 {#umount-modules}
KernelSU 提供了一種無須直接修改系統分區的方式 (systemless) 來修改系統分區,這是透過掛載 overlayfs 來實現的。但有些情況下App 可能會對這種行為比較敏感;因此,我們可以透過設定「卸載模組」來卸載掛載在這些應用程式上的模組。
另外KernelSU 管理器的設定介面還提供了一個「預設卸載模組」的開關,這個開關預設是**開啟**的,這表示**如果不對應用程式做額外的設定**,預設情況下 KernelSU 或某些模組會對此應用程式執行卸載操作。當然,如果你不喜歡這個設定或這個設定會影響某些 App你可以有以下選擇
1. 保持「預設卸載模組」的開關,然後針對不需要「卸載模組」的應用程式進行單獨的設置,在 App Profile 中關閉「卸載模組」;(相當於「白名單」)。
2. 關閉「預設卸載模組」的開關,然後針對需要「卸載模組」的應用程式進行單獨的設置,在 App Profile 中開啟「卸載模組」;(相當於「黑名單」)。
:::info 提示
KernelSU 在 5.10 及以上內核上,內核無須任何修改就可以卸載模組;但在 5.10 以下的設備上,這個開關僅僅是一個"設定"KernelSU 本身不會做任何動作,如果你希望在 5.10 以前的內核可以卸載模組,你需要將 `path_unmount` 函數向後移植到 `fs/namespace.c`,您可以在[如何為非 GKI 核心整合 KernelSU](how-to-integrate-for-non-gki.md#how-to-backport-path_unpount)獲取更多資訊。一些模組(如 ZygiskNext也會透過這個設定決定是否需要卸載。
:::

View File

@@ -0,0 +1,28 @@
# KernelSU 與 Magisk 的差異 {#difference-with-magisk}
儘管 KernelSU 模組和 Magisk 模組之間有許多相似之處,但由於它們完全不同的實作機制,不可避免地存在一些差異;如果您想讓您的模組同時在 Magisk 和 KernelSU 上運作,那麼您必須瞭解這些差異。
## 相同之處 {#similarities}
- 模組檔案格式:都以 Zip 的格式組織模組,並且模組的格式幾乎相同
- 模組安裝目錄:都位於 `/data/adb/modules`
- 無系統修改:都支援透過模組以無系統修改的方式來更改 `/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` 階段以在啟動完成時執行一些腳本。
7. KernelSU 新增了 `post-mount` 階段,以便在掛載 overlayfs 後執行一些腳本。

View File

@@ -0,0 +1,80 @@
# 常見問題
## KernelSU 是否支援我的裝置?
首先,您的裝置應該能解鎖 Bootloader。如果不能則不支援。
然後在您的裝置上安裝 KernelSU 管理員並開啟它,如果它顯示 `不支援`,那麼您的裝置沒有官方支援的開箱即用的 Boot 映像;但您可以自行建置核心來源並整合 KernelSU 以繼續使用。
## KernelSU 是否需要解鎖 Bootloader
當然需要。
## KernelSU 是否支援模組?
支援,但它是早期版本,可能存在問題。請等候它逐漸穩定 :)
## KernelSU 是否支援 Xposed
支援。[Dreamland](https://github.com/canyie/Dreamland) 和 [TaiChi](https://taichi.cool) 可以正常運作。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.md)
## 為何我的 Android 版本為 13但核心版本卻是 "android12-5.10"
核心版本與 Android 版本無關,如果您要使用 KernelSU請一律使用**核心版本**而非 Android 版本,如果你為 "android12-5.10" 的裝置寫入 Android 13 的核心,等候您的將會是開機迴圈。
## 我是 GKI1.0,能用 KernelSU 嗎?
GKI1 與 GKI2 完全不同,所以您需要自行編譯核心。
## KernelSU 支援 --mount-master/全域掛接命名空間嗎?
目前沒有 (未來可能會支援),但實際上有很多種方法手動進入全域命名空間,無需 `su` 內建支援,比如:
1. `nsenter -t 1 -m sh` 可以取得一個全域 mount namespace 的 shell.
2. 在您要執行的命令前新增 `nsenter --mount=/proc/1/ns/mnt` 即可使此命令在全域 mount namespace 下執行。KernelSU 本身也使用了 [這種方法](https://github.com/tiann/KernelSU/blob/77056a710073d7a5f7ee38f9e77c9fd0b3256576/manager/app/src/main/java/shirkneko/zako/mksu/ui/util/KsuCli.kt#L115)
## KernelSU 可以修改 Hosts 嗎? 我要怎麼使用 AdAway
當然。但是 KernelSU 沒有內建的 Hosts 支持,您可以安裝 [systemless-hosts](https://github.com/symbuzzer/systemless-hosts-KernelSU-module) 來做到這一點。
## 為什麼會有 1TB 的龐大檔案?
1 TB 大小的 `modules.img` 是一個磁碟映像文件不用擔心它的大小它是一種特殊類型的文件稱為稀疏檔案它的實際大小只有你使用的模組的大小並且在你刪除模組後會動態縮小實際上並沒有佔用1TB的磁碟空間實際上你的手機可能並沒有那麼多空間
如果你確實對檔案的大小不滿意,可以使用 `resize2fs -M` 指令將其調整為實際大小;但此時模組可能無法正常運作,我們不會這種情況提供任何支援。
## 為什麼我的設備顯示錯誤的儲存空間大小?
某些裝置使用非正規方法來計算裝置的儲存大小,可能會導致系統應用程式的儲存空間計算不準確,特別是在處理 1 TB 的稀疏檔案時。雖然這個問題似乎是三星設備特有的,僅影響三星應用程式和服務,但必須注意的是,差異主要在於總儲存大小,可用空間運算仍然準確。

View File

@@ -0,0 +1,7 @@
# 隱藏功能 {#hidden-features}
## .ksurc
預設狀況下,`/system/bin/sh` 會載入 `/system/etc/mkshrc`
可以透過建立 `/data/adb/ksu/.ksurc` 檔案來讓 `su` 載入此檔案而非 `/system/etc/mkshrc`

View File

@@ -0,0 +1,71 @@
# 如何建置 KernelSU? {#how-to-build-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)
:::
## 建置核心 {#build-kernel}
### 同步核心原始碼 {#sync-the-kernel-source-code}
```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)下載資訊清單。
### 建置 {#build}
請先查看[官方文件](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
```
:::info 你可能需要知道...
對於某些 Android 14 核心,要使 Wi-Fi/藍牙正常工作,可能需要刪除所有受 GKI 保護的匯出:
```sh
rm common/android/abi_gki_protected_exports_*
```
:::
## 與 KernelSU 一起建置核心 {#build-kernel-with-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,355 @@
# 如何為非 GKI 核心整合 KernelSU {#how-to-integrate-kernelsu-for-non-gki-kernels}
KernelSU 可以被整合到非 GKI 核心中,現在它最低支援到核心 4.14 版本;理論上也可以支援更低的版本。
由於非 GKI 核心的片段化極其嚴重,因此通常沒有統一的方法來建置它,所以我們也無法為非 GKI 裝置提供 Boot 映像。但您完全可以自行整合 KernelSU 並建置核心以繼續使用。
首先,您必須有能力從您裝置的核心原始碼建置出一個可以開機並且能夠正常使用的核心,如果核心並非開放原始碼,這通常難以做到。
如果您已經做好了上述準備,那有兩個方法來將 KernelSU 整合至您的核心之中。
1. 藉助 `kprobe` 自動整合
2. 手動修改核心原始碼
## 使用 kprobe 整合 {#integrate-with-kprobe}
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 無法正常運作**,您需要修正這個錯誤,或者使用第二種方法。
:::tip 如何檢查 kprobe 是否損毀?
`KernelSU/kernel/ksu.c` 中的 `ksu_enable_sucompat()``ksu_enable_ksud()` 註解掉,如果正常開機,即 kprobe 已損毀;或者您可以手動嘗試使用 kprobe 功能,如果不正常,手機會直接重新啟動。
:::
:::info 如何為非 GKI 核心啟用卸載模組功能
如果你的內核版本小於 5.10,你應該將 `path_umount` 向後移植至 `fs/namespace.c`。卸載模組功能依賴於這個函數。如果你沒有向後移植 `path_umount`,卸載模組功能將無法工作。你可以在[這裡查看更多關於 `path_unmount` 的資料](#how-to-backport-path_unpount)。
:::
## 手動修改核心原始碼 {#manually-modify-the-kernel-source}
如果 kprobe 無法正常運作 (在4.8之前可能是上游或核心的錯誤),那您可以嘗試這種方法:
首先,將 KernelSU 新增至您的原始碼樹狀結構,在核心的根目錄執行以下命令:
```sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5
```
請記住,在某些裝置上,您的 `defconfig` 可能位於 `arch/arm64/configs` 中,或在其他情況下位於 `arch/arm64/configs/vendor/你的defconfig` 中。無論您使用哪個 `defconfig`,請確保使用 `CONFIG_KSU=y` 啟用KernelSU或使用 `n` 停用它。例如,如果您選擇啟用它,則 `defconfig` 應包含以下字串:
```conf
# KernelSU
CONFIG_KSU=y
```
然後,手動修改核心原始碼,您可以參閱下方的 patch
::: 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;
}
+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);
static int do_execveat_common(int fd, struct filename *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp,
int flags)
{
+ if (unlikely(ksu_execveat_hook))
+ ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
+ else
+ ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
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);
}
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
+ int *flags);
/*
* 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;
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
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);
+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);
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
ssize_t ret;
+ if (unlikely(ksu_vfs_read_hook))
+ ksu_handle_vfs_read(&file, &buf, &count, &pos);
+
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);
+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
+
/**
* 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;
+ ksu_handle_stat(&dfd, &filename, &flags);
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);
+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
+
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;
+ ksu_handle_stat(&dfd, &filename, &flag);
+
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;
}
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
+ int *flags);
+
/*
* 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;
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
```
### 安全模式 {#safe-mode}
若要啟用 KernelSU 內建的安全模式,您還需要修改 `drivers/input/input.c` 中的 `input_handle_event` 方法:
:::tip 小建議
強烈建議啟用此功能,如果遇到開機迴圈,這將會非常有用!
:::
```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;
}
+extern bool ksu_input_hook __read_mostly;
+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
+
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);
+
+ if (unlikely(ksu_input_hook))
+ ksu_handle_input_handle_event(&type, &code, &value);
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
add_input_randomness(type, code, value);
```
:::info 不小心進入安全模式?
如果您使用手動整合且不停用 `CONFIG_KPROBES`,那麼您將可能會在啟動後透過按下音量來減少按鈕來觸發安全模式!因此,如果使用手動集成,您需要停用 `CONFIG_KPROBES`
:::
### 無法在終端中執行 `pm` ? {#failed-to-execute-pm-in-terminal}
你應該修改 `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;
}
+#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;
```
### 如何向後移植 path_umount {#how-to-backport-path_unpount}
你可以透過向後移植 `path_umount` 來讓卸載模組功能在低於 5.10 的非 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.
```
最後再次建置您的核心KernelSU 將會如期運作。

View File

@@ -0,0 +1,265 @@
# 安裝 {#title}
## 檢查您的裝置是否受支援 {#check-if-your-device-is-supported}
從 [GitHub Releases](https://github.com/tiann/KernelSU/releases) 下載 KernelSU 管理器,然後安裝至裝置並開啟:
- 如果顯示「不支援」,則表示您的裝置不支援 KernelSU您需要自行編譯核心才能繼續使用KernelSU 官方也永遠不會提供一個您可以寫入的 Boot 映像。
- 如果顯示「未安裝」,那麼 KernelSU 支援您的裝置。
::: info 提示
對於顯示「不支援」的裝置,這裡有一個[非官方支援裝置清單](unofficially-support-devices.md),您可以使用這個清單裡的核心自行編譯。
:::
## 備份您的原廠 boot.img {#backup-stock-boot-img}
在寫入核心映像前,您必須預先備份您的原廠 boot.img。如果您在後續寫入中出現了任何問題您都可以透過使用 Fastboot 寫回原廠 Boot 以還原系統。
::: warning 警告
寫入核心映像可能會造成資料遺失,請確保做好這一步再繼續進行下一步作業!!必要時您還可以備份您手機的所有資料。
:::
## 必要知識 {#necessary-knowledge}
### 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 系統更新,核心版本一般不會發生變化。如果您需要寫入,**請以核心版本為準!!**
## 安裝簡介 {#introduction}
`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-installation}
### 取得官方韌體 {#get-the-official-firmware}
使用 LKM 的模式,需要取得官方韌體,然後在官方韌體的基礎上修補;如果你使用的是第三方核心,可以把第三方核心的 boot.img 作為官方韌體。
取得官方韌體的方法有很多,如果你的裝置支援 `fastboot boot`,那麼我們最推薦以及最簡單的方法是使用 `fastboot boot` 臨時啟動 KernelSU 提供的 GKI 核心,並參考[使用管理器](#use-the-manager)安裝。
如果你的裝置不支援 `fastboot boot`,那麼你可能需要手動去下載官方韌體包,然後從中提取 boot。
與 GKI 模式不同LKM 模式會修改 `ramdisk`,因此在出廠 Android 13 的裝置上,通常它需要修補的是 `init_boot` 分區而非 `boot` 分區;而 GKI 模式則永遠是修改 `boot` 分區。
### 使用管理器 {#use-the-manager}
開啟管理器,點選右上角的安裝圖標,會出現若干個選項:
1. 選擇並修補一個文件:如果你手機目前沒有 root 權限,你可以選擇這個選項,然後選擇你的官方韌體,管理器會自動修補它。你只需要寫入這個修補後的文件,即可永久取得 root 權限。
2. 直接安裝:如果你手機已經 root你可以選擇這個選項管理器會自動獲取你的裝置資訊然後自動修補官方韌體然後寫入。你可以考慮使用 `fastboot boot` KernelSU 的 GKI 核心來取得臨時 root 安裝管理器,然後再使用這個選項。**這種方式也是 KernelSU 升級最主要的方式**。
3. 安裝到另一個分割區:如果你的裝置支援 A/B 分區,你可以選擇這個選項,管理器會自動修補官方韌體,然後安裝到另一個分區。這種方式適用於 OTA 後的裝置,你可以在 OTA 後直接安裝到另一個分割區,然後重新啟動裝置即可。
### 使用命令列{#use-the-command-line}
如果你不想使用管理器,你也可以使用命令列來安裝 LKM。KernelSU 提供的 `ksud` 可以幫助你快速修補官方韌體,然後寫入。
這個工具支援 macOS、Linux 和 Windows你可以在 [GitHub Release](https://github.com/tiann/KernelSU/releases) 下載對應的版本。
使用方法:`ksud boot-patch`。 你可以查看命令列的提示了解具體的使用方法。
```sh
husky:/ # 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-mode-installation}
GKI 的安裝方式有以下幾種,各自適用於不同的場景,請依需求選擇:
1. 使用 KernelSU 提供的 boot.img 透過 Fastboot 安裝
2. 使用核心寫入程式 (例如 KernelFlasher) 安裝
3. 使用自訂 Recovery (例如 TWRP) 安裝
4. 手動修補 boot.img 並安裝
## 使用 KernelSU 提供的 boot.img 安裝 {#install-with-boot-img-provided-by-kernelsu}
如果你的裝置的 `boot.img` 使用常見的壓縮格式,你可以直接寫入 KernelSU 提供的 GKI 核心映像,這種方法無需 TWRP也無需您的手機有 Root 權限;適用於您初次安裝 KernelSU。
### 找到合適的 boot.img {#find-proper-boot-img}
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.img 的核心壓縮格式。您應該使用正確的格式,例如 `lz4``gz`,如果你使用了不正確的壓縮格式,你可能會在寫入後無法開機。
::: info 關於 boot.img 的壓縮格式
1. 您可以透過 magiskboot 以取得您的原始 Boot 的壓縮格式。當然,您也可以詢問與您相同型號的其他更有經驗的使用者。另外,核心的壓縮格式通常不會出現變更,如果您使用的某個壓縮格式成功開機,後續可以優先嘗試這個格式。
2. 小米裝置通常 `gz` 或者 **不壓縮**
3. Pixel 裝置有些特殊,請遵循下方的指示。
:::
### 將 boot.img 寫入至裝置 {#flash-boot-img-to-device}
使用 `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
```
## 使用核心寫入程式安裝 {#install-with-kernel-flasher}
先決條件:您的裝置必須已經 Root。例如您已經安裝了 Magisk 並取得 Root 存取權,或者您已經安裝了舊版本的 KernelSU 需升級到其他版本的 KernelSU如果您的裝置並未 Root請嘗試其他方法。
步驟:
1. 下載 AnyKernel3 的 Zip 檔。如果你不知道你該下載哪個檔案,請詳細閱讀文檔中的 [KMI](#kmi) 與[安全性修補程式等級](#security-patch-level)。
2. 開啟核心寫入程式提供的 AnyKernel3 Zip 檔案並寫入核心。
如果您先前並未使用過核心寫入應用程式,可以嘗試下面幾個:
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)
P.S. 這種方法在更新 KernelSU 時比較方便,無需電腦即可完成 (注意備份!)。
## 手動修補 boot.img {#patch-boot-image}
對於某些裝置來說,其 boot.img 格式並不是很常見,不屬於 `lz4``gz` 和未壓縮;最典型的就是 Pixel它的 boot.img 格式是 `lz4_legacy` 壓縮ramdisk 可能是 `gz` 也可能是 `lz4_legacy` 壓縮;此時如果您直接寫入 KernelSU 提供的 boot.img手機可能無法開機。這時您可以透過手動修補 boot.img 來完成。
永遠建議使用 `magiskboot` 來修補映像,一般有兩種修補方法:
1. [magiskboot](https://github.com/topjohnwu/Magisk/releases)
2. [magiskboot_build](https://github.com/ookiineko/magiskboot_build/releases/tag/last-ci)
其中,官方的 `magiskboot` 僅能在 Android 上使用,若您想在電腦上完成,可以嘗試第二個選項。
### 準備 {#preparation}
1. 取得您手機的原廠 boot.img您可以從您的裝置製造商取得您也可能需要 [payload-dumper-go](https://github.com/ssut/payload-dumper-go)。
2. 下載 KernelSU 提供的與您的裝置 KMI 一致的 AnyKernel3 Zip 檔 (可參閱[使用自訂 Recovery 安裝](#install-with-custom-recovery))。
3. 解壓縮 AnyKernel3 Zip 檔,取得其中的 `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. 使用 Adb 將 magiskboot 推入至手機:`adb push Magisk-*/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot`
4. 使用 Adb 將原廠 boot.img 和 AnyKernel3 中的 Image 推入至手機。
5. adb shell 進入 /data/local/tmp/ 目錄,然後賦予先前推入的檔案可執行權限 `chmod +x magiskboot`
6. adb shell 進入 /data/local/tmp/ 目錄,執行 `./magiskboot unpack boot.img` 此時會將 `boot.img` 解除封裝,得到一個名為 `kernel` 的檔案,這個檔案是您的原廠核心。
7. 使用 `Image` 取代 `kernel`: `mv -f Image kernel`
8. 執行 `./magiskboot repack boot.img` 重新封裝映像,此時您會得到一個 `new-boot.img` 檔案,透過 Fastboot 將這個檔案寫入至裝置即可。
### 在 Windows/macOS/Linux PC 上使用 magiskboot {#using-magiskboot-on-PC}
1. 在 [magiskboot_build](https://github.com/ookiineko/magiskboot_build/releases/tag/last-ci) 下載對應的 magiskboot。
2. (僅linux)賦予檔案可執行權限 `chmod +x magiskboot`
3. 執行 `./magiskboot unpack boot.img` 此時會將 `boot.img` 解除封裝,得到一個名為 `kernel` 的檔案,這個檔案是您的原廠核心。
4. 使用 `Image` 取代 `kernel`: `mv -f Image kernel`
5. 執行 `./magiskboot repack boot.img` 重新封裝映像,此時您會得到一個 `new-boot.img` 檔案,透過 Fastboot 將這個檔案寫入至裝置即可。
## 使用自訂 Recovery 安裝 {#install-with-custom-recovery}
先決條件:您的裝置必須有自訂的 Recovery例如 TWRP。如果沒有或者只有官方 Recovery請使用其他方法。
步驟:
1. 在 KernelSU 的 [Release 頁面](https://github.com/tiann/KernelSU/releases) 下載與您手機版本相符的以 AnyKernel3 開頭的 Zip 檔;例如,手機核心版本為 `android12-5.10.66`,那麼您應該下載 `AnyKernel3-android12-5.10.66_yyyy-MM.zip` 這個檔案 (其中 `yyyy` 為年份,`MM` 為月份)。
2. 重新開機手機至 TWRP。
3. 使用 Adb 將 AnyKernel3-*.zip 放置到手機 `/sdcard` 然後在 TWRP 圖形使用者介面選擇並安裝;或者您也可以直接 `adb sideload AnyKernel-*.zip` 安裝。
PS. 這種方法適用於任何狀況下的安裝 (不限於初次安裝或後續更新),只要您用 TWRP 就可以進行作業。
## GKI的其他替代方法 {#other-methods}
其實所有這些安裝方法的主旨只有一個,那就是**將原廠核心取代為 KernelSU 提供的核心**。只要能實現這個目的,就可以安裝,比如以下是其他可行的方法:
1. 首先安裝 Magisk透過 Magisk 取得 Root 權限後使用核心寫入程式寫入 KernelSU 的 AnyKernel Zip。
2. 使用某些 PC 上的寫入工具組寫入 KernelSU 提供的核心。
但是,如果不起作用,請嘗試 Magiskboot 方法。

View File

@@ -0,0 +1,50 @@
# 模組 WebUI {#module-webui}
KernelSU 的模組除了執行啟動腳本和修改系統檔案之外,還支援顯示 UI 介面和與使用者互動。
該模組可以透過任何 Web 技術編寫 HTML + CSS + JavaScript 頁面。 KernelSU的管理器將透過 WebView 顯示這些頁面。它還提供了一些用於與系統互動的 JavaScript API例如執行 shell 命令。
## WebUI 根目錄 {#webroot-directory}
Web資源應放置在模組根目錄的 `webroot` 子目錄中,並且其中**必須**有一個名為 `index.html` 的文件,該檔案是模組頁面入口。
包含Web介面的最簡單的模組結構如下
```txt
tree .
.
|-- module.prop
`-- webroot
`-- index.html
```
:::warning 提醒
安裝模組時KernelSU 將自動設定`webroot`的權限和 SELinux context。如果您不知道自己在做什麼請不要自行設定該目錄的權限
:::
如果您的頁面包含 CSS 和 JavaScript您也需要將其放入此目錄中。
## JavaScript API
如果只是一個顯示頁面那和一般網頁沒有什麼不同。更重要的是KernelSU 提供了一系列的系統 API讓您可以實現模組獨特的功能。
KernelSU [在 npm 上發布](https://www.npmjs.com/package/kernelsu)了一個 JavaScript 庫,您可以在網頁的 JavaScript 程式碼中使用它。
例如,您可以執行 shell 命令來取得特定配置或修改屬性:
```JavaScript
import { exec } from 'kernelsu';
const { errno, stdout } = exec("getprop ro.product.model");
```
再例如,你可以讓網頁全螢幕顯示,或是顯示一個 Toast。
[API 文檔](https://www.npmjs.com/package/kernelsu)
如果您發現現有的 API 無法滿足您的需求或使用不方便,歡迎您在[這裡](https://github.com/tiann/KernelSU/issues)給我們建議!
## 一些技巧 {#some-tips}
1. 您可以正常使用 `localStorage` 來儲存一些數據,但解除安裝管理器後,這些數據將會遺失。如果需要持久保存,可以自行將資料寫入某個目錄。
2. 對於簡單的頁面,我建議您使用 [parceljs](https://parceljs.org/) 進行打包。它無須設定,使用非常方便。不過,如果你是前端高手或有自己的喜好,那就選擇你喜歡的吧!

View File

@@ -0,0 +1,331 @@
# 模組指南 {#module-guide}
KernelSU 提供了一個模組機制,它可以在保持系統分割區完整性的同時達到修改系統分割區的效果;這種機制一般被稱為 systemless (無系統修改)。
KernelSU 的模組運作機制與 Magisk 幾乎相同,如果您熟悉 Magisk 模組的開發,那麼開發 KernelSU 的模組大同小異,您可以跳過下列有關模組的介紹,只需要瞭解 [KernelSU 模組與 Magisk 模組的差異](difference-with-magisk.md)。
## WebUI
KernelSU 的模組支援顯示互動介面,請參閱 [WebUI 文檔](module-webui.md).
## Busybox
KernelSU 提供了一個完備的 BusyBox 二進位檔案 (包括完整的 SELinux 支援)。可執行檔位於 `/data/adb/ksu/bin/busybox`
KernelSU 的 BusyBox 支援執行時可切換的 "ASH 獨立模式"。
ASH 獨立模式在執行 BusyBox 時,每個命令都會直接使用 BusyBox 中內建的應用程式,而不論 `PATH` 的設定為何。
例如,`ls``rm``chmod` 等命令將不會使用 `PATH` 中設定的命令 (在 Android 的狀況下,預設狀況下分別為 `/system/bin/ls``/system/bin/rm``/system/bin/chmod`),而是直接呼叫 BusyBox 內建的應用程式。
這確保了腳本始終在可預測的環境中執行,並始終具有完整的命令套件,不論它執行在哪個 Android 版本上。
要強制下一個命令不使用 BusyBox您必須使用完整路徑呼叫可執行檔。
每個基於 KernelSU 上下文的腳本都將在 BusyBox 的獨立模式執行。對於第三方開發人員而言,這包括所有開機腳本和模組安裝腳本。
對於想要在 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 不存在時被掛接至系統
│ │ ├── ...
│ │ ├── ...
│ │ └── ...
│ │
│ │ *** 狀態旗標 ***
│ │
│ ├── skip_mount <--- 如果這個檔案存在,那麼 KernelSU 將不會掛接您的系統資料夾
│ ├── disable <--- 如果這個檔案存在,那麼模組將會被停用
│ ├── remove <--- 如果這個檔案存在,那麼模組將會在下次重新開機時被移除
│ │
│ │ *** 選用檔案 ***
│ │
│ ├── post-fs-data.sh <--- 這個腳本將會在 post-fs-data 中執行
│ ├── service.sh <--- 這個腳本將會在 late_start 服務中執行
| ├── uninstall.sh <--- 這個腳本將會在 KernelSU 移除模組時執行
│ ├── system.prop <--- 這個檔案中指定的屬性將會在系統啟動時透過 resetprop 變更
│ ├── sepolicy.rule <--- 這個檔案中的 SELinux 原則將會在系統開機時載入
│ │
│ │ *** 自動產生的目錄,不要手動建立或修改! ***
│ │
│ ├── vendor <--- A symlink to $MODID/system/vendor
│ ├── product <--- A symlink to $MODID/system/product
│ ├── system_ext <--- A symlink to $MODID/system/system_ext
│ │
│ │ *** 允許的其他額外檔案/資料夾 ***
│ │
│ ├── ...
│ └── ...
|
├── 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 必須是一個整數,用於比較版本。
- 其他未在上方提到的內容可以是任何單行字串。
- 請確保使用 `UNIX (LF)` 分行符號類型,而非 `Windows (CR + LF)``Macintosh (CR)`
### Shell 腳本 {#shell-scripts}
請閱讀 [開機腳本](#boot-scripts) 章節,以瞭解 `post-fs-data.sh``service.sh` 之間的差別。對於大多數模組開發人員來說,如果您只需要執行一個開機腳本,`service.sh` 應該已經足夠了。
如果您需要在啟動完成後執行腳本,請使用 `boot-completed.sh`
如果你想在掛載 overlayfs 後做一些事情,請使用 `post-mount.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` 中宣告一個名為 `REMOVE` 並且包含一系列目錄的變數以執行移除作業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
如果您的模組需要一些額外 sepolicy 修補,請將這些原則新增至這個檔案中。這個檔案的每一行都將被視為一個原則陳述。
## 模組安裝程式 {#module-installer}
KernelSU 的模組安裝程式就是一個可以透過 KernelSU 管理員應用程式刷新的 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` 的檔案,這個檔案將會在模組被解壓縮後經由 **source** 調用,如果您的模組需要依據裝置的 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): 目前模組的安裝程式 Zip
- `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 啟動之前,這意味著 Android 中的一切。
- **警告**: 使用 `setprop` 會導致開機程序死鎖!請使用 `resetprop -n <prop_name> <prop_value>` 替代。
- **僅在必要時在此模式中執行腳本**。
- late_start 服務模式
- 這個階段是 **非阻塞** 的。您的腳本會與其餘的啟動程序**平行**執行。
- **大多數腳本建議在這種模式下執行**。
在 KernelSU 中,開機腳本依據存放位置的不同還分為兩種:一般腳本和模組腳本。
- 一般腳本
- 放置於 `/data/adb/post-fs-data.d``/data/adb/service.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` 在 overlayfs 掛載後運行,而 `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 時變磚 {#brick-by-flashing-boot-partition}
在 KernelSU 中,寫入 boot 時變磚有下列原因:
1. 你寫入了錯誤格式的 Boot 映像。比如您的手機 Boot 格式為 `gz`,但您寫入 `lz4` 格式的映像,那麼此時手機將無法開機。
2. 您的手機需要關閉 AVB 驗證才可正常開機 (這通常需要抹除手機上的所有資料)。
3. 您的核心存在某些錯誤或您的核心並不適合這部手機寫入。
無論哪種狀況,您都可以透過**寫入原廠 Boot** 還原;因此,在安裝教學最開始,我們已經強烈建議大家,在寫入之前備份自己的原廠 Boot如果您沒有備份那麼您可以透過其他與您相同裝置的使用者或官方韌體擷取 Boot。
## 安裝模組變磚 {#brick-by-modules}
安裝模組變磚可能是大家遇到的更常見的狀況,但是這裡要嚴正警示大家:**不要安裝未知來源的模組!!**。因為模組擁有 Root 權限,它能完全對您的裝置造成無法復原的損壞!
### 一般模組 {#normal-modules}
如果大家安裝了某些開放原始碼的或者被證明是安全的模組使手機無法開機,那麼這種狀況在 KernelSU 中非常容易還原也無需擔心。KernelSU 內建了下列兩種機制以搶救您的裝置:
1. AB 更新
2. 透過按下「音量 -」搶救
#### AB 更新 {#ab-update}
KernelSU 的模組借鑒了 Android 系統 OTA 更新時的 AB 更新機制,如果您安裝了新模組或者對現存模組進行了更新作業,不會直接修改目前使用的模組檔案,而是會把所有模組建置為另外一個更新映像;系統重新啟動後,會使用這個更新映像嘗試重新啟動一次,如果 Android 系統成功開機,模組才會真正更新。
因此,最簡單最常用的搶救方法就是:**強制重新開機一次**。如果您在刷新某個模組之後系統無法開機,您可以長按電源按鈕超過 10 秒,系統會自動重新開機;模組會回復為更新前的狀態,先前更新的模組也將會被自動停用。
#### 透過按下「音量 -」搶救 {#rescue-by-pressing-volume-down}
如果 AB 更新仍然無法解決,您可以嘗試使用**安全模式**。進入安全模式之後,所有的模組將會被停用。
進入安全模式的方法有兩種:
1. 某些系統內建的安全模式:有些系統是長按「音量 -」,有些系統 (例如 MIUI) 可以在 Recovery 中啟用安全模式。進入系統的安全模式後KernelSU 也會進入安全模式,並自動停用模組。
2. KernelSU 內建的安全模式:開機第一個畫面後,**連續按下「音量 -」按鈕超過三次**。注意是按下-抬起、按下-抬起、按下-抬起,並非一直按下。
進入安全模式後KernelSU 管理員的模組頁面的所有模組將會被停用,但您可以執行「解除安裝」作業,將可能存在問題的模組解除安裝。
內建的安全模式在核心中實作,因此不會出現按鍵活動無法攔截的狀況。不過對於非 GKI 核心,可能需要手動整合程式碼,可以參閱官方文件指南。
### 惡意模組 {#malicious-modules}
如果以上方法無法搶救您的裝置,那麼很可能您安裝的模組存在惡意作業或透過其他方式損壞了您的裝置,在這種狀況下,只有兩個建議:
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 {#what-is-kernelsu}
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)