├── .github ├── patches │ ├── 5.10 │ │ └── 0001-Makefile-Use-CCACHE-for-faster-compilation.patch │ └── 5.15 │ │ └── 0001-Makefile-Use-CCACHE-for-faster-compilation.patch ├── scripts │ ├── build_a12.sh │ └── build_a13.sh └── workflows │ ├── build-debug-kernel.yml │ ├── build-kernel-a12-5.10.yml │ ├── build-kernel-a13-5.10.yml │ ├── build-kernel-a13-5.15.yml │ ├── build-kernel-arcvm.yml │ ├── build-kernel-wsa.yml │ ├── build-ksud.yml │ ├── build-manager.yml │ ├── build-su.yml │ ├── clippy.yml │ ├── gki-kernel.yml │ ├── ksud.yml │ ├── release.yml │ ├── rustfmt.yml │ └── shellcheck.yml ├── .gitignore ├── LICENSE ├── README.md ├── README_CN.md ├── kernel ├── .clang-format ├── .clangd ├── Kconfig ├── LICENSE ├── Makefile ├── allowlist.c ├── allowlist.h ├── apk_sign.c ├── apk_sign.h ├── arch.h ├── core_hook.c ├── core_hook.h ├── embed_ksud.c ├── export_symbol.txt ├── include │ └── ksu_hook.h ├── kernel_compat.c ├── kernel_compat.h ├── klog.h ├── ksu.c ├── ksu.h ├── ksud.c ├── ksud.h ├── manager.c ├── manager.h ├── module_api.c ├── selinux │ ├── Makefile │ ├── rules.c │ ├── selinux.c │ ├── selinux.h │ ├── sepolicy.c │ └── sepolicy.h ├── setup.sh ├── sucompat.c ├── uid_observer.c └── uid_observer.h ├── manager ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── aidl │ │ └── me │ │ │ └── weishu │ │ │ └── kernelsu │ │ │ └── IKsuInterface.aidl │ │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── jni.cc │ │ ├── ksu.cc │ │ └── ksu.h │ │ ├── java │ │ └── me │ │ │ └── weishu │ │ │ └── kernelsu │ │ │ ├── KernelSUApplication.kt │ │ │ ├── Kernels.kt │ │ │ ├── Natives.kt │ │ │ ├── profile │ │ │ ├── Capabilities.kt │ │ │ └── Groups.kt │ │ │ └── ui │ │ │ ├── KsuService.java │ │ │ ├── MainActivity.kt │ │ │ ├── component │ │ │ ├── AboutCard.kt │ │ │ ├── Dialog.kt │ │ │ ├── KeyEventBlocker.kt │ │ │ ├── SearchBar.kt │ │ │ ├── SettingsItem.kt │ │ │ └── profile │ │ │ │ ├── AppProfileConfig.kt │ │ │ │ └── RootProfileConfig.kt │ │ │ ├── screen │ │ │ ├── AppProfile.kt │ │ │ ├── BottomBarDestination.kt │ │ │ ├── Home.kt │ │ │ ├── Install.kt │ │ │ ├── Module.kt │ │ │ ├── Settings.kt │ │ │ └── SuperUser.kt │ │ │ ├── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ │ │ ├── util │ │ │ ├── CompositionProvider.kt │ │ │ ├── Downloader.kt │ │ │ ├── HanziToPinyin.java │ │ │ ├── HyperlinkText.kt │ │ │ ├── KsuCli.kt │ │ │ ├── LogEvent.kt │ │ │ └── SELinuxChecker.kt │ │ │ └── viewmodel │ │ │ ├── ModuleViewModel.kt │ │ │ └── SuperUserViewModel.kt │ │ ├── jniLibs │ │ └── .gitignore │ │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ └── ic_launcher_monochrome.xml │ │ ├── mipmap-anydpi-v26 │ │ └── ic_launcher.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-ldpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-ar │ │ └── strings.xml │ │ ├── values-az │ │ └── strings.xml │ │ ├── values-bn-rBD │ │ └── strings.xml │ │ ├── values-bn │ │ └── strings.xml │ │ ├── values-da │ │ └── strings.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-fa │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-in │ │ └── strings.xml │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-ja │ │ └── strings.xml │ │ ├── values-ko │ │ └── strings.xml │ │ ├── values-lt │ │ └── strings.xml │ │ ├── values-mr │ │ └── strings.xml │ │ ├── values-nl │ │ └── strings.xml │ │ ├── values-pl │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-pt │ │ └── strings.xml │ │ ├── values-ro │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-th │ │ └── strings.xml │ │ ├── values-tr │ │ └── strings.xml │ │ ├── values-uk │ │ └── strings.xml │ │ ├── values-vi │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values-zh-rHK │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ ├── values │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ └── filepaths.xml ├── build.gradle.kts ├── gradle.properties ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── sign.example.properties ├── scripts ├── abi_gki_all.py ├── add_device_handler.py ├── bin2c.py └── check_v2.c └── userspace ├── ksud ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── bin │ ├── aarch64 │ │ ├── busybox │ │ └── resetprop │ └── x86_64 │ │ ├── busybox │ │ └── resetprop ├── build.rs └── src │ ├── apk_sign.rs │ ├── assets.rs │ ├── cli.rs │ ├── debug.rs │ ├── defs.rs │ ├── event.rs │ ├── ksu.rs │ ├── main.rs │ ├── module.rs │ ├── profile.rs │ ├── restorecon.rs │ ├── sepolicy.rs │ └── utils.rs └── su ├── .gitignore └── jni ├── Android.mk ├── Application.mk └── su.c /.github/patches/5.10/0001-Makefile-Use-CCACHE-for-faster-compilation.patch: -------------------------------------------------------------------------------- 1 | From f1e398602b989ac197cdd0fda4a7c4c323b03eb9 Mon Sep 17 00:00:00 2001 2 | From: DozNaka 3 | Date: Mon, 11 Apr 2022 20:43:45 -0400 4 | Subject: [PATCH] Makefile: Use CCACHE for faster compilation 5 | 6 | --- 7 | Makefile | 20 ++++++++++---------- 8 | 1 file changed, 10 insertions(+), 10 deletions(-) 9 | 10 | diff --git a/Makefile b/Makefile 11 | index e8b8d5894..51e8aac6e 100644 12 | --- a/Makefile 13 | +++ b/Makefile 14 | @@ -442,21 +442,21 @@ KBUILD_HOSTLDLIBS := $(HOST_LFS_LIBS) $(HOSTLDLIBS) 15 | # Make variables (CC, etc...) 16 | CPP = $(CC) -E 17 | ifneq ($(LLVM),) 18 | -CC = clang 19 | -LD = ld.lld 20 | -AR = llvm-ar 21 | +CC = $(CCACHE) clang 22 | +LD = $(CCACHE) ld.lld 23 | +AR = $(CCACHE) llvm-ar 24 | NM = llvm-nm 25 | -OBJCOPY = llvm-objcopy 26 | -OBJDUMP = llvm-objdump 27 | +OBJCOPY = $(CCACHE) llvm-objcopy 28 | +OBJDUMP = $(CCACHE) llvm-objdump 29 | READELF = llvm-readelf 30 | STRIP = llvm-strip 31 | else 32 | -CC = $(CROSS_COMPILE)gcc 33 | -LD = $(CROSS_COMPILE)ld 34 | -AR = $(CROSS_COMPILE)ar 35 | +CC = $(CCACHE) $(CROSS_COMPILE)gcc 36 | +LD = $(CCACHE) $(CROSS_COMPILE)ld 37 | +AR = $(CCACHE) $(CROSS_COMPILE)ar 38 | NM = $(CROSS_COMPILE)nm 39 | -OBJCOPY = $(CROSS_COMPILE)objcopy 40 | -OBJDUMP = $(CROSS_COMPILE)objdump 41 | +OBJCOPY = $(CCACHE) $(CROSS_COMPILE)objcopy 42 | +OBJDUMP = $(CCACHE) $(CROSS_COMPILE)objdump 43 | READELF = $(CROSS_COMPILE)readelf 44 | STRIP = $(CROSS_COMPILE)strip 45 | endif 46 | -- 47 | 2.37.2 48 | 49 | -------------------------------------------------------------------------------- /.github/patches/5.15/0001-Makefile-Use-CCACHE-for-faster-compilation.patch: -------------------------------------------------------------------------------- 1 | From f1e398602b989ac197cdd0fda4a7c4c323b03eb9 Mon Sep 17 00:00:00 2001 2 | From: DozNaka 3 | Date: Mon, 11 Apr 2022 20:43:45 -0400 4 | Subject: [PATCH] Makefile: Use CCACHE for faster compilation 5 | 6 | --- 7 | Makefile | 20 ++++++++++---------- 8 | 1 file changed, 10 insertions(+), 10 deletions(-) 9 | 10 | diff --git a/Makefile b/Makefile 11 | index e8b8d5894..51e8aac6e 100644 12 | --- a/Makefile 13 | +++ b/Makefile 14 | @@ -442,21 +442,21 @@ KBUILD_HOSTLDLIBS := $(HOST_LFS_LIBS) $(HOSTLDLIBS) 15 | # Make variables (CC, etc...) 16 | CPP = $(CC) -E 17 | ifneq ($(LLVM),) 18 | -CC = clang 19 | -LD = ld.lld 20 | -AR = llvm-ar 21 | +CC = $(CCACHE) clang 22 | +LD = $(CCACHE) ld.lld 23 | +AR = $(CCACHE) llvm-ar 24 | NM = llvm-nm 25 | -OBJCOPY = llvm-objcopy 26 | -OBJDUMP = llvm-objdump 27 | +OBJCOPY = $(CCACHE) llvm-objcopy 28 | +OBJDUMP = $(CCACHE) llvm-objdump 29 | READELF = llvm-readelf 30 | STRIP = llvm-strip 31 | else 32 | -CC = $(CROSS_COMPILE)gcc 33 | -LD = $(CROSS_COMPILE)ld 34 | -AR = $(CROSS_COMPILE)ar 35 | +CC = $(CCACHE) $(CROSS_COMPILE)gcc 36 | +LD = $(CCACHE) $(CROSS_COMPILE)ld 37 | +AR = $(CCACHE) $(CROSS_COMPILE)ar 38 | NM = $(CROSS_COMPILE)nm 39 | -OBJCOPY = $(CROSS_COMPILE)objcopy 40 | -OBJDUMP = $(CROSS_COMPILE)objdump 41 | +OBJCOPY = $(CCACHE) $(CROSS_COMPILE)objcopy 42 | +OBJDUMP = $(CCACHE) $(CROSS_COMPILE)objdump 43 | READELF = $(CROSS_COMPILE)readelf 44 | STRIP = $(CROSS_COMPILE)strip 45 | endif 46 | -- 47 | 2.37.2 48 | 49 | -------------------------------------------------------------------------------- /.github/scripts/build_a12.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | build_from_image() { 5 | export TITLE 6 | TITLE=kernel-aarch64-${1//Image-/} 7 | echo "[+] title: $TITLE" 8 | 9 | export PATCH_LEVEL 10 | PATCH_LEVEL=$(echo "$1" | awk -F_ '{ print $2}') 11 | echo "[+] patch level: $PATCH_LEVEL" 12 | 13 | echo '[+] Download prebuilt ramdisk' 14 | GKI_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-"${PATCH_LEVEL}"_r1.zip 15 | FALLBACK_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-2023-01_r1.zip 16 | status=$(curl -sL -w "%{http_code}" "$GKI_URL" -o /dev/null) 17 | if [ "$status" = "200" ]; then 18 | curl -Lo gki-kernel.zip "$GKI_URL" 19 | else 20 | echo "[+] $GKI_URL not found, using $FALLBACK_URL" 21 | curl -Lo gki-kernel.zip "$FALLBACK_URL" 22 | fi 23 | unzip gki-kernel.zip && rm gki-kernel.zip 24 | 25 | echo '[+] Unpack prebuilt boot.img' 26 | BOOT_IMG=$(find . -maxdepth 1 -name "boot*.img") 27 | $UNPACK_BOOTIMG --boot_img="$BOOT_IMG" 28 | rm "$BOOT_IMG" 29 | 30 | echo '[+] Building Image.gz' 31 | $GZIP -n -k -f -9 Image >Image.gz 32 | 33 | echo '[+] Building boot.img' 34 | $MKBOOTIMG --header_version 4 --kernel Image --output boot.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}" 35 | $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem 36 | 37 | echo '[+] Building boot-gz.img' 38 | $MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}" 39 | $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem 40 | 41 | echo '[+] Building boot-lz4.img' 42 | $MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}" 43 | $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem 44 | 45 | echo '[+] Compress images' 46 | for image in boot*.img; do 47 | $GZIP -n -f -9 "$image" 48 | mv "$image".gz "${1//Image-/}"-"$image".gz 49 | done 50 | 51 | echo "[+] Images to upload" 52 | find . -type f -name "*.gz" 53 | } 54 | 55 | for dir in Image*; do 56 | if [ -d "$dir" ]; then 57 | echo "----- Building $dir -----" 58 | cd "$dir" 59 | build_from_image "$dir" 60 | cd .. 61 | fi 62 | done 63 | -------------------------------------------------------------------------------- /.github/scripts/build_a13.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | build_from_image() { 5 | export TITLE 6 | TITLE=kernel-aarch64-${1//Image-/} 7 | 8 | echo "[+] title: $TITLE" 9 | echo '[+] Building Image.gz' 10 | $GZIP -n -k -f -9 Image >Image.gz 11 | 12 | echo '[+] Building boot.img' 13 | $MKBOOTIMG --header_version 4 --kernel Image --output boot.img 14 | $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem 15 | 16 | echo '[+] Building boot-gz.img' 17 | $MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img 18 | $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem 19 | 20 | echo '[+] Building boot-lz4.img' 21 | $MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img 22 | $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem 23 | 24 | echo '[+] Compress images' 25 | for image in boot*.img; do 26 | $GZIP -n -f -9 "$image" 27 | mv "$image".gz "${1//Image-/}"-"$image".gz 28 | done 29 | 30 | echo '[+] Images to upload' 31 | find . -type f -name "*.gz" 32 | } 33 | 34 | for dir in Image*; do 35 | if [ -d "$dir" ]; then 36 | echo "----- Building $dir -----" 37 | cd "$dir" 38 | build_from_image "$dir" 39 | cd .. 40 | fi 41 | done 42 | -------------------------------------------------------------------------------- /.github/workflows/build-kernel-a12-5.10.yml: -------------------------------------------------------------------------------- 1 | name: Build Kernel - Android 12 5.10 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "master" ] 7 | paths: 8 | - ".github/workflows/build-kernel-a12-5.10.yml" 9 | - ".github/workflows/gki-kernel.yml" 10 | - ".github/scripts/build_a12.sh" 11 | - "kernel/**" 12 | pull_request: 13 | branches: [ "master" ] 14 | paths: 15 | - ".github/workflows/build-kernel-a12-5.10.yml" 16 | - ".github/workflows/gki-kernel.yml" 17 | - ".github/scripts/build-a12.sh" 18 | - "kernel/**" 19 | workflow_call: 20 | 21 | jobs: 22 | build-kernel: 23 | if: github.event_name != 'pull_request' 24 | strategy: 25 | matrix: 26 | include: 27 | - sub_level: 43 28 | os_patch_level: 2021-10 29 | - sub_level: 66 30 | os_patch_level: 2022-01 31 | - sub_level: 81 32 | os_patch_level: 2022-03 33 | - sub_level: 101 34 | os_patch_level: 2022-05 35 | - sub_level: 110 36 | os_patch_level: 2022-07 37 | - sub_level: 117 38 | os_patch_level: 2022-09 39 | - sub_level: 136 40 | os_patch_level: 2022-11 41 | - sub_level: 149 42 | os_patch_level: 2023-01 43 | - sub_level: 160 44 | os_patch_level: 2023-03 45 | - sub_level: 168 46 | os_patch_level: 2023-05 47 | - sub_level: 177 48 | os_patch_level: 2023-07 49 | - sub_level: 187 50 | os_patch_level: lts 51 | uses: ./.github/workflows/gki-kernel.yml 52 | secrets: inherit 53 | with: 54 | version: android12-5.10 55 | version_name: android12-5.10.${{ matrix.sub_level }} 56 | tag: android12-5.10-${{ matrix.os_patch_level }} 57 | os_patch_level: ${{ matrix.os_patch_level }} 58 | patch_path: "5.10" 59 | 60 | upload-artifacts: 61 | needs: build-kernel 62 | runs-on: ubuntu-latest 63 | if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/master' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }} 64 | env: 65 | CHAT_ID: ${{ secrets.CHAT_ID }} 66 | CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }} 67 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 68 | MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }} 69 | COMMIT_MESSAGE: ${{ github.event.head_commit.message }} 70 | COMMIT_URL: ${{ github.event.head_commit.url }} 71 | RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 72 | steps: 73 | - name: Download artifacts 74 | uses: actions/download-artifact@v3 75 | 76 | - uses: actions/checkout@v3 77 | with: 78 | path: KernelSU 79 | fetch-depth: 0 80 | 81 | - name: List artifacts 82 | run: | 83 | tree 84 | 85 | - name: Download prebuilt toolchain 86 | run: | 87 | AOSP_MIRROR=https://android.googlesource.com 88 | BRANCH=master-kernel-build-2022 89 | git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools 90 | git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools 91 | git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1 92 | 93 | - name: Set boot sign key 94 | env: 95 | BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }} 96 | run: | 97 | if [ ! -z "$BOOT_SIGN_KEY" ]; then 98 | echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem 99 | fi 100 | 101 | - name: Build boot images 102 | run: | 103 | export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool 104 | export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip 105 | export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4 106 | export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py 107 | export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py 108 | cd $GITHUB_WORKSPACE/KernelSU 109 | export VERSION=$(($(git rev-list --count HEAD) + 10200)) 110 | echo "VERSION: $VERSION" 111 | cd - 112 | bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a12.sh 113 | 114 | - name: Display structure of boot files 115 | run: ls -R 116 | 117 | - name: Upload images artifact 118 | uses: actions/upload-artifact@v3 119 | with: 120 | name: boot-images-android12 121 | path: Image-android12*/*.img.gz 122 | 123 | check-build-kernel: 124 | strategy: 125 | matrix: 126 | include: 127 | - sub_level: 185 128 | uses: ./.github/workflows/gki-kernel.yml 129 | with: 130 | version: android12-5.10 131 | version_name: android12-5.10.${{ matrix.sub_level }} 132 | tag: android12-5.10 133 | os_patch_level: mainline 134 | patch_path: "5.10" 135 | -------------------------------------------------------------------------------- /.github/workflows/build-kernel-a13-5.10.yml: -------------------------------------------------------------------------------- 1 | name: Build Kernel - Android 13 5.10 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "master" ] 7 | paths: 8 | - ".github/workflows/build-kernel-a13-5.10.yml" 9 | - ".github/workflows/gki-kernel.yml" 10 | - ".github/scripts/build_a13.sh" 11 | - "kernel/**" 12 | pull_request: 13 | branches: [ "master" ] 14 | paths: 15 | - ".github/workflows/build-kernel-a13-5.10.yml" 16 | - ".github/workflows/gki-kernel.yml" 17 | - ".github/scripts/build-a13.sh" 18 | - "kernel/**" 19 | workflow_call: 20 | 21 | jobs: 22 | build-kernel: 23 | if: github.event_name != 'pull_request' 24 | strategy: 25 | matrix: 26 | include: 27 | # Temporarily disabled for compiling via GitHub Actions 28 | - sub_level: 107 29 | os_patch_level: 2022-11 30 | - sub_level: 149 31 | os_patch_level: 2023-01 32 | - sub_level: 157 33 | os_patch_level: 2023-03 34 | - sub_level: 168 35 | os_patch_level: 2023-05 36 | - sub_level: 177 37 | os_patch_level: 2023-07 38 | - sub_level: 186 39 | os_patch_level: 2023-08 40 | - sub_level: 187 41 | os_patch_level: lts 42 | uses: ./.github/workflows/gki-kernel.yml 43 | secrets: inherit 44 | with: 45 | version: android13-5.10 46 | version_name: android13-5.10.${{ matrix.sub_level }} 47 | tag: android13-5.10-${{ matrix.os_patch_level }} 48 | os_patch_level: ${{ matrix.os_patch_level }} 49 | patch_path: "5.10" 50 | 51 | upload-artifacts: 52 | needs: build-kernel 53 | runs-on: ubuntu-latest 54 | if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/master' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }} 55 | env: 56 | CHAT_ID: ${{ secrets.CHAT_ID }} 57 | CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }} 58 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 59 | MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }} 60 | COMMIT_MESSAGE: ${{ github.event.head_commit.message }} 61 | COMMIT_URL: ${{ github.event.head_commit.url }} 62 | RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 63 | steps: 64 | - name: Download artifacts 65 | uses: actions/download-artifact@v3 66 | 67 | - uses: actions/checkout@v3 68 | with: 69 | path: KernelSU 70 | fetch-depth: 0 71 | 72 | - name: List artifacts 73 | run: | 74 | tree 75 | 76 | - name: Download prebuilt toolchain 77 | run: | 78 | AOSP_MIRROR=https://android.googlesource.com 79 | BRANCH=master-kernel-build-2022 80 | git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools 81 | git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools 82 | git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1 83 | 84 | - name: Set boot sign key 85 | env: 86 | BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }} 87 | run: | 88 | if [ ! -z "$BOOT_SIGN_KEY" ]; then 89 | echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem 90 | fi 91 | 92 | - name: Build boot images 93 | run: | 94 | export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool 95 | export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip 96 | export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4 97 | export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py 98 | export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py 99 | cd $GITHUB_WORKSPACE/KernelSU 100 | export VERSION=$(($(git rev-list --count HEAD) + 10200)) 101 | echo "VERSION: $VERSION" 102 | cd - 103 | bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh 104 | 105 | - name: Display structure of boot files 106 | run: ls -R 107 | 108 | - name: Upload images artifact 109 | uses: actions/upload-artifact@v3 110 | with: 111 | name: boot-images-android13 112 | path: Image-android13*/*.img.gz 113 | 114 | check-build-kernel: 115 | strategy: 116 | matrix: 117 | include: 118 | - sub_level: 186 119 | uses: ./.github/workflows/gki-kernel.yml 120 | with: 121 | version: android13-5.10 122 | version_name: android13-5.10.${{ matrix.sub_level }} 123 | tag: android13-5.10 124 | os_patch_level: mainline 125 | patch_path: "5.10" 126 | -------------------------------------------------------------------------------- /.github/workflows/build-kernel-a13-5.15.yml: -------------------------------------------------------------------------------- 1 | name: Build Kernel - Android 13 5.15 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "master" ] 7 | paths: 8 | - ".github/workflows/build-kernel-a13-5.15.yml" 9 | - ".github/workflows/gki-kernel.yml" 10 | - ".github/scripts/build_a13.sh" 11 | - "kernel/**" 12 | pull_request: 13 | branches: [ "master" ] 14 | paths: 15 | - ".github/workflows/build-kernel-a13-5.15.yml" 16 | - ".github/workflows/gki-kernel.yml" 17 | - ".github/scripts/build-a13.sh" 18 | - "kernel/**" 19 | workflow_call: 20 | 21 | jobs: 22 | build-kernel: 23 | if: github.event_name != 'pull_request' 24 | strategy: 25 | matrix: 26 | include: 27 | # Temporarily disabled for compiling via GitHub Actions 28 | - sub_level: 41 29 | os_patch_level: 2022-11 30 | - sub_level: 74 31 | os_patch_level: 2023-01 32 | - sub_level: 78 33 | os_patch_level: 2023-03 34 | - sub_level: 94 35 | os_patch_level: 2023-05 36 | - sub_level: 104 37 | os_patch_level: 2023-07 38 | - sub_level: 119 39 | os_patch_level: 2023-08 40 | - sub_level: 120 41 | os_patch_level: lts 42 | uses: ./.github/workflows/gki-kernel.yml 43 | secrets: inherit 44 | with: 45 | version: android13-5.15 46 | version_name: android13-5.15.${{ matrix.sub_level }} 47 | tag: android13-5.15-${{ matrix.os_patch_level }} 48 | os_patch_level: ${{ matrix.os_patch_level }} 49 | patch_path: "5.15" 50 | upload-artifacts: 51 | needs: build-kernel 52 | runs-on: ubuntu-latest 53 | if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/master' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }} 54 | env: 55 | CHAT_ID: ${{ secrets.CHAT_ID }} 56 | CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }} 57 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 58 | MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }} 59 | COMMIT_MESSAGE: ${{ github.event.head_commit.message }} 60 | COMMIT_URL: ${{ github.event.head_commit.url }} 61 | RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 62 | steps: 63 | - name: Download artifacts 64 | uses: actions/download-artifact@v3 65 | 66 | - uses: actions/checkout@v3 67 | with: 68 | path: KernelSU 69 | fetch-depth: 0 70 | 71 | - name: List artifacts 72 | run: | 73 | tree 74 | 75 | - name: Download prebuilt toolchain 76 | run: | 77 | AOSP_MIRROR=https://android.googlesource.com 78 | BRANCH=master-kernel-build-2022 79 | git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools 80 | git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools 81 | git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1 82 | 83 | - name: Set boot sign key 84 | env: 85 | BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }} 86 | run: | 87 | if [ ! -z "$BOOT_SIGN_KEY" ]; then 88 | echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem 89 | fi 90 | 91 | - name: Build boot images 92 | run: | 93 | export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool 94 | export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip 95 | export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4 96 | export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py 97 | export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py 98 | cd $GITHUB_WORKSPACE/KernelSU 99 | export VERSION=$(($(git rev-list --count HEAD) + 10200)) 100 | echo "VERSION: $VERSION" 101 | cd - 102 | bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh 103 | 104 | - name: Display structure of boot files 105 | run: ls -R 106 | 107 | - name: Upload images artifact 108 | uses: actions/upload-artifact@v3 109 | with: 110 | name: boot-images-android13 111 | path: Image-android13*/*.img.gz 112 | 113 | check-build-kernel: 114 | strategy: 115 | matrix: 116 | include: 117 | - sub_level: 119 118 | uses: ./.github/workflows/gki-kernel.yml 119 | with: 120 | version: android13-5.15 121 | version_name: android13-5.15.${{ matrix.sub_level }} 122 | tag: android13-5.15 123 | os_patch_level: mainline 124 | patch_path: "5.15" 125 | -------------------------------------------------------------------------------- /.github/workflows/build-ksud.yml: -------------------------------------------------------------------------------- 1 | name: Build KSUD 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "master" ] 7 | paths: 8 | - '.github/workflows/build-ksud.yml' 9 | - '.github/workflows/ksud.yml' 10 | - 'userspace/ksud/**' 11 | pull_request: 12 | branches: [ "master" ] 13 | paths: 14 | - '.github/workflows/build-ksud.yml' 15 | - '.github/workflows/ksud.yml' 16 | - 'userspace/ksud/**' 17 | 18 | jobs: 19 | build: 20 | strategy: 21 | matrix: 22 | include: 23 | - target: aarch64-linux-android 24 | - target: x86_64-linux-android 25 | - target: x86_64-pc-windows-gnu # only for build 26 | uses: ./.github/workflows/ksud.yml 27 | with: 28 | target: ${{ matrix.target }} 29 | -------------------------------------------------------------------------------- /.github/workflows/build-manager.yml: -------------------------------------------------------------------------------- 1 | name: Build Manager 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "master" ] 7 | paths: 8 | - '.github/workflows/build-manager.yml' 9 | - 'manager/**' 10 | - 'userspace/ksud/**' 11 | pull_request: 12 | branches: [ "master" ] 13 | paths: 14 | - 'manager/**' 15 | workflow_call: 16 | 17 | jobs: 18 | build-ksud: 19 | strategy: 20 | matrix: 21 | include: 22 | - target: aarch64-linux-android 23 | - target: x86_64-linux-android 24 | uses: ./.github/workflows/ksud.yml 25 | with: 26 | target: ${{ matrix.target }} 27 | build-manager: 28 | needs: build-ksud 29 | runs-on: ubuntu-latest 30 | defaults: 31 | run: 32 | working-directory: ./manager 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v3 36 | with: 37 | fetch-depth: 0 38 | 39 | - name: Setup need_upload 40 | id: need_upload 41 | run: | 42 | if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then 43 | echo "UPLOAD=true" >> $GITHUB_OUTPUT 44 | else 45 | echo "UPLOAD=false" >> $GITHUB_OUTPUT 46 | fi 47 | 48 | - name: Write key 49 | if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/master' ) || github.ref_type == 'tag' }} 50 | run: | 51 | if [ ! -z "${{ secrets.KEYSTORE }}" ]; then 52 | echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' >> gradle.properties 53 | echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' >> gradle.properties 54 | echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> gradle.properties 55 | echo KEYSTORE_FILE='../key.jks' >> gradle.properties 56 | echo ${{ secrets.KEYSTORE }} | base64 --decode > key.jks 57 | fi 58 | 59 | - name: Setup Java 60 | uses: actions/setup-java@v3 61 | with: 62 | distribution: "temurin" 63 | java-version: "17" 64 | 65 | - name: Setup Gradle 66 | uses: gradle/gradle-build-action@v2 67 | with: 68 | gradle-home-cache-cleanup: true 69 | 70 | - name: Download arm64 ksud 71 | uses: actions/download-artifact@v3 72 | with: 73 | name: ksud-aarch64-linux-android 74 | path: . 75 | 76 | - name: Download x86_64 ksud 77 | uses: actions/download-artifact@v3 78 | with: 79 | name: ksud-x86_64-linux-android 80 | path: . 81 | 82 | - name: Copy ksud to app jniLibs 83 | run: | 84 | mkdir -p app/src/main/jniLibs/arm64-v8a 85 | mkdir -p app/src/main/jniLibs/x86_64 86 | cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so 87 | cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so 88 | 89 | - name: Build with Gradle 90 | run: | 91 | echo 'org.gradle.parallel=true' >> gradle.properties 92 | echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties 93 | echo 'android.native.buildOutput=verbose' >> gradle.properties 94 | sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties 95 | ./gradlew clean assembleRelease assembleDebug 96 | 97 | - name: Upload build artifact - Release 98 | uses: actions/upload-artifact@v3 99 | with: 100 | name: manager-release 101 | path: manager/app/build/outputs/apk/release/*.apk 102 | 103 | - name: Upload build artifact - Debug 104 | uses: actions/upload-artifact@v3 105 | with: 106 | name: manager-debug 107 | path: manager/app/build/outputs/apk/debug/*.apk 108 | -------------------------------------------------------------------------------- /.github/workflows/build-su.yml: -------------------------------------------------------------------------------- 1 | name: Build SU 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "master" ] 7 | paths: 8 | - '.github/workflows/build-su.yml' 9 | - 'userspace/su/**' 10 | pull_request: 11 | branches: [ "master" ] 12 | paths: 13 | - 'userspace/su/**' 14 | 15 | jobs: 16 | build-su: 17 | name: Build userspace su 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | 24 | - name: Setup need_upload 25 | id: need_upload 26 | run: | 27 | if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then 28 | echo "UPLOAD=true" >> $GITHUB_OUTPUT 29 | else 30 | echo "UPLOAD=false" >> $GITHUB_OUTPUT 31 | fi 32 | 33 | - uses: nttld/setup-ndk@v1 34 | with: 35 | ndk-version: r25b 36 | local-cache: true 37 | 38 | - name: Build su 39 | working-directory: ./userspace/su 40 | run: ndk-build 41 | 42 | - name: Upload a Build Artifact 43 | uses: actions/upload-artifact@v3 44 | with: 45 | name: su 46 | path: ./userspace/su/libs 47 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | name: Clippy check 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | paths: 9 | - '.github/workflows/clippy.yml' 10 | - 'userspace/ksud/**' 11 | pull_request: 12 | branches: 13 | - master 14 | paths: 15 | - '.github/workflows/clippy.yml' 16 | - 'userspace/ksud/**' 17 | 18 | env: 19 | RUSTFLAGS: '-Dwarnings' 20 | 21 | jobs: 22 | clippy: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | # cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222 27 | - run: rustup default 1.67.0 28 | - uses: Swatinem/rust-cache@v2 29 | with: 30 | workspaces: userspace/ksud 31 | 32 | - name: Install cross 33 | run: cargo install cross 34 | 35 | - name: Run clippy 36 | run: | 37 | cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release 38 | cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release 39 | -------------------------------------------------------------------------------- /.github/workflows/ksud.yml: -------------------------------------------------------------------------------- 1 | name: ksud 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | target: 7 | required: true 8 | type: string 9 | use_cache: 10 | required: false 11 | type: boolean 12 | default: true 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | fetch-depth: 0 21 | # cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222 22 | - run: rustup default 1.67.0 23 | - uses: Swatinem/rust-cache@v2 24 | with: 25 | workspaces: userspace/ksud 26 | cache-targets: false 27 | 28 | - name: Install cross 29 | run: cargo install cross 30 | 31 | - name: Build ksud 32 | run: cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml 33 | 34 | - name: Upload ksud artifact 35 | uses: actions/upload-artifact@v3 36 | with: 37 | name: ksud-${{ inputs.target }} 38 | path: userspace/ksud/target/**/release/ksud 39 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build-manager: 11 | uses: ./.github/workflows/build-manager.yml 12 | secrets: inherit 13 | build-a12-kernel: 14 | uses: ./.github/workflows/build-kernel-a12.yml 15 | build-a13-kernel: 16 | uses: ./.github/workflows/build-kernel-a13.yml 17 | build-wsa-kernel: 18 | uses: ./.github/workflows/build-kernel-wsa.yml 19 | build-arcvm-kernel: 20 | uses: ./.github/workflows/build-kernel-arcvm.yml 21 | build-debug-kernel: 22 | uses: ./.github/workflows/build-debug-kernel.yml 23 | release: 24 | needs: 25 | - build-manager 26 | - build-a12-kernel 27 | - build-a13-kernel 28 | - build-wsa-kernel 29 | - build-arcvm-kernel 30 | - build-debug-kernel 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Download artifacts 34 | uses: actions/download-artifact@v3 35 | 36 | - name: Zip AnyKernel3 37 | run: | 38 | for dir in AnyKernel3-*; do 39 | if [ -d "$dir" ]; then 40 | echo "----- Zip $dir -----" 41 | (cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..) 42 | fi 43 | done 44 | 45 | - name: Zip WSA kernel 46 | run: | 47 | for dir in kernel-WSA-*; do 48 | if [ -d "$dir" ]; then 49 | echo "------ Zip $dir ----------" 50 | (cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..) 51 | fi 52 | done 53 | 54 | - name: Zip ChromeOS ARCVM kernel 55 | run: | 56 | for dir in kernel-ARCVM-*; do 57 | if [ -d "$dir" ]; then 58 | echo "------ Zip $dir ----------" 59 | (cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..) 60 | fi 61 | done 62 | 63 | - name: Display structure of downloaded files 64 | run: ls -R 65 | 66 | - name: release 67 | uses: softprops/action-gh-release@v1 68 | with: 69 | files: | 70 | manager/*.apk 71 | AnyKernel3-*.zip 72 | boot-images-*/Image-*/*.img.gz 73 | kernel-WSA*.zip 74 | kernel-ARCVM*.zip 75 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yml: -------------------------------------------------------------------------------- 1 | name: Rustfmt Check 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - 'master' 8 | paths: 9 | - '.github/workflows/rustfmt.yml' 10 | - 'userspace/ksud/**' 11 | pull_request: 12 | branches: 13 | - 'master' 14 | paths: 15 | - '.github/workflows/rustfmt.yml' 16 | - 'userspace/ksud/**' 17 | 18 | permissions: 19 | checks: write 20 | 21 | jobs: 22 | format: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | 27 | - uses: dtolnay/rust-toolchain@nightly 28 | with: 29 | components: rustfmt 30 | 31 | - uses: LoliGothick/rustfmt-check@master 32 | with: 33 | token: ${{ github.token }} 34 | working-directory: userspace/ksud 35 | -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | name: Shell Check 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - 'master' 8 | paths: 9 | - '.github/workflows/shellcheck.yml' 10 | - '**/*.sh' 11 | pull_request: 12 | branches: 13 | - 'master' 14 | paths: 15 | - '.github/workflows/shellcheck.yml' 16 | - '**/*.sh' 17 | 18 | jobs: 19 | shellcheck: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: Run ShellCheck 25 | uses: ludeeus/action-shellcheck@2.0.0 26 | with: 27 | ignore_names: gradlew 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | [English](README.md) | **简体中文** 2 | 3 | # KernelSU Debug 4 | 5 | 一个 Android 上基于内核的 root 方案。为调试用途做了一些修改。 6 | 7 | ## 特性 8 | 9 | 1. 基于内核的 su 和权限管理。 10 | 2. [App Profile](https://kernelsu.org/guide/app-profile.html): 把 Root 权限关进笼子里。 11 | 12 | 3. 去除无用的模块功能,让授权管理更干净。[1] 13 | 4. 去除无用的管理器签名验证,仅保留包名验证作为基础的安全校验,允许用户自行修改管理器 app。[2] 14 | 5. 默认设置 SELinux 为宽容模式,为使用 bootconfig 作为启动参数的设备提供一种永久获取**最高权限**的方式。[3] 15 | 6. 禁用了 seccomp **非安全性**权限提升,即使 SELinux 在开机时已设置为 Permissive。[3] 16 | 7. 强制伪装 SELinux 状态为 Enforcing,即使实际为 Permissive,用于欺骗通过 Play Integrity 认证。[3] 17 | 8. 永远信任 adb shell,为其提供 su 授权。[4] 18 | 9. 允许用户设置默认授予全部应用 su 授权功能,与 Magisk 自动响应功能一致。 19 | 10. init.d 支持,ksud 会在开机时各阶段解析或执行 `/data/ksu/common` 下的 [stage].sh/system.prop/sepolicy.rule 文件。 20 | 11. 允许用户通过 cli 指令修改管理器目标包名。 21 | 22 | ## 兼容状态 23 | 24 | KernelSU 官方支持 GKI 2.0 的设备(内核版本5.10以上);旧内核也是兼容的(最低4.14+),不过需要自己编译内核。 25 | 26 | WSA, ChromeOS 和运行在容器上的 Android 也可以与 KernelSU 一起工作。 27 | 28 | 目前支持架构 : `arm64-v8a` 和 `x86_64` 29 | 30 | ## 讨论 31 | 32 | - Telegram: [@MlgmXyysd_bibilailai](https://t.me/MlgmXyysd_bibilailai) (频道评论群) 33 | 34 | ## To-Dos 35 | 36 | - Root 授权对话框 37 | - 允许用户在管理器中修改管理器目标包名(转移权限) 38 | - 允许用户在管理器中开启严格执行的 SELinux 39 | - 允许用户在管理器中开关 seccomp 功能 40 | - 允许用户在管理器中开关 SELinux Enforcing 伪装功能 41 | - 使用内核模块方式安装 KernelSU 42 | 43 | ## 修改解释 44 | 45 | 1. KernelSU **只应该**提供 su 功能。模块功能是多余的,并且兼容性不佳。所以去除模块功能(保留启动脚本),让其只做应该做的事,还可以提升运行性能,没什么好解释的。 46 | 47 | 2. 在获取 root 权限的应用中限制用户自行修改分发版管理器是没有意义的,限制用户也违背了解锁刷机的初衷。用户应该有能力确认自己所安装的软件的自己可信的,否则任何的安全措施都没有意义。内核中仅保留包名验证做为基础安全校验,将签名验证交给 Android 系统的 PackageManager,使管理器授权处于一种先装先得的状态已足够。什么?核心破解?自己瞎装东西关我毛事? 48 | 49 | 3. 仅获取到 root 权限,在一些情况下执行操作仍然会被严格执行的 SELinux 策略挡掉,只有关闭 SELinux 或设置为宽容模式才允许执行,而 Android 无法直接关闭 SELinux。所以我们认为,在取得 root 的同时将 SELinux 设置为宽容模式(ROOT + Permissive),才是真正获取到了设备的最高权限。谷歌并不认为开启 SELinux Permissive 后关闭 seccomp 是安全问题,因此我们也不关心。 50 | 51 | 4. KernelSU 不提供询问授权弹窗,在授权时会很麻烦。adb shell 是系统核心组件之一,无需确认其安全性,信任并授权既保留了相对的应用安全,也方便用户操作终端调试。 52 | 53 | 54 | 如果您认为上述功能破坏了已刷入自定义固件设备的安全性,请先回顾一下 BootLoader 的解锁警告: 55 | 56 | > 57 | > 58 | > By unlocking the bootloader, you will be able to install custom operating system on this phone. A custom OS is not subject to the same level of testing as the original OS, and can cause your phone and installed applications to stop working properly. 59 | > 60 | > **Software integrity cannot be guaranteed with a custom OS, so any data stored on the phone while the bootloader is unlocked may be at risk.** 61 | > 62 | > To prevent unauthorized access to your personal data, unlocking the bootloader will also delete all personal data on your phone. 63 | > 64 | > Press the Volume keys to select whether to unlock the bootloader, then the Power Button to continue. 65 | > 66 | > __________ 67 | > DO NOT UNLOCK THE BOOTLOADER 68 | > __________ 69 | > UNLOCK THE BOOTLOADER 70 | > __________ 71 | 72 | 以及 73 | 74 | > 75 | > 76 | > The boot loader is unlocked and software integrity cannot be guaranteed. **Any data stored on the device may be available to attackers. Do not store any sensitive data on the device.** 77 | > 78 | > Visit this link on another device: 79 | > 80 | > g.co/ABH 81 | 82 | 如果这仍然不能解决您的疑惑,请您点击窗口右上角的 X 按钮 83 | 84 | ## 使用方法 85 | 86 | - [安装教程](https://kernelsu.org/zh_CN/guide/installation.html) 87 | - [如何构建?](https://kernelsu.org/zh_CN/guide/how-to-build.html) 88 | 89 | ## 许可证 90 | 91 | - 目录 `kernel` 下所有文件为 [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) 92 | - 除 `kernel` 目录的其他部分均为 [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) 93 | 94 | ## 鸣谢 95 | 96 | - [KernelSU](https://github.com/tiann/KernelSU): fork 源 97 | - [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU 的灵感 98 | - [Magisk](https://github.com/topjohnwu/Magisk): 强大的 root 工具箱 99 | - [Diamorphine](https://github.com/m0nad/Diamorphine): 一些 rootkit 技巧 100 | -------------------------------------------------------------------------------- /kernel/.clangd: -------------------------------------------------------------------------------- 1 | Diagnostics: 2 | UnusedIncludes: Strict 3 | ClangTidy: 4 | Remove: bugprone-sizeof-expression 5 | -------------------------------------------------------------------------------- /kernel/Kconfig: -------------------------------------------------------------------------------- 1 | menu "KernelSU" 2 | 3 | config KSU 4 | tristate "KernelSU function support" 5 | select OVERLAY_FS 6 | default y 7 | help 8 | Enable kernel-level root privileges on Android System. 9 | 10 | config KSU_DEBUG 11 | bool "KernelSU debug mode" 12 | depends on KSU 13 | default n 14 | help 15 | Enable KernelSU debug mode 16 | 17 | endmenu 18 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | obj-y += ksu.o 2 | obj-y += allowlist.o 3 | kernelsu-objs := apk_sign.o 4 | obj-y += kernelsu.o 5 | obj-y += module_api.o 6 | obj-y += sucompat.o 7 | obj-y += uid_observer.o 8 | obj-y += manager.o 9 | obj-y += core_hook.o 10 | obj-y += ksud.o 11 | obj-y += embed_ksud.o 12 | obj-y += kernel_compat.o 13 | 14 | obj-y += selinux/ 15 | # .git is a text file while the module is imported by 'git submodule add'. 16 | ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0) 17 | KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD) 18 | # ksu_version: major * 10000 + git version + 200 for historical reasons 19 | $(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 200)) 20 | $(info -- KernelSU version: $(KSU_VERSION)) 21 | ccflags-y += -DKSU_VERSION=$(KSU_VERSION) 22 | else # If there is no .git file, the default version will be passed. 23 | $(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!") 24 | ccflags-y += -DKSU_VERSION=16 25 | endif 26 | 27 | ifndef KSU_EXPECTED_SIZE 28 | KSU_EXPECTED_SIZE := 0x033b 29 | endif 30 | 31 | ifndef KSU_EXPECTED_HASH 32 | KSU_EXPECTED_HASH := 0xb0b91415 33 | endif 34 | 35 | $(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE)) 36 | $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH)) 37 | 38 | ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE) 39 | ccflags-y += -DEXPECTED_HASH=$(KSU_EXPECTED_HASH) 40 | ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat 41 | ccflags-y += -Wno-declaration-after-statement 42 | -------------------------------------------------------------------------------- /kernel/allowlist.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_ALLOWLIST 2 | #define __KSU_H_ALLOWLIST 3 | 4 | #include "linux/types.h" 5 | #include "ksu.h" 6 | 7 | void ksu_allowlist_init(void); 8 | 9 | void ksu_allowlist_exit(void); 10 | 11 | bool ksu_load_allow_list(void); 12 | 13 | void ksu_show_allow_list(void); 14 | 15 | bool __ksu_is_allow_uid(uid_t uid); 16 | #define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid)) 17 | 18 | bool ksu_get_allow_list(int *array, int *length, bool allow); 19 | 20 | void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data); 21 | 22 | bool ksu_get_app_profile(struct app_profile *); 23 | bool ksu_set_app_profile(struct app_profile *, bool persist); 24 | 25 | bool ksu_uid_should_umount(uid_t uid); 26 | struct root_profile *ksu_get_root_profile(uid_t uid); 27 | #endif 28 | -------------------------------------------------------------------------------- /kernel/apk_sign.c: -------------------------------------------------------------------------------- 1 | #include "linux/fs.h" 2 | #include "linux/moduleparam.h" 3 | 4 | #include "apk_sign.h" 5 | #include "klog.h" // IWYU pragma: keep 6 | #include "kernel_compat.h" 7 | 8 | static __always_inline int 9 | check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash) 10 | { 11 | return 0; 12 | } 13 | 14 | #ifdef CONFIG_KSU_DEBUG 15 | 16 | unsigned ksu_expected_size = EXPECTED_SIZE; 17 | unsigned ksu_expected_hash = EXPECTED_HASH; 18 | 19 | #include "manager.h" 20 | 21 | static int set_expected_size(const char *val, const struct kernel_param *kp) 22 | { 23 | int rv = param_set_uint(val, kp); 24 | ksu_invalidate_manager_uid(); 25 | pr_info("ksu_expected_size set to %x", ksu_expected_size); 26 | return rv; 27 | } 28 | 29 | static int set_expected_hash(const char *val, const struct kernel_param *kp) 30 | { 31 | int rv = param_set_uint(val, kp); 32 | ksu_invalidate_manager_uid(); 33 | pr_info("ksu_expected_hash set to %x", ksu_expected_hash); 34 | return rv; 35 | } 36 | 37 | static struct kernel_param_ops expected_size_ops = { 38 | .set = set_expected_size, 39 | .get = param_get_uint, 40 | }; 41 | 42 | static struct kernel_param_ops expected_hash_ops = { 43 | .set = set_expected_hash, 44 | .get = param_get_uint, 45 | }; 46 | 47 | module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size, 48 | S_IRUSR | S_IWUSR); 49 | module_param_cb(ksu_expected_hash, &expected_hash_ops, &ksu_expected_hash, 50 | S_IRUSR | S_IWUSR); 51 | 52 | int is_manager_apk(char *path) 53 | { 54 | return check_v2_signature(path, ksu_expected_size, ksu_expected_hash); 55 | } 56 | 57 | #else 58 | 59 | int is_manager_apk(char *path) 60 | { 61 | return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH); 62 | } 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /kernel/apk_sign.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_APK_V2_SIGN 2 | #define __KSU_H_APK_V2_SIGN 3 | 4 | // return 0 if signature match 5 | int is_manager_apk(char *path); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /kernel/arch.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_ARCH 2 | #define __KSU_H_ARCH 3 | 4 | #include "linux/version.h" 5 | 6 | #if defined(__aarch64__) 7 | 8 | #define __PT_PARM1_REG regs[0] 9 | #define __PT_PARM2_REG regs[1] 10 | #define __PT_PARM3_REG regs[2] 11 | #define __PT_SYSCALL_PARM4_REG regs[3] 12 | #define __PT_CCALL_PARM4_REG regs[3] 13 | #define __PT_PARM5_REG regs[4] 14 | #define __PT_PARM6_REG regs[5] 15 | #define __PT_RET_REG regs[30] 16 | #define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */ 17 | #define __PT_RC_REG regs[0] 18 | #define __PT_SP_REG sp 19 | #define __PT_IP_REG pc 20 | 21 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) 22 | #define PRCTL_SYMBOL "__arm64_sys_prctl" 23 | #else 24 | #define PRCTL_SYMBOL "sys_prctl" 25 | #endif 26 | 27 | #elif defined(__x86_64__) 28 | 29 | #define __PT_PARM1_REG di 30 | #define __PT_PARM2_REG si 31 | #define __PT_PARM3_REG dx 32 | /* syscall uses r10 for PARM4 */ 33 | #define __PT_SYSCALL_PARM4_REG r10 34 | #define __PT_CCALL_PARM4_REG cx 35 | #define __PT_PARM5_REG r8 36 | #define __PT_PARM6_REG r9 37 | #define __PT_RET_REG sp 38 | #define __PT_FP_REG bp 39 | #define __PT_RC_REG ax 40 | #define __PT_SP_REG sp 41 | #define __PT_IP_REG ip 42 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) 43 | #define PRCTL_SYMBOL "__x64_sys_prctl" 44 | #else 45 | #define PRCTL_SYMBOL "sys_prctl" 46 | #endif 47 | 48 | #else 49 | #error "Unsupported arch" 50 | #endif 51 | 52 | /* allow some architecutres to override `struct pt_regs` */ 53 | #ifndef __PT_REGS_CAST 54 | #define __PT_REGS_CAST(x) (x) 55 | #endif 56 | 57 | #define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG) 58 | #define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG) 59 | #define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG) 60 | #define PT_REGS_SYSCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_SYSCALL_PARM4_REG) 61 | #define PT_REGS_CCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_CCALL_PARM4_REG) 62 | #define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG) 63 | #define PT_REGS_PARM6(x) (__PT_REGS_CAST(x)->__PT_PARM6_REG) 64 | #define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG) 65 | #define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG) 66 | #define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG) 67 | #define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG) 68 | #define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG) 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /kernel/core_hook.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_KSU_CORE 2 | #define __KSU_H_KSU_CORE 3 | 4 | #include "linux/init.h" 5 | 6 | void __init ksu_core_init(void); 7 | void ksu_core_exit(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /kernel/embed_ksud.c: -------------------------------------------------------------------------------- 1 | // WARNING: THIS IS A STUB FILE 2 | // This file will be regenerated by CI 3 | 4 | unsigned int ksud_size = 0; 5 | const char ksud[0] = {}; 6 | -------------------------------------------------------------------------------- /kernel/export_symbol.txt: -------------------------------------------------------------------------------- 1 | register_kprobe 2 | unregister_kprobe 3 | -------------------------------------------------------------------------------- /kernel/include/ksu_hook.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_KSHOOK 2 | #define __KSU_H_KSHOOK 3 | 4 | #include "linux/fs.h" 5 | #include "linux/types.h" 6 | 7 | // For sucompat 8 | 9 | int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, 10 | int *flags); 11 | 12 | int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); 13 | 14 | // For ksud 15 | 16 | int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, 17 | size_t *count_ptr, loff_t **pos); 18 | 19 | // For ksud and sucompat 20 | 21 | int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, 22 | void *envp, int *flags); 23 | 24 | // For volume button 25 | int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, 26 | int *value); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /kernel/kernel_compat.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_KERNEL_COMPAT 2 | #define __KSU_H_KERNEL_COMPAT 3 | 4 | #include "linux/fs.h" 5 | #include "linux/key.h" 6 | #include "linux/version.h" 7 | 8 | extern long ksu_strncpy_from_user_nofault(char *dst, 9 | const void __user *unsafe_addr, 10 | long count); 11 | 12 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) 13 | extern struct key *init_session_keyring; 14 | #endif 15 | 16 | extern void ksu_android_ns_fs_check(); 17 | extern struct file *ksu_filp_open_compat(const char *filename, int flags, 18 | umode_t mode); 19 | extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, 20 | loff_t *pos); 21 | extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, 22 | size_t count, loff_t *pos); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /kernel/klog.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_KLOG 2 | #define __KSU_H_KLOG 3 | 4 | #include 5 | 6 | #ifdef pr_fmt 7 | #undef pr_fmt 8 | #define pr_fmt(fmt) "KernelSU: " fmt 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /kernel/ksu.c: -------------------------------------------------------------------------------- 1 | #include "linux/fs.h" 2 | #include "linux/module.h" 3 | #include "linux/workqueue.h" 4 | 5 | #include "allowlist.h" 6 | #include "arch.h" 7 | #include "core_hook.h" 8 | #include "klog.h" // IWYU pragma: keep 9 | #include "ksu.h" 10 | #include "uid_observer.h" 11 | 12 | static struct workqueue_struct *ksu_workqueue; 13 | 14 | bool ksu_queue_work(struct work_struct *work) 15 | { 16 | return queue_work(ksu_workqueue, work); 17 | } 18 | 19 | extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, 20 | void *argv, void *envp, int *flags); 21 | 22 | extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, 23 | void *argv, void *envp, int *flags); 24 | 25 | int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, 26 | void *envp, int *flags) 27 | { 28 | ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags); 29 | return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, 30 | flags); 31 | } 32 | 33 | extern void ksu_enable_sucompat(); 34 | extern void ksu_enable_ksud(); 35 | 36 | int __init kernelsu_init(void) 37 | { 38 | #ifdef CONFIG_KSU_DEBUG 39 | pr_alert("*************************************************************"); 40 | pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); 41 | pr_alert("** **"); 42 | pr_alert("** You are running KernelSU in DEBUG mode **"); 43 | pr_alert("** **"); 44 | pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); 45 | pr_alert("*************************************************************"); 46 | #endif 47 | 48 | ksu_core_init(); 49 | 50 | ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0); 51 | 52 | ksu_allowlist_init(); 53 | 54 | ksu_uid_observer_init(); 55 | 56 | #ifdef CONFIG_KPROBES 57 | ksu_enable_sucompat(); 58 | ksu_enable_ksud(); 59 | #else 60 | pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html"); 61 | #endif 62 | 63 | return 0; 64 | } 65 | 66 | void kernelsu_exit(void) 67 | { 68 | ksu_allowlist_exit(); 69 | 70 | ksu_uid_observer_exit(); 71 | 72 | destroy_workqueue(ksu_workqueue); 73 | 74 | ksu_core_exit(); 75 | } 76 | 77 | module_init(kernelsu_init); 78 | module_exit(kernelsu_exit); 79 | 80 | MODULE_LICENSE("GPL"); 81 | MODULE_AUTHOR("NekoYuzu"); 82 | MODULE_DESCRIPTION("KernelSU Debug"); 83 | 84 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) 85 | MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); 86 | #endif 87 | -------------------------------------------------------------------------------- /kernel/ksu.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_KSU 2 | #define __KSU_H_KSU 3 | 4 | #include "linux/types.h" 5 | #include "linux/workqueue.h" 6 | 7 | #define KERNEL_SU_VERSION KSU_VERSION 8 | #define KERNEL_SU_OPTION 0xDEADBEEF 9 | 10 | #define CMD_GRANT_ROOT 0 11 | #define CMD_BECOME_MANAGER 1 12 | #define CMD_GET_VERSION 2 13 | #define CMD_ALLOW_SU 3 14 | #define CMD_DENY_SU 4 15 | #define CMD_GET_ALLOW_LIST 5 16 | #define CMD_GET_DENY_LIST 6 17 | #define CMD_REPORT_EVENT 7 18 | #define CMD_SET_SEPOLICY 8 19 | #define CMD_CHECK_SAFEMODE 9 20 | #define CMD_GET_APP_PROFILE 10 21 | #define CMD_SET_APP_PROFILE 11 22 | #define CMD_UID_GRANTED_ROOT 12 23 | #define CMD_UID_SHOULD_UMOUNT 13 24 | 25 | #define EVENT_POST_FS_DATA 1 26 | #define EVENT_BOOT_COMPLETED 2 27 | 28 | #define KSU_APP_PROFILE_VER 2 29 | #define KSU_MAX_PACKAGE_NAME 256 30 | // NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups. 31 | #define KSU_MAX_GROUPS 32 32 | #define KSU_SELINUX_DOMAIN 64 33 | 34 | struct root_profile { 35 | int32_t uid; 36 | int32_t gid; 37 | 38 | int32_t groups_count; 39 | int32_t groups[KSU_MAX_GROUPS]; 40 | 41 | // kernel_cap_t is u32[2] for capabilities v3 42 | struct { 43 | u64 effective; 44 | u64 permitted; 45 | u64 inheritable; 46 | } capabilities; 47 | 48 | char selinux_domain[KSU_SELINUX_DOMAIN]; 49 | 50 | int32_t namespaces; 51 | }; 52 | 53 | struct non_root_profile { 54 | bool umount_modules; 55 | }; 56 | 57 | struct app_profile { 58 | // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. 59 | u32 version; 60 | 61 | // this is usually the package of the app, but can be other value for special apps 62 | char key[KSU_MAX_PACKAGE_NAME]; 63 | int32_t current_uid; 64 | bool allow_su; 65 | 66 | union { 67 | struct { 68 | bool use_default; 69 | char template_name[KSU_MAX_PACKAGE_NAME]; 70 | 71 | struct root_profile profile; 72 | } rp_config; 73 | 74 | struct { 75 | bool use_default; 76 | 77 | struct non_root_profile profile; 78 | } nrp_config; 79 | }; 80 | }; 81 | 82 | bool ksu_queue_work(struct work_struct *work); 83 | 84 | static inline int startswith(char *s, char *prefix) 85 | { 86 | return strncmp(s, prefix, strlen(prefix)); 87 | } 88 | 89 | static inline int endswith(const char *s, const char *t) 90 | { 91 | size_t slen = strlen(s); 92 | size_t tlen = strlen(t); 93 | if (tlen > slen) 94 | return 1; 95 | return strcmp(s + slen - tlen, t); 96 | } 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /kernel/ksud.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_KSUD 2 | #define __KSU_H_KSUD 3 | 4 | #define KSUD_PATH "/data/adb/ksud" 5 | 6 | void on_post_fs_data(void); 7 | 8 | bool ksu_is_safe_mode(void); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /kernel/manager.c: -------------------------------------------------------------------------------- 1 | #include "linux/cred.h" 2 | #include "linux/gfp.h" 3 | #include "linux/slab.h" 4 | #include "linux/uidgid.h" 5 | #include "linux/version.h" 6 | 7 | #include "linux/fdtable.h" 8 | #include "linux/fs.h" 9 | #include "linux/rcupdate.h" 10 | 11 | #include "apk_sign.h" 12 | #include "klog.h" // IWYU pragma: keep 13 | #include "ksu.h" 14 | #include "manager.h" 15 | 16 | uid_t ksu_manager_uid = KSU_INVALID_UID; 17 | 18 | bool become_manager(char *pkg) 19 | { 20 | struct fdtable *files_table; 21 | int i = 0; 22 | struct path files_path; 23 | char *cwd; 24 | char *buf; 25 | bool result = false; 26 | 27 | // must be zygote's direct child, otherwise any app can fork a new process and 28 | // open manager's apk 29 | if (task_uid(current->real_parent).val != 0) { 30 | pr_info("parent is not zygote!\n"); 31 | return false; 32 | } 33 | 34 | buf = (char *)kmalloc(PATH_MAX, GFP_ATOMIC); 35 | if (!buf) { 36 | pr_err("kalloc path failed.\n"); 37 | return false; 38 | } 39 | 40 | files_table = files_fdtable(current->files); 41 | 42 | int pkg_len = strlen(pkg); 43 | // todo: use iterate_fd 44 | for (i = 0; files_table->fd[i] != NULL; i++) { 45 | files_path = files_table->fd[i]->f_path; 46 | if (!d_is_reg(files_path.dentry)) { 47 | continue; 48 | } 49 | cwd = d_path(&files_path, buf, PATH_MAX); 50 | if (startswith(cwd, "/data/app/") != 0 || 51 | endswith(cwd, "/base.apk") != 0) { 52 | continue; 53 | } 54 | // we have found the apk! 55 | pr_info("found apk: %s", cwd); 56 | char *pkg_index = strstr(cwd, pkg); 57 | if (!pkg_index) { 58 | pr_info("apk path not match package name!\n"); 59 | continue; 60 | } 61 | char *next_char = pkg_index + pkg_len; 62 | // because we ensure the cwd must startswith `/data/app` and endswith `base.apk` 63 | // we don't need to check if the pointer is out of bounds 64 | if (*next_char != '-') { 65 | // from android 8.1: http://aospxref.com/android-8.1.0_r81/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#17612 66 | // to android 13: http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java#1208 67 | // /data/app/~~[randomStringA]/[packageName]-[randomStringB] 68 | // the previous char must be `/` and the next char must be `-` 69 | // because we use strstr instead of equals, this is a strong verfication. 70 | pr_info("invalid pkg: %s\n", pkg); 71 | continue; 72 | } 73 | if (is_manager_apk(cwd) == 0) { 74 | // check passed 75 | uid_t uid = current_uid().val; 76 | pr_info("manager uid: %d\n", uid); 77 | 78 | ksu_set_manager_uid(uid); 79 | 80 | result = true; 81 | goto clean; 82 | } else { 83 | pr_info("manager signature invalid!"); 84 | } 85 | 86 | break; 87 | } 88 | 89 | clean: 90 | kfree(buf); 91 | return result; 92 | } 93 | -------------------------------------------------------------------------------- /kernel/manager.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_KSU_MANAGER 2 | #define __KSU_H_KSU_MANAGER 3 | 4 | #include "linux/cred.h" 5 | #include "linux/types.h" 6 | 7 | #define KSU_INVALID_UID -1 8 | 9 | extern uid_t ksu_manager_uid; // DO NOT DIRECT USE 10 | 11 | static inline bool ksu_is_manager_uid_valid() 12 | { 13 | return ksu_manager_uid != KSU_INVALID_UID; 14 | } 15 | 16 | static inline bool is_manager() 17 | { 18 | return unlikely(ksu_manager_uid == current_uid().val); 19 | } 20 | 21 | static inline uid_t ksu_get_manager_uid() 22 | { 23 | return ksu_manager_uid; 24 | } 25 | 26 | static inline void ksu_set_manager_uid(uid_t uid) 27 | { 28 | ksu_manager_uid = uid; 29 | } 30 | 31 | static inline void ksu_invalidate_manager_uid() 32 | { 33 | ksu_manager_uid = KSU_INVALID_UID; 34 | } 35 | 36 | bool become_manager(char *pkg); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /kernel/module_api.c: -------------------------------------------------------------------------------- 1 | #include "linux/kallsyms.h" 2 | 3 | #define RE_EXPORT_SYMBOL1(ret, func, t1, v1) \ 4 | ret ksu_##func(t1 v1) \ 5 | { \ 6 | return func(v1); \ 7 | } \ 8 | EXPORT_SYMBOL(ksu_##func); 9 | 10 | #define RE_EXPORT_SYMBOL2(ret, func, t1, v1, t2, v2) \ 11 | ret ksu_##func(t1 v1, t2 v2) \ 12 | { \ 13 | return func(v1, v2); \ 14 | } \ 15 | EXPORT_SYMBOL(ksu_##func); 16 | 17 | RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char *, name) 18 | 19 | // RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p) 20 | // RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p) 21 | 22 | // RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p) 23 | // RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p) 24 | 25 | // int ksu_register_kprobe(struct kprobe *p); 26 | // void ksu_unregister_kprobe(struct kprobe *p); 27 | // int ksu_register_kprobes(struct kprobe **kps, int num); 28 | // void ksu_unregister_kprobes(struct kprobe **kps, int num); 29 | 30 | // int ksu_register_kretprobe(struct kretprobe *rp); 31 | // void unregister_kretprobe(struct kretprobe *rp); 32 | // int register_kretprobes(struct kretprobe **rps, int num); 33 | // void unregister_kretprobes(struct kretprobe **rps, int num); 34 | -------------------------------------------------------------------------------- /kernel/selinux/Makefile: -------------------------------------------------------------------------------- 1 | obj-y += selinux.o 2 | obj-y += sepolicy.o 3 | obj-y += rules.o 4 | 5 | ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) 6 | ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID 7 | endif 8 | 9 | ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0) 10 | ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE 11 | endif 12 | 13 | ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion 14 | ccflags-y += -Wno-declaration-after-statement -Wno-unused-function 15 | ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include 16 | ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h 17 | -------------------------------------------------------------------------------- /kernel/selinux/selinux.c: -------------------------------------------------------------------------------- 1 | #include "selinux.h" 2 | #include "objsec.h" 3 | #include "linux/version.h" 4 | #include "../klog.h" // IWYU pragma: keep 5 | #ifndef KSU_COMPAT_USE_SELINUX_STATE 6 | #include "avc.h" 7 | #endif 8 | 9 | #define KERNEL_SU_DOMAIN "u:r:su:s0" 10 | 11 | static u32 ksu_sid; 12 | 13 | static int transive_to_domain(const char *domain) 14 | { 15 | struct cred *cred; 16 | struct task_security_struct *tsec; 17 | u32 sid; 18 | int error; 19 | 20 | cred = (struct cred *)__task_cred(current); 21 | 22 | tsec = cred->security; 23 | if (!tsec) { 24 | pr_err("tsec == NULL!\n"); 25 | return -1; 26 | } 27 | 28 | error = security_secctx_to_secid(domain, strlen(domain), &sid); 29 | if (error) { 30 | pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n", domain, sid, error); 31 | } 32 | if (!error) { 33 | if (!ksu_sid) 34 | ksu_sid = sid; 35 | 36 | tsec->sid = sid; 37 | tsec->create_sid = 0; 38 | tsec->keycreate_sid = 0; 39 | tsec->sockcreate_sid = 0; 40 | } 41 | return error; 42 | } 43 | 44 | void setup_selinux(const char *domain) 45 | { 46 | if (transive_to_domain(domain)) { 47 | pr_err("transive domain failed."); 48 | return; 49 | } 50 | 51 | /* we didn't need this now, we have change selinux rules when boot! 52 | if (!is_domain_permissive) { 53 | if (set_domain_permissive() == 0) { 54 | is_domain_permissive = true; 55 | } 56 | }*/ 57 | } 58 | 59 | void setenforce(bool enforce) 60 | { 61 | #ifdef CONFIG_SECURITY_SELINUX_DEVELOP 62 | #ifdef KSU_COMPAT_USE_SELINUX_STATE 63 | selinux_state.enforcing = enforce; 64 | #else 65 | selinux_enforcing = enforce; 66 | #endif 67 | #endif 68 | } 69 | 70 | bool getenforce() 71 | { 72 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE 73 | #ifdef KSU_COMPAT_USE_SELINUX_STATE 74 | if (selinux_state.disabled) { 75 | #else 76 | if (selinux_disabled) { 77 | #endif 78 | return false; 79 | } 80 | #endif 81 | 82 | #ifdef CONFIG_SECURITY_SELINUX_DEVELOP 83 | #ifdef KSU_COMPAT_USE_SELINUX_STATE 84 | return selinux_state.enforcing; 85 | #else 86 | return selinux_enforcing; 87 | #endif 88 | #else 89 | return true; 90 | #endif 91 | } 92 | 93 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \ 94 | !defined(KSU_COMPAT_HAS_CURRENT_SID) 95 | /* 96 | * get the subjective security ID of the current task 97 | */ 98 | static inline u32 current_sid(void) 99 | { 100 | const struct task_security_struct *tsec = current_security(); 101 | 102 | return tsec->sid; 103 | } 104 | #endif 105 | 106 | bool is_ksu_domain() 107 | { 108 | return ksu_sid && current_sid() == ksu_sid; 109 | } 110 | -------------------------------------------------------------------------------- /kernel/selinux/selinux.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_SELINUX 2 | #define __KSU_H_SELINUX 3 | 4 | #include "linux/types.h" 5 | #include "linux/version.h" 6 | 7 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(KSU_COMPAT_HAS_SELINUX_STATE) 8 | #define KSU_COMPAT_USE_SELINUX_STATE 9 | #endif 10 | 11 | void setup_selinux(const char *); 12 | 13 | void setenforce(bool); 14 | 15 | bool getenforce(); 16 | 17 | bool is_ksu_domain(); 18 | 19 | void apply_kernelsu_rules(); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /kernel/selinux/sepolicy.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_SEPOLICY 2 | #define __KSU_H_SEPOLICY 3 | 4 | #include "linux/types.h" 5 | 6 | #include "ss/policydb.h" 7 | 8 | // Operation on types 9 | bool ksu_type(struct policydb *db, const char *name, const char *attr); 10 | bool ksu_attribute(struct policydb *db, const char *name); 11 | bool ksu_permissive(struct policydb *db, const char *type); 12 | bool ksu_enforce(struct policydb *db, const char *type); 13 | bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr); 14 | bool ksu_exists(struct policydb *db, const char *type); 15 | 16 | // Access vector rules 17 | bool ksu_allow(struct policydb *db, const char *src, const char *tgt, 18 | const char *cls, const char *perm); 19 | bool ksu_deny(struct policydb *db, const char *src, const char *tgt, 20 | const char *cls, const char *perm); 21 | bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt, 22 | const char *cls, const char *perm); 23 | bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt, 24 | const char *cls, const char *perm); 25 | 26 | // Extended permissions access vector rules 27 | bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt, 28 | const char *cls, const char *range); 29 | bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt, 30 | const char *cls, const char *range); 31 | bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt, 32 | const char *cls, const char *range); 33 | 34 | // Type rules 35 | bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt, 36 | const char *cls, const char *def, const char *obj); 37 | bool ksu_type_change(struct policydb *db, const char *src, const char *tgt, 38 | const char *cls, const char *def); 39 | bool ksu_type_member(struct policydb *db, const char *src, const char *tgt, 40 | const char *cls, const char *def); 41 | 42 | // File system labeling 43 | bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path, 44 | const char *ctx); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /kernel/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eux 3 | 4 | GKI_ROOT=$(pwd) 5 | 6 | echo "[+] GKI_ROOT: $GKI_ROOT" 7 | 8 | if test -d "$GKI_ROOT/common/drivers"; then 9 | DRIVER_DIR="$GKI_ROOT/common/drivers" 10 | elif test -d "$GKI_ROOT/drivers"; then 11 | DRIVER_DIR="$GKI_ROOT/drivers" 12 | else 13 | echo '[ERROR] "drivers/" directory is not found.' 14 | echo '[+] You should modify this script by yourself.' 15 | exit 127 16 | fi 17 | 18 | test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/MlgmXyysd/KernelSU_Debug KernelSU 19 | cd "$GKI_ROOT/KernelSU" 20 | git stash 21 | if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then 22 | git checkout main 23 | fi 24 | git pull 25 | if [ -z "${1-}" ]; then 26 | git checkout "$(git describe --abbrev=0 --tags)" 27 | else 28 | git checkout "$1" 29 | fi 30 | cd "$GKI_ROOT" 31 | 32 | echo "[+] GKI_ROOT: $GKI_ROOT" 33 | echo "[+] Copy kernel su driver to $DRIVER_DIR" 34 | 35 | cd "$DRIVER_DIR" 36 | if test -d "$GKI_ROOT/common/drivers"; then 37 | ln -sf "../../KernelSU/kernel" "kernelsu" 38 | elif test -d "$GKI_ROOT/drivers"; then 39 | ln -sf "../KernelSU/kernel" "kernelsu" 40 | fi 41 | cd "$GKI_ROOT" 42 | 43 | echo '[+] Add kernel su driver to Makefile' 44 | 45 | DRIVER_MAKEFILE=$DRIVER_DIR/Makefile 46 | DRIVER_KCONFIG=$DRIVER_DIR/Kconfig 47 | grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "obj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" 48 | grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" 49 | 50 | echo '[+] Done.' -------------------------------------------------------------------------------- /kernel/uid_observer.c: -------------------------------------------------------------------------------- 1 | #include "linux/err.h" 2 | #include "linux/fs.h" 3 | #include "linux/list.h" 4 | #include "linux/slab.h" 5 | #include "linux/string.h" 6 | #include "linux/types.h" 7 | #include "linux/version.h" 8 | #include "linux/workqueue.h" 9 | 10 | #include "allowlist.h" 11 | #include "klog.h" // IWYU pragma: keep 12 | #include "ksu.h" 13 | #include "manager.h" 14 | #include "uid_observer.h" 15 | #include "kernel_compat.h" 16 | 17 | #define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list" 18 | static struct work_struct ksu_update_uid_work; 19 | 20 | struct uid_data { 21 | struct list_head list; 22 | u32 uid; 23 | }; 24 | 25 | static bool is_uid_exist(uid_t uid, void *data) 26 | { 27 | struct list_head *list = (struct list_head *)data; 28 | struct uid_data *np; 29 | 30 | bool exist = false; 31 | list_for_each_entry (np, list, list) { 32 | if (np->uid == uid % 100000) { 33 | exist = true; 34 | break; 35 | } 36 | } 37 | return exist; 38 | } 39 | 40 | static void do_update_uid(struct work_struct *work) 41 | { 42 | struct file *fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); 43 | if (IS_ERR(fp)) { 44 | pr_err("do_update_uid, open " SYSTEM_PACKAGES_LIST_PATH 45 | " failed: %d\n", 46 | PTR_ERR(fp)); 47 | return; 48 | } 49 | 50 | struct list_head uid_list; 51 | INIT_LIST_HEAD(&uid_list); 52 | 53 | char chr = 0; 54 | loff_t pos = 0; 55 | loff_t line_start = 0; 56 | char buf[128]; 57 | for (;;) { 58 | ssize_t count = 59 | ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos); 60 | if (count != sizeof(chr)) 61 | break; 62 | if (chr != '\n') 63 | continue; 64 | 65 | count = ksu_kernel_read_compat(fp, buf, sizeof(buf), 66 | &line_start); 67 | 68 | struct uid_data *data = 69 | kmalloc(sizeof(struct uid_data), GFP_ATOMIC); 70 | if (!data) { 71 | goto out; 72 | } 73 | 74 | char *tmp = buf; 75 | const char *delim = " "; 76 | strsep(&tmp, delim); // skip package 77 | char *uid = strsep(&tmp, delim); 78 | if (!uid) { 79 | pr_err("update_uid: uid is NULL!\n"); 80 | continue; 81 | } 82 | 83 | u32 res; 84 | if (kstrtou32(uid, 10, &res)) { 85 | pr_err("update_uid: uid parse err\n"); 86 | continue; 87 | } 88 | data->uid = res; 89 | list_add_tail(&data->list, &uid_list); 90 | // reset line start 91 | line_start = pos; 92 | } 93 | 94 | // now update uid list 95 | struct uid_data *np; 96 | struct uid_data *n; 97 | 98 | // first, check if manager_uid exist! 99 | bool manager_exist = false; 100 | list_for_each_entry (np, &uid_list, list) { 101 | // if manager is installed in work profile, the uid in packages.list is still equals main profile 102 | // don't delete it in this case! 103 | int manager_uid = ksu_get_manager_uid() % 100000; 104 | if (np->uid == manager_uid) { 105 | manager_exist = true; 106 | break; 107 | } 108 | } 109 | 110 | if (!manager_exist && ksu_is_manager_uid_valid()) { 111 | pr_info("manager is uninstalled, invalidate it!\n"); 112 | ksu_invalidate_manager_uid(); 113 | } 114 | 115 | // then prune the allowlist 116 | ksu_prune_allowlist(is_uid_exist, &uid_list); 117 | out: 118 | // free uid_list 119 | list_for_each_entry_safe (np, n, &uid_list, list) { 120 | list_del(&np->list); 121 | kfree(np); 122 | } 123 | filp_close(fp, 0); 124 | } 125 | 126 | void update_uid() 127 | { 128 | ksu_queue_work(&ksu_update_uid_work); 129 | } 130 | 131 | int ksu_uid_observer_init() 132 | { 133 | INIT_WORK(&ksu_update_uid_work, do_update_uid); 134 | return 0; 135 | } 136 | 137 | int ksu_uid_observer_exit() 138 | { 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /kernel/uid_observer.h: -------------------------------------------------------------------------------- 1 | #ifndef __KSU_H_UID_OBSERVER 2 | #define __KSU_H_UID_OBSERVER 3 | 4 | int ksu_uid_observer_init(); 5 | 6 | int ksu_uid_observer_exit(); 7 | 8 | void update_uid(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /manager/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | local.properties 4 | .idea 5 | .DS_Store 6 | build 7 | captures 8 | .cxx 9 | key.jks 10 | -------------------------------------------------------------------------------- /manager/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /release/ 3 | -------------------------------------------------------------------------------- /manager/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.android.build.gradle.internal.api.BaseVariantOutputImpl 2 | 3 | plugins { 4 | alias(libs.plugins.agp.app) 5 | alias(libs.plugins.kotlin) 6 | alias(libs.plugins.ksp) 7 | alias(libs.plugins.lsplugin.apksign) 8 | id("kotlin-parcelize") 9 | } 10 | 11 | val managerVersionCode: Int by rootProject.extra 12 | val managerVersionName: String by rootProject.extra 13 | 14 | apksign { 15 | storeFileProperty = "KEYSTORE_FILE" 16 | storePasswordProperty = "KEYSTORE_PASSWORD" 17 | keyAliasProperty = "KEY_ALIAS" 18 | keyPasswordProperty = "KEY_PASSWORD" 19 | } 20 | 21 | android { 22 | namespace = "me.weishu.kernelsu" 23 | 24 | buildTypes { 25 | release { 26 | isMinifyEnabled = true 27 | isShrinkResources = true 28 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 29 | } 30 | } 31 | 32 | buildFeatures { 33 | aidl = true 34 | buildConfig = true 35 | compose = true 36 | } 37 | 38 | kotlinOptions { 39 | jvmTarget = "17" 40 | } 41 | 42 | composeOptions { 43 | kotlinCompilerExtensionVersion = "1.4.3" 44 | } 45 | 46 | packaging { 47 | jniLibs { 48 | useLegacyPackaging = true 49 | } 50 | resources { 51 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 52 | } 53 | } 54 | 55 | externalNativeBuild { 56 | cmake { 57 | path("src/main/cpp/CMakeLists.txt") 58 | } 59 | } 60 | 61 | applicationVariants.all { 62 | outputs.forEach { 63 | val output = it as BaseVariantOutputImpl 64 | output.outputFileName = "KernelSU_${managerVersionName}_${managerVersionCode}-$name.apk" 65 | } 66 | 67 | kotlin.sourceSets { 68 | getByName(name) { 69 | kotlin.srcDir("build/generated/ksp/$name/kotlin") 70 | } 71 | } 72 | } 73 | } 74 | 75 | dependencies { 76 | implementation(libs.androidx.activity.compose) 77 | implementation(libs.androidx.navigation.compose) 78 | 79 | implementation(platform(libs.androidx.compose.bom)) 80 | implementation(libs.androidx.compose.material.icons.extended) 81 | implementation(libs.androidx.compose.material) 82 | implementation(libs.androidx.compose.material3) 83 | implementation(libs.androidx.compose.ui) 84 | implementation(libs.androidx.compose.ui.tooling.preview) 85 | 86 | debugImplementation(libs.androidx.compose.ui.test.manifest) 87 | debugImplementation(libs.androidx.compose.ui.tooling) 88 | 89 | implementation(libs.androidx.lifecycle.runtime.compose) 90 | implementation(libs.androidx.lifecycle.runtime.ktx) 91 | implementation(libs.androidx.lifecycle.viewmodel.compose) 92 | 93 | implementation(libs.com.google.accompanist.drawablepainter) 94 | implementation(libs.com.google.accompanist.navigation.animation) 95 | implementation(libs.com.google.accompanist.systemuicontroller) 96 | 97 | implementation(libs.compose.destinations.animations.core) 98 | ksp(libs.compose.destinations.ksp) 99 | 100 | implementation(libs.com.github.topjohnwu.libsu.core) 101 | implementation(libs.com.github.topjohnwu.libsu.service) 102 | 103 | implementation(libs.dev.rikka.rikkax.parcelablelist) 104 | 105 | implementation(libs.io.coil.kt.coil.compose) 106 | 107 | implementation(libs.kotlinx.coroutines.core) 108 | 109 | implementation(libs.me.zhanghai.android.appiconloader.coil) 110 | 111 | implementation(libs.sheet.compose.dialogs.core) 112 | implementation(libs.sheet.compose.dialogs.list) 113 | implementation(libs.sheet.compose.dialogs.input) 114 | } 115 | -------------------------------------------------------------------------------- /manager/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -dontwarn org.bouncycastle.jsse.BCSSLParameters 2 | -dontwarn org.bouncycastle.jsse.BCSSLSocket 3 | -dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider 4 | -dontwarn org.conscrypt.Conscrypt$Version 5 | -dontwarn org.conscrypt.Conscrypt 6 | -dontwarn org.conscrypt.ConscryptHostnameVerifier 7 | -dontwarn org.openjsse.javax.net.ssl.SSLParameters 8 | -dontwarn org.openjsse.javax.net.ssl.SSLSocket 9 | -dontwarn org.openjsse.net.ssl.OpenJSSE 10 | -------------------------------------------------------------------------------- /manager/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 38 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /manager/app/src/main/aidl/me/weishu/kernelsu/IKsuInterface.aidl: -------------------------------------------------------------------------------- 1 | // IKsuInterface.aidl 2 | package me.weishu.kernelsu; 3 | 4 | import android.content.pm.PackageInfo; 5 | import rikka.parcelablelist.ParcelableListSlice; 6 | 7 | interface IKsuInterface { 8 | ParcelableListSlice getPackages(int flags); 9 | } -------------------------------------------------------------------------------- /manager/app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # For more information about using CMake with Android Studio, read the 3 | # documentation: https://d.android.com/studio/projects/add-native-code.html 4 | 5 | # Sets the minimum version of CMake required to build the native library. 6 | cmake_minimum_required(VERSION 3.18.1) 7 | 8 | project("kernelsu") 9 | 10 | add_library(kernelsu 11 | SHARED 12 | jni.cc 13 | ksu.cc 14 | ) 15 | 16 | find_library(log-lib log) 17 | 18 | target_link_libraries(kernelsu ${log-lib}) -------------------------------------------------------------------------------- /manager/app/src/main/cpp/ksu.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by weishu on 2022/12/9. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "ksu.h" 12 | 13 | #define KERNEL_SU_OPTION 0xDEADBEEF 14 | 15 | #define CMD_GRANT_ROOT 0 16 | 17 | #define CMD_BECOME_MANAGER 1 18 | #define CMD_GET_VERSION 2 19 | #define CMD_ALLOW_SU 3 20 | #define CMD_DENY_SU 4 21 | #define CMD_GET_SU_LIST 5 22 | #define CMD_GET_DENY_LIST 6 23 | #define CMD_CHECK_SAFEMODE 9 24 | 25 | #define CMD_GET_APP_PROFILE 10 26 | #define CMD_SET_APP_PROFILE 11 27 | 28 | #define CMD_IS_UID_GRANTED_ROOT 12 29 | #define CMD_IS_UID_SHOULD_UMOUNT 13 30 | 31 | static bool ksuctl(int cmd, void* arg1, void* arg2) { 32 | int32_t result = 0; 33 | prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result); 34 | return result == KERNEL_SU_OPTION; 35 | } 36 | 37 | bool become_manager(const char* pkg) { 38 | char param[128]; 39 | uid_t uid = getuid(); 40 | uint32_t userId = uid / 100000; 41 | if (userId == 0) { 42 | sprintf(param, "/data/data/%s", pkg); 43 | } else { 44 | snprintf(param, sizeof(param), "/data/user/%d/%s", userId, pkg); 45 | } 46 | 47 | return ksuctl(CMD_BECOME_MANAGER, param, nullptr); 48 | } 49 | 50 | int get_version() { 51 | int32_t version = -1; 52 | if (ksuctl(CMD_GET_VERSION, &version, nullptr)) { 53 | return version; 54 | } 55 | return version; 56 | } 57 | 58 | bool get_allow_list(int *uids, int *size) { 59 | return ksuctl(CMD_GET_SU_LIST, uids, size); 60 | } 61 | 62 | bool is_safe_mode() { 63 | return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr); 64 | } 65 | 66 | bool uid_should_umount(int uid) { 67 | bool should; 68 | return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, reinterpret_cast(uid), &should) && should; 69 | } 70 | 71 | bool set_app_profile(const app_profile *profile) { 72 | return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, nullptr); 73 | } 74 | 75 | bool get_app_profile(p_key_t key, app_profile *profile) { 76 | return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr); 77 | } 78 | -------------------------------------------------------------------------------- /manager/app/src/main/cpp/ksu.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by weishu on 2022/12/9. 3 | // 4 | 5 | #ifndef KERNELSU_KSU_H 6 | #define KERNELSU_KSU_H 7 | 8 | #include 9 | 10 | bool become_manager(const char *); 11 | 12 | int get_version(); 13 | 14 | bool get_allow_list(int *uids, int *size); 15 | 16 | bool uid_should_umount(int uid); 17 | 18 | bool is_safe_mode(); 19 | 20 | #define KSU_APP_PROFILE_VER 2 21 | #define KSU_MAX_PACKAGE_NAME 256 22 | // NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups. 23 | #define KSU_MAX_GROUPS 32 24 | #define KSU_SELINUX_DOMAIN 64 25 | 26 | using p_key_t = char[KSU_MAX_PACKAGE_NAME]; 27 | 28 | struct root_profile { 29 | int32_t uid; 30 | int32_t gid; 31 | 32 | int32_t groups_count; 33 | int32_t groups[KSU_MAX_GROUPS]; 34 | 35 | // kernel_cap_t is u32[2] for capabilities v3 36 | struct { 37 | uint64_t effective; 38 | uint64_t permitted; 39 | uint64_t inheritable; 40 | } capabilities; 41 | 42 | char selinux_domain[KSU_SELINUX_DOMAIN]; 43 | 44 | int32_t namespaces; 45 | }; 46 | 47 | struct non_root_profile { 48 | bool umount_modules; 49 | }; 50 | 51 | struct app_profile { 52 | // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. 53 | uint32_t version; 54 | 55 | // this is usually the package of the app, but can be other value for special apps 56 | char key[KSU_MAX_PACKAGE_NAME]; 57 | int32_t current_uid; 58 | bool allow_su; 59 | 60 | union { 61 | struct { 62 | bool use_default; 63 | char template_name[KSU_MAX_PACKAGE_NAME]; 64 | 65 | struct root_profile profile; 66 | } rp_config; 67 | 68 | struct { 69 | bool use_default; 70 | 71 | struct non_root_profile profile; 72 | } nrp_config; 73 | }; 74 | }; 75 | 76 | bool set_app_profile(const app_profile *profile); 77 | 78 | bool get_app_profile(p_key_t key, app_profile *profile); 79 | 80 | #endif //KERNELSU_KSU_H 81 | -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/KernelSUApplication.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu 2 | 3 | import android.app.Application 4 | import coil.Coil 5 | import coil.ImageLoader 6 | import me.zhanghai.android.appiconloader.coil.AppIconFetcher 7 | import me.zhanghai.android.appiconloader.coil.AppIconKeyer 8 | 9 | lateinit var ksuApp: KernelSUApplication 10 | 11 | class KernelSUApplication : Application() { 12 | 13 | override fun onCreate() { 14 | super.onCreate() 15 | ksuApp = this 16 | 17 | val context = this 18 | val iconSize = resources.getDimensionPixelSize(android.R.dimen.app_icon_size) 19 | Coil.setImageLoader( 20 | ImageLoader.Builder(context) 21 | .components { 22 | add(AppIconKeyer()) 23 | add(AppIconFetcher.Factory(iconSize, false, context)) 24 | } 25 | .build() 26 | ) 27 | } 28 | 29 | 30 | } -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/Kernels.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu 2 | 3 | import android.system.Os 4 | 5 | /** 6 | * @author weishu 7 | * @date 2022/12/10. 8 | */ 9 | 10 | data class KernelVersion(val major: Int, val patchLevel: Int, val subLevel: Int) { 11 | override fun toString(): String { 12 | return "$major.$patchLevel.$subLevel" 13 | } 14 | 15 | fun isGKI(): Boolean { 16 | 17 | // kernel 6.x 18 | if (major > 5) { 19 | return true 20 | } 21 | 22 | // kernel 5.10.x 23 | if (major == 5) { 24 | return patchLevel >= 10 25 | } 26 | 27 | return false 28 | } 29 | } 30 | 31 | fun parseKernelVersion(version: String): KernelVersion { 32 | val find = "(\\d+)\\.(\\d+)\\.(\\d+)".toRegex().find(version) 33 | return if (find != null) { 34 | KernelVersion(find.groupValues[1].toInt(), find.groupValues[2].toInt(), find.groupValues[3].toInt()) 35 | } else { 36 | KernelVersion(-1, -1, -1) 37 | } 38 | } 39 | 40 | fun getKernelVersion(): KernelVersion { 41 | Os.uname().release.let { 42 | return parseKernelVersion(it) 43 | } 44 | } -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/Natives.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu 2 | 3 | import android.os.Parcelable 4 | import androidx.annotation.Keep 5 | import androidx.compose.runtime.Immutable 6 | import kotlinx.parcelize.Parcelize 7 | 8 | /** 9 | * @author weishu 10 | * @date 2022/12/8. 11 | */ 12 | object Natives { 13 | // minimal supported kernel version 14 | // 10915: allowlist breaking change, add app profile 15 | // 10931: app profile struct add 'version' field 16 | // 10946: add capabilities 17 | // 10977: change groups_count and groups to avoid overflow write 18 | // 11071: Fix the issue of failing to set a custom SELinux type. 19 | const val MINIMAL_SUPPORTED_KERNEL = 11071 20 | 21 | init { 22 | System.loadLibrary("kernelsu") 23 | } 24 | 25 | // become root manager, return true if success. 26 | external fun becomeManager(pkg: String?): Boolean 27 | val version: Int 28 | external get 29 | 30 | // get the uid list of allowed su processes. 31 | val allowList: IntArray 32 | external get 33 | 34 | val isSafeMode: Boolean 35 | external get 36 | 37 | external fun uidShouldUmount(uid: Int): Boolean 38 | 39 | /** 40 | * Get the profile of the given package. 41 | * @param key usually the package name 42 | * @return return null if failed. 43 | */ 44 | external fun getAppProfile(key: String?, uid: Int): Profile 45 | external fun setAppProfile(profile: Profile?): Boolean 46 | 47 | private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$" 48 | private const val ROOT_DEFAULT_PROFILE_KEY = "#" 49 | private const val NOBODY_UID = 9999 50 | 51 | fun setDefaultUmountModules(umountModules: Boolean): Boolean { 52 | Profile( 53 | NON_ROOT_DEFAULT_PROFILE_KEY, 54 | NOBODY_UID, 55 | false, 56 | umountModules = umountModules 57 | ).let { 58 | return setAppProfile(it) 59 | } 60 | } 61 | 62 | fun isDefaultUmountModules(): Boolean { 63 | getAppProfile(NON_ROOT_DEFAULT_PROFILE_KEY, NOBODY_UID).let { 64 | return it.umountModules 65 | } 66 | } 67 | 68 | fun requireNewKernel(): Boolean { 69 | return version < MINIMAL_SUPPORTED_KERNEL 70 | } 71 | 72 | @Immutable 73 | @Parcelize 74 | @Keep 75 | data class Profile( 76 | // and there is a default profile for root and non-root 77 | val name: String, 78 | // current uid for the package, this is convivent for kernel to check 79 | // if the package name doesn't match uid, then it should be invalidated. 80 | val currentUid: Int = 0, 81 | 82 | // if this is true, kernel will grant root permission to this package 83 | val allowSu: Boolean = false, 84 | 85 | // these are used for root profile 86 | val rootUseDefault: Boolean = true, 87 | val rootTemplate: String? = null, 88 | val uid: Int = 0, 89 | val gid: Int = 0, 90 | val groups: List = mutableListOf(), 91 | val capabilities: List = mutableListOf(), 92 | val context: String = "u:r:su:s0", 93 | val namespace: Int = Namespace.Inherited.ordinal, 94 | 95 | val nonRootUseDefault: Boolean = true, 96 | val umountModules: Boolean = true, 97 | var rules: String = "", // this field is save in ksud!! 98 | ) : Parcelable { 99 | enum class Namespace { 100 | Inherited, 101 | Global, 102 | Individual, 103 | } 104 | 105 | constructor() : this("") 106 | } 107 | } -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/profile/Groups.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.profile 2 | 3 | /** 4 | * @author weishu 5 | * @date 2023/6/3. 6 | */ 7 | enum class Groups(val gid: Int, val display: String, val desc: String) { 8 | ROOT(0, "root", "traditional unix root user"), 9 | DAEMON(1, "daemon", "Traditional unix daemon owner."), 10 | BIN(2, "bin", "Traditional unix binaries owner."), 11 | SYS(3, "sys", "A group with the same gid on Linux/macOS/Android."), 12 | SYSTEM(1000, "system", "system server"), 13 | RADIO(1001, "radio", "telephony subsystem, RIL"), 14 | BLUETOOTH(1002, "bluetooth", "bluetooth subsystem"), 15 | GRAPHICS(1003, "graphics", "graphics devices"), 16 | INPUT(1004, "input", "input devices"), 17 | AUDIO(1005, "audio", "audio devices"), 18 | CAMERA(1006, "camera", "camera devices"), 19 | LOG(1007, "log", "log devices"), 20 | COMPASS(1008, "compass", "compass device"), 21 | MOUNT(1009, "mount", "mountd socket"), 22 | WIFI(1010, "wifi", "wifi subsystem"), 23 | ADB(1011, "adb", "android debug bridge (adbd)"), 24 | INSTALL(1012, "install", "group for installing packages"), 25 | MEDIA(1013, "media", "mediaserver process"), 26 | DHCP(1014, "dhcp", "dhcp client"), 27 | SDCARD_RW(1015, "sdcard_rw", "external storage write access"), 28 | VPN(1016, "vpn", "vpn system"), 29 | KEYSTORE(1017, "keystore", "keystore subsystem"), 30 | USB(1018, "usb", "USB devices"), 31 | DRM(1019, "drm", "DRM server"), 32 | MDNSR(1020, "mdnsr", "MulticastDNSResponder (service discovery)"), 33 | GPS(1021, "gps", "GPS daemon"), 34 | UNUSED1(1022, "unused1", "deprecated, DO NOT USE"), 35 | MEDIA_RW(1023, "media_rw", "internal media storage write access"), 36 | MTP(1024, "mtp", "MTP USB driver access"), 37 | UNUSED2(1025, "unused2", "deprecated, DO NOT USE"), 38 | DRMRPC(1026, "drmrpc", "group for drm rpc"), 39 | NFC(1027, "nfc", "nfc subsystem"), 40 | SDCARD_R(1028, "sdcard_r", "external storage read access"), 41 | CLAT(1029, "clat", "clat part of nat464"), 42 | LOOP_RADIO(1030, "loop_radio", "loop radio devices"), 43 | MEDIA_DRM(1031, "media_drm", "MediaDrm plugins"), 44 | PACKAGE_INFO(1032, "package_info", "access to installed package details"), 45 | SDCARD_PICS(1033, "sdcard_pics", "external storage photos access"), 46 | SDCARD_AV(1034, "sdcard_av", "external storage audio/video access"), 47 | SDCARD_ALL(1035, "sdcard_all", "access all users external storage"), 48 | LOGD(1036, "logd", "log daemon"), 49 | SHARED_RELRO(1037, "shared_relro", "creator of shared GNU RELRO files"), 50 | DBUS(1038, "dbus", "dbus-daemon IPC broker process"), 51 | TLSDATE(1039, "tlsdate", "tlsdate unprivileged user"), 52 | MEDIA_EX(1040, "media_ex", "mediaextractor process"), 53 | AUDIOSERVER(1041, "audioserver", "audioserver process"), 54 | METRICS_COLL(1042, "metrics_coll", "metrics_collector process"), 55 | METRICSD(1043, "metricsd", "metricsd process"), 56 | WEBSERV(1044, "webserv", "webservd process"), 57 | DEBUGGERD(1045, "debuggerd", "debuggerd unprivileged user"), 58 | MEDIA_CODEC(1046, "media_codec", "media_codec process"), 59 | CAMERASERVER(1047, "cameraserver", "cameraserver process"), 60 | FIREWALL(1048, "firewall", "firewall process"), 61 | TRUNKS(1049, "trunks", "trunksd process"), 62 | NVRAM(1050, "nvram", "nvram daemon"), 63 | DNS_TETHER(1051, "dns_tether", "dns_tether device"), 64 | DNS_TETHER_RESERVED(1052, "dns_tether_reserved", "Reserved range for dns_tether"), 65 | WEBVIEW_ZYGOTE(1053, "webview_zygote", "zygote process"), 66 | WEBVIEW_USER(1054, "webview_user", "webview chromium user"), 67 | ETHERNET(1055, "ethernet", "Ethernet"), 68 | TOMBSTONED(1056, "tombstoned", "tombstoned process"), 69 | GRAPHICS_RW(1057, "graphics_rw", "graphics devices"), 70 | 71 | SHELL(2000, "shell", "adb and debug shell user"), 72 | CACHE(2001, "cache", "cache access"), 73 | DIAG(2002, "diag", "diagnostics"), 74 | NET_BT_ADMIN(3001, "net_bt_admin", "bluetooth: create any socket"), 75 | NET_BT(3002, "net_bt", "bluetooth: create sco, rfcomm or l2cap sockets"), 76 | INET(3003, "inet", "can create AF_INET and AF_INET6 sockets"), 77 | NET_RAW(3004, "net_raw", "can create raw INET sockets"), 78 | NET_ADMIN(3005, "net_admin", "can configure interfaces and routing tables."), 79 | NET_BW_STATS(3006, "net_bw_stats", "read bandwidth statistics"), 80 | NET_BW_ACCT(3007, "net_bw_acct", "change bandwidth statistics accounting"), 81 | NET_BT_STACK(3008, "net_bt_stack", "access to various bluetooth management functions"), 82 | QCOM_DIAG(3009, "qcom_diag", "allow msm specific diag commands"), 83 | EVERYBODY(9997, "everybody", "Shared external storage read/write"), 84 | MISC(9998, "misc", "Access to misc storage"), 85 | NOBODY(9999, "nobody", "Reserved"), 86 | APP(10000, "app", "Access to app data"), 87 | } -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/KsuService.java: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | import android.os.IBinder; 8 | import android.os.UserHandle; 9 | import android.os.UserManager; 10 | import android.util.Log; 11 | 12 | import androidx.annotation.NonNull; 13 | 14 | import com.topjohnwu.superuser.ipc.RootService; 15 | 16 | import java.lang.reflect.Method; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | import me.weishu.kernelsu.IKsuInterface; 21 | import rikka.parcelablelist.ParcelableListSlice; 22 | 23 | /** 24 | * @author weishu 25 | * @date 2023/4/18. 26 | */ 27 | 28 | public class KsuService extends RootService { 29 | 30 | private static final String TAG = "KsuService"; 31 | 32 | class Stub extends IKsuInterface.Stub { 33 | @Override 34 | public ParcelableListSlice getPackages(int flags) { 35 | List list = getInstalledPackagesAll(flags); 36 | Log.i(TAG, "getPackages: " + list.size()); 37 | return new ParcelableListSlice<>(list); 38 | } 39 | } 40 | 41 | @Override 42 | public IBinder onBind(@NonNull Intent intent) { 43 | return new Stub(); 44 | } 45 | 46 | List getUserIds() { 47 | List result = new ArrayList<>(); 48 | UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 49 | List userProfiles = um.getUserProfiles(); 50 | for (UserHandle userProfile : userProfiles) { 51 | int userId = userProfile.hashCode(); 52 | result.add(userProfile.hashCode()); 53 | } 54 | return result; 55 | } 56 | 57 | ArrayList getInstalledPackagesAll(int flags) { 58 | ArrayList packages = new ArrayList<>(); 59 | for (Integer userId : getUserIds()) { 60 | Log.i(TAG, "getInstalledPackagesAll: " + userId); 61 | packages.addAll(getInstalledPackagesAsUser(flags, userId)); 62 | } 63 | return packages; 64 | } 65 | 66 | List getInstalledPackagesAsUser(int flags, int userId) { 67 | try { 68 | PackageManager pm = getPackageManager(); 69 | Method getInstalledPackagesAsUser = pm.getClass().getDeclaredMethod("getInstalledPackagesAsUser", int.class, int.class); 70 | return (List) getInstalledPackagesAsUser.invoke(pm, flags, userId); 71 | } catch (Throwable e) { 72 | Log.e(TAG, "err", e); 73 | } 74 | 75 | return new ArrayList<>(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.animation.ExperimentalAnimationApi 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.material3.Icon 9 | import androidx.compose.material3.NavigationBar 10 | import androidx.compose.material3.NavigationBarItem 11 | import androidx.compose.material3.Scaffold 12 | import androidx.compose.material3.SnackbarHost 13 | import androidx.compose.material3.SnackbarHostState 14 | import androidx.compose.material3.Text 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.runtime.CompositionLocalProvider 17 | import androidx.compose.runtime.getValue 18 | import androidx.compose.runtime.remember 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.res.stringResource 21 | import androidx.compose.ui.unit.dp 22 | import androidx.navigation.NavHostController 23 | import com.google.accompanist.navigation.animation.rememberAnimatedNavController 24 | import com.ramcosta.composedestinations.DestinationsNavHost 25 | import com.ramcosta.composedestinations.navigation.popBackStack 26 | import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState 27 | import me.weishu.kernelsu.Natives 28 | import me.weishu.kernelsu.ksuApp 29 | import me.weishu.kernelsu.ui.component.rememberDialogHostState 30 | import me.weishu.kernelsu.ui.screen.BottomBarDestination 31 | import me.weishu.kernelsu.ui.screen.NavGraphs 32 | import me.weishu.kernelsu.ui.theme.KernelSUTheme 33 | import me.weishu.kernelsu.ui.util.LocalDialogHost 34 | import me.weishu.kernelsu.ui.util.LocalSnackbarHost 35 | 36 | class MainActivity : ComponentActivity() { 37 | 38 | @OptIn(ExperimentalAnimationApi::class) 39 | override fun onCreate(savedInstanceState: Bundle?) { 40 | super.onCreate(savedInstanceState) 41 | 42 | setContent { 43 | KernelSUTheme { 44 | val navController = rememberAnimatedNavController() 45 | val snackbarHostState = remember { SnackbarHostState() } 46 | Scaffold( 47 | bottomBar = { BottomBar(navController) }, 48 | snackbarHost = { SnackbarHost(snackbarHostState) } 49 | ) { innerPadding -> 50 | CompositionLocalProvider( 51 | LocalSnackbarHost provides snackbarHostState, 52 | LocalDialogHost provides rememberDialogHostState(), 53 | ) { 54 | DestinationsNavHost( 55 | modifier = Modifier.padding(innerPadding), 56 | navGraph = NavGraphs.root, 57 | navController = navController 58 | ) 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | @Composable 67 | private fun BottomBar(navController: NavHostController) { 68 | val isManager = Natives.becomeManager(ksuApp.packageName) 69 | val fullFeatured = isManager && !Natives.requireNewKernel() 70 | NavigationBar(tonalElevation = 8.dp) { 71 | BottomBarDestination.values().forEach { destination -> 72 | if (!fullFeatured && destination.rootRequired) return@forEach 73 | val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) 74 | NavigationBarItem( 75 | selected = isCurrentDestOnBackStack, 76 | onClick = { 77 | if (isCurrentDestOnBackStack) { 78 | navController.popBackStack(destination.direction, false) 79 | } 80 | 81 | navController.navigate(destination.direction.route) { 82 | popUpTo(NavGraphs.root.route) { 83 | saveState = true 84 | } 85 | launchSingleTop = true 86 | restoreState = true 87 | } 88 | }, 89 | icon = { 90 | if (isCurrentDestOnBackStack) { 91 | Icon(destination.iconSelected, stringResource(destination.label)) 92 | } else { 93 | Icon(destination.iconNotSelected, stringResource(destination.label)) 94 | } 95 | }, 96 | label = { Text(stringResource(destination.label)) }, 97 | alwaysShowLabel = false 98 | ) 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.component 2 | 3 | import android.text.method.LinkMovementMethod 4 | import android.widget.TextView 5 | import androidx.compose.foundation.Image 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Row 8 | import androidx.compose.foundation.layout.Spacer 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.height 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.layout.size 13 | import androidx.compose.foundation.layout.width 14 | import androidx.compose.foundation.shape.RoundedCornerShape 15 | import androidx.compose.material3.ElevatedCard 16 | import androidx.compose.material3.LocalContentColor 17 | import androidx.compose.material3.MaterialTheme 18 | import androidx.compose.material3.Text 19 | import androidx.compose.runtime.Composable 20 | import androidx.compose.runtime.MutableState 21 | import androidx.compose.ui.Modifier 22 | import androidx.compose.ui.graphics.toArgb 23 | import androidx.compose.ui.platform.LocalContext 24 | import androidx.compose.ui.res.stringResource 25 | import androidx.compose.ui.tooling.preview.Preview 26 | import androidx.compose.ui.unit.dp 27 | import androidx.compose.ui.unit.sp 28 | import androidx.compose.ui.viewinterop.AndroidView 29 | import androidx.compose.ui.window.Dialog 30 | import androidx.core.content.res.ResourcesCompat 31 | import androidx.core.text.HtmlCompat 32 | import com.google.accompanist.drawablepainter.rememberDrawablePainter 33 | import me.weishu.kernelsu.BuildConfig 34 | import me.weishu.kernelsu.R 35 | 36 | @Preview 37 | @Composable 38 | fun AboutCard() { 39 | ElevatedCard( 40 | modifier = Modifier 41 | .fillMaxWidth(), 42 | shape = RoundedCornerShape(8.dp), 43 | ) { 44 | Row( 45 | modifier = Modifier 46 | .fillMaxWidth() 47 | .padding(24.dp) 48 | ) { 49 | AboutCardContent() 50 | } 51 | } 52 | } 53 | 54 | @Composable 55 | fun AboutDialog(showAboutDialog: MutableState) { 56 | if (showAboutDialog.value) { 57 | Dialog(onDismissRequest = { showAboutDialog.value = false }) { 58 | AboutCard() 59 | } 60 | } 61 | } 62 | 63 | @Composable 64 | private fun AboutCardContent() { 65 | Column( 66 | modifier = Modifier 67 | .fillMaxWidth() 68 | ) { 69 | val drawable = ResourcesCompat.getDrawable( 70 | LocalContext.current.resources, 71 | R.mipmap.ic_launcher, 72 | LocalContext.current.theme 73 | ) 74 | 75 | Row { 76 | Image( 77 | painter = rememberDrawablePainter(drawable), 78 | contentDescription = "icon", 79 | modifier = Modifier.size(40.dp) 80 | ) 81 | 82 | Spacer(modifier = Modifier.width(12.dp)) 83 | 84 | Column { 85 | 86 | Text( 87 | stringResource(id = R.string.app_name), 88 | style = MaterialTheme.typography.titleSmall, 89 | fontSize = 18.sp 90 | ) 91 | Text( 92 | BuildConfig.VERSION_NAME, 93 | style = MaterialTheme.typography.bodySmall, 94 | fontSize = 14.sp 95 | ) 96 | 97 | Spacer(modifier = Modifier.height(8.dp)) 98 | 99 | HtmlText( 100 | html = stringResource( 101 | id = R.string.about_source_code, 102 | "GitHub", 103 | "Telegram" 104 | ) 105 | ) 106 | } 107 | } 108 | } 109 | } 110 | 111 | @Composable 112 | fun HtmlText(html: String, modifier: Modifier = Modifier) { 113 | val contentColor = LocalContentColor.current 114 | AndroidView( 115 | modifier = modifier, 116 | factory = { context -> 117 | TextView(context).also { 118 | it.movementMethod = LinkMovementMethod.getInstance() 119 | } 120 | }, 121 | update = { 122 | it.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT) 123 | it.setTextColor(contentColor.toArgb()) 124 | } 125 | ) 126 | } -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/component/KeyEventBlocker.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.component 2 | 3 | import androidx.compose.foundation.focusable 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.runtime.LaunchedEffect 7 | import androidx.compose.runtime.remember 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.focus.FocusRequester 10 | import androidx.compose.ui.focus.focusRequester 11 | import androidx.compose.ui.input.key.KeyEvent 12 | import androidx.compose.ui.input.key.onKeyEvent 13 | 14 | @Composable 15 | fun KeyEventBlocker(predicate: (KeyEvent) -> Boolean) { 16 | val requester = remember { FocusRequester() } 17 | Box( 18 | Modifier 19 | .onKeyEvent { 20 | predicate(it) 21 | } 22 | .focusRequester(requester) 23 | .focusable() 24 | ) 25 | LaunchedEffect(Unit) { 26 | requester.requestFocus() 27 | } 28 | } -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.component 2 | 3 | import androidx.compose.material3.Icon 4 | import androidx.compose.material3.ListItem 5 | import androidx.compose.material3.RadioButton 6 | import androidx.compose.material3.Switch 7 | import androidx.compose.material3.Text 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.ui.graphics.vector.ImageVector 10 | 11 | @Composable 12 | fun SwitchItem( 13 | icon: ImageVector? = null, 14 | title: String, 15 | summary: String? = null, 16 | checked: Boolean, 17 | enabled: Boolean = true, 18 | onCheckedChange: (Boolean) -> Unit 19 | ) { 20 | ListItem( 21 | headlineContent = { 22 | Text(title) 23 | }, 24 | leadingContent = icon?.let { 25 | { Icon(icon, title) } 26 | }, 27 | trailingContent = { 28 | Switch(checked = checked, enabled = enabled, onCheckedChange = onCheckedChange) 29 | }, 30 | supportingContent = { 31 | if (summary != null) { 32 | Text(summary) 33 | } 34 | } 35 | ) 36 | } 37 | 38 | @Composable 39 | fun RadioItem( 40 | title: String, 41 | selected: Boolean, 42 | onClick: () -> Unit, 43 | ) { 44 | ListItem( 45 | headlineContent = { 46 | Text(title) 47 | }, 48 | leadingContent = { 49 | RadioButton(selected = selected, onClick = onClick) 50 | }, 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/AppProfileConfig.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.component.profile 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.material3.OutlinedTextField 5 | import androidx.compose.material3.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.getValue 8 | import androidx.compose.runtime.mutableStateOf 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.runtime.setValue 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.res.stringResource 13 | import androidx.compose.ui.tooling.preview.Preview 14 | import me.weishu.kernelsu.Natives 15 | import me.weishu.kernelsu.R 16 | import me.weishu.kernelsu.ui.component.SwitchItem 17 | 18 | @Composable 19 | fun AppProfileConfig( 20 | modifier: Modifier = Modifier, 21 | fixedName: Boolean, 22 | enabled: Boolean, 23 | profile: Natives.Profile, 24 | onProfileChange: (Natives.Profile) -> Unit, 25 | ) { 26 | Column(modifier = modifier) { 27 | if (!fixedName) { 28 | OutlinedTextField( 29 | label = { Text(stringResource(R.string.profile_name)) }, 30 | value = profile.name, 31 | onValueChange = { onProfileChange(profile.copy(name = it)) } 32 | ) 33 | } 34 | 35 | SwitchItem( 36 | title = stringResource(R.string.profile_umount_modules), 37 | summary = stringResource(R.string.profile_umount_modules_summary), 38 | checked = if (enabled) { 39 | profile.umountModules 40 | } else { 41 | Natives.isDefaultUmountModules() 42 | }, 43 | enabled = enabled, 44 | onCheckedChange = { 45 | onProfileChange( 46 | profile.copy( 47 | umountModules = it, 48 | nonRootUseDefault = false 49 | ) 50 | ) 51 | } 52 | ) 53 | } 54 | } 55 | 56 | @Preview 57 | @Composable 58 | private fun AppProfileConfigPreview() { 59 | var profile by remember { mutableStateOf(Natives.Profile("")) } 60 | AppProfileConfig(fixedName = true, enabled = false, profile = profile) { 61 | profile = it 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/screen/BottomBarDestination.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.screen 2 | 3 | import androidx.annotation.StringRes 4 | import androidx.compose.material.icons.Icons 5 | import androidx.compose.material.icons.filled.* 6 | import androidx.compose.material.icons.outlined.* 7 | import androidx.compose.ui.graphics.vector.ImageVector 8 | import com.ramcosta.composedestinations.spec.DirectionDestinationSpec 9 | import me.weishu.kernelsu.R 10 | import me.weishu.kernelsu.ui.screen.destinations.HomeScreenDestination 11 | import me.weishu.kernelsu.ui.screen.destinations.SuperUserScreenDestination 12 | import me.weishu.kernelsu.ui.screen.destinations.ModuleScreenDestination 13 | 14 | enum class BottomBarDestination( 15 | val direction: DirectionDestinationSpec, 16 | @StringRes val label: Int, 17 | val iconSelected: ImageVector, 18 | val iconNotSelected: ImageVector, 19 | val rootRequired: Boolean, 20 | ) { 21 | Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false), 22 | SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true), 23 | Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true) 24 | } 25 | -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val YELLOW = Color(0xFFeed502) 6 | val YELLOW_LIGHT = Color(0xFFffff52) 7 | val SECONDARY_LIGHT = Color(0xffa9817f) 8 | 9 | val YELLOW_DARK = Color(0xFFb7a400) 10 | val SECONDARY_DARK = Color(0xFF4c2b2b) -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.* 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.runtime.SideEffect 9 | import androidx.compose.ui.graphics.Color 10 | import androidx.compose.ui.graphics.toArgb 11 | import androidx.compose.ui.platform.LocalContext 12 | import androidx.compose.ui.platform.LocalView 13 | import androidx.compose.ui.unit.dp 14 | import androidx.core.view.ViewCompat 15 | import com.google.accompanist.systemuicontroller.rememberSystemUiController 16 | 17 | private val DarkColorScheme = darkColorScheme( 18 | primary = YELLOW, 19 | secondary = YELLOW_DARK, 20 | tertiary = SECONDARY_DARK 21 | ) 22 | 23 | private val LightColorScheme = lightColorScheme( 24 | primary = YELLOW, 25 | secondary = YELLOW_LIGHT, 26 | tertiary = SECONDARY_LIGHT 27 | ) 28 | 29 | @Composable 30 | fun KernelSUTheme( 31 | darkTheme: Boolean = isSystemInDarkTheme(), 32 | // Dynamic color is available on Android 12+ 33 | dynamicColor: Boolean = true, 34 | content: @Composable () -> Unit 35 | ) { 36 | val colorScheme = when { 37 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 38 | val context = LocalContext.current 39 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 40 | } 41 | darkTheme -> DarkColorScheme 42 | else -> LightColorScheme 43 | } 44 | 45 | val systemUiController = rememberSystemUiController() 46 | SideEffect { 47 | systemUiController.setStatusBarColor( 48 | color = colorScheme.surface, 49 | darkIcons = !darkTheme 50 | ) 51 | 52 | // To match the App Navbar color 53 | systemUiController.setNavigationBarColor( 54 | color = colorScheme.surfaceColorAtElevation(8.dp), 55 | darkIcons = !darkTheme, 56 | ) 57 | } 58 | 59 | MaterialTheme( 60 | colorScheme = colorScheme, 61 | typography = Typography, 62 | content = content 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.theme 2 | 3 | import androidx.compose.ui.text.TextStyle 4 | import androidx.compose.ui.text.font.FontFamily 5 | import androidx.compose.ui.text.font.FontWeight 6 | import androidx.compose.ui.unit.sp 7 | 8 | // Set of Material typography styles to start with 9 | val Typography = androidx.compose.material3.Typography( 10 | bodyLarge = TextStyle( 11 | fontFamily = FontFamily.Default, 12 | fontWeight = FontWeight.Normal, 13 | fontSize = 16.sp, 14 | lineHeight = 24.sp, 15 | letterSpacing = 0.5.sp 16 | ) 17 | /* Other default text styles to override 18 | titleLarge = TextStyle( 19 | fontFamily = FontFamily.Default, 20 | fontWeight = FontWeight.Normal, 21 | fontSize = 22.sp, 22 | lineHeight = 28.sp, 23 | letterSpacing = 0.sp 24 | ), 25 | labelSmall = TextStyle( 26 | fontFamily = FontFamily.Default, 27 | fontWeight = FontWeight.Medium, 28 | fontSize = 11.sp, 29 | lineHeight = 16.sp, 30 | letterSpacing = 0.5.sp 31 | ) 32 | */ 33 | ) -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/util/CompositionProvider.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.util 2 | 3 | import androidx.compose.material3.SnackbarHostState 4 | import androidx.compose.runtime.compositionLocalOf 5 | import me.weishu.kernelsu.ui.component.DialogHostState 6 | 7 | val LocalSnackbarHost = compositionLocalOf { 8 | error("CompositionLocal LocalSnackbarController not present") 9 | } 10 | 11 | val LocalDialogHost = compositionLocalOf { 12 | error("CompositionLocal LocalDialogController not present") 13 | } -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/util/HyperlinkText.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.util 2 | 3 | import androidx.compose.foundation.gestures.detectTapGestures 4 | import androidx.compose.material3.MaterialTheme 5 | import androidx.compose.material3.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.mutableStateOf 8 | import androidx.compose.runtime.remember 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.input.pointer.pointerInput 11 | import androidx.compose.ui.platform.LocalUriHandler 12 | import androidx.compose.ui.text.SpanStyle 13 | import androidx.compose.ui.text.TextLayoutResult 14 | import androidx.compose.ui.text.buildAnnotatedString 15 | import androidx.compose.ui.text.style.TextDecoration 16 | import java.util.regex.Pattern 17 | 18 | @Composable 19 | fun LinkifyText( 20 | text: String, 21 | modifier: Modifier = Modifier 22 | ) { 23 | val uriHandler = LocalUriHandler.current 24 | val layoutResult = remember { 25 | mutableStateOf(null) 26 | } 27 | val linksList = extractUrls(text) 28 | val annotatedString = buildAnnotatedString { 29 | append(text) 30 | linksList.forEach { 31 | addStyle( 32 | style = SpanStyle( 33 | color = MaterialTheme.colorScheme.primary, 34 | textDecoration = TextDecoration.Underline 35 | ), 36 | start = it.start, 37 | end = it.end 38 | ) 39 | addStringAnnotation( 40 | tag = "URL", 41 | annotation = it.url, 42 | start = it.start, 43 | end = it.end 44 | ) 45 | } 46 | } 47 | Text( 48 | text = annotatedString, 49 | modifier = modifier.pointerInput(Unit) { 50 | detectTapGestures { offsetPosition -> 51 | layoutResult.value?.let { 52 | val position = it.getOffsetForPosition(offsetPosition) 53 | annotatedString.getStringAnnotations(position, position).firstOrNull() 54 | ?.let { result -> 55 | if (result.tag == "URL") { 56 | uriHandler.openUri(result.item) 57 | } 58 | } 59 | } 60 | } 61 | }, 62 | onTextLayout = { layoutResult.value = it } 63 | ) 64 | } 65 | 66 | private val urlPattern: Pattern = Pattern.compile( 67 | "(?:^|[\\W])((ht|f)tp(s?):\\/\\/|www\\.)" 68 | + "(([\\w\\-]+\\.){1,}?([\\w\\-.~]+\\/?)*" 69 | + "[\\p{Alnum}.,%_=?&#\\-+()\\[\\]\\*$~@!:/{};']*)", 70 | Pattern.CASE_INSENSITIVE or Pattern.MULTILINE or Pattern.DOTALL 71 | ) 72 | 73 | private data class LinkInfo( 74 | val url: String, 75 | val start: Int, 76 | val end: Int 77 | ) 78 | 79 | private fun extractUrls(text: String): List = buildList { 80 | val matcher = urlPattern.matcher(text) 81 | while (matcher.find()) { 82 | val matchStart = matcher.start(1) 83 | val matchEnd = matcher.end() 84 | val url = text.substring(matchStart, matchEnd).replaceFirst("http://", "https://") 85 | add(LinkInfo(url, matchStart, matchEnd)) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.util 2 | 3 | import android.content.Context 4 | import android.os.Build 5 | import android.system.Os 6 | import com.topjohnwu.superuser.ShellUtils 7 | import me.weishu.kernelsu.Natives 8 | import me.weishu.kernelsu.ui.screen.getManagerVersion 9 | import java.io.File 10 | import java.io.FileWriter 11 | import java.io.PrintWriter 12 | import java.time.LocalDateTime 13 | import java.time.format.DateTimeFormatter 14 | 15 | fun getBugreportFile(context: Context): File { 16 | 17 | val bugreportDir = File(context.cacheDir, "bugreport") 18 | bugreportDir.mkdirs() 19 | 20 | val dmesgFile = File(bugreportDir, "dmesg.txt") 21 | val logcatFile = File(bugreportDir, "logcat.txt") 22 | val tombstonesFile = File(bugreportDir, "tombstones.tar.gz") 23 | val dropboxFile = File(bugreportDir, "dropbox.tar.gz") 24 | val pstoreFile = File(bugreportDir, "pstore.tar.gz") 25 | val diagFile = File(bugreportDir, "diag.tar.gz") 26 | val bootlogFile = File(bugreportDir, "bootlog.tar.gz") 27 | val mountsFile = File(bugreportDir, "mounts.txt") 28 | val fileSystemsFile = File(bugreportDir, "filesystems.txt") 29 | val ksuFileTree = File(bugreportDir, "ksu_tree.txt") 30 | val appListFile = File(bugreportDir, "packages.txt") 31 | val propFile = File(bugreportDir, "props.txt") 32 | val allowListFile = File(bugreportDir, "allowlist.bin") 33 | 34 | val shell = getRootShell() 35 | 36 | shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec() 37 | shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec() 38 | shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec() 39 | shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec() 40 | shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec() 41 | shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag .").exec() 42 | shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec() 43 | 44 | shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec() 45 | shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec() 46 | shell.newJob().add("ls -alRZ /data/adb > ${ksuFileTree.absolutePath}").exec() 47 | shell.newJob().add("cp /data/system/packages.list ${appListFile.absolutePath}").exec() 48 | shell.newJob().add("getprop > ${propFile.absolutePath}").exec() 49 | shell.newJob().add("cp /data/adb/ksu/.allowlist ${allowListFile.absolutePath}").exec() 50 | 51 | val selinux = ShellUtils.fastCmd(shell, "getenforce"); 52 | 53 | // basic information 54 | val buildInfo = File(bugreportDir, "basic.txt") 55 | PrintWriter(FileWriter(buildInfo)).use { pw -> 56 | pw.println("Kernel: ${System.getProperty("os.version")}") 57 | pw.println("BRAND: " + Build.BRAND) 58 | pw.println("MODEL: " + Build.MODEL) 59 | pw.println("PRODUCT: " + Build.PRODUCT) 60 | pw.println("MANUFACTURER: " + Build.MANUFACTURER) 61 | pw.println("SDK: " + Build.VERSION.SDK_INT) 62 | pw.println("PREVIEW_SDK: " + Build.VERSION.PREVIEW_SDK_INT) 63 | pw.println("FINGERPRINT: " + Build.FINGERPRINT) 64 | pw.println("DEVICE: " + Build.DEVICE) 65 | pw.println("Manager: " + getManagerVersion(context)) 66 | pw.println("SELinux: $selinux") 67 | 68 | val uname = Os.uname() 69 | pw.println("KernelRelease: ${uname.release}") 70 | pw.println("KernelVersion: ${uname.version}") 71 | pw.println("Mahcine: ${uname.machine}") 72 | pw.println("Nodename: ${uname.nodename}") 73 | pw.println("Sysname: ${uname.sysname}") 74 | 75 | val ksuKernel = Natives.version 76 | pw.println("KernelSU: $ksuKernel") 77 | val safeMode = Natives.isSafeMode 78 | pw.println("SafeMode: $safeMode") 79 | } 80 | 81 | // modules 82 | val modulesFile = File(bugreportDir, "modules.json") 83 | modulesFile.writeText(listModules()) 84 | 85 | val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm") 86 | val current = LocalDateTime.now().format(formatter) 87 | 88 | val targetFile = File(context.cacheDir, "KernelSU_bugreport_${current}.tar.gz") 89 | 90 | shell.newJob().add("tar czf ${targetFile.absolutePath} -C ${bugreportDir.absolutePath} .").exec() 91 | shell.newJob().add("rm -rf ${bugreportDir.absolutePath}").exec() 92 | shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec() 93 | 94 | return targetFile 95 | } 96 | -------------------------------------------------------------------------------- /manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt: -------------------------------------------------------------------------------- 1 | package me.weishu.kernelsu.ui.util 2 | 3 | import androidx.compose.ui.res.stringResource 4 | import androidx.compose.runtime.Composable 5 | import com.topjohnwu.superuser.Shell 6 | import me.weishu.kernelsu.R 7 | 8 | @Composable 9 | fun getSELinuxStatus(): String { 10 | val shell = Shell.Builder.create() 11 | .setFlags(Shell.FLAG_REDIRECT_STDERR) 12 | .build("sh") 13 | 14 | val list = ArrayList() 15 | val result = shell.newJob().add("getenforce").to(list, list).exec() 16 | val output = result.out.joinToString("\n").trim() 17 | 18 | if (result.isSuccess) { 19 | return when (output) { 20 | "Enforcing" -> stringResource(R.string.selinux_status_enforcing) 21 | "Permissive" -> stringResource(R.string.selinux_status_permissive) 22 | "Disabled" -> stringResource(R.string.selinux_status_disabled) 23 | else -> stringResource(R.string.selinux_status_unknown) 24 | } 25 | } 26 | 27 | return if (output.endsWith("Permission denied")) { 28 | stringResource(R.string.selinux_status_enforcing) 29 | } else { 30 | stringResource(R.string.selinux_status_unknown) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /manager/app/src/main/jniLibs/.gitignore: -------------------------------------------------------------------------------- 1 | libksud.so -------------------------------------------------------------------------------- /manager/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /manager/app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 15 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /manager/app/src/main/res/drawable/ic_launcher_monochrome.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 15 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /manager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /manager/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/manager/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /manager/app/src/main/res/mipmap-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/manager/app/src/main/res/mipmap-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /manager/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/manager/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /manager/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/manager/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /manager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /manager/app/src/main/res/values-bn-rBD/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | কর্নেল এস ইউ কেবল মাত্র জিকআই কর্নেল সাপোর্ট করে 4 | এসইলিনাক্স স্টেটাস 5 | আননোন 6 | মোডিউল ইনেবল করা যায়নি: %s 7 | ইন্সটল করটে চাপুন 8 | কাজ করছে 9 | মোডিউল: %d 10 | অমূলক 11 | কর্নেল 12 | ম্যানেজার ভারসন 13 | ফিঙ্গারপ্রিন্ট 14 | ডিসেবল 15 | এনফোর্সিং 16 | সুপার ইউজার 17 | মোডিউল 18 | আনইন্সটল 19 | ইন্সটল 20 | ইন্সটল 21 | রিবুট 22 | সেটিংস 23 | সফট রিবুট 24 | গ্লোবাল 25 | গ্রুপস 26 | এসইলিনাক্স কন্টেক্সট 27 | %s এর জন্য অ্যাপ প্রফাইল আপডেট করা যায়নি 28 | বাইডিফল্ট মোডিউল আনমাউন্ট 29 | হোম 30 | ইন্সটল হয়নী 31 | পারমিসিভ 32 | মোডিউল ডিসেবল করা যায়নি: %s 33 | কোনো মোডিউল ইন্সটল করা নেই 34 | ভারসন: %d 35 | সুপার ইউজার: %d 36 | নেইম স্পেস মাউন্ট 37 | ইনহেরিটেড 38 | ইন্ডিভিজুয়াল 39 | ক্যাপাবিলিটিস 40 | আনমাউন্ট মোডিউলস 41 | রিকভারিতে বুট 42 | বুটলোডারে বুট 43 | ডাউনলোড মডে বুট 44 | ইমারজেন্সি ডাউনলোড মডে বুট 45 | অ্যাবাউট 46 | %s মোডিউল আনইনস্টলের বেপারে নিশ্চিৎ\? 47 | %s আনইনস্টলড 48 | %s আনইনস্টল করা যায়নি 49 | ভার্সন 50 | অথার 51 | -------------------------------------------------------------------------------- /manager/app/src/main/res/values-bn/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | হোম 4 | ইনস্টল করা হয়নি 5 | ইনস্টল করার জন্য ক্লিক করুন 6 | ওয়ার্কিং 7 | ওয়ার্কিং সংস্করণ: %d 8 | সুপার ইউজার: %d 9 | মডিউল: %d 10 | অসমর্থিত 11 | কার্নেলএসইউ শুধুমাত্র জিকেআই কার্নেল সমর্থন করে 12 | 13 | কার্নেল 14 | ম্যানেজার সংস্করণ 15 | ফিঙ্গারপ্রিন্ট 16 | 17 | সেলিনাক্স স্ট্যাটাস 18 | ডিজেবল 19 | এনফোর্সিং 20 | অনুমতিমূলক 21 | অপরিচিত 22 | সুপার ইউজার 23 | মডিউল সক্ষম করতে ব্যর্থ হয়েছে: %s 24 | মডিউল নিষ্ক্রিয় করতে ব্যর্থ হয়েছে: %s 25 | কোন মডিউল ইনস্টল করা নেই 26 | 27 | মডিউল 28 | আনইন্সটল 29 | মডিউল ইনস্টল 30 | ইনস্টল 31 | রিবুট 32 | সেটিংস 33 | সফট রিবুট 34 | রিবুট রিকোভারি 35 | রিবুট বুটলোডার 36 | রিবুট ডাউনলোড 37 | রিবুট ইডিএল 38 | এবাউট 39 | মডিউল আনইনস্টল নিশ্চিত করুন %s? 40 | %s আনইনস্টল সফল 41 | আনইন্সটল ব্যর্থ: %s 42 | ভার্সন 43 | লেখক 44 | ওভারলেএফএস উপলব্ধ নয়, মডিউল কাজ করতে পারে না! 45 | রিফ্রেশ 46 | শো সিস্টেম অ্যাপস 47 | হাইড সিস্টেম অ্যাপস 48 | সেন্ড লগ 49 | সেইফ মোড 50 | রিবুট এপ্লাই 51 | মডিউলগুলি অক্ষম কারণ তারা ম্যাজিস্কের সাথে বিরোধিতা করে! 52 | লার্ন কার্নেলএসইউ 53 | https://kernelsu.org/guide/what-is-kernelsu.html 54 | কিভাবে কার্নেলএসইউ ইনস্টল করতে হয় এবং মডিউল ব্যবহার করতে হয় তা শিখুন 55 | সাপোর্ট টাইটেল 56 | কার্নেলএসইউ বিনামূল্যে এবং ওপেন সোর্স, এবং সবসময় থাকবে। আপনি সবসময় একটি অনুদান দিয়ে আপনার কৃতজ্ঞতা প্রদর্শন করতে পারেন. 57 | আমাদের %2$s চ্যানেল মার্জ করুন]]> 58 | 59 | -------------------------------------------------------------------------------- /manager/app/src/main/res/values-fa/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | خانه 3 | نصب نشده است 4 | برای نصب ضربه بزنید 5 | به درستی کار می‌کند 6 | نسخه: %d 7 | برنامه های با دسترسی روت: %d 8 | ماژول‌ها: %d 9 | پشتیبانی نشده 10 | کرنل اس یو فقط هسته های gki را پشتیبانی میکند 11 | هسته 12 | نسخه برنامه 13 | اثرانگشت 14 | وضعیت SELinux 15 | غیرفعال 16 | قانونمند 17 | آزاد 18 | ناشناخته 19 | دسترسی روت 20 | فعال کردن ماژول ناموفق بود: %s 21 | غیرفعال کردن ماژول ناموفق بود: %s 22 | هیچ ماژولی نصب نشده است 23 | ماژول 24 | لغو نصب 25 | نصب 26 | نصب 27 | راه اندازی دوباره 28 | تنظیمات 29 | راه اندازی نرم 30 | راه اندازی به ریکاوری 31 | راه اندازی به بوتلودر 32 | راه اندازی به حالت دانلود 33 | راه اندازی به EDL 34 | درباره 35 | آیا مطمئنید که میخواهید ماژول %s را پاک کنید؟ 36 | %s پاک شد 37 | پاک کردن ناموفق بود: %s 38 | نسخه 39 | سازنده 40 | overlayfs موجود نیست. مازول کار نمیکند!! 41 | تازه‌سازی 42 | نمایش برنامه های سیستمی 43 | مخفی کردن برنامه های سیستمی 44 | ارسال وقایع 45 | حالت امن 46 | راه‌اندازی مجدد برای تاثیرگذاری 47 | مازول به دلیل تعارض با مجیسک غیرفعال شده اند\'s! 48 | یادگیری کرنل اس یو 49 | https://kernelsu.org/guide/what-is-kernelsu.html 50 | یاد بگیرید چگونه از کرنل اس یو و ماژول ها استفاده کنید 51 | از ما حمایت کنید 52 | KernelSU رایگان است و همیشه خواهد بود و منبع باز است. با این حال، می توانید با اهدای کمک مالی به ما نشان دهید که برایتان مهم است. 53 | 54 | Join our %2$s channel ]]> 55 | 56 | پروفایل برنامه 57 | پیش‌فرض 58 | قالب 59 | شخصی سازی شده 60 | اسم پروفایل 61 | Mount namespace 62 | اثر گرفته 63 | گلوبال 64 | تکی 65 | جداکردن ماژول ها 66 | 67 | -------------------------------------------------------------------------------- /manager/app/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ホーム 4 | 未インストール 5 | タップでインストール 6 | 動作中 7 | バージョン: %d 8 | スーパーユーザー: %d 9 | モジュール: %d 10 | 非対応 11 | KernelSU は現在、GKI カーネルにのみ対応しています 12 | カーネル 13 | バージョン 14 | フィンガープリント 15 | SELinux の状態 16 | 無効 17 | 強制実施 18 | 寛容 19 | 不明 20 | スーパーユーザー 21 | モジュールの有効化に失敗: %s 22 | モジュールの無効化に失敗: %s 23 | モジュールをインストールしていません 24 | モジュール 25 | アンインストール 26 | インストール 27 | インストール 28 | 再起動 29 | 設定 30 | ソフトリブート 31 | リカバリーへ再起動 32 | Bootloader へ再起動 33 | ダウンロードモードへ再起動 34 | EDLへ再起動 35 | アプリについて 36 | モジュール %s をアンインストールしますか? 37 | %sをアンインストールしました 38 | アンインストールに失敗: %s 39 | バージョン 40 | 制作者 41 | OverlayFS が有効でないためモジュールは動作しません! 42 | 更新 43 | システムアプリを表示 44 | システムアプリを非表示 45 | ログを送信 46 | セーフモード 47 | 再起動すると有効化されます 48 | Magisk と競合しているためモジュールは無効になっています! 49 | KernelSU の詳細 50 | https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html 51 | KernelSU のインストール方法やモジュールの使い方はこちら 52 | 支援する 53 | KernelSU は、現在も、今後も、無料のオープン ソースです。ただし、寄付をすることで私たちを気にかけていることを示すことができます。 54 | %2$s チャンネルに参加]]> 55 | アプリのプロファイル 56 | デフォルト 57 | テンプレート 58 | カスタム 59 | プロファイル名 60 | マウント名前空間 61 | 継承 62 | グローバル 63 | 分離 64 | モジュールのアンマウント 65 | グループ 66 | SELinux コンテキスト 67 | %sのアプリのプロファイルの更新をできませでした 68 | ドメイン 69 | ルール 70 | 新しいバージョン: %s が利用可能です。クリックしてダウンロードしてください 71 | アップデート 72 | ダウンロードを開始:%s 73 | 起動 74 | 強制停止 75 | 再起動 76 | SELinux ルールの更新に失敗しました: %s 77 | ケイパビリティ 78 | ダウンロードモジュール:%s 79 | このオプションを有効にすると、KernelSU はこのアプリケーションのモジュールによって変更されたファイルを復元できるようになります。 80 | デフォルトでモジュールをアンインストールする 81 | アプリプロファイルの「モジュールのマウント解除」のグローバルデフォルト値。 有効にすると、プロファイル セットを持たないアプリケーションのシステムに対するすべてのモジュール変更が削除されます。 82 | 現在の KernelSU バージョン %d はマネージャーが適切に機能するには低すぎます。 バージョン %d 以降にアップグレードしてください! 83 | -------------------------------------------------------------------------------- /manager/app/src/main/res/values-ko/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 설치되지 않음 5 | 이 곳을 눌러 설치하기 6 | 정상 작동 중 7 | 버전: %d 8 | 루트 권한: %d개 9 | 설치된 모듈: %d개 10 | 지원되지 않음 11 | KernelSU는 현재 GKI 커널만 지원합니다 12 | 커널 13 | 매니저 버전 14 | 빌드 정보 15 | SELinux 상태 16 | 비활성화됨 17 | 적용 18 | 허용 19 | 알 수 없음 20 | 슈퍼유저 21 | 모듈 활성화 실패: %s 22 | 모듈 비활성화 실패: %s 23 | 설치된 모듈 없음 24 | 모듈 25 | 삭제 26 | 설치 27 | 설치 28 | 다시 시작 29 | 설정 30 | 빠른 다시 시작 31 | 복구 모드로 다시 시작 32 | 부트로더로 다시 시작 33 | 다운로드 모드로 다시 시작 34 | EDL 모드로 다시 시작 35 | 정보 36 | %s 모듈을 삭제할까요? 37 | %s 모듈 삭제됨 38 | 모듈 삭제 실패: %s 39 | 버전 40 | 제작자 41 | overlayfs 사용 불가, 모듈을 사용할 수 없습니다! 42 | 새로고침 43 | 시스템 앱 보이기 44 | 시스템 앱 숨기기 45 | 로그 보내기 46 | 안전 모드 47 | 다시 시작하여 변경 사항 적용 48 | Magisk와의 충돌로 인해 모듈을 사용할 수 없습니다! 49 | KernelSU 알아보기 50 | KernelSU 설치 방법과 모듈 사용 방법을 확인합니다 51 | 지원이 필요합니다 52 | KernelSU는 지금도, 앞으로도 항상 무료이며 오픈 소스로 유지됩니다. 기부를 통해 여러분의 관심을 보여주세요. 53 | %2$s 채널 참가하기]]> 54 | https://kernelsu.org/guide/what-is-kernelsu.html 55 | 앱 프로필 메뉴의 \"모듈 사용 해제\" 설정에 대한 전역 기본값을 설정합니다. 활성화 시, 개별 프로필이 설정되지 않은 앱은 시스템에 대한 모듈의 모든 수정사항이 적용되지 않습니다. 56 | 다시 시작 57 | 규칙 58 | 새 버전: %s 사용 가능, 여기를 눌러서 받기 59 | 다운로드 시작: %s 60 | 강제 중지 61 | 기본값 62 | 사용자 지정 63 | 템플릿 64 | 프로필 이름 65 | 이름 공간 마운트 66 | 상속 67 | 전역 68 | 개별 69 | 사용자 그룹 70 | 모듈 사용 해제 71 | SELinux 컨텍스트 72 | 권한 73 | %s에 대한 앱 프로필 업데이트 실패 74 | 기본값으로 모듈 사용 해제 75 | 이 옵션이 활성화되면, KernelSU는 이 애플리케이션에 대한 모듈의 모든 수정사항을 복구합니다. 76 | 업데이트 77 | 모듈 받는 중: %s 78 | 도메인 79 | 실행 80 | 다음 앱에 대한 SELinux 규칙 업데이트 실패: %s 81 | -------------------------------------------------------------------------------- /manager/app/src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 主页 4 | 未安装 5 | 点击安装 6 | 工作中 7 | 版本: %d 8 | 超级用户数:%d 9 | 不支持 10 | KernelSU 现在只支持 GKI 内核 11 | 内核版本 12 | 管理器版本 13 | 系统指纹 14 | SELinux 状态 15 | 被禁用 16 | 强制执行 17 | 宽容模式 18 | 未知 19 | 超级用户 20 | 无法启用模块: %s 21 | 无法禁用模块: %s 22 | 没有安装模块 23 | 模块 24 | 卸载 25 | 安装 26 | 安装 27 | 重启 28 | 设置 29 | 软重启 30 | 重启到 Recovery 31 | 重启到 BootLoader 32 | 重启到 Download 33 | 重启到 EDL 34 | 关于 35 | 确定要卸载模块 %s 吗? 36 | %s 已卸载 37 | 卸载失败: %s 38 | 版本 39 | 作者 40 | 内核不支持 overlayfs,模块功能无法运作! 41 | 刷新 42 | 显示系统应用 43 | 隐藏系统应用 44 | 发送日志 45 | 安全模式 46 | 重启生效 47 | 所有模块已被禁用,因为它与 Magisk 的模块系统有冲突! 48 | 模块数:%d 49 | 了解 KernelSU 50 | https://kernelsu.org/zh_CN/guide/what-is-kernelsu.html 51 | 了解如何安装 KernelSU 以及如何开发模块 52 | 支持开发 53 | KernelSU 将保持免费和开源,向开发者捐赠以表示支持。 54 | 加入我们的 %2$s 频道
加入我们的 QQ 频道]]>
55 | 默认 56 | 模版 57 | 自定义 58 | 名称 59 | 命名空间 60 | 继承 61 | 全局 62 | 私有 63 | 64 | 权能 65 | SELinux 66 | 卸载模块 67 | 为 %s 更新 App Profile 失败 68 | 当前 KernelSU 版本 %d 过低,管理器无法正常工作,请升级内核 KernelSU 版本至 %d 或以上! 69 | 默认卸载模块 70 | App Profile 中\"卸载模块\"的全局默认值,如果启用,将会为没有设置 Profile 的应用移除所有模块针对系统的修改。 71 | 启用后将允许 KernelSU 为本应用还原被模块修改过的文件。 72 | 73 | 规则 74 | 更新 75 | 正在下载模块:%s 76 | 开始下载:%s 77 | 发现新版本:%s,点击下载 78 | 启动 79 | 强制停止 80 | 重新启动 81 | 为:%s 更新翻译失败 82 |
83 | -------------------------------------------------------------------------------- /manager/app/src/main/res/values-zh-rHK/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 首頁 4 | 未安裝 5 | 按一下以安裝 6 | 正在處理 7 | 版本:%d 8 | 超級使用者:%d 個 9 | 模組:%d 個 10 | 不支援 11 | KernelSU 現在僅支援 GKI 核心 12 | 核心 13 | 管理員版本 14 | 指紋 15 | SELinux 狀態 16 | 已停用 17 | 強制 18 | 寬鬆 19 | 未知 20 | 超級使用者 21 | 無法啟用模組:%s 22 | 無法停用模組:%s 23 | 尚未安裝模組 24 | 模組 25 | 解除安裝 26 | 安裝 27 | 安裝 28 | 重新啟動 29 | 設定 30 | 軟啟動 31 | 重新啟動至 Recovery 32 | 重新啟動至 Bootloader 33 | 重新啟動至 Download 34 | 重新啟動至 EDL 35 | 關於 36 | 您確定要解除安裝模組「%s」嗎? 37 | 「%s」已解除安裝 38 | 無法解除安裝:%s 39 | 版本 40 | 作者 41 | OverlayFS 無法使用,模組無法正常運作! 42 | 重新整理 43 | 顯示系統應用程式 44 | 隱藏系統應用程式 45 | 傳送記錄 46 | 安全模式 47 | 重新啟動以生效 48 | 模組已停用,因其與 Magisk 的模組存在衝突! 49 | 深入瞭解 KernelSU 50 | https://kernelsu.org/zh_TW/guide/what-is-kernelsu.html 51 | 瞭解如何安裝 KernelSU 和使用模組 52 | 支持我們 53 | KernelSU 是免費且開源的,您可以透過捐贈來向我們展示您對我們的關心。 54 | 加入我們的 %2$s 頻道]]> 55 | 預設 56 | 設定檔名稱 57 | 範本 58 | 繼承 59 | 全域 60 | 功能 61 | 卸載模組 62 | 無法更新 %s 應用程式設定檔 63 | 規則 64 | 目前 KernelSU 版本 %d 過低,管理員無法正常運作。請升級至 %d 或更高版本! 65 | 應用程式設定檔中「卸載模組」的全域預設值。如果啟用,將會為沒有設定檔的應用程式移除所有模組對系統的修改。 66 | 啟用此選項將允許 KernelSU 為這個應用程式還原任何被模組修改過的檔案。 67 | 網域 68 | 更新 69 | 自訂 70 | 掛載命名空間 71 | 個人 72 | 群組 73 | SELinux 內容 74 | 預設卸載模組 75 | 正在下載模組:%s 76 | 開始下載:%s 77 | 新版本:%s 已可供使用,按一下以下載 78 | 啟動 79 | 強制停止 80 | 重新啟動 81 | 無法為 %s 更新 SELinux 規則 82 | 83 | -------------------------------------------------------------------------------- /manager/app/src/main/res/values-zh-rTW/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 首頁 4 | 未安裝 5 | 按一下以安裝 6 | 正在處理 7 | KernelSU 版本:%d 8 | 已授權 Root:%d 個 9 | 不支援 10 | KernelSU 現在僅支援 GKI 核心 11 | 核心 12 | 管理器版本 13 | 指紋 14 | SELinux 狀態 15 | 已停用 16 | 強制 17 | 寬鬆 18 | 未知 19 | Root 授權 20 | 無法啟用模組:%s 21 | 無法停用模組:%s 22 | 尚未安裝模組 23 | 模組 24 | 解除安裝 25 | 安裝 26 | 安裝 27 | 重新啟動 28 | 設定 29 | 軟重新啟動 30 | 重新啟動至 Recovery 31 | 重新啟動至 Bootloader 32 | 重新啟動至 Download 33 | 重新啟動至 EDL 34 | 關於 35 | 您確定要解除安裝模組「%s」嗎? 36 | 「%s」已解除安裝 37 | 無法解除安裝:%s 38 | 版本 39 | 作者 40 | OverlayFS 無法使用,模組無法正常運作! 41 | 重新整理 42 | 顯示系統應用程式 43 | 隱藏系統應用程式 44 | 傳送記錄 45 | 安全模式 46 | 重新啟動以生效 47 | 模組已停用,因其與 Magisk 的模組存在衝突! 48 | 已安裝模組:%d 個 49 | 深入瞭解 KernelSU 50 | https://kernelsu.org/zh_TW/guide/what-is-kernelsu.html 51 | 瞭解如何安裝 KernelSU 以及如何開發模組 52 | 支持開發 53 | KernelSU 將保持免費和開源,您可以考慮向開發人員捐贈以表示支持。 54 | 加入我們的 %2$s 頻道]]> 55 | 解除安裝模組 56 | 無法更新 %s 應用程式設定檔 57 | 目前安裝的 KernelSU 版本 %d 過低,管理器無法正常工作,請升級核心 KernelSU 版本至 %d 或以上! 58 | 預設解除安裝模組 59 | 應用程式設定檔中「解除安裝模組」的全域預設值,如果啟用,將會為沒有設定檔的應用程式移除所有模組針對系統的修改。 60 | 啟用後將允許 KernelSU 為本應用程式還原被模組修改過的檔案。 61 | 預設 62 | 自訂 63 | 功能 64 | 規則 65 | 正在下載模組:%s 66 | 重新啟動 67 | 範本 68 | 設定檔名稱 69 | 掛載命名空間 70 | 繼承 71 | 全域 72 | 個人 73 | 群組 74 | SELinux 環境 75 | 網域 76 | 更新 77 | 開始下載:%s 78 | 發現新版本:%s 已可供使用,按一下即可下載 79 | 啟動 80 | 強制停止 81 | 無法為 %s 更新 SELinux 82 | 83 | -------------------------------------------------------------------------------- /manager/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /manager/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /manager/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /manager/app/src/main/res/xml/filepaths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /manager/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.android.build.api.dsl.ApplicationDefaultConfig 2 | import com.android.build.api.dsl.CommonExtension 3 | import com.android.build.gradle.api.AndroidBasePlugin 4 | import java.io.ByteArrayOutputStream 5 | 6 | plugins { 7 | alias(libs.plugins.agp.app) apply false 8 | alias(libs.plugins.agp.lib) apply false 9 | alias(libs.plugins.kotlin) apply false 10 | alias(libs.plugins.lsplugin.cmaker) 11 | } 12 | 13 | cmaker { 14 | default { 15 | arguments.addAll( 16 | arrayOf( 17 | "-DANDROID_STL=c++_static", 18 | ) 19 | ) 20 | val flags = arrayOf( 21 | "-Wno-gnu-string-literal-operator-template", 22 | "-Wno-c++2b-extensions", 23 | ) 24 | cFlags.addAll(flags) 25 | cppFlags.addAll(flags) 26 | abiFilters("arm64-v8a", "x86_64") 27 | } 28 | buildTypes { 29 | if (it.name == "release") { 30 | arguments += "-DDEBUG_SYMBOLS_PATH=${buildDir.absolutePath}/symbols" 31 | } 32 | } 33 | } 34 | 35 | val androidMinSdkVersion = 26 36 | val androidTargetSdkVersion = 33 37 | val androidCompileSdkVersion = 33 38 | val androidBuildToolsVersion = "33.0.2" 39 | val androidCompileNdkVersion = "25.2.9519653" 40 | val androidSourceCompatibility = JavaVersion.VERSION_17 41 | val androidTargetCompatibility = JavaVersion.VERSION_17 42 | val managerVersionCode by extra(getVersionCode()) 43 | val managerVersionName by extra(getVersionName()) 44 | 45 | tasks.register("clean") { 46 | delete(rootProject.buildDir) 47 | } 48 | 49 | fun getGitCommitCount(): Int { 50 | val out = ByteArrayOutputStream() 51 | exec { 52 | commandLine("git", "rev-list", "--count", "HEAD") 53 | standardOutput = out 54 | } 55 | return out.toString().trim().toInt() 56 | } 57 | 58 | fun getGitDescribe(): String { 59 | val out = ByteArrayOutputStream() 60 | exec { 61 | commandLine("git", "describe", "--tags", "--always") 62 | standardOutput = out 63 | } 64 | return out.toString().trim() 65 | } 66 | 67 | fun getVersionCode(): Int { 68 | val commitCount = getGitCommitCount() 69 | val major = 1 70 | return major * 10000 + commitCount + 200 71 | } 72 | 73 | fun getVersionName(): String { 74 | return getGitDescribe() 75 | } 76 | 77 | subprojects { 78 | plugins.withType(AndroidBasePlugin::class.java) { 79 | extensions.configure(CommonExtension::class.java) { 80 | compileSdk = androidCompileSdkVersion 81 | ndkVersion = androidCompileNdkVersion 82 | buildToolsVersion = androidBuildToolsVersion 83 | 84 | defaultConfig { 85 | minSdk = androidMinSdkVersion 86 | if (this is ApplicationDefaultConfig) { 87 | targetSdk = androidTargetSdkVersion 88 | versionCode = managerVersionCode 89 | versionName = managerVersionName 90 | } 91 | } 92 | 93 | lint { 94 | abortOnError = true 95 | checkReleaseBuilds = false 96 | } 97 | 98 | compileOptions { 99 | sourceCompatibility = androidSourceCompatibility 100 | targetCompatibility = androidTargetCompatibility 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /manager/gradle.properties: -------------------------------------------------------------------------------- 1 | android.experimental.enableNewResourceShrinker.preciseShrinking=true 2 | android.enableAppCompileTimeRClass=true 3 | android.useAndroidX=true 4 | -------------------------------------------------------------------------------- /manager/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.0.1" 3 | kotlin = "1.8.10" 4 | ksp = "1.8.10-1.0.9" 5 | compose-bom = "2023.05.01" 6 | lifecycle = "2.6.1" 7 | accompanist = "0.30.0" 8 | navigation = "2.5.3" 9 | compose-destination = "1.9.42-beta" 10 | libsu = "5.0.5" 11 | sheets-compose-dialogs = "1.2.0" 12 | 13 | [plugins] 14 | agp-app = { id = "com.android.application", version.ref = "agp" } 15 | agp-lib = { id = "com.android.library", version.ref = "agp" } 16 | kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 17 | ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } 18 | lsplugin-apksign = { id = "org.lsposed.lsplugin.apksign", version = "1.1" } 19 | lsplugin-cmaker = { id = "org.lsposed.lsplugin.cmaker", version = "1.1" } 20 | 21 | [libraries] 22 | androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version = "1.7.1" } 23 | androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" } 24 | 25 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } 26 | androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" } 27 | androidx-compose-material = { group = "androidx.compose.material", name = "material" } 28 | androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } 29 | androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } 30 | androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } 31 | androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } 32 | androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } 33 | 34 | androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" } 35 | androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" } 36 | androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version = "lifecycle" } 37 | 38 | com-google-accompanist-drawablepainter = { group = "com.google.accompanist", name = "accompanist-drawablepainter", version.ref = "accompanist" } 39 | com-google-accompanist-navigation-animation = { group = "com.google.accompanist", name = "accompanist-navigation-animation", version.ref = "accompanist" } 40 | com-google-accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" } 41 | 42 | com-github-topjohnwu-libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" } 43 | com-github-topjohnwu-libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" } 44 | 45 | dev-rikka-rikkax-parcelablelist = { module = "dev.rikka.rikkax.parcelablelist:parcelablelist", version = "2.0.1" } 46 | 47 | io-coil-kt-coil-compose = { group = "io.coil-kt", name = "coil-compose", version = "2.3.0" } 48 | 49 | kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.7.1" } 50 | 51 | me-zhanghai-android-appiconloader-coil = { group = "me.zhanghai.android.appiconloader", name = "appiconloader-coil", version = "1.5.0" } 52 | 53 | compose-destinations-animations-core = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "compose-destination" } 54 | compose-destinations-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "compose-destination" } 55 | 56 | sheet-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "core", version.ref = "sheets-compose-dialogs"} 57 | sheet-compose-dialogs-list = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "list", version.ref = "sheets-compose-dialogs"} 58 | sheet-compose-dialogs-input = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "input", version.ref = "sheets-compose-dialogs"} 59 | -------------------------------------------------------------------------------- /manager/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/manager/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /manager/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 3 | distributionPath=wrapper/dists 4 | zipStorePath=wrapper/dists 5 | zipStoreBase=GRADLE_USER_HOME 6 | -------------------------------------------------------------------------------- /manager/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /manager/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 2 | 3 | pluginManagement { 4 | repositories { 5 | gradlePluginPortal() 6 | google() 7 | mavenCentral() 8 | } 9 | } 10 | 11 | dependencyResolutionManagement { 12 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 13 | repositories { 14 | google() 15 | mavenCentral() 16 | maven("https://jitpack.io") 17 | } 18 | } 19 | 20 | rootProject.name = "KernelSU" 21 | include(":app") 22 | -------------------------------------------------------------------------------- /manager/sign.example.properties: -------------------------------------------------------------------------------- 1 | KEYSTORE_FILE= 2 | KEYSTORE_PASSWORD= 3 | KEY_ALIAS= 4 | KEY_PASSWORD= 5 | -------------------------------------------------------------------------------- /scripts/abi_gki_all.py: -------------------------------------------------------------------------------- 1 | from xml.dom.minidom import parse 2 | import xml.dom.minidom 3 | import sys 4 | 5 | 6 | DOMTree = xml.dom.minidom.parse(sys.argv[1]) 7 | symbols = DOMTree.getElementsByTagName("elf-symbol") 8 | print("[abi_symbol_list]") 9 | for symbol in symbols: 10 | print(" " + symbol.getAttribute("name")) 11 | -------------------------------------------------------------------------------- /scripts/add_device_handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | import os 4 | 5 | 6 | def main(): 7 | assert len(sys.argv) == 2 8 | file_name = sys.argv[1] 9 | github = "https://github.com/" 10 | issue_content = os.environ["ISSUE_CONTENT"] 11 | lines = issue_content.split("\n\n") 12 | assert len(lines) == 6 13 | url = lines[1] 14 | print(url) 15 | device = lines[3] 16 | print(device) 17 | code_of_conduct = lines[5] 18 | print(code_of_conduct) 19 | assert code_of_conduct.find("[X]") > 0 20 | tmp = url.removesuffix("/").replace(github, "").split("/") 21 | print(tmp) 22 | assert len(tmp) == 2 23 | maintainer = tmp[0] 24 | print(maintainer) 25 | maintainer_link = "%s%s" % (github, maintainer) 26 | print(maintainer_link) 27 | kernel_name = tmp[1] 28 | print(kernel_name) 29 | kernel_link = "%s%s/%s" % (github, maintainer, kernel_name) 30 | print(kernel_link) 31 | with open(file_name, "r") as f: 32 | data = json.loads(f.read()) 33 | data.append( 34 | { 35 | "maintainer": maintainer, 36 | "maintainer_link": maintainer_link, 37 | "kernel_name": kernel_name, 38 | "kernel_link": kernel_link, 39 | "devices": device, 40 | } 41 | ) 42 | os.remove(file_name) 43 | with open(file_name, "w") as f: 44 | f.write(json.dumps(data, indent=4)) 45 | os.system("echo success=true >> $GITHUB_OUTPUT") 46 | os.system("echo device=%s >> $GITHUB_OUTPUT" % device) 47 | 48 | 49 | if __name__ == "__main__": 50 | main() -------------------------------------------------------------------------------- /scripts/bin2c.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import argparse 4 | import os 5 | import re 6 | 7 | line_size = 80 8 | 9 | 10 | def bin2c(filename, varname='data'): 11 | if not os.path.isfile(filename): 12 | print('File "%s" is not found!' % filename) 13 | return '' 14 | if not re.match('[a-zA-Z_][a-zA-Z0-9_]*', varname): 15 | print('Invalid variable name "%s"' % varname) 16 | return 17 | with open(filename, 'rb') as in_file: 18 | data = in_file.read() 19 | # limit the line length 20 | byte_len = 6 # '0x00, ' 21 | out = 'unsigned int %s_size = %d;\n' \ 22 | 'const char %s[%d] = {\n' % (varname, len(data), varname, len(data)) 23 | line = '' 24 | for byte in data: 25 | line += '0x%02x, ' % byte 26 | if len(line) + 4 + byte_len >= line_size: 27 | out += ' ' * 4 + line + '\n' 28 | line = '' 29 | # add the last line 30 | if len(line) + 4 + byte_len < line_size: 31 | out += ' ' * 4 + line + '\n' 32 | # strip the last comma 33 | out = out.rstrip(', \n') + '\n' 34 | out += '};' 35 | return out 36 | 37 | 38 | def main(): 39 | """ Main func """ 40 | parser = argparse.ArgumentParser() 41 | parser.add_argument( 42 | 'filename', help='filename to convert to C array') 43 | parser.add_argument( 44 | 'varname', nargs='?', help='variable name', default='data') 45 | args = parser.parse_args() 46 | # print out the data 47 | print(bin2c(args.filename, args.varname)) 48 | 49 | 50 | if __name__ == '__main__': 51 | main() 52 | -------------------------------------------------------------------------------- /scripts/check_v2.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/8. 3 | // 4 | 5 | // Credits: https://github.com/brevent/genuine/blob/master/src/main/jni/apk-sign-v2.c 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define MAIN 12 | 13 | #ifdef MAIN 14 | #include 15 | #else 16 | 17 | #include "common.h" 18 | #include "openat.h" 19 | 20 | #endif 21 | 22 | static bool isApkSigBlock42(const char *buffer) { 23 | // APK Sig Block 42 24 | return *buffer == 'A' 25 | && *++buffer == 'P' 26 | && *++buffer == 'K' 27 | && *++buffer == ' ' 28 | && *++buffer == 'S' 29 | && *++buffer == 'i' 30 | && *++buffer == 'g' 31 | && *++buffer == ' ' 32 | && *++buffer == 'B' 33 | && *++buffer == 'l' 34 | && *++buffer == 'o' 35 | && *++buffer == 'c' 36 | && *++buffer == 'k' 37 | && *++buffer == ' ' 38 | && *++buffer == '4' 39 | && *++buffer == '2'; 40 | } 41 | 42 | int checkSignature(const char *path) { 43 | unsigned char buffer[0x11] = {0}; 44 | uint32_t size4; 45 | uint64_t size8, size_of_block; 46 | 47 | #ifdef DEBUG 48 | LOGI("check signature for %s", path); 49 | #endif 50 | 51 | int sign = -1; 52 | int fd = (int) openat(AT_FDCWD, path, O_RDONLY); 53 | #ifdef DEBUG_OPENAT 54 | LOGI("openat %s returns %d", path, fd); 55 | #endif 56 | if (fd < 0) { 57 | return sign; 58 | } 59 | 60 | sign = 1; 61 | // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD) 62 | for (int i = 0;; ++i) { 63 | unsigned short n; 64 | lseek(fd, -i - 2, SEEK_END); 65 | read(fd, &n, 2); 66 | if (n == i) { 67 | lseek(fd, -22, SEEK_CUR); 68 | read(fd, &size4, 4); 69 | if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) { 70 | #ifdef MAIN 71 | if (i > 0) { 72 | printf("warning: comment length is %d\n", i); 73 | } 74 | #endif 75 | break; 76 | } 77 | } 78 | if (i == 0xffff) { 79 | #ifdef MAIN 80 | printf("error: cannot find eocd\n"); 81 | #endif 82 | goto clean; 83 | } 84 | } 85 | 86 | lseek(fd, 12, SEEK_CUR); 87 | // offset 88 | read(fd, &size4, 0x4); 89 | lseek(fd, (off_t) (size4 - 0x18), SEEK_SET); 90 | 91 | read(fd, &size8, 0x8); 92 | read(fd, buffer, 0x10); 93 | if (!isApkSigBlock42((char *) buffer)) { 94 | goto clean; 95 | } 96 | 97 | lseek(fd, (off_t) (size4 - (size8 + 0x8)), SEEK_SET); 98 | read(fd, &size_of_block, 0x8); 99 | if (size_of_block != size8) { 100 | goto clean; 101 | } 102 | 103 | for (;;) { 104 | uint32_t id; 105 | uint32_t offset; 106 | read(fd, &size8, 0x8); // sequence length 107 | if (size8 == size_of_block) { 108 | break; 109 | } 110 | read(fd, &id, 0x4); // id 111 | offset = 4; 112 | #ifdef MAIN 113 | // printf("id: 0x%08x\n", id); 114 | #endif 115 | if ((id ^ 0xdeadbeefu) == 0xafa439f5u || (id ^ 0xdeadbeefu) == 0x2efed62f) { 116 | read(fd, &size4, 0x4); // signer-sequence length 117 | read(fd, &size4, 0x4); // signer length 118 | read(fd, &size4, 0x4); // signed data length 119 | offset += 0x4 * 3; 120 | 121 | read(fd, &size4, 0x4); // digests-sequence length 122 | lseek(fd, (off_t) (size4), SEEK_CUR);// skip digests 123 | offset += 0x4 + size4; 124 | 125 | read(fd, &size4, 0x4); // certificates length 126 | read(fd, &size4, 0x4); // certificate length 127 | offset += 0x4 * 2; 128 | #ifdef MAIN 129 | int hash = 1; 130 | signed char c; 131 | for (unsigned i = 0; i < size4; ++i) { 132 | read(fd, &c, 0x1); 133 | hash = 31 * hash + c; 134 | } 135 | offset += size4; 136 | // printf(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u); 137 | printf("0x%04x 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u); 138 | #else 139 | #if defined(GENUINE_SIZE) && defined(GENUINE_HASH) 140 | if (size4 == GENUINE_SIZE) { 141 | int hash = 1; 142 | signed char c; 143 | for (unsigned i = 0; i < size4; ++i) { 144 | read(fd, &c, 0x1); 145 | hash = 31 * hash + c; 146 | } 147 | offset += size4; 148 | if ((((unsigned) hash) ^ 0x14131211u) == GENUINE_HASH) { 149 | sign = 0; 150 | break; 151 | } 152 | } 153 | #else 154 | sign = 0; 155 | break; 156 | #endif 157 | #endif 158 | } 159 | lseek(fd, (off_t) (size8 - offset), SEEK_CUR); 160 | } 161 | 162 | clean: 163 | close(fd); 164 | 165 | return sign; 166 | } 167 | 168 | #ifdef MAIN 169 | int main(int argc, char **argv) { 170 | if (argc > 1) { 171 | checkSignature(argv[1]); 172 | } 173 | } 174 | #endif 175 | -------------------------------------------------------------------------------- /userspace/ksud/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .cargo/ -------------------------------------------------------------------------------- /userspace/ksud/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ksud" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.65" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1.0.68" 11 | clap = { version = "4.0.32", features = ["derive"] } 12 | const_format = "0.2.30" 13 | zip = "0.6.3" 14 | zip-extensions = "0.6.1" 15 | java-properties = "1.4.1" 16 | log = "0.4.17" 17 | env_logger = "0.10.0" 18 | serde = { version = "1.0" } 19 | serde_json = "1.0" 20 | regex = "1.5.4" 21 | encoding = "0.2.33" 22 | retry = "2.0.0" 23 | humansize = "2.0.0" 24 | libc = "0.2" 25 | extattr = "1.0.0" 26 | jwalk = "0.8.1" 27 | is_executable = "1.0.1" 28 | nom = "7" 29 | derive-new = "0.5" 30 | rust-embed = { version = "6.4.2", features = [ 31 | "debug-embed", 32 | "compression", # must clean build after updating binaries 33 | ] } 34 | which = "4.2.2" 35 | getopts = "0.2.21" 36 | 37 | [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] 38 | sys-mount = { git = "https://github.com/tiann/sys-mount", branch = "loopfix" } 39 | # some android specific dependencies which compiles under unix are also listed here for convenience of coding 40 | android-properties = { version = "0.2.2", features = ["bionic-deprecated"] } 41 | procfs = "0.15" 42 | 43 | [target.'cfg(target_os = "android")'.dependencies] 44 | android_logger = "0.13" 45 | 46 | [profile.release] 47 | strip = true 48 | opt-level = "z" 49 | lto = true 50 | -------------------------------------------------------------------------------- /userspace/ksud/bin/aarch64/busybox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/userspace/ksud/bin/aarch64/busybox -------------------------------------------------------------------------------- /userspace/ksud/bin/aarch64/resetprop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/userspace/ksud/bin/aarch64/resetprop -------------------------------------------------------------------------------- /userspace/ksud/bin/x86_64/busybox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/userspace/ksud/bin/x86_64/busybox -------------------------------------------------------------------------------- /userspace/ksud/bin/x86_64/resetprop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MlgmXyysd/KernelSU_Debug/b59ebb0d9c0a1d950c5f657ad035713dc6b3d585/userspace/ksud/bin/x86_64/resetprop -------------------------------------------------------------------------------- /userspace/ksud/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::Path; 5 | use std::process::Command; 6 | 7 | fn get_git_version() -> Result<(u32, String), std::io::Error> { 8 | let output = Command::new("git") 9 | .args(["rev-list", "--count", "HEAD"]) 10 | .output()?; 11 | 12 | let output = output.stdout; 13 | let version_code = String::from_utf8(output).expect("Failed to read git count stdout"); 14 | let version_code: u32 = version_code 15 | .trim() 16 | .parse() 17 | .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Failed to parse git count"))?; 18 | let version_code = 10000 + 200 + version_code; // For historical reasons 19 | 20 | let version_name = String::from_utf8( 21 | Command::new("git") 22 | .args(["describe", "--tags", "--always"]) 23 | .output()? 24 | .stdout, 25 | ) 26 | .map_err(|_| { 27 | std::io::Error::new( 28 | std::io::ErrorKind::Other, 29 | "Failed to read git describe stdout", 30 | ) 31 | })?; 32 | let version_name = version_name.trim_start_matches('v').to_string(); 33 | Ok((version_code, version_name)) 34 | } 35 | 36 | fn main() { 37 | let (code, name) = match get_git_version() { 38 | Ok((code, name)) => (code, name), 39 | Err(_) => { 40 | // show warning if git is not installed 41 | println!("cargo:warning=Failed to get git version, using 0.0.0"); 42 | (0, "0.0.0".to_string()) 43 | } 44 | }; 45 | let out_dir = env::var("OUT_DIR").expect("Failed to get $OUT_DIR"); 46 | let out_dir = Path::new(&out_dir); 47 | File::create(Path::new(out_dir).join("VERSION_CODE")) 48 | .expect("Failed to create VERSION_CODE") 49 | .write_all(code.to_string().as_bytes()) 50 | .expect("Failed to write VERSION_CODE"); 51 | 52 | File::create(Path::new(out_dir).join("VERSION_NAME")) 53 | .expect("Failed to create VERSION_NAME") 54 | .write_all(name.trim().as_bytes()) 55 | .expect("Failed to write VERSION_NAME"); 56 | } 57 | -------------------------------------------------------------------------------- /userspace/ksud/src/apk_sign.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{ensure, Result}; 2 | use std::io::{Read, Seek, SeekFrom}; 3 | 4 | pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> { 5 | let mut buffer = [0u8; 0x10]; 6 | let mut size4 = [0u8; 4]; 7 | let mut size8 = [0u8; 8]; 8 | let mut size_of_block = [0u8; 8]; 9 | 10 | let mut f = std::fs::File::open(apk)?; 11 | 12 | let mut i = 0; 13 | loop { 14 | let mut n = [0u8; 2]; 15 | f.seek(SeekFrom::End(-i - 2))?; 16 | f.read_exact(&mut n)?; 17 | 18 | let n = u16::from_le_bytes(n); 19 | if i64::from(n) == i { 20 | f.seek(SeekFrom::Current(-22))?; 21 | f.read_exact(&mut size4)?; 22 | 23 | if u32::from_le_bytes(size4) ^ 0xcafe_babe_u32 == 0xccfb_f1ee_u32 { 24 | if i > 0 { 25 | println!("warning: comment length is {i}"); 26 | } 27 | break; 28 | } 29 | } 30 | 31 | ensure!(n != 0xffff, "not a zip file"); 32 | 33 | i += 1; 34 | } 35 | 36 | f.seek(SeekFrom::Current(12))?; 37 | // offset 38 | f.read_exact(&mut size4)?; 39 | f.seek(SeekFrom::Start(u64::from(u32::from_le_bytes(size4)) - 0x18))?; 40 | 41 | f.read_exact(&mut size8)?; 42 | f.read_exact(&mut buffer)?; 43 | 44 | ensure!(&buffer == b"APK Sig Block 42", "Can not found sig block"); 45 | 46 | let pos = u64::from(u32::from_le_bytes(size4)) - (u64::from_le_bytes(size8) + 0x8); 47 | f.seek(SeekFrom::Start(pos))?; 48 | f.read_exact(&mut size_of_block)?; 49 | 50 | ensure!(size_of_block == size8, "not a signed apk"); 51 | 52 | loop { 53 | let mut id = [0u8; 4]; 54 | let offset = 4u32; 55 | 56 | f.read_exact(&mut size8)?; // sequence length 57 | if size8 == size_of_block { 58 | break; 59 | } 60 | 61 | f.read_exact(&mut id)?; // id 62 | 63 | let id = u32::from_le_bytes(id); 64 | if (id ^ 0xdead_beef_u32) == 0xafa4_39f5_u32 || (id ^ 0xdead_beef_u32) == 0x2efe_d62f_u32 { 65 | f.read_exact(&mut size4)?; // signer-sequence length 66 | f.read_exact(&mut size4)?; // signer length 67 | f.read_exact(&mut size4)?; // signed data length 68 | // offset += 0x4 * 3; 69 | 70 | f.read_exact(&mut size4)?; // digests-sequcence length 71 | let pos = u32::from_le_bytes(size4); 72 | f.seek(SeekFrom::Current(i64::from(pos)))?; 73 | // offset += 0x4 + pos; 74 | 75 | f.read_exact(&mut size4)?; // certificates length 76 | f.read_exact(&mut size4)?; // certificate length 77 | // offset += 0x4 * 2; 78 | 79 | let mut hash = 1i32; 80 | let mut c = [0u8; 1]; 81 | 82 | let j = u32::from_le_bytes(size4); 83 | for _ in 0..j { 84 | f.read_exact(&mut c)?; 85 | hash = hash.wrapping_mul(31).wrapping_add(i32::from(c[0] as i8)); 86 | } 87 | 88 | // offset += j; 89 | 90 | let out_size = j; 91 | let out_hash = (hash as u32) ^ 0x1413_1211_u32; 92 | 93 | return Ok((out_size, out_hash)); 94 | } 95 | 96 | f.seek(SeekFrom::Current( 97 | i64::from_le_bytes(size8) - i64::from(offset), 98 | ))?; 99 | } 100 | 101 | Err(anyhow::anyhow!("Unknown error")) 102 | } 103 | -------------------------------------------------------------------------------- /userspace/ksud/src/assets.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use const_format::concatcp; 3 | use rust_embed::RustEmbed; 4 | 5 | use crate::{defs::BINARY_DIR, utils}; 6 | 7 | pub const RESETPROP_PATH: &str = concatcp!(BINARY_DIR, "resetprop"); 8 | pub const BUSYBOX_PATH: &str = concatcp!(BINARY_DIR, "busybox"); 9 | 10 | #[cfg(target_arch = "aarch64")] 11 | #[derive(RustEmbed)] 12 | #[folder = "bin/aarch64"] 13 | struct Asset; 14 | 15 | #[cfg(target_arch = "x86_64")] 16 | #[derive(RustEmbed)] 17 | #[folder = "bin/x86_64"] 18 | struct Asset; 19 | 20 | pub fn ensure_binaries() -> Result<()> { 21 | for file in Asset::iter() { 22 | utils::ensure_binary( 23 | format!("{BINARY_DIR}{file}"), 24 | &Asset::get(&file).unwrap().data, 25 | )? 26 | } 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /userspace/ksud/src/debug.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Ok, Result}; 2 | use std::{ 3 | path::{Path, PathBuf}, 4 | process::Command, 5 | }; 6 | 7 | use crate::apk_sign::get_apk_signature; 8 | 9 | const KERNEL_PARAM_PATH: &str = "/sys/module/kernelsu"; 10 | 11 | fn read_u32(path: &PathBuf) -> Result { 12 | let content = std::fs::read_to_string(path)?; 13 | let content = content.trim(); 14 | let content = content.parse::()?; 15 | Ok(content) 16 | } 17 | 18 | fn set_kernel_param(size: u32, hash: u32) -> Result<()> { 19 | let kernel_param_path = Path::new(KERNEL_PARAM_PATH).join("parameters"); 20 | let expeced_size_path = kernel_param_path.join("ksu_expected_size"); 21 | let expeced_hash_path = kernel_param_path.join("ksu_expected_hash"); 22 | 23 | println!( 24 | "before size: {:#x} hash: {:#x}", 25 | read_u32(&expeced_size_path)?, 26 | read_u32(&expeced_hash_path)? 27 | ); 28 | 29 | std::fs::write(&expeced_size_path, size.to_string())?; 30 | std::fs::write(&expeced_hash_path, hash.to_string())?; 31 | 32 | println!( 33 | "after size: {:#x} hash: {:#x}", 34 | read_u32(&expeced_size_path)?, 35 | read_u32(&expeced_hash_path)? 36 | ); 37 | 38 | Ok(()) 39 | } 40 | 41 | fn get_apk_path(package_name: &str) -> Result { 42 | // `cmd package path` is not available below Android 9 43 | let output = Command::new("pm").args(["path", package_name]).output()?; 44 | 45 | // package:/data/app//base.apk 46 | let output = String::from_utf8_lossy(&output.stdout); 47 | let path = output.trim().replace("package:", ""); 48 | Ok(path) 49 | } 50 | 51 | pub fn set_manager(pkg: &str) -> Result<()> { 52 | let path = get_apk_path(pkg).with_context(|| format!("{pkg} does not exist!"))?; 53 | let sign = get_apk_signature(&path)?; 54 | set_kernel_param(sign.0, sign.1)?; 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /userspace/ksud/src/defs.rs: -------------------------------------------------------------------------------- 1 | use const_format::concatcp; 2 | 3 | pub const ADB_DIR: &str = "/data/adb/"; 4 | pub const WORKING_DIR: &str = concatcp!(ADB_DIR, "ksu/"); 5 | pub const BINARY_DIR: &str = concatcp!(WORKING_DIR, "bin/"); 6 | pub const LOG_DIR: &str = concatcp!(WORKING_DIR, "log/"); 7 | 8 | pub const PROFILE_DIR: &str = concatcp!(WORKING_DIR, "profile/"); 9 | pub const PROFILE_SELINUX_DIR: &str = concatcp!(PROFILE_DIR, "selinux/"); 10 | pub const PROFILE_TEMPLATE_DIR: &str = concatcp!(PROFILE_DIR, "templates/"); 11 | 12 | pub const KSURC_PATH: &str = concatcp!(WORKING_DIR, ".ksurc"); 13 | pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud"); 14 | 15 | #[cfg(target_os = "android")] 16 | pub const DAEMON_LINK_PATH: &str = concatcp!(BINARY_DIR, "ksud"); 17 | 18 | pub const COMMON_DIR: &str = concatcp!(ADB_DIR, "common/"); 19 | 20 | pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE")); 21 | pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_NAME")); 22 | -------------------------------------------------------------------------------- /userspace/ksud/src/event.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use log::{info, warn}; 3 | use std::{path::Path, path::PathBuf}; 4 | 5 | use crate::{ 6 | assets, defs, restorecon, 7 | utils::{self, ensure_dir_exists}, 8 | }; 9 | 10 | pub fn on_post_data_fs() -> Result<()> { 11 | crate::ksu::report_post_fs_data(); 12 | 13 | utils::umask(0); 14 | 15 | #[cfg(unix)] 16 | let _ = catch_bootlog(); 17 | 18 | let safe_mode = crate::utils::is_safe_mode(); 19 | 20 | if safe_mode { 21 | // we should still mount modules.img to `/data/adb/modules` in safe mode 22 | // becuase we may need to operate the module dir in safe mode 23 | warn!("safe mode, skip common post-fs-data.d scripts"); 24 | } else { 25 | // Then exec common post-fs-data scripts 26 | if let Err(e) = crate::module::exec_common_scripts("post-fs-data.d", true) { 27 | warn!("exec common post-fs-data scripts failed: {}", e); 28 | } 29 | } 30 | 31 | assets::ensure_binaries().with_context(|| "Failed to extract bin assets")?; 32 | 33 | // if we are in safe mode, we should disable all modules 34 | if safe_mode { 35 | warn!("safe mode, skip post-fs-data scripts"); 36 | return Ok(()); 37 | } 38 | 39 | if let Err(e) = restorecon::restorecon() { 40 | warn!("restorecon failed: {}", e); 41 | } 42 | 43 | // load sepolicy.rule 44 | if crate::module::load_sepolicy_rule().is_err() { 45 | warn!("load sepolicy.rule failed"); 46 | } 47 | 48 | if let Err(e) = crate::profile::apply_sepolies() { 49 | warn!("apply root profile sepolicy failed: {}", e); 50 | } 51 | 52 | // exec modules post-fs-data scripts 53 | // TODO: Add timeout 54 | if let Err(e) = crate::module::exec_post_fs_data() { 55 | warn!("exec post-fs-data scripts failed: {}", e); 56 | } 57 | 58 | // load system.prop 59 | if let Err(e) = crate::module::load_system_prop() { 60 | warn!("load system.prop failed: {}", e); 61 | } 62 | 63 | std::env::set_current_dir("/").with_context(|| "failed to chdir to /")?; 64 | 65 | Ok(()) 66 | } 67 | 68 | fn run_stage(stage: &str) { 69 | utils::umask(0); 70 | 71 | if crate::utils::is_safe_mode() { 72 | warn!("safe mode, skip {stage} scripts"); 73 | return; 74 | } 75 | 76 | if let Err(e) = crate::module::exec_common_scripts(&format!("{stage}.d"), false) { 77 | warn!("Failed to exec common {stage} scripts: {e}"); 78 | } 79 | if let Err(e) = crate::module::exec_stage_scripts(stage) { 80 | warn!("Failed to exec {stage} scripts: {e}"); 81 | } 82 | } 83 | 84 | pub fn on_services() -> Result<()> { 85 | run_stage("service"); 86 | 87 | Ok(()) 88 | } 89 | 90 | pub fn on_boot_completed() -> Result<()> { 91 | crate::ksu::report_boot_complete(); 92 | info!("on_boot_completed triggered!"); 93 | 94 | run_stage("boot-completed"); 95 | 96 | Ok(()) 97 | } 98 | 99 | pub fn install() -> Result<()> { 100 | ensure_dir_exists(defs::ADB_DIR)?; 101 | std::fs::copy("/proc/self/exe", defs::DAEMON_PATH)?; 102 | restorecon::lsetfilecon(defs::DAEMON_PATH, restorecon::ADB_CON)?; 103 | // install binary assets 104 | assets::ensure_binaries().with_context(|| "Failed to extract assets")?; 105 | 106 | #[cfg(target_os = "android")] 107 | link_ksud_to_bin()?; 108 | 109 | Ok(()) 110 | } 111 | 112 | #[cfg(target_os = "android")] 113 | fn link_ksud_to_bin() -> Result<()> { 114 | let ksu_bin = PathBuf::from(defs::DAEMON_PATH); 115 | let ksu_bin_link = PathBuf::from(defs::DAEMON_LINK_PATH); 116 | if ksu_bin.exists() && !ksu_bin_link.exists() { 117 | std::os::unix::fs::symlink(&ksu_bin, &ksu_bin_link)?; 118 | } 119 | Ok(()) 120 | } 121 | 122 | #[cfg(unix)] 123 | fn catch_bootlog() -> Result<()> { 124 | use std::os::unix::process::CommandExt; 125 | use std::process::Stdio; 126 | 127 | let logdir = Path::new(defs::LOG_DIR); 128 | utils::ensure_dir_exists(logdir)?; 129 | let bootlog = logdir.join("boot.log"); 130 | let oldbootlog = logdir.join("boot.old.log"); 131 | 132 | if bootlog.exists() { 133 | std::fs::rename(&bootlog, oldbootlog)?; 134 | } 135 | 136 | let bootlog = std::fs::File::create(bootlog)?; 137 | 138 | // timeout -s 9 30s logcat > boot.log 139 | let result = unsafe { 140 | std::process::Command::new("timeout") 141 | .process_group(0) 142 | .pre_exec(|| { 143 | utils::switch_cgroups(); 144 | Ok(()) 145 | }) 146 | .arg("-s") 147 | .arg("9") 148 | .arg("30s") 149 | .arg("logcat") 150 | .stdout(Stdio::from(bootlog)) 151 | .spawn() 152 | }; 153 | 154 | if let Err(e) = result { 155 | warn!("Failed to start logcat: {:#}", e); 156 | } 157 | 158 | Ok(()) 159 | } 160 | -------------------------------------------------------------------------------- /userspace/ksud/src/main.rs: -------------------------------------------------------------------------------- 1 | mod apk_sign; 2 | mod assets; 3 | mod cli; 4 | mod debug; 5 | mod defs; 6 | mod event; 7 | mod ksu; 8 | mod module; 9 | mod profile; 10 | mod restorecon; 11 | mod sepolicy; 12 | mod utils; 13 | 14 | fn main() -> anyhow::Result<()> { 15 | cli::run() 16 | } 17 | -------------------------------------------------------------------------------- /userspace/ksud/src/module.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::wildcard_imports)] 2 | use crate::{assets, defs, sepolicy, utils::*}; 3 | 4 | use anyhow::{anyhow, Context, Result}; 5 | use is_executable::is_executable; 6 | use log::{info, warn}; 7 | use std::{env::var as env_var, path::Path, process::Command}; 8 | 9 | #[cfg(unix)] 10 | use std::os::unix::process::CommandExt; 11 | 12 | pub fn load_sepolicy_rule() -> Result<()> { 13 | let rule_file = Path::new(defs::COMMON_DIR).join("sepolicy.rule"); 14 | if !rule_file.exists() { 15 | return Ok(()); 16 | } 17 | info!("load policy: {}", &rule_file.display()); 18 | 19 | if sepolicy::apply_file(&rule_file).is_err() { 20 | warn!("Failed to load sepolicy.rule for {}", &rule_file.display()); 21 | } 22 | Ok(()) 23 | } 24 | 25 | fn exec_script>(path: T, wait: bool) -> Result<()> { 26 | info!("exec {}", path.as_ref().display()); 27 | 28 | let mut command = &mut Command::new(assets::BUSYBOX_PATH); 29 | #[cfg(unix)] 30 | { 31 | command = command.process_group(0); 32 | command = unsafe { 33 | command.pre_exec(|| { 34 | // ignore the error? 35 | switch_cgroups(); 36 | Ok(()) 37 | }) 38 | }; 39 | } 40 | command = command 41 | .current_dir(path.as_ref().parent().unwrap()) 42 | .arg("sh") 43 | .arg(path.as_ref()) 44 | .env("ASH_STANDALONE", "1") 45 | .env("KSU", "true") 46 | .env("KSU_KERNEL_VER_CODE", crate::ksu::get_version().to_string()) 47 | .env("KSU_VER_CODE", defs::VERSION_CODE) 48 | .env("KSU_VER", defs::VERSION_NAME) 49 | .env( 50 | "PATH", 51 | format!( 52 | "{}:{}", 53 | env_var("PATH").unwrap(), 54 | defs::BINARY_DIR.trim_end_matches('/') 55 | ), 56 | ); 57 | 58 | let result = if wait { 59 | command.status().map(|_| ()) 60 | } else { 61 | command.spawn().map(|_| ()) 62 | }; 63 | result.map_err(|err| anyhow!("Failed to exec {}: {}", path.as_ref().display(), err)) 64 | } 65 | 66 | pub fn exec_post_fs_data() -> Result<()> { 67 | let post_fs_data = Path::new(defs::COMMON_DIR).join("post-fs-data.sh"); 68 | if !post_fs_data.exists() { 69 | return Ok(()); 70 | } 71 | 72 | exec_script(&post_fs_data, true) 73 | } 74 | 75 | pub fn exec_common_scripts(dir: &str, wait: bool) -> Result<()> { 76 | let script_dir = Path::new(defs::ADB_DIR).join(dir); 77 | if !script_dir.exists() { 78 | info!("{} not exists, skip", script_dir.display()); 79 | return Ok(()); 80 | } 81 | 82 | let dir = std::fs::read_dir(&script_dir)?; 83 | for entry in dir.flatten() { 84 | let path = entry.path(); 85 | 86 | if !is_executable(&path) { 87 | warn!("{} is not executable, skip", path.display()); 88 | continue; 89 | } 90 | 91 | exec_script(path, wait)?; 92 | } 93 | 94 | Ok(()) 95 | } 96 | 97 | /// execute every modules' [stage].sh (service.sh, boot-completed.sh) 98 | pub fn exec_stage_scripts(stage: &str) -> Result<()> { 99 | let service = Path::new(defs::COMMON_DIR).join(format!("{stage}.sh")); 100 | if !service.exists() { 101 | return Ok(()); 102 | } 103 | 104 | exec_script(&service, false) 105 | } 106 | 107 | pub fn load_system_prop() -> Result<()> { 108 | let system_prop = Path::new(defs::COMMON_DIR).join("system.prop"); 109 | if !system_prop.exists() { 110 | return Ok(()); 111 | } 112 | info!("load system.prop"); 113 | 114 | // resetprop -n --file system.prop 115 | Command::new(assets::RESETPROP_PATH) 116 | .arg("-n") 117 | .arg("--file") 118 | .arg(&system_prop) 119 | .status() 120 | .with_context(|| format!("Failed to exec {}", system_prop.display()))?; 121 | 122 | Ok(()) 123 | } 124 | -------------------------------------------------------------------------------- /userspace/ksud/src/profile.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::ensure_dir_exists; 2 | use crate::{defs, sepolicy}; 3 | use anyhow::{Context, Result}; 4 | use std::path::Path; 5 | 6 | pub fn set_sepolicy(pkg: String, policy: String) -> Result<()> { 7 | ensure_dir_exists(defs::PROFILE_SELINUX_DIR)?; 8 | let policy_file = Path::new(defs::PROFILE_SELINUX_DIR).join(pkg); 9 | std::fs::write(&policy_file, policy)?; 10 | sepolicy::apply_file(&policy_file)?; 11 | Ok(()) 12 | } 13 | 14 | pub fn get_sepolicy(pkg: String) -> Result<()> { 15 | let policy_file = Path::new(defs::PROFILE_SELINUX_DIR).join(pkg); 16 | let policy = std::fs::read_to_string(policy_file)?; 17 | println!("{policy}"); 18 | Ok(()) 19 | } 20 | 21 | pub fn set_template(name: String, template: String) -> Result<()> { 22 | ensure_dir_exists(defs::PROFILE_TEMPLATE_DIR)?; 23 | let template_file = Path::new(defs::PROFILE_TEMPLATE_DIR).join(name); 24 | std::fs::write(template_file, template)?; 25 | Ok(()) 26 | } 27 | 28 | pub fn get_template(name: String) -> Result<()> { 29 | let template_file = Path::new(defs::PROFILE_TEMPLATE_DIR).join(name); 30 | let template = std::fs::read_to_string(template_file)?; 31 | println!("{template}"); 32 | Ok(()) 33 | } 34 | 35 | pub fn list_templates() -> Result<()> { 36 | let templates = std::fs::read_dir(defs::PROFILE_TEMPLATE_DIR)?; 37 | for template in templates { 38 | let template = template?; 39 | let template = template.file_name(); 40 | if let Some(template) = template.to_str() { 41 | println!("{template}"); 42 | }; 43 | } 44 | Ok(()) 45 | } 46 | 47 | pub fn apply_sepolies() -> Result<()> { 48 | let path = Path::new(defs::PROFILE_SELINUX_DIR); 49 | if !path.exists() { 50 | log::info!("profile sepolicy dir not exists."); 51 | return Ok(()); 52 | } 53 | 54 | let sepolicies = 55 | std::fs::read_dir(path).with_context(|| "profile sepolicy dir open failed.".to_string())?; 56 | for sepolicy in sepolicies { 57 | let Ok(sepolicy) = sepolicy else { 58 | log::info!("profile sepolicy dir read failed."); 59 | continue; 60 | }; 61 | let sepolicy = sepolicy.path(); 62 | if sepolicy::apply_file(&sepolicy).is_ok() { 63 | log::info!("profile sepolicy applied: {:?}", sepolicy); 64 | } else { 65 | log::info!("profile sepolicy apply failed: {:?}", sepolicy); 66 | } 67 | } 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /userspace/ksud/src/restorecon.rs: -------------------------------------------------------------------------------- 1 | use crate::defs; 2 | use anyhow::Result; 3 | use std::path::Path; 4 | 5 | #[cfg(any(target_os = "linux", target_os = "android"))] 6 | use anyhow::{Context, Ok}; 7 | #[cfg(any(target_os = "linux", target_os = "android"))] 8 | use extattr::{lsetxattr, Flags as XattrFlags}; 9 | 10 | pub const ADB_CON: &str = "u:object_r:adb_data_file:s0"; 11 | 12 | const SELINUX_XATTR: &str = "security.selinux"; 13 | 14 | pub fn lsetfilecon>(path: P, con: &str) -> Result<()> { 15 | #[cfg(any(target_os = "linux", target_os = "android"))] 16 | lsetxattr(&path, SELINUX_XATTR, con, XattrFlags::empty()).with_context(|| { 17 | format!( 18 | "Failed to change SELinux context for {}", 19 | path.as_ref().display() 20 | ) 21 | })?; 22 | Ok(()) 23 | } 24 | 25 | pub fn restorecon() -> Result<()> { 26 | lsetfilecon(defs::DAEMON_PATH, ADB_CON)?; 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /userspace/ksud/src/utils.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Error, Ok, Result}; 2 | use std::{ 3 | fs::{create_dir_all, write, OpenOptions}, 4 | io::Write, 5 | path::Path, 6 | }; 7 | 8 | #[allow(unused_imports)] 9 | use std::fs::{set_permissions, Permissions}; 10 | #[cfg(unix)] 11 | use std::os::unix::prelude::PermissionsExt; 12 | 13 | pub fn ensure_dir_exists>(dir: T) -> Result<()> { 14 | let result = create_dir_all(&dir).map_err(Error::from); 15 | if dir.as_ref().is_dir() { 16 | result 17 | } else if result.is_ok() { 18 | bail!("{} is not a regular directory", dir.as_ref().display()) 19 | } else { 20 | result 21 | } 22 | } 23 | 24 | pub fn ensure_binary>(path: T, contents: &[u8]) -> Result<()> { 25 | if path.as_ref().exists() { 26 | return Ok(()); 27 | } 28 | 29 | ensure_dir_exists(path.as_ref().parent().ok_or_else(|| { 30 | anyhow::anyhow!( 31 | "{} does not have parent directory", 32 | path.as_ref().to_string_lossy() 33 | ) 34 | })?)?; 35 | 36 | write(&path, contents)?; 37 | #[cfg(unix)] 38 | set_permissions(&path, Permissions::from_mode(0o755))?; 39 | Ok(()) 40 | } 41 | 42 | #[cfg(any(target_os = "linux", target_os = "android"))] 43 | pub fn getprop(prop: &str) -> Option { 44 | android_properties::getprop(prop).value() 45 | } 46 | 47 | #[cfg(not(any(target_os = "linux", target_os = "android")))] 48 | pub fn getprop(_prop: &str) -> Option { 49 | unimplemented!() 50 | } 51 | 52 | pub fn is_safe_mode() -> bool { 53 | let safemode = getprop("persist.sys.safemode") 54 | .filter(|prop| prop == "1") 55 | .is_some() 56 | || getprop("ro.sys.safemode") 57 | .filter(|prop| prop == "1") 58 | .is_some(); 59 | log::info!("safemode: {}", safemode); 60 | if safemode { 61 | return true; 62 | } 63 | let safemode = crate::ksu::check_kernel_safemode(); 64 | log::info!("kernel_safemode: {}", safemode); 65 | safemode 66 | } 67 | 68 | #[cfg(any(target_os = "linux", target_os = "android"))] 69 | pub fn switch_mnt_ns(pid: i32) -> Result<()> { 70 | use anyhow::ensure; 71 | use std::os::fd::AsRawFd; 72 | let path = format!("/proc/{pid}/ns/mnt"); 73 | let fd = std::fs::File::open(path)?; 74 | let current_dir = std::env::current_dir(); 75 | let ret = unsafe { libc::setns(fd.as_raw_fd(), libc::CLONE_NEWNS) }; 76 | if let std::result::Result::Ok(current_dir) = current_dir { 77 | let _ = std::env::set_current_dir(current_dir); 78 | } 79 | ensure!(ret == 0, "switch mnt ns failed"); 80 | Ok(()) 81 | } 82 | 83 | #[cfg(any(target_os = "linux", target_os = "android"))] 84 | pub fn unshare_mnt_ns() -> Result<()> { 85 | use anyhow::ensure; 86 | let ret = unsafe { libc::unshare(libc::CLONE_NEWNS) }; 87 | ensure!(ret == 0, "unshare mnt ns failed"); 88 | Ok(()) 89 | } 90 | 91 | fn switch_cgroup(grp: &str, pid: u32) { 92 | let path = Path::new(grp).join("cgroup.procs"); 93 | if !path.exists() { 94 | return; 95 | } 96 | 97 | let fp = OpenOptions::new().append(true).open(path); 98 | if let std::result::Result::Ok(mut fp) = fp { 99 | let _ = writeln!(fp, "{pid}"); 100 | } 101 | } 102 | 103 | pub fn switch_cgroups() { 104 | let pid = std::process::id(); 105 | switch_cgroup("/acct", pid); 106 | switch_cgroup("/dev/cg2_bpf", pid); 107 | switch_cgroup("/sys/fs/cgroup", pid); 108 | 109 | if getprop("ro.config.per_app_memcg") 110 | .filter(|prop| prop == "false") 111 | .is_none() 112 | { 113 | switch_cgroup("/dev/memcg/apps", pid); 114 | } 115 | } 116 | 117 | #[cfg(any(target_os = "linux", target_os = "android"))] 118 | pub fn umask(mask: u32) { 119 | unsafe { libc::umask(mask) }; 120 | } 121 | 122 | #[cfg(not(any(target_os = "linux", target_os = "android")))] 123 | pub fn umask(_mask: u32) { 124 | unimplemented!("umask is not supported on this platform") 125 | } 126 | -------------------------------------------------------------------------------- /userspace/su/.gitignore: -------------------------------------------------------------------------------- 1 | /obj 2 | /libs 3 | -------------------------------------------------------------------------------- /userspace/su/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := su 5 | LOCAL_SRC_FILES := su.c 6 | include $(BUILD_EXECUTABLE) 7 | -------------------------------------------------------------------------------- /userspace/su/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := arm64-v8a x86_64 2 | APP_PLATFORM := android-24 3 | APP_STL := none 4 | -------------------------------------------------------------------------------- /userspace/su/jni/su.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // This is a simple example. If you want a full-featured "su", please use "/data/adb/ksud debug su". 6 | int main(){ 7 | int32_t result = 0; 8 | prctl(0xdeadbeef, 0, 0, 0, &result); 9 | system("/system/bin/sh"); 10 | return 0; 11 | } 12 | --------------------------------------------------------------------------------